aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock2
-rw-r--r--components/compositing/compositor_thread.rs3
-rw-r--r--components/servo/lib.rs15
-rw-r--r--ports/servo/Cargo.toml2
-rw-r--r--ports/servo/browser.rs319
-rw-r--r--ports/servo/glutin_app/keyutils.rs2
-rw-r--r--ports/servo/glutin_app/mod.rs2
-rw-r--r--ports/servo/glutin_app/window.rs523
-rw-r--r--ports/servo/main.rs27
9 files changed, 478 insertions, 417 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 3b9407c176d..3bd64a6ef46 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2698,14 +2698,12 @@ dependencies = [
"libservo 0.0.1",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"msg 0.0.1",
- "net_traits 0.0.1",
"osmesa-src 17.3.1-devel (git+https://github.com/servo/osmesa-src)",
"osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"script_traits 0.0.1",
"servo-egl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"servo_config 0.0.1",
"servo_geometry 0.0.1",
- "servo_url 0.0.1",
"sig 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"style_traits 0.0.1",
"tinyfiledialogs 3.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/components/compositing/compositor_thread.rs b/components/compositing/compositor_thread.rs
index a8d37e6459c..3f43c2ba2bb 100644
--- a/components/compositing/compositor_thread.rs
+++ b/components/compositing/compositor_thread.rs
@@ -141,6 +141,8 @@ pub enum EmbedderMsg {
LoadComplete(TopLevelBrowsingContextId),
/// A pipeline panicked. First string is the reason, second one is the backtrace.
Panic(TopLevelBrowsingContextId, String, Option<String>),
+ /// Servo has shut down
+ Shutdown,
}
/// Messages from the painting thread and the constellation thread to the compositor thread.
@@ -239,6 +241,7 @@ impl Debug for EmbedderMsg {
EmbedderMsg::LoadStart(..) => write!(f, "LoadStart"),
EmbedderMsg::LoadComplete(..) => write!(f, "LoadComplete"),
EmbedderMsg::Panic(..) => write!(f, "Panic"),
+ EmbedderMsg::Shutdown => write!(f, "Shutdown"),
}
}
}
diff --git a/components/servo/lib.rs b/components/servo/lib.rs
index a54d5dd52dd..0baf5be6f6f 100644
--- a/components/servo/lib.rs
+++ b/components/servo/lib.rs
@@ -107,7 +107,7 @@ use webvr::{WebVRThread, WebVRCompositorHandler};
pub use gleam::gl;
pub use servo_config as config;
pub use servo_url as url;
-pub use msg::constellation_msg::TopLevelBrowsingContextId as BrowserId;
+pub use msg::constellation_msg::{KeyState, TopLevelBrowsingContextId as BrowserId};
/// The in-process interface to Servo.
///
@@ -372,6 +372,14 @@ impl<Window> Servo<Window> where Window: WindowMethods + 'static {
(_, ShutdownState::ShuttingDown) => {},
+ (EmbedderMsg::KeyEvent(top_level_browsing_context, ch, key, state, modified),
+ ShutdownState::NotShuttingDown) => {
+ if state == KeyState::Pressed {
+ let msg = EmbedderMsg::KeyEvent(top_level_browsing_context, ch, key, state, modified);
+ self.embedder_events.push(msg);
+ }
+ },
+
(msg, ShutdownState::NotShuttingDown) => {
self.embedder_events.push(msg);
},
@@ -383,7 +391,7 @@ impl<Window> Servo<Window> where Window: WindowMethods + 'static {
::std::mem::replace(&mut self.embedder_events, Vec::new())
}
- pub fn handle_events(&mut self, events: Vec<WindowEvent>) -> bool {
+ pub fn handle_events(&mut self, events: Vec<WindowEvent>) {
if self.compositor.receive_messages() {
self.receive_messages();
}
@@ -392,8 +400,9 @@ impl<Window> Servo<Window> where Window: WindowMethods + 'static {
}
if self.compositor.shutdown_state != ShutdownState::FinishedShuttingDown {
self.compositor.perform_updates();
+ } else {
+ self.embedder_events.push(EmbedderMsg::Shutdown);
}
- self.compositor.shutdown_state != ShutdownState::FinishedShuttingDown
}
pub fn repaint_synchronously(&mut self) {
diff --git a/ports/servo/Cargo.toml b/ports/servo/Cargo.toml
index e156fabe37b..4f42def7c53 100644
--- a/ports/servo/Cargo.toml
+++ b/ports/servo/Cargo.toml
@@ -43,11 +43,9 @@ glutin = "0.13"
libservo = {path = "../../components/servo"}
log = "0.3.5"
msg = {path = "../../components/msg"}
-net_traits = {path = "../../components/net_traits"}
script_traits = {path = "../../components/script_traits"}
servo_geometry = {path = "../../components/geometry"}
servo_config = {path = "../../components/config"}
-servo_url = {path = "../../components/url"}
style_traits = {path = "../../components/style_traits"}
tinyfiledialogs = "3.0"
webrender_api = {git = "https://github.com/servo/webrender", features = ["ipc"]}
diff --git a/ports/servo/browser.rs b/ports/servo/browser.rs
new file mode 100644
index 00000000000..25e6952c0c0
--- /dev/null
+++ b/ports/servo/browser.rs
@@ -0,0 +1,319 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use compositing::compositor_thread::EmbedderMsg;
+use compositing::windowing::{WebRenderDebugOption, WindowEvent};
+use euclid::{TypedPoint2D, TypedVector2D};
+use glutin_app::keyutils::{CMD_OR_CONTROL, CMD_OR_ALT};
+use glutin_app::window::{Window, LINE_HEIGHT};
+use msg::constellation_msg::{Key, TopLevelBrowsingContextId as BrowserId};
+use msg::constellation_msg::{KeyModifiers, KeyState, TraversalDirection};
+use script_traits::TouchEventType;
+use servo::net_traits::pub_domains::is_reg_domain;
+use servo::servo_url::ServoUrl;
+use servo_config::prefs::PREFS;
+use std::mem;
+use std::rc::Rc;
+use tinyfiledialogs;
+use webrender_api::ScrollLocation;
+
+pub struct Browser {
+ current_url: Option<ServoUrl>,
+ /// id of the top level browsing context. It is unique as tabs
+ /// are not supported yet. None until created.
+ browser_id: Option<BrowserId>,
+
+ title: Option<String>,
+ status: Option<String>,
+ favicon: Option<ServoUrl>,
+ loading_state: Option<LoadingState>,
+ window: Rc<Window>,
+ event_queue: Vec<WindowEvent>,
+ shutdown_requested: bool,
+}
+
+enum LoadingState {
+ Connecting,
+ Loading,
+ Loaded,
+}
+
+impl Browser {
+ pub fn new(window: Rc<Window>) -> Browser {
+ Browser {
+ title: None,
+ current_url: None,
+ browser_id: None,
+ status: None,
+ favicon: None,
+ loading_state: None,
+ window: window,
+ event_queue: Vec::new(),
+ shutdown_requested: false,
+ }
+ }
+
+ pub fn get_events(&mut self) -> Vec<WindowEvent> {
+ mem::replace(&mut self.event_queue, Vec::new())
+ }
+
+ pub fn set_browser_id(&mut self, browser_id: BrowserId) {
+ self.browser_id = Some(browser_id);
+ }
+
+ pub fn handle_window_events(&mut self, events: Vec<WindowEvent>) {
+ for event in events {
+ match event {
+ WindowEvent::KeyEvent(ch, key, state, mods) => {
+ self.handle_key_from_window(ch, key, state, mods);
+ },
+ event => {
+ self.event_queue.push(event);
+ }
+ }
+ }
+ }
+
+ pub fn shutdown_requested(&self) -> bool {
+ self.shutdown_requested
+ }
+
+ /// Handle key events before sending them to Servo.
+ fn handle_key_from_window(&mut self, ch: Option<char>, key: Key, state: KeyState, mods: KeyModifiers) {
+ match (mods, ch, key) {
+ (CMD_OR_CONTROL, Some('r'), _) => {
+ if let Some(true) = PREFS.get("shell.builtin-key-shortcuts.enabled").as_boolean() {
+ if let Some(id) = self.browser_id {
+ self.event_queue.push(WindowEvent::Reload(id));
+ }
+ }
+ }
+ (CMD_OR_CONTROL, Some('l'), _) => {
+ if let Some(id) = self.browser_id {
+ let url: String = if let Some(ref current_url) = self.current_url {
+ current_url.to_string()
+ } else {
+ String::from("")
+ };
+ let title = "URL or search query";
+ if let Some(input) = tinyfiledialogs::input_box(title, title, &url) {
+ if let Some(url) = sanitize_url(&input) {
+ self.event_queue.push(WindowEvent::LoadUrl(id, url));
+ }
+ }
+ }
+ }
+ (CMD_OR_CONTROL, Some('q'), _) => {
+ if let Some(true) = PREFS.get("shell.builtin-key-shortcuts.enabled").as_boolean() {
+ self.event_queue.push(WindowEvent::Quit);
+ }
+ }
+ (_, Some('3'), _) => if mods ^ KeyModifiers::CONTROL == KeyModifiers::SHIFT {
+ self.event_queue.push(WindowEvent::CaptureWebRender);
+ }
+ (KeyModifiers::CONTROL, None, Key::F10) => {
+ let event = WindowEvent::ToggleWebRenderDebug(WebRenderDebugOption::RenderTargetDebug);
+ self.event_queue.push(event);
+ }
+ (KeyModifiers::CONTROL, None, Key::F11) => {
+ let event = WindowEvent::ToggleWebRenderDebug(WebRenderDebugOption::TextureCacheDebug);
+ self.event_queue.push(event);
+ }
+ (KeyModifiers::CONTROL, None, Key::F12) => {
+ let event = WindowEvent::ToggleWebRenderDebug(WebRenderDebugOption::Profiler);
+ self.event_queue.push(event);
+ }
+ (CMD_OR_ALT, None, Key::Right) | (KeyModifiers::NONE, None, Key::NavigateForward) => {
+ if let Some(id) = self.browser_id {
+ let event = WindowEvent::Navigation(id, TraversalDirection::Forward(1));
+ self.event_queue.push(event);
+ }
+ }
+ (CMD_OR_ALT, None, Key::Left) | (KeyModifiers::NONE, None, Key::NavigateBackward) => {
+ if let Some(id) = self.browser_id {
+ let event = WindowEvent::Navigation(id, TraversalDirection::Back(1));
+ self.event_queue.push(event);
+ }
+ }
+ (KeyModifiers::NONE, None, Key::Escape) => {
+ if let Some(true) = PREFS.get("shell.builtin-key-shortcuts.enabled").as_boolean() {
+ self.event_queue.push(WindowEvent::Quit);
+ }
+ }
+ _ => {
+ let event = self.platform_handle_key(key, mods);
+ self.event_queue.push(event.unwrap_or(WindowEvent::KeyEvent(ch, key, state, mods)));
+ }
+ }
+
+ }
+
+ #[cfg(not(target_os = "win"))]
+ fn platform_handle_key(&self, key: Key, mods: KeyModifiers) -> Option<WindowEvent> {
+ match (mods, key, self.browser_id) {
+ (CMD_OR_CONTROL, Key::LeftBracket, Some(id)) => {
+ Some(WindowEvent::Navigation(id, TraversalDirection::Back(1)))
+ }
+ (CMD_OR_CONTROL, Key::RightBracket, Some(id)) => {
+ Some(WindowEvent::Navigation(id, TraversalDirection::Forward(1)))
+ }
+ _ => None
+ }
+ }
+
+ #[cfg(target_os = "win")]
+ fn platform_handle_key(&self, key: Key, mods: KeyModifiers) -> Option<WindowEvent> {
+ None
+ }
+
+ /// Handle key events after they have been handled by Servo.
+ fn handle_key_from_servo(&mut self, _: Option<BrowserId>, ch: Option<char>,
+ key: Key, _: KeyState, mods: KeyModifiers) {
+ match (mods, ch, key) {
+ (_, Some('+'), _) => {
+ if mods & !KeyModifiers::SHIFT == CMD_OR_CONTROL {
+ self.event_queue.push(WindowEvent::Zoom(1.1));
+ } else if mods & !KeyModifiers::SHIFT == CMD_OR_CONTROL | KeyModifiers::ALT {
+ self.event_queue.push(WindowEvent::PinchZoom(1.1));
+ }
+ }
+ (CMD_OR_CONTROL, Some('-'), _) => {
+ self.event_queue.push(WindowEvent::Zoom(1.0 / 1.1));
+ }
+ (_, Some('-'), _) if mods == CMD_OR_CONTROL | KeyModifiers::ALT => {
+ self.event_queue.push(WindowEvent::PinchZoom(1.0 / 1.1));
+ }
+ (CMD_OR_CONTROL, Some('0'), _) => {
+ self.event_queue.push(WindowEvent::ResetZoom);
+ }
+
+ (KeyModifiers::NONE, None, Key::PageDown) => {
+ let scroll_location = ScrollLocation::Delta(TypedVector2D::new(0.0,
+ -self.window.page_height() + 2.0 * LINE_HEIGHT));
+ self.scroll_window_from_key(scroll_location, TouchEventType::Move);
+ }
+ (KeyModifiers::NONE, None, Key::PageUp) => {
+ let scroll_location = ScrollLocation::Delta(TypedVector2D::new(0.0,
+ self.window.page_height() - 2.0 * LINE_HEIGHT));
+ self.scroll_window_from_key(scroll_location, TouchEventType::Move);
+ }
+
+ (KeyModifiers::NONE, None, Key::Home) => {
+ self.scroll_window_from_key(ScrollLocation::Start, TouchEventType::Move);
+ }
+
+ (KeyModifiers::NONE, None, Key::End) => {
+ self.scroll_window_from_key(ScrollLocation::End, TouchEventType::Move);
+ }
+
+ (KeyModifiers::NONE, None, Key::Up) => {
+ self.scroll_window_from_key(ScrollLocation::Delta(TypedVector2D::new(0.0, 3.0 * LINE_HEIGHT)),
+ TouchEventType::Move);
+ }
+ (KeyModifiers::NONE, None, Key::Down) => {
+ self.scroll_window_from_key(ScrollLocation::Delta(TypedVector2D::new(0.0, -3.0 * LINE_HEIGHT)),
+ TouchEventType::Move);
+ }
+ (KeyModifiers::NONE, None, Key::Left) => {
+ self.scroll_window_from_key(ScrollLocation::Delta(TypedVector2D::new(LINE_HEIGHT, 0.0)),
+ TouchEventType::Move);
+ }
+ (KeyModifiers::NONE, None, Key::Right) => {
+ self.scroll_window_from_key(ScrollLocation::Delta(TypedVector2D::new(-LINE_HEIGHT, 0.0)),
+ TouchEventType::Move);
+ }
+
+ _ => {
+ }
+ }
+ }
+
+ fn scroll_window_from_key(&mut self, scroll_location: ScrollLocation, phase: TouchEventType) {
+ let event = WindowEvent::Scroll(scroll_location, TypedPoint2D::zero(), phase);
+ self.event_queue.push(event);
+ }
+
+ pub fn handle_servo_events(&mut self, events: Vec<EmbedderMsg>) {
+ for event in events {
+ match event {
+ EmbedderMsg::Status(_browser_id, status) => {
+ self.status = status;
+ },
+ EmbedderMsg::ChangePageTitle(_browser_id, title) => {
+ self.title = title;
+
+ let fallback_title: String = if let Some(ref current_url) = self.current_url {
+ current_url.to_string()
+ } else {
+ String::from("Untitled")
+ };
+ let title = match self.title {
+ Some(ref title) if title.len() > 0 => &**title,
+ _ => &fallback_title,
+ };
+ let title = format!("{} - Servo", title);
+ self.window.set_title(&title);
+ }
+ EmbedderMsg::MoveTo(_browser_id, point) => {
+ self.window.set_position(point);
+ }
+ EmbedderMsg::ResizeTo(_browser_id, size) => {
+ self.window.set_inner_size(size);
+ }
+ EmbedderMsg::AllowNavigation(_browser_id, _url, response_chan) => {
+ if let Err(e) = response_chan.send(true) {
+ warn!("Failed to send allow_navigation() response: {}", e);
+ };
+ }
+ EmbedderMsg::KeyEvent(browser_id, ch, key, state, modified) => {
+ self.handle_key_from_servo(browser_id, ch, key, state, modified);
+ }
+ EmbedderMsg::SetCursor(cursor) => {
+ self.window.set_cursor(cursor);
+ }
+ EmbedderMsg::NewFavicon(_browser_id, url) => {
+ self.favicon = Some(url);
+ }
+ EmbedderMsg::HeadParsed(_browser_id, ) => {
+ self.loading_state = Some(LoadingState::Loading);
+ }
+ EmbedderMsg::HistoryChanged(_browser_id, entries, current) => {
+ self.current_url = Some(entries[current].url.clone());
+ }
+ EmbedderMsg::SetFullscreenState(_browser_id, state) => {
+ self.window.set_fullscreen(state);
+ }
+ EmbedderMsg::LoadStart(_browser_id) => {
+ self.loading_state = Some(LoadingState::Connecting);
+ }
+ EmbedderMsg::LoadComplete(_browser_id) => {
+ self.loading_state = Some(LoadingState::Loaded);
+ }
+ EmbedderMsg::Shutdown => {
+ self.shutdown_requested = true;
+ },
+ EmbedderMsg::Panic(_browser_id, _reason, _backtrace) => {
+ }
+ }
+ }
+ }
+
+}
+
+fn sanitize_url(request: &str) -> Option<ServoUrl> {
+ let request = request.trim();
+ ServoUrl::parse(&request).ok()
+ .or_else(|| {
+ if request.contains('/') || is_reg_domain(request) {
+ ServoUrl::parse(&format!("http://{}", request)).ok()
+ } else {
+ None
+ }
+ }).or_else(|| {
+ PREFS.get("shell.searchpage").as_string().and_then(|s: &str| {
+ let url = s.replace("%s", request);
+ ServoUrl::parse(&url).ok()
+ })
+ })
+}
diff --git a/ports/servo/glutin_app/keyutils.rs b/ports/servo/glutin_app/keyutils.rs
index 228491a4e5a..bce62893cde 100644
--- a/ports/servo/glutin_app/keyutils.rs
+++ b/ports/servo/glutin_app/keyutils.rs
@@ -332,7 +332,7 @@ pub fn is_printable(key_code: VirtualKeyCode) -> bool {
/// Detect if given char is default ignorable in unicode
/// http://www.unicode.org/L2/L2002/02368-default-ignorable.pdf
-pub fn is_identifier_ignorable(&self, ch: &char) -> bool {
+pub fn is_identifier_ignorable(ch: &char) -> bool {
match *ch {
'\u{0000}'...'\u{0008}' | '\u{000E}'...'\u{001F}' |
'\u{007F}'...'\u{0084}' | '\u{0086}'...'\u{009F}' |
diff --git a/ports/servo/glutin_app/mod.rs b/ports/servo/glutin_app/mod.rs
index 9f1107228bc..a7e8ac8f5e8 100644
--- a/ports/servo/glutin_app/mod.rs
+++ b/ports/servo/glutin_app/mod.rs
@@ -4,7 +4,7 @@
//! A simple application that uses glutin to open a window for Servo to display in.
-mod keyutils;
+pub mod keyutils;
pub mod window;
use servo_config::opts;
diff --git a/ports/servo/glutin_app/window.rs b/ports/servo/glutin_app/window.rs
index 82020e26bbe..7fb77d27273 100644
--- a/ports/servo/glutin_app/window.rs
+++ b/ports/servo/glutin_app/window.rs
@@ -4,26 +4,20 @@
//! A windowing implementation using glutin.
-use compositing::compositor_thread::{EmbedderMsg, EventLoopWaker};
+use compositing::compositor_thread::EventLoopWaker;
use compositing::windowing::{AnimationState, MouseWindowEvent, WindowEvent};
-use compositing::windowing::{EmbedderCoordinates, WebRenderDebugOption, WindowMethods};
+use compositing::windowing::{EmbedderCoordinates, WindowMethods};
use euclid::{Length, TypedPoint2D, TypedVector2D, TypedScale, TypedSize2D};
#[cfg(target_os = "windows")]
use gdi32;
use gleam::gl;
-use glutin;
-use glutin::{Api, GlContext, GlRequest};
-use msg::constellation_msg::{self, Key, TopLevelBrowsingContextId as BrowserId};
-use msg::constellation_msg::{KeyModifiers, KeyState, TraversalDirection};
-use net_traits::pub_domains::is_reg_domain;
+use glutin::{self, Api, GlContext, GlRequest};
+use msg::constellation_msg::{Key, KeyState};
#[cfg(any(target_os = "linux", target_os = "macos"))]
use osmesa_sys;
-use script_traits::{LoadData, TouchEventType};
-use servo::ipc_channel::ipc::IpcSender;
+use script_traits::TouchEventType;
use servo_config::opts;
-use servo_config::prefs::PREFS;
use servo_geometry::DeviceIndependentPixel;
-use servo_url::ServoUrl;
use std::cell::{Cell, RefCell};
#[cfg(any(target_os = "linux", target_os = "macos"))]
use std::ffi::CString;
@@ -36,8 +30,7 @@ use std::thread;
use std::time;
use style_traits::DevicePixel;
use style_traits::cursor::CursorKind;
-use super::keyutils::{self, CMD_OR_ALT, CMD_OR_CONTROL, GlutinKeyModifiers};
-use tinyfiledialogs;
+use super::keyutils::{self, GlutinKeyModifiers};
#[cfg(target_os = "windows")]
use user32;
use webrender_api::{DeviceIntPoint, DeviceUintRect, DeviceUintSize, ScrollLocation};
@@ -50,7 +43,7 @@ use winit::os::macos::{ActivationPolicy, WindowBuilderExt};
// This should vary by zoom level and maybe actual text size (focused or under cursor)
-const LINE_HEIGHT: f32 = 38.0;
+pub const LINE_HEIGHT: f32 = 38.0;
const MULTISAMPLES: u16 = 16;
@@ -154,25 +147,14 @@ pub struct Window {
kind: WindowKind,
screen_size: TypedSize2D<u32, DeviceIndependentPixel>,
inner_size: Cell<TypedSize2D<u32, DeviceIndependentPixel>>,
-
mouse_down_button: Cell<Option<winit::MouseButton>>,
mouse_down_point: Cell<TypedPoint2D<i32, DevicePixel>>,
event_queue: RefCell<Vec<WindowEvent>>,
-
- /// id of the top level browsing context. It is unique as tabs
- /// are not supported yet. None until created.
- browser_id: Cell<Option<BrowserId>>,
-
mouse_pos: Cell<TypedPoint2D<i32, DevicePixel>>,
key_modifiers: Cell<GlutinKeyModifiers>,
- current_url: RefCell<Option<ServoUrl>>,
-
- last_pressed_key: Cell<Option<constellation_msg::Key>>,
-
+ last_pressed_key: Cell<Option<Key>>,
animation_state: Cell<AnimationState>,
-
fullscreen: Cell<bool>,
-
gl: Rc<gl::Gl>,
suspended: Cell<bool>,
}
@@ -191,10 +173,6 @@ fn window_creation_scale_factor() -> TypedScale<f32, DeviceIndependentPixel, Dev
impl Window {
- pub fn set_browser_id(&self, browser_id: BrowserId) {
- self.browser_id.set(Some(browser_id));
- }
-
pub fn new(is_foreground: bool,
window_size: TypedSize2D<u32, DeviceIndependentPixel>) -> Rc<Window> {
let win_size: DeviceUintSize = (window_size.to_f32() * window_creation_scale_factor()).to_u32();
@@ -291,11 +269,8 @@ impl Window {
mouse_down_button: Cell::new(None),
mouse_down_point: Cell::new(TypedPoint2D::new(0, 0)),
- browser_id: Cell::new(None),
-
mouse_pos: Cell::new(TypedPoint2D::new(0, 0)),
key_modifiers: Cell::new(GlutinKeyModifiers::empty()),
- current_url: RefCell::new(None),
last_pressed_key: Cell::new(None),
gl: gl.clone(),
@@ -311,6 +286,107 @@ impl Window {
Rc::new(window)
}
+ pub fn get_events(&self) -> Vec<WindowEvent> {
+ mem::replace(&mut *self.event_queue.borrow_mut(), Vec::new())
+ }
+
+ pub fn page_height(&self) -> f32 {
+ let dpr = self.hidpi_factor();
+ match self.kind {
+ WindowKind::Window(ref window, _) => {
+ let (_, height) = window.get_inner_size().expect("Failed to get window inner size.");
+ height as f32 * dpr.get()
+ },
+ WindowKind::Headless(ref context) => {
+ context.height as f32 * dpr.get()
+ }
+ }
+ }
+
+ pub fn set_title(&self, title: &str) {
+ if let WindowKind::Window(ref window, _) = self.kind {
+ window.set_title(title);
+ }
+ }
+
+ pub fn set_inner_size(&self, size: DeviceUintSize) {
+ if let WindowKind::Window(ref window, _) = self.kind {
+ let size = size.to_f32() / self.hidpi_factor();
+ window.set_inner_size(size.width as u32, size.height as u32)
+ }
+ }
+
+ pub fn set_position(&self, point: DeviceIntPoint) {
+ if let WindowKind::Window(ref window, _) = self.kind {
+ let point = point.to_f32() / self.hidpi_factor();
+ window.set_position(point.x as i32, point.y as i32)
+ }
+ }
+
+ pub fn set_fullscreen(&self, state: bool) {
+ match self.kind {
+ WindowKind::Window(ref window, ..) => {
+ if self.fullscreen.get() != state {
+ window.set_fullscreen(None);
+ }
+ },
+ WindowKind::Headless(..) => {}
+ }
+ self.fullscreen.set(state);
+ }
+
+ fn is_animating(&self) -> bool {
+ self.animation_state.get() == AnimationState::Animating && !self.suspended.get()
+ }
+
+ pub fn run<T>(&self, mut servo_callback: T) where T: FnMut() -> bool {
+ match self.kind {
+ WindowKind::Window(_, ref events_loop) => {
+ let mut stop = false;
+ loop {
+ if self.is_animating() {
+ // We block on compositing (servo_callback ends up calling swap_buffers)
+ events_loop.borrow_mut().poll_events(|e| {
+ self.glutin_event_to_servo_event(e);
+ });
+ stop = servo_callback();
+ } else {
+ // We block on glutin's event loop (window events)
+ events_loop.borrow_mut().run_forever(|e| {
+ self.glutin_event_to_servo_event(e);
+ if !self.event_queue.borrow().is_empty() {
+ if !self.suspended.get() {
+ stop = servo_callback();
+ }
+ }
+ if stop || self.is_animating() {
+ winit::ControlFlow::Break
+ } else {
+ winit::ControlFlow::Continue
+ }
+ });
+ }
+ if stop {
+ break;
+ }
+ }
+ }
+ WindowKind::Headless(..) => {
+ loop {
+ // Sleep the main thread to avoid using 100% CPU
+ // This can be done better, see comments in #18777
+ if self.event_queue.borrow().is_empty() {
+ thread::sleep(time::Duration::from_millis(5));
+ }
+ let stop = servo_callback();
+ if stop {
+ break;
+ }
+ }
+ }
+ }
+ }
+
#[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
fn gl_version() -> GlRequest {
return GlRequest::Specific(Api::OpenGl, (3, 2));
@@ -379,7 +455,7 @@ impl Window {
}
}
- fn handle_window_event(&self, event: winit::Event) {
+ fn glutin_event_to_servo_event(&self, event: winit::Event) {
match event {
Event::WindowEvent {
event: winit::WindowEvent::ReceivedCharacter(ch),
@@ -416,13 +492,22 @@ impl Window {
event: winit::WindowEvent::MouseWheel { delta, phase, .. },
..
} => {
- let (dx, dy) = match delta {
+ let (mut dx, mut dy) = match delta {
MouseScrollDelta::LineDelta(dx, dy) => (dx, dy * LINE_HEIGHT),
MouseScrollDelta::PixelDelta(dx, dy) => (dx, dy),
};
+ // Scroll events snap to the major axis of movement, with vertical
+ // preferred over horizontal.
+ if dy.abs() >= dx.abs() {
+ dx = 0.0;
+ } else {
+ dy = 0.0;
+ }
+
let scroll_location = ScrollLocation::Delta(TypedVector2D::new(dx, dy));
let phase = glutin_phase_to_touch_event_type(phase);
- self.scroll_window(scroll_location, phase);
+ let event = WindowEvent::Scroll(scroll_location, self.mouse_pos.get(), phase);
+ self.event_queue.borrow_mut().push(event);
},
Event::WindowEvent {
event: winit::WindowEvent::Touch(touch),
@@ -481,22 +566,6 @@ impl Window {
self.key_modifiers.set(modifiers);
}
- /// Helper function to send a scroll event.
- fn scroll_window(&self, mut scroll_location: ScrollLocation, phase: TouchEventType) {
- // Scroll events snap to the major axis of movement, with vertical
- // preferred over horizontal.
- if let ScrollLocation::Delta(ref mut delta) = scroll_location {
- if delta.y.abs() >= delta.x.abs() {
- delta.x = 0.0;
- } else {
- delta.y = 0.0;
- }
- }
-
- let event = WindowEvent::Scroll(scroll_location, self.mouse_pos.get(), phase);
- self.event_queue.borrow_mut().push(event);
- }
-
/// Helper function to handle a click
fn handle_mouse(&self, button: winit::MouseButton,
action: winit::ElementState,
@@ -532,115 +601,6 @@ impl Window {
self.event_queue.borrow_mut().push(WindowEvent::MouseWindowEventClass(event));
}
- pub fn get_events(&self) -> Vec<WindowEvent> {
- mem::replace(&mut *self.event_queue.borrow_mut(), Vec::new())
- }
-
- pub fn handle_servo_events(&self, events: Vec<EmbedderMsg>) {
- for event in events {
- match event {
- EmbedderMsg::Status(top_level_browsing_context, message) => self.status(top_level_browsing_context, message),
- EmbedderMsg::ChangePageTitle(top_level_browsing_context, title) => self.set_page_title(top_level_browsing_context, title),
- EmbedderMsg::MoveTo(top_level_browsing_context, point) => self.set_position(top_level_browsing_context, point),
- EmbedderMsg::ResizeTo(top_level_browsing_context, size) => self.set_inner_size(top_level_browsing_context, size),
- EmbedderMsg::AllowNavigation(top_level_browsing_context, url, response_chan) => self.allow_navigation(top_level_browsing_context, url, response_chan),
- EmbedderMsg::KeyEvent(top_level_browsing_context, ch, key, state, modified) => self.handle_key(top_level_browsing_context, ch, key, state, modified),
- EmbedderMsg::SetCursor(cursor) => self.set_cursor(cursor),
- EmbedderMsg::NewFavicon(top_level_browsing_context, url) => self.set_favicon(top_level_browsing_context, url),
- EmbedderMsg::HeadParsed(top_level_browsing_context, ) => self.head_parsed(top_level_browsing_context, ),
- EmbedderMsg::HistoryChanged(top_level_browsing_context, entries, current) => self.history_changed(top_level_browsing_context, entries, current),
- EmbedderMsg::SetFullscreenState(top_level_browsing_context, state) => self.set_fullscreen_state(top_level_browsing_context, state),
- EmbedderMsg::LoadStart(top_level_browsing_context) => self.load_start(top_level_browsing_context),
- EmbedderMsg::LoadComplete(top_level_browsing_context) => self.load_end(top_level_browsing_context),
- EmbedderMsg::Panic(top_level_browsing_context, reason, backtrace) => self.handle_panic(top_level_browsing_context, reason, backtrace),
- }
- }
- }
-
- fn is_animating(&self) -> bool {
- self.animation_state.get() == AnimationState::Animating && !self.suspended.get()
- }
-
- pub fn run<T>(&self, mut servo_callback: T) where T: FnMut() -> bool {
- match self.kind {
- WindowKind::Window(_, ref events_loop) => {
- let mut stop = false;
- loop {
- if self.is_animating() {
- // We block on compositing (servo_callback ends up calling swap_buffers)
- events_loop.borrow_mut().poll_events(|e| {
- self.handle_window_event(e);
- });
- stop = servo_callback();
- } else {
- // We block on glutin's event loop (window events)
- events_loop.borrow_mut().run_forever(|e| {
- self.handle_window_event(e);
- if !self.event_queue.borrow().is_empty() {
- if !self.suspended.get() {
- stop = servo_callback();
- }
- }
- if stop || self.is_animating() {
- winit::ControlFlow::Break
- } else {
- winit::ControlFlow::Continue
- }
- });
- }
- if stop {
- break;
- }
- }
- }
- WindowKind::Headless(..) => {
- loop {
- // Sleep the main thread to avoid using 100% CPU
- // This can be done better, see comments in #18777
- if self.event_queue.borrow().is_empty() {
- thread::sleep(time::Duration::from_millis(5));
- }
- let stop = servo_callback();
- if stop {
- break;
- }
- }
- }
- }
- }
-
- #[cfg(not(target_os = "win"))]
- fn platform_handle_key(&self, key: Key, mods: constellation_msg::KeyModifiers, browser_id: BrowserId) {
- match (mods, key) {
- (CMD_OR_CONTROL, Key::LeftBracket) => {
- let event = WindowEvent::Navigation(browser_id, TraversalDirection::Back(1));
- self.event_queue.borrow_mut().push(event);
- }
- (CMD_OR_CONTROL, Key::RightBracket) => {
- let event = WindowEvent::Navigation(browser_id, TraversalDirection::Forward(1));
- self.event_queue.borrow_mut().push(event);
- }
- _ => {}
- }
- }
-
- #[cfg(target_os = "win")]
- fn platform_handle_key(&self, key: Key, mods: constellation_msg::KeyModifiers, browser_id: BrowserId) {
- }
-
- fn page_height(&self) -> f32 {
- let dpr = self.hidpi_factor();
- match self.kind {
- WindowKind::Window(ref window, _) => {
- let (_, height) = window.get_inner_size().expect("Failed to get window inner size.");
- height as f32 * dpr.get()
- },
- WindowKind::Headless(ref context) => {
- context.height as f32 * dpr.get()
- }
- }
- }
-
#[cfg(not(target_os = "windows"))]
fn hidpi_factor(&self) -> TypedScale<f32, DeviceIndependentPixel, DevicePixel> {
match self.kind {
@@ -660,84 +620,8 @@ impl Window {
TypedScale::new(ppi as f32 / 96.0)
}
- fn set_inner_size(&self, _: BrowserId, size: DeviceUintSize) {
- match self.kind {
- WindowKind::Window(ref window, ..) => {
- let size = size.to_f32() / self.hidpi_factor();
- window.set_inner_size(size.width as u32, size.height as u32)
- }
- WindowKind::Headless(..) => {}
- }
- }
-
- fn set_position(&self, _: BrowserId, point: DeviceIntPoint) {
- match self.kind {
- WindowKind::Window(ref window, ..) => {
- let point = point.to_f32() / self.hidpi_factor();
- window.set_position(point.x as i32, point.y as i32)
- }
- WindowKind::Headless(..) => {}
- }
- }
-
- fn set_fullscreen_state(&self, _: BrowserId, state: bool) {
- match self.kind {
- WindowKind::Window(ref window, ..) => {
- if self.fullscreen.get() != state {
- window.set_fullscreen(None);
- }
- },
- WindowKind::Headless(..) => {}
- }
- self.fullscreen.set(state);
- }
-
- fn set_page_title(&self, _: BrowserId, title: Option<String>) {
- match self.kind {
- WindowKind::Window(ref window, ..) => {
- let fallback_title: String = if let Some(ref current_url) = *self.current_url.borrow() {
- current_url.to_string()
- } else {
- String::from("Untitled")
- };
-
- let title = match title {
- Some(ref title) if title.len() > 0 => &**title,
- _ => &fallback_title,
- };
- let title = format!("{} - Servo", title);
- window.set_title(&title);
- }
- WindowKind::Headless(..) => {}
- }
- }
-
- fn status(&self, _: BrowserId, _: Option<String>) {
- }
-
- fn load_start(&self, _: BrowserId) {
- }
-
- fn load_end(&self, _: BrowserId) {
- if opts::get().no_native_titlebar {
- match self.kind {
- WindowKind::Window(ref window, ..) => {
- window.show();
- }
- WindowKind::Headless(..) => {}
- }
- }
- }
-
- fn history_changed(&self, _: BrowserId, history: Vec<LoadData>, current: usize) {
- *self.current_url.borrow_mut() = Some(history[current].url.clone());
- }
-
- fn head_parsed(&self, _: BrowserId) {
- }
-
/// Has no effect on Android.
- fn set_cursor(&self, cursor: CursorKind) {
+ pub fn set_cursor(&self, cursor: CursorKind) {
match self.kind {
WindowKind::Window(ref window, ..) => {
use winit::MouseCursor;
@@ -785,152 +669,6 @@ impl Window {
WindowKind::Headless(..) => {}
}
}
-
- fn set_favicon(&self, _: BrowserId, _: ServoUrl) {
- }
-
- /// Helper function to handle keyboard events.
- fn handle_key(&self, _: Option<BrowserId>, ch: Option<char>, key: Key, state: KeyState, mods: constellation_msg::KeyModifiers) {
- if state == KeyState::Pressed {
- return;
- }
- let browser_id = match self.browser_id.get() {
- Some(id) => id,
- None => { unreachable!("Can't get keys without a browser"); }
- };
- match (mods, ch, key) {
- (_, Some('+'), _) => {
- if mods & !KeyModifiers::SHIFT == CMD_OR_CONTROL {
- self.event_queue.borrow_mut().push(WindowEvent::Zoom(1.1));
- } else if mods & !KeyModifiers::SHIFT == CMD_OR_CONTROL | KeyModifiers::ALT {
- self.event_queue.borrow_mut().push(WindowEvent::PinchZoom(1.1));
- }
- }
- (CMD_OR_CONTROL, Some('-'), _) => {
- self.event_queue.borrow_mut().push(WindowEvent::Zoom(1.0 / 1.1));
- }
- (_, Some('-'), _) if mods == CMD_OR_CONTROL | KeyModifiers::ALT => {
- self.event_queue.borrow_mut().push(WindowEvent::PinchZoom(1.0 / 1.1));
- }
- (CMD_OR_CONTROL, Some('0'), _) => {
- self.event_queue.borrow_mut().push(WindowEvent::ResetZoom);
- }
-
- (KeyModifiers::NONE, None, Key::NavigateForward) => {
- let event = WindowEvent::Navigation(browser_id, TraversalDirection::Forward(1));
- self.event_queue.borrow_mut().push(event);
- }
- (KeyModifiers::NONE, None, Key::NavigateBackward) => {
- let event = WindowEvent::Navigation(browser_id, TraversalDirection::Back(1));
- self.event_queue.borrow_mut().push(event);
- }
-
- (KeyModifiers::NONE, None, Key::Escape) => {
- if let Some(true) = PREFS.get("shell.builtin-key-shortcuts.enabled").as_boolean() {
- self.event_queue.borrow_mut().push(WindowEvent::Quit);
- }
- }
-
- (CMD_OR_ALT, None, Key::Right) => {
- let event = WindowEvent::Navigation(browser_id, TraversalDirection::Forward(1));
- self.event_queue.borrow_mut().push(event);
- }
- (CMD_OR_ALT, None, Key::Left) => {
- let event = WindowEvent::Navigation(browser_id, TraversalDirection::Back(1));
- self.event_queue.borrow_mut().push(event);
- }
-
- (KeyModifiers::NONE, None, Key::PageDown) => {
- let scroll_location = ScrollLocation::Delta(TypedVector2D::new(0.0,
- -self.page_height() + 2.0 * LINE_HEIGHT));
- self.scroll_window(scroll_location,
- TouchEventType::Move);
- }
- (KeyModifiers::NONE, None, Key::PageUp) => {
- let scroll_location = ScrollLocation::Delta(TypedVector2D::new(0.0,
- self.page_height() - 2.0 * LINE_HEIGHT));
- self.scroll_window(scroll_location,
- TouchEventType::Move);
- }
-
- (KeyModifiers::NONE, None, Key::Home) => {
- self.scroll_window(ScrollLocation::Start, TouchEventType::Move);
- }
-
- (KeyModifiers::NONE, None, Key::End) => {
- self.scroll_window(ScrollLocation::End, TouchEventType::Move);
- }
-
- (KeyModifiers::NONE, None, Key::Up) => {
- self.scroll_window(ScrollLocation::Delta(TypedVector2D::new(0.0, 3.0 * LINE_HEIGHT)),
- TouchEventType::Move);
- }
- (KeyModifiers::NONE, None, Key::Down) => {
- self.scroll_window(ScrollLocation::Delta(TypedVector2D::new(0.0, -3.0 * LINE_HEIGHT)),
- TouchEventType::Move);
- }
- (KeyModifiers::NONE, None, Key::Left) => {
- self.scroll_window(ScrollLocation::Delta(TypedVector2D::new(LINE_HEIGHT, 0.0)), TouchEventType::Move);
- }
- (KeyModifiers::NONE, None, Key::Right) => {
- self.scroll_window(ScrollLocation::Delta(TypedVector2D::new(-LINE_HEIGHT, 0.0)), TouchEventType::Move);
- }
- (CMD_OR_CONTROL, Some('r'), _) => {
- if let Some(true) = PREFS.get("shell.builtin-key-shortcuts.enabled").as_boolean() {
- self.event_queue.borrow_mut().push(WindowEvent::Reload(browser_id));
- }
- }
- (CMD_OR_CONTROL, Some('l'), _) => {
- if let Some(true) = PREFS.get("shell.builtin-key-shortcuts.enabled").as_boolean() {
- let url: String = if let Some(ref url) = *self.current_url.borrow() {
- url.to_string()
- } else {
- String::from("")
- };
- let title = "URL or search query";
- if let Some(input) = tinyfiledialogs::input_box(title, title, &url) {
- if let Some(url) = sanitize_url(&input) {
- self.event_queue.borrow_mut().push(WindowEvent::LoadUrl(browser_id, url));
- }
- }
- }
- }
- (CMD_OR_CONTROL, Some('q'), _) => {
- if let Some(true) = PREFS.get("shell.builtin-key-shortcuts.enabled").as_boolean() {
- self.event_queue.borrow_mut().push(WindowEvent::Quit);
- }
- }
- (_, Some('3'), _) => if mods ^ KeyModifiers::CONTROL == KeyModifiers::SHIFT {
- self.event_queue.borrow_mut().push(WindowEvent::CaptureWebRender);
- }
- (KeyModifiers::CONTROL, None, Key::F10) => {
- let event = WindowEvent::ToggleWebRenderDebug(WebRenderDebugOption::RenderTargetDebug);
- self.event_queue.borrow_mut().push(event);
- }
- (KeyModifiers::CONTROL, None, Key::F11) => {
- let event = WindowEvent::ToggleWebRenderDebug(WebRenderDebugOption::TextureCacheDebug);
- self.event_queue.borrow_mut().push(event);
- }
- (KeyModifiers::CONTROL, None, Key::F12) => {
- let event = WindowEvent::ToggleWebRenderDebug(WebRenderDebugOption::Profiler);
- self.event_queue.borrow_mut().push(event);
- }
-
- _ => {
- self.platform_handle_key(key, mods, browser_id);
- }
- }
- }
-
- fn allow_navigation(&self, _: BrowserId, _: ServoUrl, response_chan: IpcSender<bool>) {
- if let Err(e) = response_chan.send(true) {
- warn!("Failed to send allow_navigation() response: {}", e);
- };
- }
-
- fn handle_panic(&self, _: BrowserId, _reason: String, _backtrace: Option<String>) {
- // Nothing to do here yet. The crash has already been reported on the console.
- }
}
impl WindowMethods for Window {
@@ -1046,20 +784,3 @@ fn glutin_phase_to_touch_event_type(phase: TouchPhase) -> TouchEventType {
TouchPhase::Cancelled => TouchEventType::Cancel,
}
}
-
-fn sanitize_url(request: &str) -> Option<ServoUrl> {
- let request = request.trim();
- ServoUrl::parse(&request).ok()
- .or_else(|| {
- if request.contains('/') || is_reg_domain(request) {
- ServoUrl::parse(&format!("http://{}", request)).ok()
- } else {
- None
- }
- }).or_else(|| {
- PREFS.get("shell.searchpage").as_string().and_then(|s: &str| {
- let url = s.replace("%s", request);
- ServoUrl::parse(&url).ok()
- })
- })
-}
diff --git a/ports/servo/main.rs b/ports/servo/main.rs
index d440bf53bcf..8fa036a6658 100644
--- a/ports/servo/main.rs
+++ b/ports/servo/main.rs
@@ -29,13 +29,11 @@ extern crate glutin;
// The window backed by glutin
#[macro_use] extern crate log;
extern crate msg;
-extern crate net_traits;
#[cfg(any(target_os = "linux", target_os = "macos"))] extern crate osmesa_sys;
extern crate script_traits;
extern crate servo;
extern crate servo_config;
extern crate servo_geometry;
-extern crate servo_url;
#[cfg(all(feature = "unstable", not(target_os = "android")))]
#[macro_use]
extern crate sig;
@@ -63,6 +61,8 @@ use std::panic;
use std::process;
use std::thread;
+mod browser;
+
pub mod platform {
#[cfg(target_os = "macos")]
pub use platform::macos::deinit;
@@ -163,6 +163,8 @@ fn main() {
let window = glutin_app::create_window();
+ let mut browser = browser::Browser::new(window.clone());
+
// If the url is not provided, we fallback to the homepage in PREFS,
// or a blank page in case the homepage is not set either.
let cwd = env::current_dir().unwrap();
@@ -178,23 +180,34 @@ fn main() {
let (sender, receiver) = ipc::channel().unwrap();
servo.handle_events(vec![WindowEvent::NewBrowser(target_url, sender)]);
let browser_id = receiver.recv().unwrap();
- window.set_browser_id(browser_id);
+ browser.set_browser_id(browser_id);
servo.handle_events(vec![WindowEvent::SelectBrowser(browser_id)]);
servo.setup_logging();
window.run(|| {
let win_events = window.get_events();
+ let servo_events = servo.get_events();
+
+ // FIXME: this could be handled by Servo. We don't need
+ // a repaint_synchronously function exposed.
let need_resize = win_events.iter().any(|e| match *e {
WindowEvent::Resize => true,
- _ => false
+ _ => false,
});
- let stop = !servo.handle_events(win_events);
+
+ browser.handle_servo_events(servo_events);
+ browser.handle_window_events(win_events);
+
+ if browser.shutdown_requested() {
+ return true;
+ }
+
+ servo.handle_events(browser.get_events());
if need_resize {
servo.repaint_synchronously();
}
- window.handle_servo_events(servo.get_events());
- stop
+ false
});
servo.deinit();