mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
split window code and browser code in two different files
This commit is contained in:
parent
34c2150e4f
commit
1cdba8843d
9 changed files with 478 additions and 417 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -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)",
|
||||||
|
|
|
@ -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"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
319
ports/servo/browser.rs
Normal 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()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
|
@ -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}' |
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue