split window code and browser code in two different files

This commit is contained in:
Paul Rouget 2018-02-22 10:22:53 +01:00
parent 34c2150e4f
commit 1cdba8843d
9 changed files with 478 additions and 417 deletions

2
Cargo.lock generated
View file

@ -2698,14 +2698,12 @@ dependencies = [
"libservo 0.0.1", "libservo 0.0.1",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"msg 0.0.1", "msg 0.0.1",
"net_traits 0.0.1",
"osmesa-src 17.3.1-devel (git+https://github.com/servo/osmesa-src)", "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)", "osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"script_traits 0.0.1", "script_traits 0.0.1",
"servo-egl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "servo-egl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"servo_config 0.0.1", "servo_config 0.0.1",
"servo_geometry 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)", "sig 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"style_traits 0.0.1", "style_traits 0.0.1",
"tinyfiledialogs 3.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "tinyfiledialogs 3.3.5 (registry+https://github.com/rust-lang/crates.io-index)",

View file

@ -141,6 +141,8 @@ pub enum EmbedderMsg {
LoadComplete(TopLevelBrowsingContextId), LoadComplete(TopLevelBrowsingContextId),
/// A pipeline panicked. First string is the reason, second one is the backtrace. /// A pipeline panicked. First string is the reason, second one is the backtrace.
Panic(TopLevelBrowsingContextId, String, Option<String>), Panic(TopLevelBrowsingContextId, String, Option<String>),
/// Servo has shut down
Shutdown,
} }
/// Messages from the painting thread and the constellation thread to the compositor thread. /// 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::LoadStart(..) => write!(f, "LoadStart"),
EmbedderMsg::LoadComplete(..) => write!(f, "LoadComplete"), EmbedderMsg::LoadComplete(..) => write!(f, "LoadComplete"),
EmbedderMsg::Panic(..) => write!(f, "Panic"), EmbedderMsg::Panic(..) => write!(f, "Panic"),
EmbedderMsg::Shutdown => write!(f, "Shutdown"),
} }
} }
} }

View file

@ -107,7 +107,7 @@ use webvr::{WebVRThread, WebVRCompositorHandler};
pub use gleam::gl; pub use gleam::gl;
pub use servo_config as config; pub use servo_config as config;
pub use servo_url as url; 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. /// The in-process interface to Servo.
/// ///
@ -372,6 +372,14 @@ impl<Window> Servo<Window> where Window: WindowMethods + 'static {
(_, ShutdownState::ShuttingDown) => {}, (_, 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) => { (msg, ShutdownState::NotShuttingDown) => {
self.embedder_events.push(msg); 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()) ::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() { if self.compositor.receive_messages() {
self.receive_messages(); self.receive_messages();
} }
@ -392,8 +400,9 @@ impl<Window> Servo<Window> where Window: WindowMethods + 'static {
} }
if self.compositor.shutdown_state != ShutdownState::FinishedShuttingDown { if self.compositor.shutdown_state != ShutdownState::FinishedShuttingDown {
self.compositor.perform_updates(); self.compositor.perform_updates();
} else {
self.embedder_events.push(EmbedderMsg::Shutdown);
} }
self.compositor.shutdown_state != ShutdownState::FinishedShuttingDown
} }
pub fn repaint_synchronously(&mut self) { pub fn repaint_synchronously(&mut self) {

View file

@ -43,11 +43,9 @@ glutin = "0.13"
libservo = {path = "../../components/servo"} libservo = {path = "../../components/servo"}
log = "0.3.5" log = "0.3.5"
msg = {path = "../../components/msg"} msg = {path = "../../components/msg"}
net_traits = {path = "../../components/net_traits"}
script_traits = {path = "../../components/script_traits"} script_traits = {path = "../../components/script_traits"}
servo_geometry = {path = "../../components/geometry"} servo_geometry = {path = "../../components/geometry"}
servo_config = {path = "../../components/config"} servo_config = {path = "../../components/config"}
servo_url = {path = "../../components/url"}
style_traits = {path = "../../components/style_traits"} style_traits = {path = "../../components/style_traits"}
tinyfiledialogs = "3.0" tinyfiledialogs = "3.0"
webrender_api = {git = "https://github.com/servo/webrender", features = ["ipc"]} webrender_api = {git = "https://github.com/servo/webrender", features = ["ipc"]}

319
ports/servo/browser.rs Normal file
View file

@ -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()
})
})
}

View file

@ -332,7 +332,7 @@ pub fn is_printable(key_code: VirtualKeyCode) -> bool {
/// Detect if given char is default ignorable in unicode /// Detect if given char is default ignorable in unicode
/// http://www.unicode.org/L2/L2002/02368-default-ignorable.pdf /// 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 { match *ch {
'\u{0000}'...'\u{0008}' | '\u{000E}'...'\u{001F}' | '\u{0000}'...'\u{0008}' | '\u{000E}'...'\u{001F}' |
'\u{007F}'...'\u{0084}' | '\u{0086}'...'\u{009F}' | '\u{007F}'...'\u{0084}' | '\u{0086}'...'\u{009F}' |

View file

@ -4,7 +4,7 @@
//! A simple application that uses glutin to open a window for Servo to display in. //! A simple application that uses glutin to open a window for Servo to display in.
mod keyutils; pub mod keyutils;
pub mod window; pub mod window;
use servo_config::opts; use servo_config::opts;

View file

@ -4,26 +4,20 @@
//! A windowing implementation using glutin. //! 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::{AnimationState, MouseWindowEvent, WindowEvent};
use compositing::windowing::{EmbedderCoordinates, WebRenderDebugOption, WindowMethods}; use compositing::windowing::{EmbedderCoordinates, WindowMethods};
use euclid::{Length, TypedPoint2D, TypedVector2D, TypedScale, TypedSize2D}; use euclid::{Length, TypedPoint2D, TypedVector2D, TypedScale, TypedSize2D};
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
use gdi32; use gdi32;
use gleam::gl; use gleam::gl;
use glutin; use glutin::{self, Api, GlContext, GlRequest};
use glutin::{Api, GlContext, GlRequest}; use msg::constellation_msg::{Key, KeyState};
use msg::constellation_msg::{self, Key, TopLevelBrowsingContextId as BrowserId};
use msg::constellation_msg::{KeyModifiers, KeyState, TraversalDirection};
use net_traits::pub_domains::is_reg_domain;
#[cfg(any(target_os = "linux", target_os = "macos"))] #[cfg(any(target_os = "linux", target_os = "macos"))]
use osmesa_sys; use osmesa_sys;
use script_traits::{LoadData, TouchEventType}; use script_traits::TouchEventType;
use servo::ipc_channel::ipc::IpcSender;
use servo_config::opts; use servo_config::opts;
use servo_config::prefs::PREFS;
use servo_geometry::DeviceIndependentPixel; use servo_geometry::DeviceIndependentPixel;
use servo_url::ServoUrl;
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
#[cfg(any(target_os = "linux", target_os = "macos"))] #[cfg(any(target_os = "linux", target_os = "macos"))]
use std::ffi::CString; use std::ffi::CString;
@ -36,8 +30,7 @@ use std::thread;
use std::time; use std::time;
use style_traits::DevicePixel; use style_traits::DevicePixel;
use style_traits::cursor::CursorKind; use style_traits::cursor::CursorKind;
use super::keyutils::{self, CMD_OR_ALT, CMD_OR_CONTROL, GlutinKeyModifiers}; use super::keyutils::{self, GlutinKeyModifiers};
use tinyfiledialogs;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
use user32; use user32;
use webrender_api::{DeviceIntPoint, DeviceUintRect, DeviceUintSize, ScrollLocation}; 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) // 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; const MULTISAMPLES: u16 = 16;
@ -154,25 +147,14 @@ pub struct Window {
kind: WindowKind, kind: WindowKind,
screen_size: TypedSize2D<u32, DeviceIndependentPixel>, screen_size: TypedSize2D<u32, DeviceIndependentPixel>,
inner_size: Cell<TypedSize2D<u32, DeviceIndependentPixel>>, inner_size: Cell<TypedSize2D<u32, DeviceIndependentPixel>>,
mouse_down_button: Cell<Option<winit::MouseButton>>, mouse_down_button: Cell<Option<winit::MouseButton>>,
mouse_down_point: Cell<TypedPoint2D<i32, DevicePixel>>, mouse_down_point: Cell<TypedPoint2D<i32, DevicePixel>>,
event_queue: RefCell<Vec<WindowEvent>>, 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>>, mouse_pos: Cell<TypedPoint2D<i32, DevicePixel>>,
key_modifiers: Cell<GlutinKeyModifiers>, key_modifiers: Cell<GlutinKeyModifiers>,
current_url: RefCell<Option<ServoUrl>>, last_pressed_key: Cell<Option<Key>>,
last_pressed_key: Cell<Option<constellation_msg::Key>>,
animation_state: Cell<AnimationState>, animation_state: Cell<AnimationState>,
fullscreen: Cell<bool>, fullscreen: Cell<bool>,
gl: Rc<gl::Gl>, gl: Rc<gl::Gl>,
suspended: Cell<bool>, suspended: Cell<bool>,
} }
@ -191,10 +173,6 @@ fn window_creation_scale_factor() -> TypedScale<f32, DeviceIndependentPixel, Dev
impl Window { impl Window {
pub fn set_browser_id(&self, browser_id: BrowserId) {
self.browser_id.set(Some(browser_id));
}
pub fn new(is_foreground: bool, pub fn new(is_foreground: bool,
window_size: TypedSize2D<u32, DeviceIndependentPixel>) -> Rc<Window> { window_size: TypedSize2D<u32, DeviceIndependentPixel>) -> Rc<Window> {
let win_size: DeviceUintSize = (window_size.to_f32() * window_creation_scale_factor()).to_u32(); 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_button: Cell::new(None),
mouse_down_point: Cell::new(TypedPoint2D::new(0, 0)), mouse_down_point: Cell::new(TypedPoint2D::new(0, 0)),
browser_id: Cell::new(None),
mouse_pos: Cell::new(TypedPoint2D::new(0, 0)), mouse_pos: Cell::new(TypedPoint2D::new(0, 0)),
key_modifiers: Cell::new(GlutinKeyModifiers::empty()), key_modifiers: Cell::new(GlutinKeyModifiers::empty()),
current_url: RefCell::new(None),
last_pressed_key: Cell::new(None), last_pressed_key: Cell::new(None),
gl: gl.clone(), gl: gl.clone(),
@ -311,6 +286,107 @@ impl Window {
Rc::new(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")))] #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
fn gl_version() -> GlRequest { fn gl_version() -> GlRequest {
return GlRequest::Specific(Api::OpenGl, (3, 2)); 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 { match event {
Event::WindowEvent { Event::WindowEvent {
event: winit::WindowEvent::ReceivedCharacter(ch), event: winit::WindowEvent::ReceivedCharacter(ch),
@ -416,13 +492,22 @@ impl Window {
event: winit::WindowEvent::MouseWheel { delta, phase, .. }, 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::LineDelta(dx, dy) => (dx, dy * LINE_HEIGHT),
MouseScrollDelta::PixelDelta(dx, dy) => (dx, dy), 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 scroll_location = ScrollLocation::Delta(TypedVector2D::new(dx, dy));
let phase = glutin_phase_to_touch_event_type(phase); 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::WindowEvent {
event: winit::WindowEvent::Touch(touch), event: winit::WindowEvent::Touch(touch),
@ -481,22 +566,6 @@ impl Window {
self.key_modifiers.set(modifiers); 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 /// Helper function to handle a click
fn handle_mouse(&self, button: winit::MouseButton, fn handle_mouse(&self, button: winit::MouseButton,
action: winit::ElementState, action: winit::ElementState,
@ -532,115 +601,6 @@ impl Window {
self.event_queue.borrow_mut().push(WindowEvent::MouseWindowEventClass(event)); 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"))] #[cfg(not(target_os = "windows"))]
fn hidpi_factor(&self) -> TypedScale<f32, DeviceIndependentPixel, DevicePixel> { fn hidpi_factor(&self) -> TypedScale<f32, DeviceIndependentPixel, DevicePixel> {
match self.kind { match self.kind {
@ -660,84 +620,8 @@ impl Window {
TypedScale::new(ppi as f32 / 96.0) 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. /// Has no effect on Android.
fn set_cursor(&self, cursor: CursorKind) { pub fn set_cursor(&self, cursor: CursorKind) {
match self.kind { match self.kind {
WindowKind::Window(ref window, ..) => { WindowKind::Window(ref window, ..) => {
use winit::MouseCursor; use winit::MouseCursor;
@ -785,152 +669,6 @@ impl Window {
WindowKind::Headless(..) => {} 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 { impl WindowMethods for Window {
@ -1046,20 +784,3 @@ fn glutin_phase_to_touch_event_type(phase: TouchPhase) -> TouchEventType {
TouchPhase::Cancelled => TouchEventType::Cancel, 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()
})
})
}

View file

@ -29,13 +29,11 @@ extern crate glutin;
// The window backed by glutin // The window backed by glutin
#[macro_use] extern crate log; #[macro_use] extern crate log;
extern crate msg; extern crate msg;
extern crate net_traits;
#[cfg(any(target_os = "linux", target_os = "macos"))] extern crate osmesa_sys; #[cfg(any(target_os = "linux", target_os = "macos"))] extern crate osmesa_sys;
extern crate script_traits; extern crate script_traits;
extern crate servo; extern crate servo;
extern crate servo_config; extern crate servo_config;
extern crate servo_geometry; extern crate servo_geometry;
extern crate servo_url;
#[cfg(all(feature = "unstable", not(target_os = "android")))] #[cfg(all(feature = "unstable", not(target_os = "android")))]
#[macro_use] #[macro_use]
extern crate sig; extern crate sig;
@ -63,6 +61,8 @@ use std::panic;
use std::process; use std::process;
use std::thread; use std::thread;
mod browser;
pub mod platform { pub mod platform {
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
pub use platform::macos::deinit; pub use platform::macos::deinit;
@ -163,6 +163,8 @@ fn main() {
let window = glutin_app::create_window(); 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, // 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. // or a blank page in case the homepage is not set either.
let cwd = env::current_dir().unwrap(); let cwd = env::current_dir().unwrap();
@ -178,23 +180,34 @@ fn main() {
let (sender, receiver) = ipc::channel().unwrap(); let (sender, receiver) = ipc::channel().unwrap();
servo.handle_events(vec![WindowEvent::NewBrowser(target_url, sender)]); servo.handle_events(vec![WindowEvent::NewBrowser(target_url, sender)]);
let browser_id = receiver.recv().unwrap(); 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.handle_events(vec![WindowEvent::SelectBrowser(browser_id)]);
servo.setup_logging(); servo.setup_logging();
window.run(|| { window.run(|| {
let win_events = window.get_events(); 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 { let need_resize = win_events.iter().any(|e| match *e {
WindowEvent::Resize => true, 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 { if need_resize {
servo.repaint_synchronously(); servo.repaint_synchronously();
} }
window.handle_servo_events(servo.get_events()); false
stop
}); });
servo.deinit(); servo.deinit();