author | Paul Rouget <me@paulrouget.com> |
Thu, 22 Mar 2018 08:10:45 -0400 | |
changeset 409561 | 2c6f444e76cee053ee27c3010d1c55d594e428e4 |
parent 409560 | a348763a5ee1eead73ff2f2701cb72f35b4a7782 |
child 409562 | 37e07672384db6648f4477b98c3646728a0c9d9a |
push id | 101247 |
push user | nerli@mozilla.com |
push date | Thu, 22 Mar 2018 23:00:51 +0000 |
treeherder | mozilla-inbound@02e384bdf97d [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jdm |
milestone | 61.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/servo/Cargo.lock +++ b/servo/Cargo.lock @@ -2693,24 +2693,22 @@ dependencies = [ "compositing 0.0.1", "euclid 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)", "gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "gleam 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", "glutin 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", "libservo 0.0.1", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "msg 0.0.1", - "net_traits 0.0.1", "osmesa-src 17.3.1-devel (git+https://github.com/servo/osmesa-src)", "osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "script_traits 0.0.1", "servo-egl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "servo_config 0.0.1", "servo_geometry 0.0.1", - "servo_url 0.0.1", "sig 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "style_traits 0.0.1", "tinyfiledialogs 3.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "user32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "webrender_api 0.57.0 (git+https://github.com/servo/webrender)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "winit 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", "winres 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
--- a/servo/components/compositing/compositor.rs +++ b/servo/components/compositing/compositor.rs @@ -29,19 +29,19 @@ use std::rc::Rc; use std::sync::mpsc::Sender; use std::time::{Duration, Instant}; use style_traits::{CSSPixel, DevicePixel, PinchZoomFactor}; use style_traits::cursor::CursorKind; use style_traits::viewport::ViewportConstraints; use time::{now, precise_time_ns, precise_time_s}; use touch::{TouchHandler, TouchAction}; use webrender; -use webrender_api::{self, DeviceIntPoint, DevicePoint, DeviceUintRect, DeviceUintSize, HitTestFlags, HitTestResult}; +use webrender_api::{self, DeviceIntPoint, DevicePoint, HitTestFlags, HitTestResult}; use webrender_api::{LayoutVector2D, ScrollEventPhase, ScrollLocation}; -use windowing::{self, MouseWindowEvent, WebRenderDebugOption, WindowMethods}; +use windowing::{self, EmbedderCoordinates, MouseWindowEvent, WebRenderDebugOption, WindowMethods}; #[derive(Debug, PartialEq)] enum UnableToComposite { WindowUnprepared, NotReadyToPaintImage(NotReadyToPaint), } #[derive(Debug, PartialEq)] @@ -105,35 +105,26 @@ pub struct IOCompositor<Window: WindowMe root_pipeline: Option<CompositionPipeline>, /// Tracks details about each active pipeline that the compositor knows about. pipeline_details: HashMap<PipelineId, PipelineDetails>, /// The scene scale, to allow for zooming and high-resolution painting. scale: TypedScale<f32, LayerPixel, DevicePixel>, - /// The size of the rendering area. - frame_size: DeviceUintSize, - - /// The position and size of the window within the rendering area. - window_rect: DeviceUintRect, - /// "Mobile-style" zoom that does not reflow the page. viewport_zoom: PinchZoomFactor, /// Viewport zoom constraints provided by @viewport. min_viewport_zoom: Option<PinchZoomFactor>, max_viewport_zoom: Option<PinchZoomFactor>, /// "Desktop-style" zoom that resizes the viewport to fit the window. page_zoom: TypedScale<f32, CSSPixel, DeviceIndependentPixel>, - /// The device pixel ratio for this window. - scale_factor: TypedScale<f32, DeviceIndependentPixel, DevicePixel>, - /// The type of composition to perform composite_target: CompositeTarget, /// Tracks whether we should composite this frame. composition_request: CompositionRequest, /// Tracks whether we are in the process of shutting down, or have shut down and should close /// the compositor. @@ -188,16 +179,19 @@ pub struct IOCompositor<Window: WindowMe gl: Rc<gl::Gl>, /// Map of the pending paint metrics per layout thread. /// The layout thread for each specific pipeline expects the compositor to /// paint frames with specific given IDs (epoch). Once the compositor paints /// these frames, it records the paint time for each of them and sends the /// metric to the corresponding layout thread. pending_paint_metrics: HashMap<PipelineId, Epoch>, + + /// The coordinates of the native window, its view and the screen. + embedder_coordinates: EmbedderCoordinates, } #[derive(Clone, Copy)] struct ScrollZoomEvent { /// Change the pinch zoom level by this factor magnification: f32, /// Scroll by this offset, or to Start or End scroll_location: ScrollLocation, @@ -345,34 +339,29 @@ impl webrender_api::RenderNotifier for R self.wake_up(); } } } impl<Window: WindowMethods> IOCompositor<Window> { fn new(window: Rc<Window>, state: InitialCompositorState) -> IOCompositor<Window> { - let frame_size = window.framebuffer_size(); - let window_rect = window.window_rect(); - let scale_factor = window.hidpi_factor(); let composite_target = match opts::get().output_file { Some(_) => CompositeTarget::PngFile, None => CompositeTarget::Window }; IOCompositor { gl: window.gl(), + embedder_coordinates: window.get_coordinates(), window: window, port: state.receiver, root_pipeline: None, pipeline_details: HashMap::new(), - frame_size: frame_size, - window_rect: window_rect, scale: TypedScale::new(1.0), - scale_factor: scale_factor, composition_request: CompositionRequest::NoCompositingNecessary, touch_handler: TouchHandler::new(), pending_scroll_zoom_events: Vec::new(), waiting_for_results_of_scroll: false, composite_target: composite_target, shutdown_state: ShutdownState::NotShuttingDown, page_zoom: TypedScale::new(1.0), viewport_zoom: PinchZoomFactor::new(1.0), @@ -543,16 +532,34 @@ impl<Window: WindowMethods> IOCompositor self.composite_if_necessary(CompositingReason::Headless); } }, (Msg::PendingPaintMetric(pipeline_id, epoch), _) => { self.pending_paint_metrics.insert(pipeline_id, epoch); } + (Msg::GetClientWindow(req), ShutdownState::NotShuttingDown) => { + if let Err(e) = req.send(self.embedder_coordinates.window) { + warn!("Sending response to get client window failed ({}).", e); + } + } + + (Msg::GetScreenSize(req), ShutdownState::NotShuttingDown) => { + if let Err(e) = req.send(self.embedder_coordinates.screen) { + warn!("Sending response to get screen size failed ({}).", e); + } + } + + (Msg::GetScreenAvailSize(req), ShutdownState::NotShuttingDown) => { + if let Err(e) = req.send(self.embedder_coordinates.screen_avail) { + warn!("Sending response to get screen avail size failed ({}).", e); + } + } + // When we are shutting_down, we need to avoid performing operations // such as Paint that may crash because we have begun tearing down // the rest of our resources. (_, ShutdownState::ShuttingDown) => {} } true } @@ -629,24 +636,24 @@ impl<Window: WindowMethods> IOCompositor } } fn remove_pipeline_root_layer(&mut self, pipeline_id: PipelineId) { self.pipeline_details.remove(&pipeline_id); } fn send_window_size(&self, size_type: WindowSizeType) { - let dppx = self.page_zoom * self.hidpi_factor(); + let dppx = self.page_zoom * self.embedder_coordinates.hidpi_factor; self.webrender_api.set_window_parameters(self.webrender_document, - self.frame_size, - self.window_rect, - self.hidpi_factor().get()); + self.embedder_coordinates.framebuffer, + self.embedder_coordinates.viewport, + self.embedder_coordinates.hidpi_factor.get()); - let initial_viewport = self.window_rect.size.to_f32() / dppx; + let initial_viewport = self.embedder_coordinates.viewport.size.to_f32() / dppx; let data = WindowSizeData { device_pixel_ratio: dppx, initial_viewport: initial_viewport, }; let top_level_browsing_context_id = self.root_pipeline.as_ref().map(|pipeline| { pipeline.top_level_browsing_context_id @@ -657,34 +664,29 @@ impl<Window: WindowMethods> IOCompositor if let Err(e) = self.constellation_chan.send(msg) { warn!("Sending window resize to constellation failed ({}).", e); } } pub fn on_resize_window_event(&mut self) { debug!("compositor resize requested"); + let old_coords = self.embedder_coordinates; + self.embedder_coordinates = self.window.get_coordinates(); + // A size change could also mean a resolution change. - let new_scale_factor = self.window.hidpi_factor(); - if self.scale_factor != new_scale_factor { - self.scale_factor = new_scale_factor; + if self.embedder_coordinates.hidpi_factor != old_coords.hidpi_factor { self.update_zoom_transform(); } - let new_window_rect = self.window.window_rect(); - let new_frame_size = self.window.framebuffer_size(); - - if self.window_rect == new_window_rect && - self.frame_size == new_frame_size { + if self.embedder_coordinates.viewport == old_coords.viewport && + self.embedder_coordinates.framebuffer == old_coords.framebuffer { return; } - self.frame_size = new_frame_size; - self.window_rect = new_window_rect; - self.send_window_size(WindowSizeType::Resize); } pub fn on_mouse_window_event_class(&mut self, mouse_window_event: MouseWindowEvent) { if opts::get().convert_mouse_to_touch { match mouse_window_event { MouseWindowEvent::Click(_, _) => {} MouseWindowEvent::MouseDown(_, p) => self.on_touch_down(TouchId(0), p), @@ -1091,17 +1093,17 @@ impl<Window: WindowMethods> IOCompositor } } fn hidpi_factor(&self) -> TypedScale<f32, DeviceIndependentPixel, DevicePixel> { match opts::get().device_pixels_per_px { Some(device_pixels_per_px) => TypedScale::new(device_pixels_per_px), None => match opts::get().output_file { Some(_) => TypedScale::new(1.0), - None => self.scale_factor + None => self.embedder_coordinates.hidpi_factor, } } } fn device_pixels_per_page_px(&self) -> TypedScale<f32, CSSPixel, DevicePixel> { self.page_zoom * self.hidpi_factor() } @@ -1251,17 +1253,18 @@ impl<Window: WindowMethods> IOCompositor /// Composite either to the screen or to a png image or both. /// Returns Ok if composition was performed or Err if it was not possible to composite /// for some reason. If CompositeTarget is Window or Png no image data is returned; /// in the latter case the image is written directly to a file. If CompositeTarget /// is WindowAndPng Ok(Some(png::Image)) is returned. fn composite_specific_target(&mut self, target: CompositeTarget) -> Result<Option<Image>, UnableToComposite> { - let (width, height) = (self.frame_size.width_typed(), self.frame_size.height_typed()); + let width = self.embedder_coordinates.framebuffer.width_typed(); + let height = self.embedder_coordinates.framebuffer.height_typed(); if !self.window.prepare_for_composite(width, height) { return Err(UnableToComposite::WindowUnprepared) } self.webrender.update(); let wait_for_stable_image = match target { CompositeTarget::WindowAndPng | CompositeTarget::PngFile => true, @@ -1286,17 +1289,17 @@ impl<Window: WindowMethods> IOCompositor _ => initialize_png(&*self.gl, width, height) }; profile(ProfilerCategory::Compositing, None, self.time_profiler_chan.clone(), || { debug!("compositor: compositing"); // Paint the scene. // TODO(gw): Take notice of any errors the renderer returns! - self.webrender.render(self.frame_size).ok(); + self.webrender.render(self.embedder_coordinates.framebuffer).ok(); }); // If there are pending paint metrics, we check if any of the painted epochs is // one of the ones that the paint metrics recorder is expecting . In that case, // we get the current time, inform the layout thread about it and remove the // pending metric from the list. if !self.pending_paint_metrics.is_empty() { let paint_time = precise_time_ns();
--- a/servo/components/compositing/compositor_thread.rs +++ b/servo/components/compositing/compositor_thread.rs @@ -116,23 +116,16 @@ pub enum EmbedderMsg { /// A status message to be displayed by the browser chrome. Status(TopLevelBrowsingContextId, Option<String>), /// Alerts the embedder that the current page has changed its title. ChangePageTitle(TopLevelBrowsingContextId, Option<String>), /// Move the window to a point MoveTo(TopLevelBrowsingContextId, DeviceIntPoint), /// Resize the window to size ResizeTo(TopLevelBrowsingContextId, DeviceUintSize), - /// Get Window Informations size and position - GetClientWindow(TopLevelBrowsingContextId, - IpcSender<(DeviceUintSize, DeviceIntPoint)>), - /// Get screen size (pixel) - GetScreenSize(TopLevelBrowsingContextId, IpcSender<(DeviceUintSize)>), - /// Get screen available size (pixel) - GetScreenAvailSize(TopLevelBrowsingContextId, IpcSender<(DeviceUintSize)>), /// Wether or not to follow a link AllowNavigation(TopLevelBrowsingContextId, ServoUrl, IpcSender<bool>), /// Sends an unconsumed key event back to the embedder. KeyEvent(Option<TopLevelBrowsingContextId>, Option<char>, Key, KeyState, KeyModifiers), /// Changes the cursor. SetCursor(CursorKind), /// A favicon was detected NewFavicon(TopLevelBrowsingContextId, ServoUrl), @@ -143,16 +136,18 @@ pub enum EmbedderMsg { /// Enter or exit fullscreen SetFullscreenState(TopLevelBrowsingContextId, bool), /// The load of a page has begun LoadStart(TopLevelBrowsingContextId), /// The load of a page has completed LoadComplete(TopLevelBrowsingContextId), /// A pipeline panicked. First string is the reason, second one is the backtrace. Panic(TopLevelBrowsingContextId, String, Option<String>), + /// Servo has shut down + Shutdown, } /// Messages from the painting thread and the constellation thread to the compositor thread. pub enum Msg { /// Requests that the compositor shut down. Exit, /// Informs the compositor that the constellation has completed shutdown. @@ -191,16 +186,22 @@ pub enum Msg { Dispatch(Box<Fn() + Send>), /// Indicates to the compositor that it needs to record the time when the frame with /// the given ID (epoch) is painted and report it to the layout thread of the given /// pipeline ID. PendingPaintMetric(PipelineId, Epoch), /// The load of a page has completed LoadComplete(TopLevelBrowsingContextId), + /// Get Window Informations size and position. + GetClientWindow(IpcSender<(DeviceUintSize, DeviceIntPoint)>), + /// Get screen size. + GetScreenSize(IpcSender<DeviceUintSize>), + /// Get screen available size. + GetScreenAvailSize(IpcSender<DeviceUintSize>), } impl Debug for Msg { fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { match *self { Msg::Exit => write!(f, "Exit"), Msg::ShutdownComplete => write!(f, "ShutdownComplete"), Msg::ChangeRunningAnimationsState(..) => write!(f, "ChangeRunningAnimationsState"), @@ -211,40 +212,41 @@ impl Debug for Msg { Msg::ViewportConstrained(..) => write!(f, "ViewportConstrained"), Msg::IsReadyToSaveImageReply(..) => write!(f, "IsReadyToSaveImageReply"), Msg::PipelineVisibilityChanged(..) => write!(f, "PipelineVisibilityChanged"), Msg::PipelineExited(..) => write!(f, "PipelineExited"), Msg::NewScrollFrameReady(..) => write!(f, "NewScrollFrameReady"), Msg::Dispatch(..) => write!(f, "Dispatch"), Msg::PendingPaintMetric(..) => write!(f, "PendingPaintMetric"), Msg::LoadComplete(..) => write!(f, "LoadComplete"), + Msg::GetClientWindow(..) => write!(f, "GetClientWindow"), + Msg::GetScreenSize(..) => write!(f, "GetScreenSize"), + Msg::GetScreenAvailSize(..) => write!(f, "GetScreenAvailSize"), } } } impl Debug for EmbedderMsg { fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { match *self { EmbedderMsg::Status(..) => write!(f, "Status"), EmbedderMsg::ChangePageTitle(..) => write!(f, "ChangePageTitle"), EmbedderMsg::MoveTo(..) => write!(f, "MoveTo"), EmbedderMsg::ResizeTo(..) => write!(f, "ResizeTo"), - EmbedderMsg::GetClientWindow(..) => write!(f, "GetClientWindow"), - EmbedderMsg::GetScreenSize(..) => write!(f, "GetScreenSize"), - EmbedderMsg::GetScreenAvailSize(..) => write!(f, "GetScreenAvailSize"), EmbedderMsg::AllowNavigation(..) => write!(f, "AllowNavigation"), EmbedderMsg::KeyEvent(..) => write!(f, "KeyEvent"), EmbedderMsg::SetCursor(..) => write!(f, "SetCursor"), EmbedderMsg::NewFavicon(..) => write!(f, "NewFavicon"), EmbedderMsg::HeadParsed(..) => write!(f, "HeadParsed"), EmbedderMsg::HistoryChanged(..) => write!(f, "HistoryChanged"), EmbedderMsg::SetFullscreenState(..) => write!(f, "SetFullscreenState"), EmbedderMsg::LoadStart(..) => write!(f, "LoadStart"), EmbedderMsg::LoadComplete(..) => write!(f, "LoadComplete"), EmbedderMsg::Panic(..) => write!(f, "Panic"), + EmbedderMsg::Shutdown => write!(f, "Shutdown"), } } } /// Data used to construct a compositor. pub struct InitialCompositorState { /// A channel to the compositor. pub sender: CompositorProxy,
--- a/servo/components/compositing/windowing.rs +++ b/servo/components/compositing/windowing.rs @@ -4,24 +4,22 @@ //! Abstract windowing methods. The concrete implementations of these can be found in `platform/`. use compositor_thread::EventLoopWaker; use euclid::TypedScale; use gleam::gl; use ipc_channel::ipc::IpcSender; use msg::constellation_msg::{Key, KeyModifiers, KeyState, TopLevelBrowsingContextId, TraversalDirection}; -use net_traits::net_error_list::NetError; -use script_traits::{LoadData, MouseButton, TouchEventType, TouchId}; +use script_traits::{MouseButton, TouchEventType, TouchId}; use servo_geometry::{DeviceIndependentPixel, DeviceUintLength}; use servo_url::ServoUrl; use std::fmt::{Debug, Error, Formatter}; use std::rc::Rc; use style_traits::DevicePixel; -use style_traits::cursor::CursorKind; use webrender_api::{DeviceIntPoint, DevicePoint, DeviceUintSize, DeviceUintRect, ScrollLocation}; #[derive(Clone)] pub enum MouseWindowEvent { Click(MouseButton, DevicePoint), MouseDown(MouseButton, DevicePoint), MouseUp(MouseButton, DevicePoint), } @@ -116,80 +114,44 @@ impl Debug for WindowEvent { #[derive(Clone, Copy, Debug, PartialEq)] pub enum AnimationState { Idle, Animating, } pub trait WindowMethods { - /// Returns the rendering area size in hardware pixels. - fn framebuffer_size(&self) -> DeviceUintSize; - /// Returns the position and size of the window within the rendering area. - fn window_rect(&self) -> DeviceUintRect; /// Presents the window to the screen (perhaps by page flipping). fn present(&self); - - /// Return the size of the window with head and borders and position of the window values - fn client_window(&self, ctx: TopLevelBrowsingContextId) -> (DeviceUintSize, DeviceIntPoint); - /// Return the size of the screen. - fn screen_size(&self, ctx: TopLevelBrowsingContextId) -> DeviceUintSize; - /// Return the available size of the screen. - fn screen_avail_size(&self, ctx: TopLevelBrowsingContextId) -> DeviceUintSize; - /// Set the size inside of borders and head - fn set_inner_size(&self, ctx: TopLevelBrowsingContextId, size: DeviceUintSize); - /// Set the window position - fn set_position(&self, ctx: TopLevelBrowsingContextId, point: DeviceIntPoint); - /// Set fullscreen state - fn set_fullscreen_state(&self, ctx: TopLevelBrowsingContextId, state: bool); - - /// Sets the page title for the current page. - fn set_page_title(&self, ctx: TopLevelBrowsingContextId, title: Option<String>); - /// Called when the browser chrome should display a status message. - fn status(&self, ctx: TopLevelBrowsingContextId, Option<String>); - /// Called when the browser has started loading a frame. - fn load_start(&self, ctx: TopLevelBrowsingContextId); - /// Called when the browser is done loading a frame. - fn load_end(&self, ctx: TopLevelBrowsingContextId); - /// Called when the browser encounters an error while loading a URL - fn load_error(&self, ctx: TopLevelBrowsingContextId, code: NetError, url: String); - /// Wether or not to follow a link - fn allow_navigation(&self, ctx: TopLevelBrowsingContextId, url: ServoUrl, IpcSender<bool>); - /// Called when the <head> tag has finished parsing - fn head_parsed(&self, ctx: TopLevelBrowsingContextId); - /// Called when the history state has changed. - fn history_changed(&self, ctx: TopLevelBrowsingContextId, Vec<LoadData>, usize); - - /// Returns the scale factor of the system (device pixels / device independent pixels). - fn hidpi_factor(&self) -> TypedScale<f32, DeviceIndependentPixel, DevicePixel>; - - /// Returns a thread-safe object to wake up the window's event loop. - fn create_event_loop_waker(&self) -> Box<EventLoopWaker>; - /// Requests that the window system prepare a composite. Typically this will involve making /// some type of platform-specific graphics context current. Returns true if the composite may /// proceed and false if it should not. fn prepare_for_composite(&self, width: DeviceUintLength, height: DeviceUintLength) -> bool; - - /// Sets the cursor to be used in the window. - fn set_cursor(&self, cursor: CursorKind); - - /// Process a key event. - fn handle_key(&self, ctx: Option<TopLevelBrowsingContextId>, ch: Option<char>, key: Key, mods: KeyModifiers); - + /// Return the GL function pointer trait. + fn gl(&self) -> Rc<gl::Gl>; + /// Returns a thread-safe object to wake up the window's event loop. + fn create_event_loop_waker(&self) -> Box<EventLoopWaker>; + /// Get the coordinates of the native window, the screen and the framebuffer. + fn get_coordinates(&self) -> EmbedderCoordinates; /// Does this window support a clipboard fn supports_clipboard(&self) -> bool; - - /// Add a favicon - fn set_favicon(&self, ctx: TopLevelBrowsingContextId, url: ServoUrl); - - /// Return the GL function pointer trait. - fn gl(&self) -> Rc<gl::Gl>; - /// Set whether the application is currently animating. /// Typically, when animations are active, the window /// will want to avoid blocking on UI events, and just /// run the event loop at the vsync interval. - fn set_animation_state(&self, _state: AnimationState) {} + fn set_animation_state(&self, _state: AnimationState); +} - /// Called when a pipeline panics. - fn handle_panic(&self, browser_id: TopLevelBrowsingContextId, reason: String, backtrace: Option<String>); +#[derive(Clone, Copy, Debug)] +pub struct EmbedderCoordinates { + /// The pixel density of the display. + pub hidpi_factor: TypedScale<f32, DeviceIndependentPixel, DevicePixel>, + /// Size of the screen. + pub screen: DeviceUintSize, + /// Size of the available screen space (screen without toolbars and docks). + pub screen_avail: DeviceUintSize, + /// Size of the native window. + pub window: (DeviceUintSize, DeviceIntPoint), + /// Size of the GL buffer in the window. + pub framebuffer: DeviceUintSize, + /// Coordinates of the document within the framebuffer. + pub viewport: DeviceUintRect, }
--- a/servo/components/constellation/constellation.rs +++ b/servo/components/constellation/constellation.rs @@ -1224,34 +1224,33 @@ impl<Message, LTF, STF> Constellation<Me FromScriptMsg::SetDocumentState(state) => { debug!("constellation got SetDocumentState message"); self.document_states.insert(source_pipeline_id, state); } FromScriptMsg::Alert(message, sender) => { debug!("constellation got Alert message"); self.handle_alert(source_top_ctx_id, message, sender); } - FromScriptMsg::GetClientWindow(send) => { - self.embedder_proxy.send(EmbedderMsg::GetClientWindow(source_top_ctx_id, send)); - } FromScriptMsg::MoveTo(point) => { self.embedder_proxy.send(EmbedderMsg::MoveTo(source_top_ctx_id, point)); } FromScriptMsg::ResizeTo(size) => { self.embedder_proxy.send(EmbedderMsg::ResizeTo(source_top_ctx_id, size)); } - FromScriptMsg::GetScreenSize(send) => { - self.embedder_proxy.send(EmbedderMsg::GetScreenSize(source_top_ctx_id, send)); + FromScriptMsg::GetClientWindow(send) => { + self.compositor_proxy.send(ToCompositorMsg::GetClientWindow(send)); } - + FromScriptMsg::GetScreenSize(send) => { + self.compositor_proxy.send(ToCompositorMsg::GetScreenSize(send)); + } FromScriptMsg::GetScreenAvailSize(send) => { - self.embedder_proxy.send(EmbedderMsg::GetScreenAvailSize(source_top_ctx_id, send)); + self.compositor_proxy.send(ToCompositorMsg::GetScreenAvailSize(send)); } FromScriptMsg::Exit => { self.compositor_proxy.send(ToCompositorMsg::Exit); } FromScriptMsg::LogEntry(thread_name, entry) => { self.handle_log_entry(Some(source_top_ctx_id), thread_name, entry); }
--- a/servo/components/script_traits/script_msg.rs +++ b/servo/components/script_traits/script_msg.rs @@ -131,18 +131,16 @@ pub enum ScriptMsg { SetFinalUrl(ServoUrl), /// Check if an alert dialog box should be presented Alert(String, IpcSender<bool>), /// Set title of current page /// <https://html.spec.whatwg.org/multipage/#document.title> SetTitle(Option<String>), /// Send a key event SendKeyEvent(Option<char>, Key, KeyState, KeyModifiers), - /// Get Window Informations size and position - GetClientWindow(IpcSender<(DeviceUintSize, DeviceIntPoint)>), /// Move the window to a point MoveTo(DeviceIntPoint), /// Resize the window to size ResizeTo(DeviceUintSize), /// Script has handled a touch event, and either prevented or allowed default actions. TouchEventProcessed(EventResult), /// A log entry, with the top-level browsing context id and thread name LogEntry(Option<String>, LogEntry), @@ -150,16 +148,18 @@ pub enum ScriptMsg { PipelineExited, /// Send messages from postMessage calls from serviceworker /// to constellation for storing in service worker manager ForwardDOMMessage(DOMMessage, ServoUrl), /// Store the data required to activate a service worker for the given scope RegisterServiceWorker(ScopeThings, ServoUrl), /// Enter or exit fullscreen SetFullscreenState(bool), + /// Get Window Informations size and position + GetClientWindow(IpcSender<(DeviceUintSize, DeviceIntPoint)>), /// Get the screen size (pixel) GetScreenSize(IpcSender<(DeviceUintSize)>), /// Get the available screen size (pixel) GetScreenAvailSize(IpcSender<(DeviceUintSize)>), /// Requests that the compositor shut down. Exit, }
--- a/servo/components/servo/lib.rs +++ b/servo/components/servo/lib.rs @@ -69,30 +69,28 @@ fn webdriver(_port: u16, _constellation: use bluetooth::BluetoothThreadFactory; use bluetooth_traits::BluetoothRequest; use canvas::gl_context::GLContextFactory; use canvas::webgl_thread::WebGLThreads; use compositing::{IOCompositor, ShutdownState, RenderNotifier}; use compositing::compositor_thread::{self, CompositorProxy, CompositorReceiver, InitialCompositorState}; use compositing::compositor_thread::{EmbedderMsg, EmbedderProxy, EmbedderReceiver}; -use compositing::windowing::WindowEvent; -use compositing::windowing::WindowMethods; +use compositing::windowing::{WindowEvent, WindowMethods}; use constellation::{Constellation, InitialConstellationState, UnprivilegedPipelineContent}; use constellation::{FromCompositorLogger, FromScriptLogger}; #[cfg(all(not(target_os = "windows"), not(target_os = "ios")))] use constellation::content_process_sandbox_profile; use env_logger::Logger as EnvLogger; use euclid::Length; #[cfg(all(not(target_os = "windows"), not(target_os = "ios")))] use gaol::sandbox::{ChildSandbox, ChildSandboxMethods}; use gfx::font_cache_thread::FontCacheThread; use ipc_channel::ipc::{self, IpcSender}; use log::{Log, LogMetadata, LogRecord}; -use msg::constellation_msg::KeyState; use net::resource_thread::new_resource_threads; use net_traits::IpcSend; use profile::mem as profile_mem; use profile::time as profile_time; use profile_traits::mem; use profile_traits::time; use script_traits::{ConstellationMsg, SWManagerSenders, ScriptToConstellationChan}; use servo_config::opts; @@ -104,33 +102,34 @@ use std::path::PathBuf; use std::rc::Rc; use std::sync::mpsc::{Sender, channel}; use webrender::RendererKind; use webvr::{WebVRThread, WebVRCompositorHandler}; pub use gleam::gl; pub use servo_config as config; pub use servo_url as url; -pub use msg::constellation_msg::TopLevelBrowsingContextId as BrowserId; +pub use msg::constellation_msg::{KeyState, TopLevelBrowsingContextId as BrowserId}; /// The in-process interface to Servo. /// /// It does everything necessary to render the web, primarily /// orchestrating the interaction between JavaScript, CSS layout, /// rendering, and the client window. /// /// Clients create a `Servo` instance for a given reference-counted type /// implementing `WindowMethods`, which is the bridge to whatever /// application Servo is embedded in. Clients then create an event /// loop to pump messages between the embedding application and /// various browser components. pub struct Servo<Window: WindowMethods + 'static> { compositor: IOCompositor<Window>, constellation_chan: Sender<ConstellationMsg>, - embedder_receiver: EmbedderReceiver + embedder_receiver: EmbedderReceiver, + embedder_events: Vec<EmbedderMsg>, } impl<Window> Servo<Window> where Window: WindowMethods + 'static { pub fn new(window: Rc<Window>) -> Servo<Window> { // Global configuration options, parsed from the command line. let opts = opts::get(); // Make sure the gl context is made current. @@ -153,27 +152,19 @@ impl<Window> Servo<Window> where Window: }); let devtools_chan = opts.devtools_port.map(|port| { devtools::start_server(port) }); let mut resource_path = resources_dir_path().unwrap(); resource_path.push("shaders"); + let coordinates = window.get_coordinates(); + let (mut webrender, webrender_api_sender) = { - // TODO(gw): Duplicates device_pixels_per_screen_px from compositor. Tidy up! - let scale_factor = window.hidpi_factor().get(); - let device_pixel_ratio = match opts.device_pixels_per_px { - Some(device_pixels_per_px) => device_pixels_per_px, - None => match opts.output_file { - Some(_) => 1.0, - None => scale_factor, - } - }; - let renderer_kind = if opts::get().should_use_osmesa() { RendererKind::OSMesa } else { RendererKind::Native }; let recorder = if opts.webrender_record { let record_path = PathBuf::from("wr-record.bin"); @@ -184,33 +175,33 @@ impl<Window> Servo<Window> where Window: }; let mut debug_flags = webrender::DebugFlags::empty(); debug_flags.set(webrender::DebugFlags::PROFILER_DBG, opts.webrender_stats); let render_notifier = Box::new(RenderNotifier::new(compositor_proxy.clone())); webrender::Renderer::new(window.gl(), render_notifier, webrender::RendererOptions { - device_pixel_ratio: device_pixel_ratio, + device_pixel_ratio: coordinates.hidpi_factor.get(), resource_override_path: Some(resource_path), enable_aa: opts.enable_text_antialiasing, debug_flags: debug_flags, debug: opts.webrender_debug, recorder: recorder, precache_shaders: opts.precache_shaders, enable_scrollbars: opts.output_file.is_none(), renderer_kind: renderer_kind, enable_subpixel_aa: opts.enable_subpixel_text_antialiasing, ..Default::default() }).expect("Unable to initialize webrender!") }; let webrender_api = webrender_api_sender.create_api(); let wr_document_layer = 0; //TODO - let webrender_document = webrender_api.add_document(window.framebuffer_size(), wr_document_layer); + let webrender_document = webrender_api.add_document(coordinates.framebuffer, wr_document_layer); // Important that this call is done in a single-threaded fashion, we // can't defer it after `create_constellation` has started. script::init(); // Create the constellation, which maintains the engine // pipelines, including the script and layout threads, as well // as the navigation context. @@ -249,16 +240,17 @@ impl<Window> Servo<Window> where Window: webrender_document, webrender_api, }); Servo { compositor: compositor, constellation_chan: constellation_chan, embedder_receiver: embedder_receiver, + embedder_events: Vec::new(), } } fn handle_window_event(&mut self, event: WindowEvent) { match event { WindowEvent::Idle => { } @@ -365,128 +357,47 @@ impl<Window> Servo<Window> where Window: while let Some(msg) = self.embedder_receiver.try_recv_embedder_msg() { match (msg, self.compositor.shutdown_state) { (_, ShutdownState::FinishedShuttingDown) => { error!("embedder shouldn't be handling messages after compositor has shut down"); }, (_, ShutdownState::ShuttingDown) => {}, - (EmbedderMsg::Status(top_level_browsing_context, message), ShutdownState::NotShuttingDown) => { - self.compositor.window.status(top_level_browsing_context, message); - }, - - (EmbedderMsg::ChangePageTitle(top_level_browsing_context, title), ShutdownState::NotShuttingDown) => { - self.compositor.window.set_page_title(top_level_browsing_context, title); - }, - - (EmbedderMsg::MoveTo(top_level_browsing_context, point), - ShutdownState::NotShuttingDown) => { - self.compositor.window.set_position(top_level_browsing_context, point); - }, - - (EmbedderMsg::ResizeTo(top_level_browsing_context, size), - ShutdownState::NotShuttingDown) => { - self.compositor.window.set_inner_size(top_level_browsing_context, size); - }, - - (EmbedderMsg::GetClientWindow(top_level_browsing_context, send), + (EmbedderMsg::KeyEvent(top_level_browsing_context, ch, key, state, modified), ShutdownState::NotShuttingDown) => { - let rect = self.compositor.window.client_window(top_level_browsing_context); - if let Err(e) = send.send(rect) { - warn!("Sending response to get client window failed ({}).", e); - } - }, - - (EmbedderMsg::GetScreenSize(top_level_browsing_context, send), - ShutdownState::NotShuttingDown) => { - let rect = self.compositor.window.screen_size(top_level_browsing_context); - if let Err(e) = send.send(rect) { - warn!("Sending response to get screen size failed ({}).", e); - } - }, - - (EmbedderMsg::GetScreenAvailSize(top_level_browsing_context, send), - ShutdownState::NotShuttingDown) => { - let rect = self.compositor.window.screen_avail_size(top_level_browsing_context); - if let Err(e) = send.send(rect) { - warn!("Sending response to get screen available size failed ({}).", e); + if state == KeyState::Pressed { + let msg = EmbedderMsg::KeyEvent(top_level_browsing_context, ch, key, state, modified); + self.embedder_events.push(msg); } }, - (EmbedderMsg::AllowNavigation(top_level_browsing_context, - url, - response_chan), - ShutdownState::NotShuttingDown) => { - self.compositor.window.allow_navigation(top_level_browsing_context, url, response_chan); - }, - - (EmbedderMsg::KeyEvent(top_level_browsing_context, - ch, - key, - state, - modified), - ShutdownState::NotShuttingDown) => { - if state == KeyState::Pressed { - self.compositor.window.handle_key(top_level_browsing_context, ch, key, modified); - } - }, - - (EmbedderMsg::SetCursor(cursor), ShutdownState::NotShuttingDown) => { - self.compositor.window.set_cursor(cursor) - }, - - (EmbedderMsg::NewFavicon(top_level_browsing_context, url), ShutdownState::NotShuttingDown) => { - self.compositor.window.set_favicon(top_level_browsing_context, url); + (msg, ShutdownState::NotShuttingDown) => { + self.embedder_events.push(msg); }, - - (EmbedderMsg::HeadParsed(top_level_browsing_context, ), ShutdownState::NotShuttingDown) => { - self.compositor.window.head_parsed(top_level_browsing_context, ); - }, - - (EmbedderMsg::HistoryChanged(top_level_browsing_context, entries, current), - ShutdownState::NotShuttingDown) => { - self.compositor.window.history_changed(top_level_browsing_context, entries, current); - }, - - (EmbedderMsg::SetFullscreenState(top_level_browsing_context, state), - ShutdownState::NotShuttingDown) => { - self.compositor.window.set_fullscreen_state(top_level_browsing_context, state); - }, - - (EmbedderMsg::LoadStart(top_level_browsing_context), ShutdownState::NotShuttingDown) => { - self.compositor.window.load_start(top_level_browsing_context); - }, - - (EmbedderMsg::LoadComplete(top_level_browsing_context), ShutdownState::NotShuttingDown) => { - // Inform the embedder that the load has finished. - // - // TODO(pcwalton): Specify which frame's load completed. - self.compositor.window.load_end(top_level_browsing_context); - }, - (EmbedderMsg::Panic(top_level_browsing_context, reason, backtrace), - ShutdownState::NotShuttingDown) => { - self.compositor.window.handle_panic(top_level_browsing_context, reason, backtrace); - }, - } } } - pub fn handle_events(&mut self, events: Vec<WindowEvent>) -> bool { + pub fn get_events(&mut self) -> Vec<EmbedderMsg> { + ::std::mem::replace(&mut self.embedder_events, Vec::new()) + } + + pub fn handle_events(&mut self, events: Vec<WindowEvent>) { if self.compositor.receive_messages() { self.receive_messages(); } for event in events { self.handle_window_event(event); } if self.compositor.shutdown_state != ShutdownState::FinishedShuttingDown { self.compositor.perform_updates(); + } else { + self.embedder_events.push(EmbedderMsg::Shutdown); } - self.compositor.shutdown_state != ShutdownState::FinishedShuttingDown } pub fn repaint_synchronously(&mut self) { self.compositor.repaint_synchronously() } pub fn pinch_zoom_level(&self) -> f32 { self.compositor.pinch_zoom_level()
--- a/servo/ports/servo/Cargo.toml +++ b/servo/ports/servo/Cargo.toml @@ -38,21 +38,19 @@ backtrace = "0.3" bitflags = "1.0" compositing = {path = "../../components/compositing"} euclid = "0.17" gleam = "0.4" glutin = "0.13" libservo = {path = "../../components/servo"} log = "0.3.5" msg = {path = "../../components/msg"} -net_traits = {path = "../../components/net_traits"} script_traits = {path = "../../components/script_traits"} servo_geometry = {path = "../../components/geometry"} servo_config = {path = "../../components/config"} -servo_url = {path = "../../components/url"} style_traits = {path = "../../components/style_traits"} tinyfiledialogs = "3.0" webrender_api = {git = "https://github.com/servo/webrender", features = ["ipc"]} winit = "0.11.2" [target.'cfg(not(target_os = "android"))'.dependencies] sig = "0.1"
new file mode 100644 --- /dev/null +++ b/servo/ports/servo/browser.rs @@ -0,0 +1,313 @@ +/* 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(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'), _) => { + 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) => { + 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() + }) + }) +}
new file mode 100644 --- /dev/null +++ b/servo/ports/servo/glutin_app/keyutils.rs @@ -0,0 +1,352 @@ +/* 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 msg::constellation_msg::{self, Key, KeyModifiers}; +use winit::{self, VirtualKeyCode}; + +bitflags! { + pub struct GlutinKeyModifiers: u8 { + const LEFT_CONTROL = 1; + const RIGHT_CONTROL = 2; + const LEFT_SHIFT = 4; + const RIGHT_SHIFT = 8; + const LEFT_ALT = 16; + const RIGHT_ALT = 32; + const LEFT_SUPER = 64; + const RIGHT_SUPER = 128; + } +} + +// Some shortcuts use Cmd on Mac and Control on other systems. +#[cfg(target_os = "macos")] +pub const CMD_OR_CONTROL: KeyModifiers = KeyModifiers::SUPER; +#[cfg(not(target_os = "macos"))] +pub const CMD_OR_CONTROL: KeyModifiers = KeyModifiers::CONTROL; + +// Some shortcuts use Cmd on Mac and Alt on other systems. +#[cfg(target_os = "macos")] +pub const CMD_OR_ALT: KeyModifiers = KeyModifiers::SUPER; +#[cfg(not(target_os = "macos"))] +pub const CMD_OR_ALT: KeyModifiers = KeyModifiers::ALT; + +pub fn char_to_script_key(c: char) -> Option<constellation_msg::Key> { + match c { + ' ' => Some(Key::Space), + '"' => Some(Key::Apostrophe), + '\'' => Some(Key::Apostrophe), + '<' => Some(Key::Comma), + ',' => Some(Key::Comma), + '_' => Some(Key::Minus), + '-' => Some(Key::Minus), + '>' => Some(Key::Period), + '.' => Some(Key::Period), + '?' => Some(Key::Slash), + '/' => Some(Key::Slash), + '~' => Some(Key::GraveAccent), + '`' => Some(Key::GraveAccent), + ')' => Some(Key::Num0), + '0' => Some(Key::Num0), + '!' => Some(Key::Num1), + '1' => Some(Key::Num1), + '@' => Some(Key::Num2), + '2' => Some(Key::Num2), + '#' => Some(Key::Num3), + '3' => Some(Key::Num3), + '$' => Some(Key::Num4), + '4' => Some(Key::Num4), + '%' => Some(Key::Num5), + '5' => Some(Key::Num5), + '^' => Some(Key::Num6), + '6' => Some(Key::Num6), + '&' => Some(Key::Num7), + '7' => Some(Key::Num7), + '*' => Some(Key::Num8), + '8' => Some(Key::Num8), + '(' => Some(Key::Num9), + '9' => Some(Key::Num9), + ':' => Some(Key::Semicolon), + ';' => Some(Key::Semicolon), + '+' => Some(Key::Equal), + '=' => Some(Key::Equal), + 'A' => Some(Key::A), + 'a' => Some(Key::A), + 'B' => Some(Key::B), + 'b' => Some(Key::B), + 'C' => Some(Key::C), + 'c' => Some(Key::C), + 'D' => Some(Key::D), + 'd' => Some(Key::D), + 'E' => Some(Key::E), + 'e' => Some(Key::E), + 'F' => Some(Key::F), + 'f' => Some(Key::F), + 'G' => Some(Key::G), + 'g' => Some(Key::G), + 'H' => Some(Key::H), + 'h' => Some(Key::H), + 'I' => Some(Key::I), + 'i' => Some(Key::I), + 'J' => Some(Key::J), + 'j' => Some(Key::J), + 'K' => Some(Key::K), + 'k' => Some(Key::K), + 'L' => Some(Key::L), + 'l' => Some(Key::L), + 'M' => Some(Key::M), + 'm' => Some(Key::M), + 'N' => Some(Key::N), + 'n' => Some(Key::N), + 'O' => Some(Key::O), + 'o' => Some(Key::O), + 'P' => Some(Key::P), + 'p' => Some(Key::P), + 'Q' => Some(Key::Q), + 'q' => Some(Key::Q), + 'R' => Some(Key::R), + 'r' => Some(Key::R), + 'S' => Some(Key::S), + 's' => Some(Key::S), + 'T' => Some(Key::T), + 't' => Some(Key::T), + 'U' => Some(Key::U), + 'u' => Some(Key::U), + 'V' => Some(Key::V), + 'v' => Some(Key::V), + 'W' => Some(Key::W), + 'w' => Some(Key::W), + 'X' => Some(Key::X), + 'x' => Some(Key::X), + 'Y' => Some(Key::Y), + 'y' => Some(Key::Y), + 'Z' => Some(Key::Z), + 'z' => Some(Key::Z), + '{' => Some(Key::LeftBracket), + '[' => Some(Key::LeftBracket), + '|' => Some(Key::Backslash), + '\\' => Some(Key::Backslash), + '}' => Some(Key::RightBracket), + ']' => Some(Key::RightBracket), + _ => None + } +} + +pub fn glutin_key_to_script_key(key: winit::VirtualKeyCode) -> Result<constellation_msg::Key, ()> { + // TODO(negge): add more key mappings + match key { + VirtualKeyCode::A => Ok(Key::A), + VirtualKeyCode::B => Ok(Key::B), + VirtualKeyCode::C => Ok(Key::C), + VirtualKeyCode::D => Ok(Key::D), + VirtualKeyCode::E => Ok(Key::E), + VirtualKeyCode::F => Ok(Key::F), + VirtualKeyCode::G => Ok(Key::G), + VirtualKeyCode::H => Ok(Key::H), + VirtualKeyCode::I => Ok(Key::I), + VirtualKeyCode::J => Ok(Key::J), + VirtualKeyCode::K => Ok(Key::K), + VirtualKeyCode::L => Ok(Key::L), + VirtualKeyCode::M => Ok(Key::M), + VirtualKeyCode::N => Ok(Key::N), + VirtualKeyCode::O => Ok(Key::O), + VirtualKeyCode::P => Ok(Key::P), + VirtualKeyCode::Q => Ok(Key::Q), + VirtualKeyCode::R => Ok(Key::R), + VirtualKeyCode::S => Ok(Key::S), + VirtualKeyCode::T => Ok(Key::T), + VirtualKeyCode::U => Ok(Key::U), + VirtualKeyCode::V => Ok(Key::V), + VirtualKeyCode::W => Ok(Key::W), + VirtualKeyCode::X => Ok(Key::X), + VirtualKeyCode::Y => Ok(Key::Y), + VirtualKeyCode::Z => Ok(Key::Z), + + VirtualKeyCode::Numpad0 => Ok(Key::Kp0), + VirtualKeyCode::Numpad1 => Ok(Key::Kp1), + VirtualKeyCode::Numpad2 => Ok(Key::Kp2), + VirtualKeyCode::Numpad3 => Ok(Key::Kp3), + VirtualKeyCode::Numpad4 => Ok(Key::Kp4), + VirtualKeyCode::Numpad5 => Ok(Key::Kp5), + VirtualKeyCode::Numpad6 => Ok(Key::Kp6), + VirtualKeyCode::Numpad7 => Ok(Key::Kp7), + VirtualKeyCode::Numpad8 => Ok(Key::Kp8), + VirtualKeyCode::Numpad9 => Ok(Key::Kp9), + + VirtualKeyCode::Key0 => Ok(Key::Num0), + VirtualKeyCode::Key1 => Ok(Key::Num1), + VirtualKeyCode::Key2 => Ok(Key::Num2), + VirtualKeyCode::Key3 => Ok(Key::Num3), + VirtualKeyCode::Key4 => Ok(Key::Num4), + VirtualKeyCode::Key5 => Ok(Key::Num5), + VirtualKeyCode::Key6 => Ok(Key::Num6), + VirtualKeyCode::Key7 => Ok(Key::Num7), + VirtualKeyCode::Key8 => Ok(Key::Num8), + VirtualKeyCode::Key9 => Ok(Key::Num9), + + VirtualKeyCode::Return => Ok(Key::Enter), + VirtualKeyCode::Space => Ok(Key::Space), + VirtualKeyCode::Escape => Ok(Key::Escape), + VirtualKeyCode::Equals => Ok(Key::Equal), + VirtualKeyCode::Minus => Ok(Key::Minus), + VirtualKeyCode::Back => Ok(Key::Backspace), + VirtualKeyCode::PageDown => Ok(Key::PageDown), + VirtualKeyCode::PageUp => Ok(Key::PageUp), + + VirtualKeyCode::Insert => Ok(Key::Insert), + VirtualKeyCode::Home => Ok(Key::Home), + VirtualKeyCode::Delete => Ok(Key::Delete), + VirtualKeyCode::End => Ok(Key::End), + + VirtualKeyCode::Left => Ok(Key::Left), + VirtualKeyCode::Up => Ok(Key::Up), + VirtualKeyCode::Right => Ok(Key::Right), + VirtualKeyCode::Down => Ok(Key::Down), + + VirtualKeyCode::LShift => Ok(Key::LeftShift), + VirtualKeyCode::LControl => Ok(Key::LeftControl), + VirtualKeyCode::LAlt => Ok(Key::LeftAlt), + VirtualKeyCode::LWin => Ok(Key::LeftSuper), + VirtualKeyCode::RShift => Ok(Key::RightShift), + VirtualKeyCode::RControl => Ok(Key::RightControl), + VirtualKeyCode::RAlt => Ok(Key::RightAlt), + VirtualKeyCode::RWin => Ok(Key::RightSuper), + + VirtualKeyCode::Apostrophe => Ok(Key::Apostrophe), + VirtualKeyCode::Backslash => Ok(Key::Backslash), + VirtualKeyCode::Comma => Ok(Key::Comma), + VirtualKeyCode::Grave => Ok(Key::GraveAccent), + VirtualKeyCode::LBracket => Ok(Key::LeftBracket), + VirtualKeyCode::Period => Ok(Key::Period), + VirtualKeyCode::RBracket => Ok(Key::RightBracket), + VirtualKeyCode::Semicolon => Ok(Key::Semicolon), + VirtualKeyCode::Slash => Ok(Key::Slash), + VirtualKeyCode::Tab => Ok(Key::Tab), + VirtualKeyCode::Subtract => Ok(Key::Minus), + + VirtualKeyCode::F1 => Ok(Key::F1), + VirtualKeyCode::F2 => Ok(Key::F2), + VirtualKeyCode::F3 => Ok(Key::F3), + VirtualKeyCode::F4 => Ok(Key::F4), + VirtualKeyCode::F5 => Ok(Key::F5), + VirtualKeyCode::F6 => Ok(Key::F6), + VirtualKeyCode::F7 => Ok(Key::F7), + VirtualKeyCode::F8 => Ok(Key::F8), + VirtualKeyCode::F9 => Ok(Key::F9), + VirtualKeyCode::F10 => Ok(Key::F10), + VirtualKeyCode::F11 => Ok(Key::F11), + VirtualKeyCode::F12 => Ok(Key::F12), + + VirtualKeyCode::NavigateBackward => Ok(Key::NavigateBackward), + VirtualKeyCode::NavigateForward => Ok(Key::NavigateForward), + _ => Err(()), + } +} + +pub fn glutin_mods_to_script_mods(modifiers: GlutinKeyModifiers) -> constellation_msg::KeyModifiers { + let mut result = constellation_msg::KeyModifiers::empty(); + if modifiers.intersects(GlutinKeyModifiers::LEFT_SHIFT | GlutinKeyModifiers::RIGHT_SHIFT) { + result.insert(KeyModifiers::SHIFT); + } + if modifiers.intersects(GlutinKeyModifiers::LEFT_CONTROL | GlutinKeyModifiers::RIGHT_CONTROL) { + result.insert(KeyModifiers::CONTROL); + } + if modifiers.intersects(GlutinKeyModifiers::LEFT_ALT | GlutinKeyModifiers::RIGHT_ALT) { + result.insert(KeyModifiers::ALT); + } + if modifiers.intersects(GlutinKeyModifiers::LEFT_SUPER | GlutinKeyModifiers::RIGHT_SUPER) { + result.insert(KeyModifiers::SUPER); + } + result +} + +pub fn is_printable(key_code: VirtualKeyCode) -> bool { + use winit::VirtualKeyCode::*; + match key_code { + Escape | + F1 | + F2 | + F3 | + F4 | + F5 | + F6 | + F7 | + F8 | + F9 | + F10 | + F11 | + F12 | + F13 | + F14 | + F15 | + Snapshot | + Scroll | + Pause | + Insert | + Home | + Delete | + End | + PageDown | + PageUp | + Left | + Up | + Right | + Down | + Back | + LAlt | + LControl | + LMenu | + LShift | + LWin | + Mail | + MediaSelect | + MediaStop | + Mute | + MyComputer | + NavigateForward | + NavigateBackward | + NextTrack | + NoConvert | + PlayPause | + Power | + PrevTrack | + RAlt | + RControl | + RMenu | + RShift | + RWin | + Sleep | + Stop | + VolumeDown | + VolumeUp | + Wake | + WebBack | + WebFavorites | + WebForward | + WebHome | + WebRefresh | + WebSearch | + WebStop => false, + _ => true, + } +} + +/// Detect if given char is default ignorable in unicode +/// http://www.unicode.org/L2/L2002/02368-default-ignorable.pdf +pub fn is_identifier_ignorable(ch: &char) -> bool { + match *ch { + '\u{0000}'...'\u{0008}' | '\u{000E}'...'\u{001F}' | + '\u{007F}'...'\u{0084}' | '\u{0086}'...'\u{009F}' | + '\u{06DD}' | '\u{070F}' | + '\u{180B}'...'\u{180D}' | '\u{180E}' | + '\u{200C}'...'\u{200F}' | + '\u{202A}'...'\u{202E}' | '\u{2060}'...'\u{2063}' | + '\u{2064}'...'\u{2069}' | '\u{206A}'...'\u{206F}' | + '\u{FE00}'...'\u{FE0F}' | '\u{FEFF}' | + '\u{FFF0}'...'\u{FFF8}' | '\u{FFF9}'...'\u{FFFB}' | + '\u{1D173}'...'\u{1D17A}' | '\u{E0000}' | + '\u{E0001}' | + '\u{E0002}'...'\u{E001F}' | '\u{E0020}'...'\u{E007F}' | + '\u{E0080}'...'\u{E0FFF}' => true, + _ => false + } +}
--- a/servo/ports/servo/glutin_app/mod.rs +++ b/servo/ports/servo/glutin_app/mod.rs @@ -1,14 +1,15 @@ /* 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/. */ //! A simple application that uses glutin to open a window for Servo to display in. +pub mod keyutils; pub mod window; use servo_config::opts; use std::rc::Rc; pub fn create_window() -> Rc<window::Window> { // Read command-line options. let opts = opts::get();
--- a/servo/ports/servo/glutin_app/window.rs +++ b/servo/ports/servo/glutin_app/window.rs @@ -1,86 +1,54 @@ /* 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/. */ //! A windowing implementation using glutin. use compositing::compositor_thread::EventLoopWaker; use compositing::windowing::{AnimationState, MouseWindowEvent, WindowEvent}; -use compositing::windowing::{WebRenderDebugOption, WindowMethods}; +use compositing::windowing::{EmbedderCoordinates, WindowMethods}; use euclid::{Length, TypedPoint2D, TypedVector2D, TypedScale, TypedSize2D}; #[cfg(target_os = "windows")] use gdi32; use gleam::gl; -use glutin; -use glutin::{Api, GlContext, GlRequest}; -use msg::constellation_msg::{self, Key, TopLevelBrowsingContextId as BrowserId}; -use msg::constellation_msg::{KeyModifiers, KeyState, TraversalDirection}; -use net_traits::net_error_list::NetError; -use net_traits::pub_domains::is_reg_domain; +use glutin::{self, Api, GlContext, GlRequest}; +use msg::constellation_msg::{Key, KeyState}; #[cfg(any(target_os = "linux", target_os = "macos"))] use osmesa_sys; -use script_traits::{LoadData, TouchEventType}; -use servo::ipc_channel::ipc::IpcSender; +use script_traits::TouchEventType; use servo_config::opts; -use servo_config::prefs::PREFS; use servo_geometry::DeviceIndependentPixel; -use servo_url::ServoUrl; use std::cell::{Cell, RefCell}; #[cfg(any(target_os = "linux", target_os = "macos"))] use std::ffi::CString; use std::mem; use std::os::raw::c_void; use std::ptr; use std::rc::Rc; use std::sync::Arc; use std::thread; use std::time; use style_traits::DevicePixel; use style_traits::cursor::CursorKind; -use tinyfiledialogs; +use super::keyutils::{self, GlutinKeyModifiers}; #[cfg(target_os = "windows")] use user32; use webrender_api::{DeviceIntPoint, DeviceUintRect, DeviceUintSize, ScrollLocation}; #[cfg(target_os = "windows")] use winapi; use winit; use winit::{ElementState, Event, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode}; #[cfg(target_os = "macos")] use winit::os::macos::{ActivationPolicy, WindowBuilderExt}; -bitflags! { - struct GlutinKeyModifiers: u8 { - const LEFT_CONTROL = 1; - const RIGHT_CONTROL = 2; - const LEFT_SHIFT = 4; - const RIGHT_SHIFT = 8; - const LEFT_ALT = 16; - const RIGHT_ALT = 32; - const LEFT_SUPER = 64; - const RIGHT_SUPER = 128; - } -} - -// Some shortcuts use Cmd on Mac and Control on other systems. -#[cfg(target_os = "macos")] -const CMD_OR_CONTROL: KeyModifiers = KeyModifiers::SUPER; -#[cfg(not(target_os = "macos"))] -const CMD_OR_CONTROL: KeyModifiers = KeyModifiers::CONTROL; - -// Some shortcuts use Cmd on Mac and Alt on other systems. -#[cfg(target_os = "macos")] -const CMD_OR_ALT: KeyModifiers = KeyModifiers::SUPER; -#[cfg(not(target_os = "macos"))] -const CMD_OR_ALT: KeyModifiers = KeyModifiers::ALT; - // 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; #[cfg(target_os = "macos")] fn builder_with_platform_options(mut builder: winit::WindowBuilder) -> winit::WindowBuilder { if opts::get().headless || opts::get().output_file.is_some() { // Prevent the window from showing in Dock.app, stealing focus, // or appearing at all when running in headless mode or generating an @@ -174,35 +142,24 @@ enum WindowKind { Headless(HeadlessContext), } /// The type of a window. pub struct Window { kind: WindowKind, screen_size: TypedSize2D<u32, DeviceIndependentPixel>, inner_size: Cell<TypedSize2D<u32, DeviceIndependentPixel>>, - mouse_down_button: Cell<Option<winit::MouseButton>>, mouse_down_point: Cell<TypedPoint2D<i32, DevicePixel>>, event_queue: RefCell<Vec<WindowEvent>>, - - /// id of the top level browsing context. It is unique as tabs - /// are not supported yet. None until created. - browser_id: Cell<Option<BrowserId>>, - mouse_pos: Cell<TypedPoint2D<i32, DevicePixel>>, key_modifiers: Cell<GlutinKeyModifiers>, - current_url: RefCell<Option<ServoUrl>>, - - last_pressed_key: Cell<Option<constellation_msg::Key>>, - + last_pressed_key: Cell<Option<Key>>, animation_state: Cell<AnimationState>, - fullscreen: Cell<bool>, - gl: Rc<gl::Gl>, suspended: Cell<bool>, } #[cfg(not(target_os = "windows"))] fn window_creation_scale_factor() -> TypedScale<f32, DeviceIndependentPixel, DevicePixel> { TypedScale::new(1.0) } @@ -211,20 +168,16 @@ fn window_creation_scale_factor() -> Typ fn window_creation_scale_factor() -> TypedScale<f32, DeviceIndependentPixel, DevicePixel> { let hdc = unsafe { user32::GetDC(::std::ptr::null_mut()) }; let ppi = unsafe { gdi32::GetDeviceCaps(hdc, winapi::wingdi::LOGPIXELSY) }; TypedScale::new(ppi as f32 / 96.0) } impl Window { - pub fn set_browser_id(&self, browser_id: BrowserId) { - self.browser_id.set(Some(browser_id)); - } - pub fn new(is_foreground: bool, window_size: TypedSize2D<u32, DeviceIndependentPixel>) -> Rc<Window> { let win_size: DeviceUintSize = (window_size.to_f32() * window_creation_scale_factor()).to_u32(); let width = win_size.to_untyped().width; let height = win_size.to_untyped().height; // If there's no chrome, start off with the window invisible. It will be set to visible in // `load_end()`. This avoids an ugly flash of unstyled content (especially important since @@ -311,79 +264,156 @@ impl Window { gl.finish(); let window = Window { kind: window_kind, event_queue: RefCell::new(vec!()), mouse_down_button: Cell::new(None), mouse_down_point: Cell::new(TypedPoint2D::new(0, 0)), - browser_id: Cell::new(None), - mouse_pos: Cell::new(TypedPoint2D::new(0, 0)), key_modifiers: Cell::new(GlutinKeyModifiers::empty()), - current_url: RefCell::new(None), last_pressed_key: Cell::new(None), gl: gl.clone(), animation_state: Cell::new(AnimationState::Idle), fullscreen: Cell::new(false), inner_size: Cell::new(inner_size), screen_size, suspended: Cell::new(false), }; window.present(); Rc::new(window) } + pub fn get_events(&self) -> Vec<WindowEvent> { + mem::replace(&mut *self.event_queue.borrow_mut(), Vec::new()) + } + + pub fn page_height(&self) -> f32 { + let dpr = self.hidpi_factor(); + match self.kind { + WindowKind::Window(ref window, _) => { + let (_, height) = window.get_inner_size().expect("Failed to get window inner size."); + height as f32 * dpr.get() + }, + WindowKind::Headless(ref context) => { + context.height as f32 * dpr.get() + } + } + } + + pub fn set_title(&self, title: &str) { + if let WindowKind::Window(ref window, _) = self.kind { + window.set_title(title); + } + } + + pub fn set_inner_size(&self, size: DeviceUintSize) { + if let WindowKind::Window(ref window, _) = self.kind { + let size = size.to_f32() / self.hidpi_factor(); + window.set_inner_size(size.width as u32, size.height as u32) + } + } + + pub fn set_position(&self, point: DeviceIntPoint) { + if let WindowKind::Window(ref window, _) = self.kind { + let point = point.to_f32() / self.hidpi_factor(); + window.set_position(point.x as i32, point.y as i32) + } + } + + pub fn set_fullscreen(&self, state: bool) { + match self.kind { + WindowKind::Window(ref window, ..) => { + if self.fullscreen.get() != state { + window.set_fullscreen(None); + } + }, + WindowKind::Headless(..) => {} + } + self.fullscreen.set(state); + } + + fn is_animating(&self) -> bool { + self.animation_state.get() == AnimationState::Animating && !self.suspended.get() + } + + pub fn run<T>(&self, mut servo_callback: T) where T: FnMut() -> bool { + match self.kind { + WindowKind::Window(_, ref events_loop) => { + let mut stop = false; + loop { + if self.is_animating() { + // We block on compositing (servo_callback ends up calling swap_buffers) + events_loop.borrow_mut().poll_events(|e| { + self.glutin_event_to_servo_event(e); + }); + stop = servo_callback(); + } else { + // We block on glutin's event loop (window events) + events_loop.borrow_mut().run_forever(|e| { + self.glutin_event_to_servo_event(e); + if !self.event_queue.borrow().is_empty() { + if !self.suspended.get() { + stop = servo_callback(); + } + } + if stop || self.is_animating() { + winit::ControlFlow::Break + } else { + winit::ControlFlow::Continue + } + }); + } + if stop { + break; + } + } + } + WindowKind::Headless(..) => { + loop { + // Sleep the main thread to avoid using 100% CPU + // This can be done better, see comments in #18777 + if self.event_queue.borrow().is_empty() { + thread::sleep(time::Duration::from_millis(5)); + } + let stop = servo_callback(); + if stop { + break; + } + } + } + } + } + #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))] fn gl_version() -> GlRequest { return GlRequest::Specific(Api::OpenGl, (3, 2)); } #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] fn gl_version() -> GlRequest { GlRequest::Specific(Api::OpenGlEs, (3, 0)) } - /// Detect if given char is default ignorable in unicode - /// http://www.unicode.org/L2/L2002/02368-default-ignorable.pdf - fn is_identifier_ignorable(&self, ch: &char) -> bool { - match *ch { - '\u{0000}'...'\u{0008}' | '\u{000E}'...'\u{001F}' | - '\u{007F}'...'\u{0084}' | '\u{0086}'...'\u{009F}' | - '\u{06DD}' | '\u{070F}' | - '\u{180B}'...'\u{180D}' | '\u{180E}' | - '\u{200C}'...'\u{200F}' | - '\u{202A}'...'\u{202E}' | '\u{2060}'...'\u{2063}' | - '\u{2064}'...'\u{2069}' | '\u{206A}'...'\u{206F}' | - '\u{FE00}'...'\u{FE0F}' | '\u{FEFF}' | - '\u{FFF0}'...'\u{FFF8}' | '\u{FFF9}'...'\u{FFFB}' | - '\u{1D173}'...'\u{1D17A}' | '\u{E0000}' | - '\u{E0001}' | - '\u{E0002}'...'\u{E001F}' | '\u{E0020}'...'\u{E007F}' | - '\u{E0080}'...'\u{E0FFF}' => true, - _ => false - } - } - fn handle_received_character(&self, ch: char) { - let modifiers = Window::glutin_mods_to_script_mods(self.key_modifiers.get()); - if self.is_identifier_ignorable(&ch) { + let modifiers = keyutils::glutin_mods_to_script_mods(self.key_modifiers.get()); + if keyutils::is_identifier_ignorable(&ch) { return } if let Some(last_pressed_key) = self.last_pressed_key.get() { let event = WindowEvent::KeyEvent(Some(ch), last_pressed_key, KeyState::Pressed, modifiers); self.event_queue.borrow_mut().push(event); } else { // Only send the character if we can print it (by ignoring characters like backspace) if !ch.is_control() { - match Window::char_to_script_key(ch) { + match keyutils::char_to_script_key(ch) { Some(key) => { let event = WindowEvent::KeyEvent(Some(ch), key, KeyState::Pressed, modifiers); self.event_queue.borrow_mut().push(event); } None => {} @@ -405,32 +435,32 @@ impl Window { VirtualKeyCode::RWin => self.toggle_modifier(GlutinKeyModifiers::RIGHT_SUPER), _ => {} } } fn handle_keyboard_input(&self, element_state: ElementState, virtual_key_code: VirtualKeyCode) { self.toggle_keyboard_modifiers(virtual_key_code); - if let Ok(key) = Window::glutin_key_to_script_key(virtual_key_code) { + if let Ok(key) = keyutils::glutin_key_to_script_key(virtual_key_code) { let state = match element_state { ElementState::Pressed => KeyState::Pressed, ElementState::Released => KeyState::Released, }; if element_state == ElementState::Pressed { - if is_printable(virtual_key_code) { + if keyutils::is_printable(virtual_key_code) { self.last_pressed_key.set(Some(key)); } } - let modifiers = Window::glutin_mods_to_script_mods(self.key_modifiers.get()); + let modifiers = keyutils::glutin_mods_to_script_mods(self.key_modifiers.get()); self.event_queue.borrow_mut().push(WindowEvent::KeyEvent(None, key, state, modifiers)); } } - fn handle_window_event(&self, event: winit::Event) { + fn glutin_event_to_servo_event(&self, event: winit::Event) { match event { Event::WindowEvent { event: winit::WindowEvent::ReceivedCharacter(ch), .. } => self.handle_received_character(ch), Event::WindowEvent { event: winit::WindowEvent::KeyboardInput { input: winit::KeyboardInput { @@ -457,23 +487,32 @@ impl Window { self.mouse_pos.set(TypedPoint2D::new(x as i32, y as i32)); self.event_queue.borrow_mut().push( WindowEvent::MouseWindowMoveEventClass(TypedPoint2D::new(x as f32, y as f32))); } Event::WindowEvent { event: winit::WindowEvent::MouseWheel { delta, phase, .. }, .. } => { - let (dx, dy) = match delta { + let (mut dx, mut dy) = match delta { MouseScrollDelta::LineDelta(dx, dy) => (dx, dy * LINE_HEIGHT), MouseScrollDelta::PixelDelta(dx, dy) => (dx, dy), }; + // Scroll events snap to the major axis of movement, with vertical + // preferred over horizontal. + if dy.abs() >= dx.abs() { + dx = 0.0; + } else { + dy = 0.0; + } + let scroll_location = ScrollLocation::Delta(TypedVector2D::new(dx, dy)); let phase = glutin_phase_to_touch_event_type(phase); - self.scroll_window(scroll_location, phase); + let event = WindowEvent::Scroll(scroll_location, self.mouse_pos.get(), phase); + self.event_queue.borrow_mut().push(event); }, Event::WindowEvent { event: winit::WindowEvent::Touch(touch), .. } => { use script_traits::TouchId; let phase = glutin_phase_to_touch_event_type(touch.phase); @@ -522,32 +561,16 @@ impl Window { } fn toggle_modifier(&self, modifier: GlutinKeyModifiers) { let mut modifiers = self.key_modifiers.get(); modifiers.toggle(modifier); self.key_modifiers.set(modifiers); } - /// Helper function to send a scroll event. - fn scroll_window(&self, mut scroll_location: ScrollLocation, phase: TouchEventType) { - // Scroll events snap to the major axis of movement, with vertical - // preferred over horizontal. - if let ScrollLocation::Delta(ref mut delta) = scroll_location { - if delta.y.abs() >= delta.x.abs() { - delta.x = 0.0; - } else { - delta.y = 0.0; - } - } - - let event = WindowEvent::Scroll(scroll_location, self.mouse_pos.get(), phase); - self.event_queue.borrow_mut().push(event); - } - /// Helper function to handle a click fn handle_mouse(&self, button: winit::MouseButton, action: winit::ElementState, coords: TypedPoint2D<i32, DevicePixel>) { use script_traits::MouseButton; let max_pixel_dist = 10.0 * self.hidpi_factor().get(); let event = match action { @@ -573,400 +596,139 @@ impl Window { }, Some(_) => mouse_up_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()) - } - - fn is_animating(&self) -> bool { - self.animation_state.get() == AnimationState::Animating && !self.suspended.get() + fn hidpi_factor(&self) -> TypedScale<f32, DeviceIndependentPixel, DevicePixel> { + match opts::get().device_pixels_per_px { + Some(device_pixels_per_px) => TypedScale::new(device_pixels_per_px), + None => match opts::get().output_file { + Some(_) => TypedScale::new(1.0), + None => self.platform_hidpi_factor() + } + } } - pub fn run<T>(&self, mut servo_callback: T) where T: FnMut() -> bool { + #[cfg(not(target_os = "windows"))] + fn platform_hidpi_factor(&self) -> TypedScale<f32, DeviceIndependentPixel, DevicePixel> { 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::Window(ref window, ..) => { + TypedScale::new(window.hidpi_factor()) } 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; - } - } + TypedScale::new(1.0) } } } - fn char_to_script_key(c: char) -> Option<constellation_msg::Key> { - match c { - ' ' => Some(Key::Space), - '"' => Some(Key::Apostrophe), - '\'' => Some(Key::Apostrophe), - '<' => Some(Key::Comma), - ',' => Some(Key::Comma), - '_' => Some(Key::Minus), - '-' => Some(Key::Minus), - '>' => Some(Key::Period), - '.' => Some(Key::Period), - '?' => Some(Key::Slash), - '/' => Some(Key::Slash), - '~' => Some(Key::GraveAccent), - '`' => Some(Key::GraveAccent), - ')' => Some(Key::Num0), - '0' => Some(Key::Num0), - '!' => Some(Key::Num1), - '1' => Some(Key::Num1), - '@' => Some(Key::Num2), - '2' => Some(Key::Num2), - '#' => Some(Key::Num3), - '3' => Some(Key::Num3), - '$' => Some(Key::Num4), - '4' => Some(Key::Num4), - '%' => Some(Key::Num5), - '5' => Some(Key::Num5), - '^' => Some(Key::Num6), - '6' => Some(Key::Num6), - '&' => Some(Key::Num7), - '7' => Some(Key::Num7), - '*' => Some(Key::Num8), - '8' => Some(Key::Num8), - '(' => Some(Key::Num9), - '9' => Some(Key::Num9), - ':' => Some(Key::Semicolon), - ';' => Some(Key::Semicolon), - '+' => Some(Key::Equal), - '=' => Some(Key::Equal), - 'A' => Some(Key::A), - 'a' => Some(Key::A), - 'B' => Some(Key::B), - 'b' => Some(Key::B), - 'C' => Some(Key::C), - 'c' => Some(Key::C), - 'D' => Some(Key::D), - 'd' => Some(Key::D), - 'E' => Some(Key::E), - 'e' => Some(Key::E), - 'F' => Some(Key::F), - 'f' => Some(Key::F), - 'G' => Some(Key::G), - 'g' => Some(Key::G), - 'H' => Some(Key::H), - 'h' => Some(Key::H), - 'I' => Some(Key::I), - 'i' => Some(Key::I), - 'J' => Some(Key::J), - 'j' => Some(Key::J), - 'K' => Some(Key::K), - 'k' => Some(Key::K), - 'L' => Some(Key::L), - 'l' => Some(Key::L), - 'M' => Some(Key::M), - 'm' => Some(Key::M), - 'N' => Some(Key::N), - 'n' => Some(Key::N), - 'O' => Some(Key::O), - 'o' => Some(Key::O), - 'P' => Some(Key::P), - 'p' => Some(Key::P), - 'Q' => Some(Key::Q), - 'q' => Some(Key::Q), - 'R' => Some(Key::R), - 'r' => Some(Key::R), - 'S' => Some(Key::S), - 's' => Some(Key::S), - 'T' => Some(Key::T), - 't' => Some(Key::T), - 'U' => Some(Key::U), - 'u' => Some(Key::U), - 'V' => Some(Key::V), - 'v' => Some(Key::V), - 'W' => Some(Key::W), - 'w' => Some(Key::W), - 'X' => Some(Key::X), - 'x' => Some(Key::X), - 'Y' => Some(Key::Y), - 'y' => Some(Key::Y), - 'Z' => Some(Key::Z), - 'z' => Some(Key::Z), - '{' => Some(Key::LeftBracket), - '[' => Some(Key::LeftBracket), - '|' => Some(Key::Backslash), - '\\' => Some(Key::Backslash), - '}' => Some(Key::RightBracket), - ']' => Some(Key::RightBracket), - _ => None - } + #[cfg(target_os = "windows")] + fn platform_hidpi_factor(&self) -> TypedScale<f32, DeviceIndependentPixel, DevicePixel> { + let hdc = unsafe { user32::GetDC(::std::ptr::null_mut()) }; + let ppi = unsafe { gdi32::GetDeviceCaps(hdc, winapi::wingdi::LOGPIXELSY) }; + TypedScale::new(ppi as f32 / 96.0) } - fn glutin_key_to_script_key(key: winit::VirtualKeyCode) -> Result<constellation_msg::Key, ()> { - // TODO(negge): add more key mappings - match key { - VirtualKeyCode::A => Ok(Key::A), - VirtualKeyCode::B => Ok(Key::B), - VirtualKeyCode::C => Ok(Key::C), - VirtualKeyCode::D => Ok(Key::D), - VirtualKeyCode::E => Ok(Key::E), - VirtualKeyCode::F => Ok(Key::F), - VirtualKeyCode::G => Ok(Key::G), - VirtualKeyCode::H => Ok(Key::H), - VirtualKeyCode::I => Ok(Key::I), - VirtualKeyCode::J => Ok(Key::J), - VirtualKeyCode::K => Ok(Key::K), - VirtualKeyCode::L => Ok(Key::L), - VirtualKeyCode::M => Ok(Key::M), - VirtualKeyCode::N => Ok(Key::N), - VirtualKeyCode::O => Ok(Key::O), - VirtualKeyCode::P => Ok(Key::P), - VirtualKeyCode::Q => Ok(Key::Q), - VirtualKeyCode::R => Ok(Key::R), - VirtualKeyCode::S => Ok(Key::S), - VirtualKeyCode::T => Ok(Key::T), - VirtualKeyCode::U => Ok(Key::U), - VirtualKeyCode::V => Ok(Key::V), - VirtualKeyCode::W => Ok(Key::W), - VirtualKeyCode::X => Ok(Key::X), - VirtualKeyCode::Y => Ok(Key::Y), - VirtualKeyCode::Z => Ok(Key::Z), - - VirtualKeyCode::Numpad0 => Ok(Key::Kp0), - VirtualKeyCode::Numpad1 => Ok(Key::Kp1), - VirtualKeyCode::Numpad2 => Ok(Key::Kp2), - VirtualKeyCode::Numpad3 => Ok(Key::Kp3), - VirtualKeyCode::Numpad4 => Ok(Key::Kp4), - VirtualKeyCode::Numpad5 => Ok(Key::Kp5), - VirtualKeyCode::Numpad6 => Ok(Key::Kp6), - VirtualKeyCode::Numpad7 => Ok(Key::Kp7), - VirtualKeyCode::Numpad8 => Ok(Key::Kp8), - VirtualKeyCode::Numpad9 => Ok(Key::Kp9), - - VirtualKeyCode::Key0 => Ok(Key::Num0), - VirtualKeyCode::Key1 => Ok(Key::Num1), - VirtualKeyCode::Key2 => Ok(Key::Num2), - VirtualKeyCode::Key3 => Ok(Key::Num3), - VirtualKeyCode::Key4 => Ok(Key::Num4), - VirtualKeyCode::Key5 => Ok(Key::Num5), - VirtualKeyCode::Key6 => Ok(Key::Num6), - VirtualKeyCode::Key7 => Ok(Key::Num7), - VirtualKeyCode::Key8 => Ok(Key::Num8), - VirtualKeyCode::Key9 => Ok(Key::Num9), - - VirtualKeyCode::Return => Ok(Key::Enter), - VirtualKeyCode::Space => Ok(Key::Space), - VirtualKeyCode::Escape => Ok(Key::Escape), - VirtualKeyCode::Equals => Ok(Key::Equal), - VirtualKeyCode::Minus => Ok(Key::Minus), - VirtualKeyCode::Back => Ok(Key::Backspace), - VirtualKeyCode::PageDown => Ok(Key::PageDown), - VirtualKeyCode::PageUp => Ok(Key::PageUp), - - VirtualKeyCode::Insert => Ok(Key::Insert), - VirtualKeyCode::Home => Ok(Key::Home), - VirtualKeyCode::Delete => Ok(Key::Delete), - VirtualKeyCode::End => Ok(Key::End), - - VirtualKeyCode::Left => Ok(Key::Left), - VirtualKeyCode::Up => Ok(Key::Up), - VirtualKeyCode::Right => Ok(Key::Right), - VirtualKeyCode::Down => Ok(Key::Down), + /// Has no effect on Android. + pub fn set_cursor(&self, cursor: CursorKind) { + match self.kind { + WindowKind::Window(ref window, ..) => { + use winit::MouseCursor; - VirtualKeyCode::LShift => Ok(Key::LeftShift), - VirtualKeyCode::LControl => Ok(Key::LeftControl), - VirtualKeyCode::LAlt => Ok(Key::LeftAlt), - VirtualKeyCode::LWin => Ok(Key::LeftSuper), - VirtualKeyCode::RShift => Ok(Key::RightShift), - VirtualKeyCode::RControl => Ok(Key::RightControl), - VirtualKeyCode::RAlt => Ok(Key::RightAlt), - VirtualKeyCode::RWin => Ok(Key::RightSuper), - - VirtualKeyCode::Apostrophe => Ok(Key::Apostrophe), - VirtualKeyCode::Backslash => Ok(Key::Backslash), - VirtualKeyCode::Comma => Ok(Key::Comma), - VirtualKeyCode::Grave => Ok(Key::GraveAccent), - VirtualKeyCode::LBracket => Ok(Key::LeftBracket), - VirtualKeyCode::Period => Ok(Key::Period), - VirtualKeyCode::RBracket => Ok(Key::RightBracket), - VirtualKeyCode::Semicolon => Ok(Key::Semicolon), - VirtualKeyCode::Slash => Ok(Key::Slash), - VirtualKeyCode::Tab => Ok(Key::Tab), - VirtualKeyCode::Subtract => Ok(Key::Minus), - - VirtualKeyCode::F1 => Ok(Key::F1), - VirtualKeyCode::F2 => Ok(Key::F2), - VirtualKeyCode::F3 => Ok(Key::F3), - VirtualKeyCode::F4 => Ok(Key::F4), - VirtualKeyCode::F5 => Ok(Key::F5), - VirtualKeyCode::F6 => Ok(Key::F6), - VirtualKeyCode::F7 => Ok(Key::F7), - VirtualKeyCode::F8 => Ok(Key::F8), - VirtualKeyCode::F9 => Ok(Key::F9), - VirtualKeyCode::F10 => Ok(Key::F10), - VirtualKeyCode::F11 => Ok(Key::F11), - VirtualKeyCode::F12 => Ok(Key::F12), - - VirtualKeyCode::NavigateBackward => Ok(Key::NavigateBackward), - VirtualKeyCode::NavigateForward => Ok(Key::NavigateForward), - _ => Err(()), + let glutin_cursor = match cursor { + CursorKind::Auto => MouseCursor::Default, + CursorKind::None => MouseCursor::NoneCursor, + CursorKind::Default => MouseCursor::Default, + CursorKind::Pointer => MouseCursor::Hand, + CursorKind::ContextMenu => MouseCursor::ContextMenu, + CursorKind::Help => MouseCursor::Help, + CursorKind::Progress => MouseCursor::Progress, + CursorKind::Wait => MouseCursor::Wait, + CursorKind::Cell => MouseCursor::Cell, + CursorKind::Crosshair => MouseCursor::Crosshair, + CursorKind::Text => MouseCursor::Text, + CursorKind::VerticalText => MouseCursor::VerticalText, + CursorKind::Alias => MouseCursor::Alias, + CursorKind::Copy => MouseCursor::Copy, + CursorKind::Move => MouseCursor::Move, + CursorKind::NoDrop => MouseCursor::NoDrop, + CursorKind::NotAllowed => MouseCursor::NotAllowed, + CursorKind::Grab => MouseCursor::Grab, + CursorKind::Grabbing => MouseCursor::Grabbing, + CursorKind::EResize => MouseCursor::EResize, + CursorKind::NResize => MouseCursor::NResize, + CursorKind::NeResize => MouseCursor::NeResize, + CursorKind::NwResize => MouseCursor::NwResize, + CursorKind::SResize => MouseCursor::SResize, + CursorKind::SeResize => MouseCursor::SeResize, + CursorKind::SwResize => MouseCursor::SwResize, + CursorKind::WResize => MouseCursor::WResize, + CursorKind::EwResize => MouseCursor::EwResize, + CursorKind::NsResize => MouseCursor::NsResize, + CursorKind::NeswResize => MouseCursor::NeswResize, + CursorKind::NwseResize => MouseCursor::NwseResize, + CursorKind::ColResize => MouseCursor::ColResize, + CursorKind::RowResize => MouseCursor::RowResize, + CursorKind::AllScroll => MouseCursor::AllScroll, + CursorKind::ZoomIn => MouseCursor::ZoomIn, + CursorKind::ZoomOut => MouseCursor::ZoomOut, + }; + window.set_cursor(glutin_cursor); + } + WindowKind::Headless(..) => {} } } - - fn glutin_mods_to_script_mods(modifiers: GlutinKeyModifiers) -> constellation_msg::KeyModifiers { - let mut result = constellation_msg::KeyModifiers::empty(); - if modifiers.intersects(GlutinKeyModifiers::LEFT_SHIFT | GlutinKeyModifiers::RIGHT_SHIFT) { - result.insert(KeyModifiers::SHIFT); - } - if modifiers.intersects(GlutinKeyModifiers::LEFT_CONTROL | GlutinKeyModifiers::RIGHT_CONTROL) { - result.insert(KeyModifiers::CONTROL); - } - if modifiers.intersects(GlutinKeyModifiers::LEFT_ALT | GlutinKeyModifiers::RIGHT_ALT) { - result.insert(KeyModifiers::ALT); - } - if modifiers.intersects(GlutinKeyModifiers::LEFT_SUPER | GlutinKeyModifiers::RIGHT_SUPER) { - result.insert(KeyModifiers::SUPER); - } - result - } - - #[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) { - } } impl WindowMethods for Window { fn gl(&self) -> Rc<gl::Gl> { self.gl.clone() } - fn framebuffer_size(&self) -> DeviceUintSize { - (self.inner_size.get().to_f32() * self.hidpi_factor()).to_u32() - } - - fn window_rect(&self) -> DeviceUintRect { - let size = self.framebuffer_size(); - let origin = TypedPoint2D::zero(); - DeviceUintRect::new(origin, size) - } - - fn client_window(&self, _: BrowserId) -> (DeviceUintSize, DeviceIntPoint) { - let (size, point) = match self.kind { - WindowKind::Window(ref window, ..) => { + fn get_coordinates(&self) -> EmbedderCoordinates { + let dpr = self.hidpi_factor(); + match self.kind { + WindowKind::Window(ref window, _) => { // TODO(ajeffrey): can this fail? let (width, height) = window.get_outer_size().expect("Failed to get window outer size."); - let size = TypedSize2D::new(width as f32, height as f32); - // TODO(ajeffrey): can this fail? - let (x, y) = window.get_position().expect("Failed to get window position."); - let origin = TypedPoint2D::new(x as f32, y as f32); - (size, origin) - } - WindowKind::Headless(ref context) => { - let size = TypedSize2D::new(context.width as f32, context.height as f32); - let origin = TypedPoint2D::zero(); - (size, origin) - } - }; - let dpr = self.hidpi_factor(); - ((size * dpr).to_u32(), (point * dpr).to_i32()) - } + let (x, y) = window.get_position().unwrap_or((0, 0)); + let win_size = (TypedSize2D::new(width as f32, height as f32) * dpr).to_u32(); + let win_origin = (TypedPoint2D::new(x as f32, y as f32) * dpr).to_i32(); + let screen = (self.screen_size.to_f32() * dpr).to_u32(); - fn screen_size(&self, _: BrowserId) -> DeviceUintSize { - (self.screen_size.to_f32() * self.hidpi_factor()).to_u32() - } - - fn screen_avail_size(&self, browser_id: BrowserId) -> DeviceUintSize { - // FIXME: Glutin doesn't have API for available size. Fallback to screen size - self.screen_size(browser_id) - } + let (width, height) = window.get_inner_size().expect("Failed to get window inner size."); + let inner_size = (TypedSize2D::new(width as f32, height as f32) * dpr).to_u32(); - fn set_animation_state(&self, state: AnimationState) { - self.animation_state.set(state); - } - - 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(..) => {} - } - } + let viewport = DeviceUintRect::new(TypedPoint2D::zero(), inner_size); - 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); + EmbedderCoordinates { + viewport: viewport, + framebuffer: inner_size, + window: (win_size, win_origin), + screen: screen, + // FIXME: Glutin doesn't have API for available size. Fallback to screen size + screen_avail: screen, + hidpi_factor: dpr, } }, - WindowKind::Headless(..) => {} + WindowKind::Headless(ref context) => { + let size = (TypedSize2D::new(context.width, context.height).to_f32() * dpr).to_u32(); + EmbedderCoordinates { + viewport: DeviceUintRect::new(TypedPoint2D::zero(), size), + framebuffer: size, + window: (size, TypedPoint2D::zero()), + screen: size, + screen_avail: size, + hidpi_factor: dpr, + } + } } - self.fullscreen.set(state); } fn present(&self) { match self.kind { WindowKind::Window(ref window, ..) => { if let Err(err) = window.swap_buffers() { warn!("Failed to swap window buffers ({}).", err); } @@ -1006,378 +768,29 @@ impl WindowMethods for Window { proxy: self.proxy.clone(), }) } } Box::new(GlutinEventLoopWaker::new(&self)) } - #[cfg(not(target_os = "windows"))] - fn hidpi_factor(&self) -> TypedScale<f32, DeviceIndependentPixel, DevicePixel> { - match self.kind { - WindowKind::Window(ref window, ..) => { - TypedScale::new(window.hidpi_factor()) - } - WindowKind::Headless(..) => { - TypedScale::new(1.0) - } - } - } - - #[cfg(target_os = "windows")] - fn hidpi_factor(&self) -> TypedScale<f32, DeviceIndependentPixel, DevicePixel> { - let hdc = unsafe { user32::GetDC(::std::ptr::null_mut()) }; - let ppi = unsafe { gdi32::GetDeviceCaps(hdc, winapi::wingdi::LOGPIXELSY) }; - TypedScale::new(ppi as f32 / 96.0) - } - - 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 load_error(&self, _: BrowserId, _: NetError, _: String) { - } - - fn head_parsed(&self, _: BrowserId) { - } - - /// Has no effect on Android. - fn set_cursor(&self, cursor: CursorKind) { - match self.kind { - WindowKind::Window(ref window, ..) => { - use winit::MouseCursor; - - let glutin_cursor = match cursor { - CursorKind::Auto => MouseCursor::Default, - CursorKind::None => MouseCursor::NoneCursor, - CursorKind::Default => MouseCursor::Default, - CursorKind::Pointer => MouseCursor::Hand, - CursorKind::ContextMenu => MouseCursor::ContextMenu, - CursorKind::Help => MouseCursor::Help, - CursorKind::Progress => MouseCursor::Progress, - CursorKind::Wait => MouseCursor::Wait, - CursorKind::Cell => MouseCursor::Cell, - CursorKind::Crosshair => MouseCursor::Crosshair, - CursorKind::Text => MouseCursor::Text, - CursorKind::VerticalText => MouseCursor::VerticalText, - CursorKind::Alias => MouseCursor::Alias, - CursorKind::Copy => MouseCursor::Copy, - CursorKind::Move => MouseCursor::Move, - CursorKind::NoDrop => MouseCursor::NoDrop, - CursorKind::NotAllowed => MouseCursor::NotAllowed, - CursorKind::Grab => MouseCursor::Grab, - CursorKind::Grabbing => MouseCursor::Grabbing, - CursorKind::EResize => MouseCursor::EResize, - CursorKind::NResize => MouseCursor::NResize, - CursorKind::NeResize => MouseCursor::NeResize, - CursorKind::NwResize => MouseCursor::NwResize, - CursorKind::SResize => MouseCursor::SResize, - CursorKind::SeResize => MouseCursor::SeResize, - CursorKind::SwResize => MouseCursor::SwResize, - CursorKind::WResize => MouseCursor::WResize, - CursorKind::EwResize => MouseCursor::EwResize, - CursorKind::NsResize => MouseCursor::NsResize, - CursorKind::NeswResize => MouseCursor::NeswResize, - CursorKind::NwseResize => MouseCursor::NwseResize, - CursorKind::ColResize => MouseCursor::ColResize, - CursorKind::RowResize => MouseCursor::RowResize, - CursorKind::AllScroll => MouseCursor::AllScroll, - CursorKind::ZoomIn => MouseCursor::ZoomIn, - CursorKind::ZoomOut => MouseCursor::ZoomOut, - }; - window.set_cursor(glutin_cursor); - } - WindowKind::Headless(..) => {} - } - } - - fn set_favicon(&self, _: BrowserId, _: ServoUrl) { + fn set_animation_state(&self, state: AnimationState) { + self.animation_state.set(state); } fn prepare_for_composite(&self, _width: Length<u32, DevicePixel>, _height: Length<u32, DevicePixel>) -> bool { true } - /// Helper function to handle keyboard events. - fn handle_key(&self, _: Option<BrowserId>, ch: Option<char>, key: Key, mods: constellation_msg::KeyModifiers) { - 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.framebuffer_size() - .to_f32() - .to_untyped() - .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.framebuffer_size() - .to_f32() - .to_untyped() - .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 supports_clipboard(&self) -> bool { true } - - fn handle_panic(&self, _: BrowserId, _reason: String, _backtrace: Option<String>) { - // Nothing to do here yet. The crash has already been reported on the console. - } } fn glutin_phase_to_touch_event_type(phase: TouchPhase) -> TouchEventType { match phase { TouchPhase::Started => TouchEventType::Down, TouchPhase::Moved => TouchEventType::Move, TouchPhase::Ended => TouchEventType::Up, TouchPhase::Cancelled => TouchEventType::Cancel, } } - -fn is_printable(key_code: VirtualKeyCode) -> bool { - use winit::VirtualKeyCode::*; - match key_code { - Escape | - F1 | - F2 | - F3 | - F4 | - F5 | - F6 | - F7 | - F8 | - F9 | - F10 | - F11 | - F12 | - F13 | - F14 | - F15 | - Snapshot | - Scroll | - Pause | - Insert | - Home | - Delete | - End | - PageDown | - PageUp | - Left | - Up | - Right | - Down | - Back | - LAlt | - LControl | - LMenu | - LShift | - LWin | - Mail | - MediaSelect | - MediaStop | - Mute | - MyComputer | - NavigateForward | - NavigateBackward | - NextTrack | - NoConvert | - PlayPause | - Power | - PrevTrack | - RAlt | - RControl | - RMenu | - RShift | - RWin | - Sleep | - Stop | - VolumeDown | - VolumeUp | - Wake | - WebBack | - WebFavorites | - WebForward | - WebHome | - WebRefresh | - WebSearch | - WebStop => false, - _ => true, - } -} - -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() - }) - }) -}
--- a/servo/ports/servo/main.rs +++ b/servo/ports/servo/main.rs @@ -24,23 +24,21 @@ extern crate backtrace; extern crate compositing; extern crate euclid; #[cfg(target_os = "windows")] extern crate gdi32; extern crate gleam; extern crate glutin; // The window backed by glutin #[macro_use] extern crate log; extern crate msg; -extern crate net_traits; #[cfg(any(target_os = "linux", target_os = "macos"))] extern crate osmesa_sys; extern crate script_traits; extern crate servo; extern crate servo_config; extern crate servo_geometry; -extern crate servo_url; #[cfg(all(feature = "unstable", not(target_os = "android")))] #[macro_use] extern crate sig; extern crate style_traits; extern crate tinyfiledialogs; extern crate webrender_api; extern crate winit; #[cfg(target_os = "windows")] extern crate winapi; @@ -58,16 +56,18 @@ use servo::config::servo_version; use servo::ipc_channel::ipc; use servo::servo_config::prefs::PREFS; use servo::servo_url::ServoUrl; use std::env; use std::panic; use std::process; use std::thread; +mod browser; + pub mod platform { #[cfg(target_os = "macos")] pub use platform::macos::deinit; #[cfg(target_os = "macos")] pub mod macos; #[cfg(not(target_os = "macos"))] @@ -158,47 +158,67 @@ fn main() { if opts::get().is_printing_version { println!("{}", servo_version()); process::exit(0); } let window = glutin_app::create_window(); + let mut browser = browser::Browser::new(window.clone()); + // If the url is not provided, we fallback to the homepage in PREFS, // or a blank page in case the homepage is not set either. let cwd = env::current_dir().unwrap(); let cmdline_url = opts::get().url.clone(); let pref_url = PREFS.get("shell.homepage").as_string() .and_then(|str| parse_url_or_filename(&cwd, str).ok()); let blank_url = ServoUrl::parse("about:blank").ok(); let target_url = cmdline_url.or(pref_url).or(blank_url).unwrap(); let mut servo = Servo::new(window.clone()); let (sender, receiver) = ipc::channel().unwrap(); servo.handle_events(vec![WindowEvent::NewBrowser(target_url, sender)]); let browser_id = receiver.recv().unwrap(); - window.set_browser_id(browser_id); + browser.set_browser_id(browser_id); servo.handle_events(vec![WindowEvent::SelectBrowser(browser_id)]); servo.setup_logging(); window.run(|| { - let events = window.get_events(); - let need_resize = events.iter().any(|e| match *e { + let win_events = window.get_events(); + + // FIXME: this could be handled by Servo. We don't need + // a repaint_synchronously function exposed. + let need_resize = win_events.iter().any(|e| match *e { WindowEvent::Resize => true, - _ => false + _ => false, }); - let stop = !servo.handle_events(events); + + browser.handle_window_events(win_events); + + let mut servo_events = servo.get_events(); + loop { + browser.handle_servo_events(servo_events); + servo.handle_events(browser.get_events()); + if browser.shutdown_requested() { + return true; + } + servo_events = servo.get_events(); + if servo_events.is_empty() { + break; + } + } + if need_resize { servo.repaint_synchronously(); } - stop + false }); servo.deinit(); platform::deinit() } #[cfg(target_os = "android")]
--- a/servo/resources/prefs.json +++ b/servo/resources/prefs.json @@ -58,16 +58,15 @@ "js.werror.enabled": false, "layout.animations.test.enabled": false, "layout.columns.enabled": false, "layout.viewport.enabled": false, "layout.writing-mode.enabled": false, "network.http-cache.disabled": false, "network.mime.sniff": false, "session-history.max-length": 20, - "shell.builtin-key-shortcuts.enabled": true, "shell.homepage": "https://servo.org", "shell.keep_screen_on.enabled": false, "shell.native-orientation": "both", "shell.native-titlebar.enabled": true, "shell.searchpage": "https://duckduckgo.com/html/?q=%s", "webgl.testing.context_creation_error": false }