diff options
author | Patrick Walton <pcwalton@mimiga.net> | 2014-11-24 17:23:30 -0800 |
---|---|---|
committer | Patrick Walton <pcwalton@mimiga.net> | 2014-12-10 08:35:47 -0800 |
commit | 8b2aadc30be9e7da163b0eb5b88f2988f5b43f9e (patch) | |
tree | 722684b4013644a1ccc3d635f45f3521dd66dc08 | |
parent | 315e166cf7f24f4586e8ce863be597ce0a3f34c9 (diff) | |
download | servo-8b2aadc30be9e7da163b0eb5b88f2988f5b43f9e.tar.gz servo-8b2aadc30be9e7da163b0eb5b88f2988f5b43f9e.zip |
ports/cef: Implement accelerated compositing for the CEF port.
35 files changed, 1747 insertions, 643 deletions
diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index 44015c8012c..c5a11fa1605 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -3,23 +3,23 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use compositor_layer::{CompositorData, CompositorLayer, DoesntWantScrollEvents}; -use compositor_layer::WantsScrollEvents; -use compositor_task::{ChangeReadyState, ChangePaintState, CompositorEventListener}; -use compositor_task::{CompositorProxy, CompositorReceiver, CompositorTask}; -use compositor_task::{CreateOrUpdateDescendantLayer, CreateOrUpdateRootLayer, Exit}; -use compositor_task::{FrameTreeUpdateMsg, GetGraphicsMetadata, LayerProperties}; +use compositor_layer::{WantsScrollEvents}; +use compositor_task::{ChangePageLoadData, ChangePageTitle, ChangePaintState, ChangeReadyState}; +use compositor_task::{CompositorEventListener, CompositorProxy, CompositorReceiver}; +use compositor_task::{CompositorTask, CreateOrUpdateDescendantLayer, CreateOrUpdateRootLayer}; +use compositor_task::{Exit, FrameTreeUpdateMsg, GetGraphicsMetadata, LayerProperties}; use compositor_task::{LoadComplete, Msg, Paint, PaintMsgDiscarded, ScrollFragmentPoint}; use compositor_task::{ScrollTimeout, SetIds, SetLayerOrigin, ShutdownComplete}; -use constellation::{SendableFrameTree, FrameTreeDiff}; +use constellation::{FrameId, FrameTreeDiff, SendableFrameTree}; use pipeline::CompositionPipeline; use scrolling::ScrollingTimerProxy; use windowing; -use windowing::{IdleWindowEvent, LoadUrlWindowEvent, MouseWindowClickEvent}; -use windowing::{MouseWindowEvent, MouseWindowEventClass, MouseWindowMouseDownEvent}; -use windowing::{MouseWindowMouseUpEvent, MouseWindowMoveEventClass, NavigationWindowEvent}; +use windowing::{IdleWindowEvent, InitializeCompositingWindowEvent}; +use windowing::{KeyEvent, LoadUrlWindowEvent, MouseWindowClickEvent, MouseWindowEvent}; +use windowing::{MouseWindowEventClass, MouseWindowMouseDownEvent, MouseWindowMouseUpEvent}; +use windowing::{MouseWindowMoveEventClass, NavigationWindowEvent, PinchZoomWindowEvent}; use windowing::{QuitWindowEvent, RefreshWindowEvent, ResizeWindowEvent, ScrollWindowEvent}; use windowing::{WindowEvent, WindowMethods, WindowNavigateMsg, ZoomWindowEvent}; -use windowing::{PinchZoomWindowEvent, KeyEvent}; use azure::azure_hl; use std::cmp; @@ -40,11 +40,11 @@ use gleam::gl::types::{GLint, GLsizei}; use gleam::gl; use script_traits::{ViewportMsg, ScriptControlChan}; use servo_msg::compositor_msg::{Blank, Epoch, FinishedLoading, IdlePaintState, LayerId}; -use servo_msg::compositor_msg::{ReadyState, PaintingPaintState, PaintState, Scrollable}; -use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, LoadUrlMsg}; -use servo_msg::constellation_msg::{NavigateMsg, LoadData, PipelineId, ResizedWindowMsg}; -use servo_msg::constellation_msg::{WindowSizeData, KeyState, Key, KeyModifiers}; -use servo_msg::constellation_msg; +use servo_msg::compositor_msg::{ReadyState, PaintState, PaintingPaintState, Scrollable}; +use servo_msg::constellation_msg::{mod, ConstellationChan, ExitMsg}; +use servo_msg::constellation_msg::{GetPipelineTitleMsg, Key, KeyModifiers, KeyState, LoadData}; +use servo_msg::constellation_msg::{LoadUrlMsg, NavigateMsg, PipelineId, ResizedWindowMsg}; +use servo_msg::constellation_msg::{WindowSizeData}; use servo_util::geometry::{PagePx, ScreenPx, ViewportPx}; use servo_util::memory::MemoryProfilerChan; use servo_util::opts; @@ -58,6 +58,7 @@ use std::slice::bytes::copy_memory; use time::{precise_time_ns, precise_time_s}; use url::Url; +/// NB: Never block on the constellation, because sometimes the constellation blocks on us. pub struct IOCompositor<Window: WindowMethods> { /// The application window. window: Rc<Window>, @@ -65,8 +66,9 @@ pub struct IOCompositor<Window: WindowMethods> { /// The port on which we receive messages. port: Box<CompositorReceiver>, - /// The render context. - context: RenderContext, + /// The render context. This will be `None` if the windowing system has not yet sent us a + /// `PrepareRenderingEvent`. + context: Option<RenderContext>, /// The root pipeline. root_pipeline: Option<CompositionPipeline>, @@ -177,13 +179,10 @@ impl<Window: WindowMethods> IOCompositor<Window> { // display list. This is only here because we don't have that logic in the painter yet. let window_size = window.framebuffer_size(); let hidpi_factor = window.hidpi_factor(); - let context = CompositorTask::create_graphics_context(&window.native_metadata()); - - let show_debug_borders = opts::get().show_debug_borders; IOCompositor { window: window, port: receiver, - context: rendergl::RenderContext::new(context, show_debug_borders), + context: None, root_pipeline: None, scene: Scene::new(Rect { origin: Zero::zero(), @@ -262,6 +261,14 @@ impl<Window: WindowMethods> IOCompositor<Window> { self.change_paint_state(pipeline_id, paint_state); } + (ChangePageTitle(pipeline_id, title), NotShuttingDown) => { + self.change_page_title(pipeline_id, title); + } + + (ChangePageLoadData(frame_id, load_data), NotShuttingDown) => { + self.change_page_load_data(frame_id, load_data); + } + (PaintMsgDiscarded, NotShuttingDown) => { self.remove_outstanding_paint_msg(); } @@ -305,13 +312,18 @@ impl<Window: WindowMethods> IOCompositor<Window> { self.scroll_fragment_to_point(pipeline_id, layer_id, point); } - (LoadComplete(..), NotShuttingDown) => { + (LoadComplete, NotShuttingDown) => { self.got_load_complete_message = true; // If we're painting in headless mode, schedule a recomposite. if opts::get().output_file.is_some() { self.composite_if_necessary(); } + + // Inform the embedder that the load has finished. + // + // TODO(pcwalton): Specify which frame's load completed. + self.window.load_end(); } (ScrollTimeout(timestamp), NotShuttingDown) => { @@ -371,6 +383,14 @@ impl<Window: WindowMethods> IOCompositor<Window> { self.window.set_paint_state(paint_state); } + fn change_page_title(&mut self, _: PipelineId, title: Option<String>) { + self.window.set_page_title(title); + } + + fn change_page_load_data(&mut self, _: FrameId, load_data: LoadData) { + self.window.set_page_load_data(load_data); + } + fn all_pipelines_in_idle_paint_state(&self) -> bool { if self.ready_states.len() == 0 { return false; @@ -629,7 +649,11 @@ impl<Window: WindowMethods> IOCompositor<Window> { IdleWindowEvent => {} RefreshWindowEvent => { - self.composite_if_necessary() + self.composite(); + } + + InitializeCompositingWindowEvent => { + self.initialize_compositing(); } ResizeWindowEvent(size) => { @@ -678,6 +702,8 @@ impl<Window: WindowMethods> IOCompositor<Window> { } fn on_resize_window_event(&mut self, new_size: TypedSize2D<DevicePixel, uint>) { + debug!("compositor resizing to {}", new_size.to_untyped()); + // A size change could also mean a resolution change. let new_hidpi_factor = self.window.hidpi_factor(); if self.hidpi_factor != new_hidpi_factor { @@ -960,6 +986,10 @@ impl<Window: WindowMethods> IOCompositor<Window> { } fn composite(&mut self) { + if !self.window.prepare_for_composite() { + return + } + let output_image = opts::get().output_file.is_some() && self.is_ready_to_paint_image_output(); @@ -995,7 +1025,14 @@ impl<Window: WindowMethods> IOCompositor<Window> { // paint the scene. match self.scene.root { Some(ref layer) => { - rendergl::render_scene(layer.clone(), self.context, &self.scene); + match self.context { + None => { + debug!("compositor: not compositing because context not yet set up") + } + Some(context) => { + rendergl::render_scene(layer.clone(), context, &self.scene); + } + } } None => {} } @@ -1053,6 +1090,12 @@ impl<Window: WindowMethods> IOCompositor<Window> { } } + fn initialize_compositing(&mut self) { + let context = CompositorTask::create_graphics_context(&self.window.native_metadata()); + let show_debug_borders = opts::get().show_debug_borders; + self.context = Some(rendergl::RenderContext::new(context, show_debug_borders)) + } + fn find_topmost_layer_at_point_for_layer(&self, layer: Rc<Layer<CompositorData>>, point: TypedPoint2D<LayerPixel, f32>) @@ -1229,4 +1272,17 @@ impl<Window> CompositorEventListener for IOCompositor<Window> where Window: Wind self.scrolling_timer.shutdown(); } + + fn pinch_zoom_level(&self) -> f32 { + self.viewport_zoom.get() as f32 + } + + fn get_title_for_main_frame(&self) { + let root_pipeline_id = match self.root_pipeline { + None => return, + Some(ref root_pipeline) => root_pipeline.id, + }; + let ConstellationChan(ref chan) = self.constellation_chan; + chan.send(GetPipelineTitleMsg(root_pipeline_id)); + } } diff --git a/components/compositing/compositor_task.rs b/components/compositing/compositor_task.rs index 89aed5defa0..e7d851ceaf6 100644 --- a/components/compositing/compositor_task.rs +++ b/components/compositing/compositor_task.rs @@ -5,7 +5,7 @@ //! Communication with the compositor task. pub use windowing; -pub use constellation::{SendableFrameTree, FrameTreeDiff}; +pub use constellation::{FrameId, SendableFrameTree, FrameTreeDiff}; use compositor; use headless; @@ -19,7 +19,7 @@ use layers::platform::surface::{NativeCompositingGraphicsContext, NativeGraphics use layers::layers::LayerBufferSet; use servo_msg::compositor_msg::{Epoch, LayerId, LayerMetadata, ReadyState}; use servo_msg::compositor_msg::{PaintListener, PaintState, ScriptListener, ScrollPolicy}; -use servo_msg::constellation_msg::{ConstellationChan, PipelineId}; +use servo_msg::constellation_msg::{ConstellationChan, LoadData, PipelineId}; use servo_util::memory::MemoryProfilerChan; use servo_util::time::TimeProfilerChan; use std::comm::{channel, Sender, Receiver}; @@ -81,8 +81,13 @@ impl ScriptListener for Box<CompositorProxy+'static+Send> { fn dup(&mut self) -> Box<ScriptListener+'static> { box self.clone_compositor_proxy() as Box<ScriptListener+'static> } + + fn set_title(&mut self, pipeline_id: PipelineId, title: Option<String>) { + self.send(ChangePageTitle(pipeline_id, title)) + } } +/// Information about each layer that the compositor keeps. pub struct LayerProperties { pub pipeline_id: PipelineId, pub epoch: Epoch, @@ -184,9 +189,13 @@ pub enum Msg { ChangeReadyState(PipelineId, ReadyState), /// Alerts the compositor to the current status of painting. ChangePaintState(PipelineId, PaintState), - /// Alerts the compositor that the PaintMsg has been discarded. + /// Alerts the compositor that the current page has changed its title. + ChangePageTitle(PipelineId, Option<String>), + /// Alerts the compositor that the current page has changed its load data (including URL). + ChangePageLoadData(FrameId, LoadData), + /// Alerts the compositor that a `PaintMsg` has been discarded. PaintMsgDiscarded, - /// Sets the channel to the current layout and paint tasks, along with their id + /// Sets the channel to the current layout and paint tasks, along with their ID. SetIds(SendableFrameTree, Sender<()>, ConstellationChan), /// Sends an updated version of the frame tree. FrameTreeUpdateMsg(FrameTreeDiff, Sender<()>), @@ -210,6 +219,8 @@ impl Show for Msg { Paint(..) => write!(f, "Paint"), ChangeReadyState(..) => write!(f, "ChangeReadyState"), ChangePaintState(..) => write!(f, "ChangePaintState"), + ChangePageTitle(..) => write!(f, "ChangePageTitle"), + ChangePageLoadData(..) => write!(f, "ChangePageLoadData"), PaintMsgDiscarded(..) => write!(f, "PaintMsgDiscarded"), SetIds(..) => write!(f, "SetIds"), FrameTreeUpdateMsg(..) => write!(f, "FrameTreeUpdateMsg"), @@ -269,5 +280,8 @@ pub trait CompositorEventListener { fn handle_event(&mut self, event: WindowEvent) -> bool; fn repaint_synchronously(&mut self); fn shutdown(&mut self); + fn pinch_zoom_level(&self) -> f32; + /// Requests that the compositor send the title for the main frame as soon as possible. + fn get_title_for_main_frame(&self); } diff --git a/components/compositing/constellation.rs b/components/compositing/constellation.rs index 5155801f553..a993394de13 100644 --- a/components/compositing/constellation.rs +++ b/components/compositing/constellation.rs @@ -4,7 +4,8 @@ use pipeline::{Pipeline, CompositionPipeline}; -use compositor_task::{CompositorProxy, FrameTreeUpdateMsg, LoadComplete, ShutdownComplete, SetLayerOrigin, SetIds}; +use compositor_task::{ChangePageLoadData, ChangePageTitle, CompositorProxy, FrameTreeUpdateMsg}; +use compositor_task::{LoadComplete, SetLayerOrigin, SetIds, ShutdownComplete}; use devtools_traits; use devtools_traits::DevtoolsControlChan; use geom::rect::{Rect, TypedRect}; @@ -14,16 +15,16 @@ use gfx::paint_task; use layers::geometry::DevicePixel; use layout_traits::{LayoutControlChan, LayoutTaskFactory, ExitNowMsg}; use libc; -use script_traits; -use script_traits::{ResizeMsg, ResizeInactiveMsg, ExitPipelineMsg, SendEventMsg}; +use script_traits::{mod, GetTitleMsg, ResizeMsg, ResizeInactiveMsg, ExitPipelineMsg, SendEventMsg}; use script_traits::{ScriptControlChan, ScriptTaskFactory}; use servo_msg::compositor_msg::LayerId; use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, FailureMsg, Failure, FrameRectMsg}; +use servo_msg::constellation_msg::{GetPipelineTitleMsg}; use servo_msg::constellation_msg::{IFrameSandboxState, IFrameUnsandboxed, InitLoadUrlMsg}; -use servo_msg::constellation_msg::{LoadCompleteMsg, LoadUrlMsg, LoadData, Msg, NavigateMsg}; -use servo_msg::constellation_msg::{NavigationType, PipelineId, PainterReadyMsg, ResizedWindowMsg}; +use servo_msg::constellation_msg::{KeyEvent, Key, KeyState, KeyModifiers, LoadCompleteMsg}; +use servo_msg::constellation_msg::{LoadData, LoadUrlMsg, Msg, NavigateMsg, NavigationType}; +use servo_msg::constellation_msg::{PainterReadyMsg, PipelineId, ResizedWindowMsg}; use servo_msg::constellation_msg::{ScriptLoadedURLInIFrameMsg, SubpageId, WindowSizeData}; -use servo_msg::constellation_msg::{KeyEvent, Key, KeyState, KeyModifiers}; use servo_msg::constellation_msg; use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient}; use servo_net::resource_task::ResourceTask; @@ -76,6 +77,10 @@ pub struct Constellation<LTF, STF> { /// The next free ID to assign to a pipeline. next_pipeline_id: PipelineId, + /// The next free ID to assign to a frame. + next_frame_id: FrameId, + + /// Navigation operations that are in progress. pending_frames: Vec<FrameChange>, pending_sizes: HashMap<(PipelineId, SubpageId), TypedRect<PagePx, f32>>, @@ -86,17 +91,28 @@ pub struct Constellation<LTF, STF> { pub window_size: WindowSizeData, } -/// Stores the Id of the outermost frame's pipeline, along with a vector of children frames +/// A unique ID used to identify a frame. +pub struct FrameId(u32); + +/// One frame in the hierarchy. struct FrameTree { + /// The ID of this frame. + pub id: FrameId, + /// The pipeline for this frame. pub pipeline: Rc<Pipeline>, + /// The parent frame's pipeline. pub parent: RefCell<Option<Rc<Pipeline>>>, + /// A vector of child frames. pub children: RefCell<Vec<ChildFrameTree>>, + /// Whether this frame has a compositor layer. pub has_compositor_layer: Cell<bool>, } impl FrameTree { - fn new(pipeline: Rc<Pipeline>, parent_pipeline: Option<Rc<Pipeline>>) -> FrameTree { + fn new(id: FrameId, pipeline: Rc<Pipeline>, parent_pipeline: Option<Rc<Pipeline>>) + -> FrameTree { FrameTree { + id: id, pipeline: pipeline, parent: RefCell::new(parent_pipeline), children: RefCell::new(vec!()), @@ -234,16 +250,19 @@ impl Iterator<Rc<FrameTree>> for FrameTreeIterator { /// Represents the portion of a page that is changing in navigating. struct FrameChange { + /// The old pipeline ID. pub before: Option<PipelineId>, + /// The resulting frame tree after navigation. pub after: Rc<FrameTree>, + /// The kind of navigation that is occurring. pub navigation_type: NavigationType, } /// Stores the Id's of the pipelines previous and next in the browser's history struct NavigationContext { - pub previous: Vec<Rc<FrameTree>>, - pub next: Vec<Rc<FrameTree>>, - pub current: Option<Rc<FrameTree>>, + previous: Vec<Rc<FrameTree>>, + next: Vec<Rc<FrameTree>>, + current: Option<Rc<FrameTree>>, } impl NavigationContext { @@ -258,29 +277,30 @@ impl NavigationContext { /* Note that the following two methods can fail. They should only be called * * when it is known that there exists either a previous page or a next page. */ - fn back(&mut self) -> Rc<FrameTree> { + fn back(&mut self, compositor_proxy: &mut CompositorProxy) -> Rc<FrameTree> { self.next.push(self.current.take().unwrap()); let prev = self.previous.pop().unwrap(); - self.current = Some(prev.clone()); + self.set_current(prev.clone(), compositor_proxy); prev } - fn forward(&mut self) -> Rc<FrameTree> { + fn forward(&mut self, compositor_proxy: &mut CompositorProxy) -> Rc<FrameTree> { self.previous.push(self.current.take().unwrap()); let next = self.next.pop().unwrap(); - self.current = Some(next.clone()); + self.set_current(next.clone(), compositor_proxy); next } /// Loads a new set of page frames, returning all evicted frame trees - fn load(&mut self, frame_tree: Rc<FrameTree>) -> Vec<Rc<FrameTree>> { + fn load(&mut self, frame_tree: Rc<FrameTree>, compositor_proxy: &mut CompositorProxy) + -> Vec<Rc<FrameTree>> { debug!("navigating to {}", frame_tree.pipeline.id); let evicted = replace(&mut self.next, vec!()); match self.current.take() { Some(current) => self.previous.push(current), None => (), } - self.current = Some(frame_tree); + self.set_current(frame_tree, compositor_proxy); evicted } @@ -308,6 +328,14 @@ impl NavigationContext { frame_tree.contains(pipeline_id) }) } + + /// Always use this method to set the currently-displayed frame. It correctly informs the + /// compositor of the new URLs. + fn set_current(&mut self, new_frame: Rc<FrameTree>, compositor_proxy: &mut CompositorProxy) { + self.current = Some(new_frame.clone()); + compositor_proxy.send(ChangePageLoadData(new_frame.id, + new_frame.pipeline.load_data.clone())); + } } impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> { @@ -334,6 +362,7 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> { pipelines: HashMap::new(), navigation_context: NavigationContext::new(), next_pipeline_id: PipelineId(0), + next_frame_id: FrameId(0), pending_frames: vec!(), pending_sizes: HashMap::new(), time_profiler_chan: time_profiler_chan, @@ -358,31 +387,30 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> { } /// Helper function for creating a pipeline - fn new_pipeline(&self, + fn new_pipeline(&mut self, id: PipelineId, subpage_id: Option<SubpageId>, script_pipeline: Option<Rc<Pipeline>>, load_data: LoadData) -> Rc<Pipeline> { - let pipe = Pipeline::create::<LTF, STF>(id, - subpage_id, - self.chan.clone(), - self.compositor_proxy.clone_compositor_proxy(), - self.devtools_chan.clone(), - self.image_cache_task.clone(), - self.font_cache_task.clone(), - self.resource_task.clone(), - self.storage_task.clone(), - self.time_profiler_chan.clone(), - self.window_size, - script_pipeline, - load_data); - pipe.load(); - Rc::new(pipe) - } - - - /// Helper function for getting a unique pipeline Id + let pipe = Pipeline::create::<LTF, STF>(id, + subpage_id, + self.chan.clone(), + self.compositor_proxy.clone_compositor_proxy(), + self.devtools_chan.clone(), + self.image_cache_task.clone(), + self.font_cache_task.clone(), + self.resource_task.clone(), + self.storage_task.clone(), + self.time_profiler_chan.clone(), + self.window_size, + script_pipeline, + load_data.clone()); + pipe.load(); + Rc::new(pipe) + } + + /// Helper function for getting a unique pipeline ID. fn get_next_pipeline_id(&mut self) -> PipelineId { let id = self.next_pipeline_id; let PipelineId(ref mut i) = self.next_pipeline_id; @@ -390,6 +418,14 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> { id } + /// Helper function for getting a unique frame ID. + fn get_next_frame_id(&mut self) -> FrameId { + let id = self.next_frame_id; + let FrameId(ref mut i) = self.next_frame_id; + *i += 1; + id + } + /// Convenience function for getting the currently active frame tree. /// The currently active frame tree should always be the current painter fn current_frame<'a>(&'a self) -> &'a Option<Rc<FrameTree>> { @@ -465,6 +501,10 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> { debug!("constellation got key event message"); self.handle_key_msg(key, state, modifiers); } + GetPipelineTitleMsg(pipeline_id) => { + debug!("constellation got get-pipeline-title message"); + self.handle_get_pipeline_title_msg(pipeline_id); + } } true } @@ -529,27 +569,38 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> { debug!("creating replacement pipeline for about:failure"); let new_id = self.get_next_pipeline_id(); + let new_frame_id = self.get_next_frame_id(); let pipeline = self.new_pipeline(new_id, subpage_id, None, LoadData::new(Url::parse("about:failure").unwrap())); - self.pending_frames.push(FrameChange{ - before: Some(pipeline_id), - after: Rc::new(FrameTree::new(pipeline.clone(), None)), - navigation_type: constellation_msg::Load, - }); + self.browse(Some(pipeline_id), + Rc::new(FrameTree::new(new_frame_id, pipeline.clone(), None)), + constellation_msg::Load); self.pipelines.insert(new_id, pipeline); } + /// Performs navigation. This pushes a `FrameChange` object onto the list of pending frames. + /// + /// TODO(pcwalton): Send a `BeforeBrowse` message to the embedder and allow cancellation. + fn browse(&mut self, + before: Option<PipelineId>, + after: Rc<FrameTree>, + navigation_type: NavigationType) { + self.pending_frames.push(FrameChange { + before: before, + after: after, + navigation_type: navigation_type, + }); + } + fn handle_init_load(&mut self, url: Url) { let next_pipeline_id = self.get_next_pipeline_id(); + let next_frame_id = self.get_next_frame_id(); let pipeline = self.new_pipeline(next_pipeline_id, None, None, LoadData::new(url)); - - self.pending_frames.push(FrameChange { - before: None, - after: Rc::new(FrameTree::new(pipeline.clone(), None)), - navigation_type: constellation_msg::Load, - }); + self.browse(None, + Rc::new(FrameTree::new(next_frame_id, pipeline.clone(), None)), + constellation_msg::Load); self.pipelines.insert(pipeline.id, pipeline); } @@ -690,15 +741,19 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> { let rect = self.pending_sizes.remove(&(source_pipeline_id, subpage_id)); for frame_tree in frame_trees.iter() { + let next_frame_id = self.get_next_frame_id(); frame_tree.children.borrow_mut().push(ChildFrameTree::new( - Rc::new(FrameTree::new(pipeline.clone(), Some(source_pipeline.clone()))), + Rc::new(FrameTree::new(next_frame_id, + pipeline.clone(), + Some(source_pipeline.clone()))), rect)); } self.pipelines.insert(pipeline.id, pipeline); } fn handle_load_url_msg(&mut self, source_id: PipelineId, load_data: LoadData) { - debug!("Constellation: received message to load {:s}", load_data.url.to_string()); + let url = load_data.url.to_string(); + debug!("Constellation: received message to load {:s}", url); // Make sure no pending page would be overridden. let source_frame = self.current_frame().as_ref().unwrap().find(source_id).expect( "Constellation: received a LoadUrlMsg from a pipeline_id associated @@ -723,14 +778,13 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> { let parent = source_frame.parent.clone(); let subpage_id = source_frame.pipeline.subpage_id; let next_pipeline_id = self.get_next_pipeline_id(); - + let next_frame_id = self.get_next_frame_id(); let pipeline = self.new_pipeline(next_pipeline_id, subpage_id, None, load_data); - - self.pending_frames.push(FrameChange { - before: Some(source_id), - after: Rc::new(FrameTree::new(pipeline.clone(), parent.borrow().clone())), - navigation_type: constellation_msg::Load, - }); + self.browse(Some(source_id), + Rc::new(FrameTree::new(next_frame_id, + pipeline.clone(), + parent.borrow().clone())), + constellation_msg::Load); self.pipelines.insert(pipeline.id, pipeline); } @@ -752,7 +806,7 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> { frame.pipeline.revoke_paint_permission(); } } - self.navigation_context.forward() + self.navigation_context.forward(&mut *self.compositor_proxy) } constellation_msg::Back => { if self.navigation_context.previous.is_empty() { @@ -764,7 +818,7 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> { frame.pipeline.revoke_paint_permission(); } } - self.navigation_context.back() + self.navigation_context.back(&mut *self.compositor_proxy) } }; @@ -787,6 +841,16 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> { }); } + fn handle_get_pipeline_title_msg(&mut self, pipeline_id: PipelineId) { + match self.pipelines.get(&pipeline_id) { + None => self.compositor_proxy.send(ChangePageTitle(pipeline_id, None)), + Some(pipeline) => { + let ScriptControlChan(ref script_channel) = pipeline.script_chan; + script_channel.send(GetTitleMsg(pipeline_id)); + } + } + } + fn handle_painter_ready_msg(&mut self, pipeline_id: PipelineId) { debug!("Painter {} ready to send paint msg", pipeline_id); // This message could originate from a pipeline in the navigation context or @@ -941,7 +1005,8 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> { match navigation_type { constellation_msg::Load => { debug!("evicting old frames due to load"); - let evicted = self.navigation_context.load(frame_tree); + let evicted = self.navigation_context.load(frame_tree, + &mut *self.compositor_proxy); self.handle_evicted_frames(evicted); } _ => { diff --git a/components/compositing/headless.rs b/components/compositing/headless.rs index 459af00fcc2..7fc1df7dbe6 100644 --- a/components/compositing/headless.rs +++ b/components/compositing/headless.rs @@ -5,7 +5,8 @@ use compositor_task::{GetGraphicsMetadata, CreateOrUpdateRootLayer, CreateOrUpdateDescendantLayer}; use compositor_task::{Exit, ChangeReadyState, LoadComplete, Paint, ScrollFragmentPoint, SetIds}; use compositor_task::{SetLayerOrigin, ShutdownComplete, ChangePaintState, PaintMsgDiscarded}; -use compositor_task::{CompositorEventListener, CompositorReceiver, ScrollTimeout, FrameTreeUpdateMsg}; +use compositor_task::{CompositorEventListener, CompositorReceiver, ScrollTimeout, ChangePageTitle}; +use compositor_task::{ChangePageLoadData, FrameTreeUpdateMsg}; use windowing::WindowEvent; use geom::scale_factor::ScaleFactor; @@ -104,7 +105,8 @@ impl CompositorEventListener for NullCompositor { CreateOrUpdateDescendantLayer(..) | SetLayerOrigin(..) | Paint(..) | ChangeReadyState(..) | ChangePaintState(..) | ScrollFragmentPoint(..) | - LoadComplete | PaintMsgDiscarded(..) | ScrollTimeout(..) => () + LoadComplete | PaintMsgDiscarded(..) | ScrollTimeout(..) | ChangePageTitle(..) | + ChangePageLoadData(..) => () } true } @@ -119,4 +121,10 @@ impl CompositorEventListener for NullCompositor { self.time_profiler_chan.send(time::ExitMsg); self.memory_profiler_chan.send(memory::ExitMsg); } + + fn pinch_zoom_level(&self) -> f32 { + 1.0 + } + + fn get_title_for_main_frame(&self) {} } diff --git a/components/compositing/pipeline.rs b/components/compositing/pipeline.rs index cf1ebd16f8c..6dd9dabb0bf 100644 --- a/components/compositing/pipeline.rs +++ b/components/compositing/pipeline.rs @@ -28,8 +28,10 @@ pub struct Pipeline { pub paint_chan: PaintChan, pub layout_shutdown_port: Receiver<()>, pub paint_shutdown_port: Receiver<()>, - /// The most recently loaded page + /// Load data corresponding to the most recently-loaded page. pub load_data: LoadData, + /// The title of the most recently-loaded page. + pub title: Option<String>, } /// The subset of the pipeline that is needed for layer composition. @@ -44,21 +46,21 @@ impl Pipeline { /// Starts a paint task, layout task, and possibly a script task. /// Returns the channels wrapped in a struct. /// If script_pipeline is not None, then subpage_id must also be not None. - pub fn create<LTF:LayoutTaskFactory, STF:ScriptTaskFactory>( - id: PipelineId, - subpage_id: Option<SubpageId>, - constellation_chan: ConstellationChan, - compositor_proxy: Box<CompositorProxy+'static+Send>, - devtools_chan: Option<DevtoolsControlChan>, - image_cache_task: ImageCacheTask, - font_cache_task: FontCacheTask, - resource_task: ResourceTask, - storage_task: StorageTask, - time_profiler_chan: TimeProfilerChan, - window_size: WindowSizeData, - script_pipeline: Option<Rc<Pipeline>>, - load_data: LoadData) - -> Pipeline { + pub fn create<LTF,STF>(id: PipelineId, + subpage_id: Option<SubpageId>, + constellation_chan: ConstellationChan, + compositor_proxy: Box<CompositorProxy+'static+Send>, + devtools_chan: Option<DevtoolsControlChan>, + image_cache_task: ImageCacheTask, + font_cache_task: FontCacheTask, + resource_task: ResourceTask, + storage_task: StorageTask, + time_profiler_chan: TimeProfilerChan, + window_size: WindowSizeData, + script_pipeline: Option<Rc<Pipeline>>, + load_data: LoadData) + -> Pipeline + where LTF: LayoutTaskFactory, STF:ScriptTaskFactory { let layout_pair = ScriptTaskFactory::create_layout_channel(None::<&mut STF>); let (paint_port, paint_chan) = PaintChan::new(); let (paint_shutdown_chan, paint_shutdown_port) = channel(); @@ -153,6 +155,7 @@ impl Pipeline { layout_shutdown_port: layout_shutdown_port, paint_shutdown_port: paint_shutdown_port, load_data: load_data, + title: None, } } diff --git a/components/compositing/windowing.rs b/components/compositing/windowing.rs index 0dffe0a8f94..339a5fe789d 100644 --- a/components/compositing/windowing.rs +++ b/components/compositing/windowing.rs @@ -11,8 +11,8 @@ use geom::scale_factor::ScaleFactor; use geom::size::TypedSize2D; use layers::geometry::DevicePixel; use layers::platform::surface::NativeGraphicsMetadata; -use servo_msg::constellation_msg::{Key, KeyState, KeyModifiers}; -use servo_msg::compositor_msg::{ReadyState, PaintState}; +use servo_msg::compositor_msg::{PaintState, ReadyState}; +use servo_msg::constellation_msg::{Key, KeyState, KeyModifiers, LoadData}; use servo_util::geometry::ScreenPx; use std::fmt::{FormatError, Formatter, Show}; use std::rc::Rc; @@ -37,8 +37,12 @@ pub enum WindowEvent { /// It's possible that this should be something like /// `CompositorMessageWindowEvent(compositor_task::Msg)` instead. IdleWindowEvent, - /// Sent when part of the window is marked dirty and needs to be redrawn. + /// Sent when part of the window is marked dirty and needs to be redrawn. Before sending this + /// message, the window must make the same GL context as in `PrepareRenderingEvent` current. RefreshWindowEvent, + /// Sent to initialize the GL context. The windowing system must have a valid, current GL + /// context when this message is sent. + InitializeCompositingWindowEvent, /// Sent when the window is resized. ResizeWindowEvent(TypedSize2D<DevicePixel, uint>), /// Sent when a new URL is to be loaded. @@ -47,7 +51,8 @@ pub enum WindowEvent { MouseWindowEventClass(MouseWindowEvent), /// Sent when a mouse move. MouseWindowMoveEventClass(TypedPoint2D<DevicePixel, f32>), - /// Sent when the user scrolls. Includes the current cursor position. + /// Sent when the user scrolls. The first point is the delta and the second point is the + /// origin. ScrollWindowEvent(TypedPoint2D<DevicePixel, f32>, TypedPoint2D<DevicePixel, i32>), /// Sent when the user zooms. ZoomWindowEvent(f32), @@ -66,6 +71,7 @@ impl Show for WindowEvent { match *self { IdleWindowEvent => write!(f, "Idle"), RefreshWindowEvent => write!(f, "Refresh"), + InitializeCompositingWindowEvent => write!(f, "InitializeCompositing"), ResizeWindowEvent(..) => write!(f, "Resize"), KeyEvent(..) => write!(f, "Key"), LoadUrlWindowEvent(..) => write!(f, "LoadUrl"), @@ -92,6 +98,12 @@ pub trait WindowMethods { fn set_ready_state(&self, ready_state: ReadyState); /// Sets the paint state of the current page. fn set_paint_state(&self, paint_state: PaintState); + /// Sets the page title for the current page. + fn set_page_title(&self, title: Option<String>); + /// Sets the load data for the current page. + fn set_page_load_data(&self, load_data: LoadData); + /// Called when the browser is done loading a frame. + fn load_end(&self); /// Returns the hidpi factor of the monitor. fn hidpi_factor(&self) -> ScaleFactor<ScreenPx, DevicePixel, f32>; @@ -106,5 +118,10 @@ pub trait WindowMethods { /// magic to wake the up window's event loop. fn create_compositor_channel(_: &Option<Rc<Self>>) -> (Box<CompositorProxy+Send>, Box<CompositorReceiver>); + + /// 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) -> bool; } diff --git a/components/msg/compositor_msg.rs b/components/msg/compositor_msg.rs index 4635e2667d4..dad31c08976 100644 --- a/components/msg/compositor_msg.rs +++ b/components/msg/compositor_msg.rs @@ -19,7 +19,7 @@ pub enum PaintState { PaintingPaintState, } -#[deriving(Eq, Ord, PartialEq, PartialOrd, Clone)] +#[deriving(Eq, Ord, PartialEq, PartialOrd, Clone, Show)] pub enum ReadyState { /// Informs the compositor that nothing has been done yet. Used for setting status Blank, @@ -111,6 +111,8 @@ pub trait ScriptListener { pipeline_id: PipelineId, layer_id: LayerId, point: Point2D<f32>); + /// Informs the compositor that the title of the page with the given pipeline ID has changed. + fn set_title(&mut self, pipeline_id: PipelineId, new_title: Option<String>); fn close(&mut self); fn dup(&mut self) -> Box<ScriptListener+'static>; } diff --git a/components/msg/constellation_msg.rs b/components/msg/constellation_msg.rs index 483f03803cf..9b2a654310c 100644 --- a/components/msg/constellation_msg.rs +++ b/components/msg/constellation_msg.rs @@ -205,6 +205,9 @@ pub enum Msg { PainterReadyMsg(PipelineId), ResizedWindowMsg(WindowSizeData), KeyEvent(Key, KeyState, KeyModifiers), + /// Requests that the constellation inform the compositor of the title of the pipeline + /// immediately. + GetPipelineTitleMsg(PipelineId), } /// Similar to net::resource_task::LoadData diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 4fbdaab837d..738ac6be90e 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -189,6 +189,7 @@ pub trait DocumentHelpers<'a> { fn begin_focus_transaction(self); fn request_focus(self, elem: JSRef<Element>); fn commit_focus_transaction(self); + fn send_title_to_compositor(self); } impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { @@ -369,6 +370,12 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { //TODO: dispatch blur, focus, focusout, and focusin events self.focused.assign(self.possibly_focused.get()); } + + /// Sends this document's title to the compositor. + fn send_title_to_compositor(self) { + let window = self.window().root(); + window.page().send_title_to_compositor(); + } } #[deriving(PartialEq)] @@ -985,3 +992,4 @@ impl<'a> DocumentMethods for JSRef<'a, Document> { global_event_handlers!() event_handler!(readystatechange, GetOnreadystatechange, SetOnreadystatechange) } + diff --git a/components/script/dom/htmltitleelement.rs b/components/script/dom/htmltitleelement.rs index ef8b70aa4fa..4e6e9f7e9bb 100644 --- a/components/script/dom/htmltitleelement.rs +++ b/components/script/dom/htmltitleelement.rs @@ -5,15 +5,17 @@ use dom::bindings::codegen::Bindings::HTMLTitleElementBinding; use dom::bindings::codegen::Bindings::HTMLTitleElementBinding::HTMLTitleElementMethods; use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; -use dom::bindings::codegen::InheritTypes::{HTMLTitleElementDerived, NodeCast, TextCast}; +use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLTitleElementDerived, NodeCast}; +use dom::bindings::codegen::InheritTypes::{TextCast}; use dom::bindings::js::{JSRef, Temporary}; use dom::bindings::utils::{Reflectable, Reflector}; -use dom::document::Document; +use dom::document::{Document, DocumentHelpers}; use dom::element::HTMLTitleElementTypeId; use dom::eventtarget::{EventTarget, NodeTargetTypeId}; use dom::htmlelement::HTMLElement; use dom::node::{Node, NodeHelpers, ElementNodeTypeId}; use dom::text::Text; +use dom::virtualmethods::VirtualMethods; use servo_util::str::DOMString; #[dom_struct] @@ -68,3 +70,19 @@ impl Reflectable for HTMLTitleElement { self.htmlelement.reflector() } } + +impl<'a> VirtualMethods for JSRef<'a, HTMLTitleElement> { + fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> { + let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_borrowed_ref(self); + Some(htmlelement as &VirtualMethods) + } + + fn bind_to_tree(&self, is_in_doc: bool) { + let node: JSRef<Node> = NodeCast::from_ref(*self); + if is_in_doc { + let document = node.owner_doc().root(); + document.send_title_to_compositor() + } + } +} + diff --git a/components/script/dom/virtualmethods.rs b/components/script/dom/virtualmethods.rs index 66378463a82..a90055bc50d 100644 --- a/components/script/dom/virtualmethods.rs +++ b/components/script/dom/virtualmethods.rs @@ -24,6 +24,7 @@ use dom::bindings::codegen::InheritTypes::HTMLSelectElementCast; use dom::bindings::codegen::InheritTypes::HTMLStyleElementCast; use dom::bindings::codegen::InheritTypes::HTMLTableCellElementCast; use dom::bindings::codegen::InheritTypes::HTMLTextAreaElementCast; +use dom::bindings::codegen::InheritTypes::HTMLTitleElementCast; use dom::bindings::js::JSRef; use dom::document::Document; use dom::element::Element; @@ -47,6 +48,7 @@ use dom::element::HTMLStyleElementTypeId; use dom::element::HTMLTableDataCellElementTypeId; use dom::element::HTMLTableHeaderCellElementTypeId; use dom::element::HTMLTextAreaElementTypeId; +use dom::element::HTMLTitleElementTypeId; use dom::event::Event; use dom::htmlanchorelement::HTMLAnchorElement; use dom::htmlareaelement::HTMLAreaElement; @@ -67,6 +69,7 @@ use dom::htmlselectelement::HTMLSelectElement; use dom::htmlstyleelement::HTMLStyleElement; use dom::htmltablecellelement::HTMLTableCellElement; use dom::htmltextareaelement::HTMLTextAreaElement; +use dom::htmltitleelement::HTMLTitleElement; use dom::node::{Node, NodeHelpers, ElementNodeTypeId, CloneChildrenFlag}; use servo_util::str::DOMString; @@ -232,6 +235,11 @@ pub fn vtable_for<'a>(node: &'a JSRef<'a, Node>) -> &'a VirtualMethods + 'a { let element: &'a JSRef<'a, HTMLTextAreaElement> = HTMLTextAreaElementCast::to_borrowed_ref(node).unwrap(); element as &'a VirtualMethods + 'a } + ElementNodeTypeId(HTMLTitleElementTypeId) => { + let element: &'a JSRef<'a, HTMLTitleElement> = + HTMLTitleElementCast::to_borrowed_ref(node).unwrap(); + element as &'a VirtualMethods + 'a + } ElementNodeTypeId(ElementTypeId_) => { let element: &'a JSRef<'a, Element> = ElementCast::to_borrowed_ref(node).unwrap(); element as &'a VirtualMethods + 'a diff --git a/components/script/page.rs b/components/script/page.rs index 37f4ba155bb..c8e46463077 100644 --- a/components/script/page.rs +++ b/components/script/page.rs @@ -21,7 +21,6 @@ use script_traits::{UntrustedNodeAddress, ScriptControlChan}; use geom::{Point2D, Rect, Size2D}; use js::rust::Cx; -use servo_msg::compositor_msg::PerformingLayout; use servo_msg::compositor_msg::ScriptListener; use servo_msg::constellation_msg::{ConstellationChan, WindowSizeData}; use servo_msg::constellation_msg::{PipelineId, SubpageId}; @@ -266,6 +265,17 @@ impl Page { // because it was built for infinite clip (MAX_RECT). had_clip_rect } + + pub fn send_title_to_compositor(&self) { + match *self.frame() { + None => {} + Some(ref frame) => { + let window = frame.window.root(); + let document = frame.document.root(); + window.compositor().set_title(self.id, Some(document.Title())); + } + } + } } impl Iterator<Rc<Page>> for PageIterator { @@ -356,7 +366,7 @@ impl Page { pub fn reflow(&self, goal: ReflowGoal, script_chan: ScriptControlChan, - compositor: &mut ScriptListener, + _: &mut ScriptListener, query_type: ReflowQueryType) { let root = match *self.frame() { None => return, @@ -376,9 +386,6 @@ impl Page { // Now, join the layout so that they will see the latest changes we have made. self.join_layout(); - // Tell the user that we're performing layout. - compositor.set_ready_state(self.id, PerformingLayout); - // Layout will let us know when it's done. let (join_chan, join_port) = channel(); let mut layout_join_port = self.layout_join_port.borrow_mut(); diff --git a/components/script/script_task.rs b/components/script/script_task.rs index f41558b598c..b4fdaa6f833 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -43,10 +43,12 @@ use script_traits::{MouseMoveEvent, MouseUpEvent, ConstellationControlMsg, Scrip use script_traits::{ResizeMsg, AttachLayoutMsg, LoadMsg, ViewportMsg, SendEventMsg}; use script_traits::{ResizeInactiveMsg, ExitPipelineMsg, NewLayoutInfo, OpaqueScriptLayoutChannel}; use script_traits::{ScriptControlChan, ReflowCompleteMsg, UntrustedNodeAddress, KeyEvent}; -use servo_msg::compositor_msg::{FinishedLoading, LayerId, Loading}; +use script_traits::{GetTitleMsg}; +use servo_msg::compositor_msg::{FinishedLoading, LayerId, Loading, PerformingLayout}; use servo_msg::compositor_msg::{ScriptListener}; -use servo_msg::constellation_msg::{ConstellationChan, LoadCompleteMsg, LoadUrlMsg, NavigationDirection}; -use servo_msg::constellation_msg::{LoadData, PipelineId, Failure, FailureMsg, WindowSizeData, Key, KeyState}; +use servo_msg::constellation_msg::{ConstellationChan, LoadCompleteMsg}; +use servo_msg::constellation_msg::{LoadData, LoadUrlMsg, NavigationDirection, PipelineId}; +use servo_msg::constellation_msg::{Failure, FailureMsg, WindowSizeData, Key, KeyState}; use servo_msg::constellation_msg::{KeyModifiers, SUPER, SHIFT, CONTROL, ALT, Repeated, Pressed}; use servo_msg::constellation_msg::{Released}; use servo_msg::constellation_msg; @@ -550,6 +552,9 @@ impl ScriptTask { FromConstellation(ViewportMsg(..)) => panic!("should have handled ViewportMsg already"), FromScript(ExitWindowMsg(id)) => self.handle_exit_window_msg(id), FromConstellation(ResizeMsg(..)) => panic!("should have handled ResizeMsg already"), + FromConstellation(GetTitleMsg(pipeline_id)) => { + self.handle_get_title_msg(pipeline_id) + } FromScript(XHRProgressMsg(addr, progress)) => XMLHttpRequest::handle_progress(addr, progress), FromScript(XHRReleaseMsg(addr)) => XMLHttpRequest::handle_release(addr), FromScript(DOMMessage(..)) => panic!("unexpected message"), @@ -661,6 +666,11 @@ impl ScriptTask { self.compositor.borrow_mut().close(); } + /// Handles a request for the window title. + fn handle_get_title_msg(&self, pipeline_id: PipelineId) { + get_page(&*self.page.borrow(), pipeline_id).send_title_to_compositor(); + } + /// Handles a request to exit the script task and shut down layout. /// Returns true if the script task should shut down and false otherwise. fn handle_exit_pipeline_msg(&self, id: PipelineId) -> bool { @@ -784,6 +794,7 @@ impl ScriptTask { parse_html(*document, parser_input, &final_url); document.set_ready_state(DocumentReadyStateValues::Interactive); + self.compositor.borrow_mut().set_ready_state(pipeline_id, PerformingLayout); // Kick off the initial reflow of the page. debug!("kicking off initial reflow of {}", final_url); diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index dec1d090178..59a1447a75a 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -65,7 +65,10 @@ pub enum ConstellationControlMsg { SendEventMsg(PipelineId, CompositorEvent), /// Notifies script that reflow is finished. ReflowCompleteMsg(PipelineId, uint), + /// Notifies script of the viewport. ViewportMsg(PipelineId, Rect<f32>), + /// Requests that the script task immediately send the constellation the title of a pipeline. + GetTitleMsg(PipelineId), } /// Events from the compositor that the script task needs to know about diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock index ade8b873593..f9200ea627c 100644 --- a/components/servo/Cargo.lock +++ b/components/servo/Cargo.lock @@ -58,7 +58,7 @@ dependencies = [ [[package]] name = "cocoa" version = "0.1.1" -source = "git+https://github.com/servo/rust-cocoa#78b823bec1affcab20b6977e1057125088a6034c" +source = "git+https://github.com/servo/rust-cocoa#084f8e1baf40391eb12819d16765af25ca96c7ec" [[package]] name = "compile_msg" @@ -394,7 +394,7 @@ source = "git+https://github.com/bjz/gl-rs.git#79cd3b3f9f19aa0e39f6af572fc8673a6 [[package]] name = "layers" version = "0.1.0" -source = "git+https://github.com/servo/rust-layers#b068d2a96d54bf173b548aece36f5ea4ef9353cf" +source = "git+https://github.com/servo/rust-layers#63d1093f2a01a6fb9599ea6d932aadf79598451f" dependencies = [ "cgl 0.0.1 (git+https://github.com/servo/rust-cgl)", "core_foundation 0.1.0 (git+https://github.com/servo/rust-core-foundation)", diff --git a/components/servo/lib.rs b/components/servo/lib.rs index 1ddfbc1dc5d..9e5d4be9467 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -153,6 +153,14 @@ impl<Window> Browser<Window> where Window: WindowMethods + 'static { self.compositor.repaint_synchronously() } + pub fn pinch_zoom_level(&self) -> f32 { + self.compositor.pinch_zoom_level() + } + + pub fn get_title_for_main_frame(&self) { + self.compositor.get_title_for_main_frame() + } + pub fn shutdown(mut self) { self.compositor.shutdown(); } diff --git a/components/servo/main.rs b/components/servo/main.rs index 16ef1980668..a6d7f28db73 100644 --- a/components/servo/main.rs +++ b/components/servo/main.rs @@ -30,7 +30,9 @@ use servo_util::rtinstrument; #[cfg(not(any(test,target_os="android")))] use servo::Browser; #[cfg(not(any(test,target_os="android")))] -use compositing::windowing::{IdleWindowEvent, ResizeWindowEvent, WindowEvent}; +use compositing::windowing::{IdleWindowEvent, InitializeCompositingWindowEvent, ResizeWindowEvent}; +#[cfg(not(any(test,target_os="android")))] +use compositing::windowing::{WindowEvent}; #[cfg(not(any(test,target_os="android")))] use std::os; @@ -65,6 +67,8 @@ fn start(argc: int, argv: *const *const u8) -> int { } } + browser.browser.handle_event(InitializeCompositingWindowEvent); + loop { let should_continue = match window { None => browser.browser.handle_event(IdleWindowEvent), diff --git a/ports/android/glut_app/Cargo.lock b/ports/android/glut_app/Cargo.lock index dddf42fec3e..7c8b12f9acb 100644 --- a/ports/android/glut_app/Cargo.lock +++ b/ports/android/glut_app/Cargo.lock @@ -327,7 +327,7 @@ source = "git+https://github.com/bjz/gl-rs.git#79cd3b3f9f19aa0e39f6af572fc8673a6 [[package]] name = "layers" version = "0.1.0" -source = "git+https://github.com/servo/rust-layers#0f6edd58b3b572f2aac97567b752c15db5fa1982" +source = "git+https://github.com/servo/rust-layers#63d1093f2a01a6fb9599ea6d932aadf79598451f" dependencies = [ "cgl 0.0.1 (git+https://github.com/servo/rust-cgl)", "core_foundation 0.1.0 (git+https://github.com/servo/rust-core-foundation)", diff --git a/ports/android/glut_app/window.rs b/ports/android/glut_app/window.rs index a8f76ae628b..29ec9cdcd2b 100644 --- a/ports/android/glut_app/window.rs +++ b/ports/android/glut_app/window.rs @@ -19,8 +19,8 @@ use geom::scale_factor::ScaleFactor; use geom::size::TypedSize2D; use layers::geometry::DevicePixel; use layers::platform::surface::NativeGraphicsMetadata; -use msg::compositor_msg::{IdlePaintState, PaintState}; -use msg::compositor_msg::{Blank, ReadyState}; +use msg::compositor_msg::{Blank, IdlePaintState, PaintState, ReadyState}; +use msg::constellation_msg::LoadData; use util::geometry::ScreenPx; use glut::glut::{ACTIVE_SHIFT, WindowHeight}; @@ -176,19 +176,23 @@ impl WindowMethods for Window { /// Sets the ready state. fn set_ready_state(&self, ready_state: ReadyState) { self.ready_state.set(ready_state); - //FIXME: set_window_title causes crash with Android version of freeGLUT. Temporarily blocked. - //self.update_window_title() + // FIXME: set_window_title causes crash with Android version of freeGLUT. Temporarily + // blocked. + // + // self.update_window_title() } /// Sets the paint state. fn set_paint_state(&self, paint_state: PaintState) { self.paint_state.set(paint_state); - //FIXME: set_window_title causes crash with Android version of freeGLUT. Temporarily blocked. - //self.update_window_title() + // FIXME: set_window_title causes crash with Android version of freeGLUT. Temporarily + // blocked. + // + // self.update_window_title() } fn hidpi_factor(&self) -> ScaleFactor<ScreenPx, DevicePixel, f32> { - //FIXME: Do nothing in GLUT now. + // FIXME: Do nothing in GLUT now. ScaleFactor(1.0) } @@ -198,6 +202,22 @@ impl WindowMethods for Window { display: GetCurrentDisplay(), } } + + fn set_page_title(&self, _: Option<String>) { + // TODO(pcwalton) + } + + fn set_page_load_data(&self, _: LoadData) { + // TODO(pcwalton) + } + + fn load_end(&self) { + // TODO(pcwalton) + } + + fn prepare_for_composite(&self) -> bool { + true + } } impl Window { diff --git a/ports/cef/Cargo.lock b/ports/cef/Cargo.lock index e1b04196d67..f27fc02021c 100644 --- a/ports/cef/Cargo.lock +++ b/ports/cef/Cargo.lock @@ -3,6 +3,7 @@ name = "embedding" version = "0.0.1" dependencies = [ "azure 0.1.0 (git+https://github.com/servo/rust-azure)", + "cocoa 0.1.1 (git+https://github.com/servo/rust-cocoa)", "core_graphics 0.1.0 (git+https://github.com/servo/rust-core-graphics)", "core_text 0.1.0 (git+https://github.com/servo/rust-core-text)", "devtools 0.0.1", @@ -57,6 +58,11 @@ dependencies = [ ] [[package]] +name = "cocoa" +version = "0.1.1" +source = "git+https://github.com/servo/rust-cocoa#084f8e1baf40391eb12819d16765af25ca96c7ec" + +[[package]] name = "compositing" version = "0.0.1" dependencies = [ @@ -358,7 +364,7 @@ source = "git+https://github.com/bjz/gl-rs.git#79cd3b3f9f19aa0e39f6af572fc8673a6 [[package]] name = "layers" version = "0.1.0" -source = "git+https://github.com/servo/rust-layers#b068d2a96d54bf173b548aece36f5ea4ef9353cf" +source = "git+https://github.com/servo/rust-layers#0f6edd58b3b572f2aac97567b752c15db5fa1982" dependencies = [ "cgl 0.0.1 (git+https://github.com/servo/rust-cgl)", "core_foundation 0.1.0 (git+https://github.com/servo/rust-core-foundation)", diff --git a/ports/cef/Cargo.toml b/ports/cef/Cargo.toml index bfbbda58bca..817ba629c94 100644 --- a/ports/cef/Cargo.toml +++ b/ports/cef/Cargo.toml @@ -65,3 +65,6 @@ git = "https://github.com/servo/rust-core-graphics" [dependencies.core_text] git = "https://github.com/servo/rust-core-text" + +[dependencies.cocoa] +git = "https://github.com/servo/rust-cocoa" diff --git a/ports/cef/browser.rs b/ports/cef/browser.rs index 6bdfba87772..2bd93ac1712 100644 --- a/ports/cef/browser.rs +++ b/ports/cef/browser.rs @@ -2,38 +2,90 @@ * 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 interfaces::{CefBrowser, CefClient, CefRequestContext, cef_browser_t, cef_client_t}; +use browser_host::{ServoCefBrowserHost, ServoCefBrowserHostExtensions}; +use core::{mod, OffScreenGlobals, OnScreenGlobals, globals}; +use eutil::Downcast; +use frame::ServoCefFrame; +use interfaces::{CefBrowser, CefBrowserHost, CefClient, CefFrame, CefRequestContext}; +use interfaces::{cef_browser_t, cef_browser_host_t, cef_client_t, cef_frame_t}; use interfaces::{cef_request_context_t}; +use servo::Browser; use types::{cef_browser_settings_t, cef_string_t, cef_window_info_t}; +use window; -use eutil::Downcast; +use compositing::windowing::{Back, Forward, NavigationWindowEvent}; use glfw_app; use libc::c_int; -use servo::Browser; use servo_util::opts; use std::cell::{Cell, RefCell}; -use std::rc::Rc; + +cef_class_impl! { + ServoCefBrowser : CefBrowser, cef_browser_t { + fn get_host(&this) -> *mut cef_browser_host_t { + this.downcast().host.clone() + } + + fn go_back(&_this) -> () { + core::send_window_event(NavigationWindowEvent(Back)); + } + + fn go_forward(&_this) -> () { + core::send_window_event(NavigationWindowEvent(Forward)); + } + + // Returns the main (top-level) frame for the browser window. + fn get_main_frame(&this) -> *mut cef_frame_t { + this.downcast().frame.clone() + } + } +} pub struct ServoCefBrowser { + /// A reference to the browser's primary frame. + pub frame: CefFrame, + /// A reference to the browser's host. + pub host: CefBrowserHost, + /// A reference to the browser client. pub client: CefClient, - pub servo_browser: RefCell<Option<Browser<glfw_app::window::Window>>>, - pub window: RefCell<Option<Rc<glfw_app::window::Window>>>, + /// Whether the on-created callback has fired yet. pub callback_executed: Cell<bool>, } impl ServoCefBrowser { - pub fn new(client: CefClient) -> ServoCefBrowser { + pub fn new(window_info: &cef_window_info_t, client: CefClient) -> ServoCefBrowser { + let frame = ServoCefFrame::new().as_cef_interface(); + let host = ServoCefBrowserHost::new(client.clone()).as_cef_interface(); + if window_info.windowless_rendering_enabled == 0 { + let glfw_window = glfw_app::create_window(); + globals.replace(Some(OnScreenGlobals(RefCell::new(glfw_window.clone()), + RefCell::new(Browser::new(Some(glfw_window)))))); + } + ServoCefBrowser { + frame: frame, + host: host, client: client, - servo_browser: RefCell::new(None), - window: RefCell::new(None), callback_executed: Cell::new(false), } } } -cef_class_impl! { - ServoCefBrowser : CefBrowser, cef_browser_t {} +trait ServoCefBrowserExtensions { + fn init(&self, window_info: &cef_window_info_t); +} + +impl ServoCefBrowserExtensions for CefBrowser { + fn init(&self, window_info: &cef_window_info_t) { + if window_info.windowless_rendering_enabled != 0 { + let window = window::Window::new(); + let servo_browser = Browser::new(Some(window.clone())); + window.set_browser(self.clone()); + globals.replace(Some(OffScreenGlobals(RefCell::new(window), + RefCell::new(servo_browser)))); + } + + self.downcast().host.set_browser((*self).clone()); + } } local_data_key!(pub GLOBAL_BROWSERS: RefCell<Vec<CefBrowser>>) @@ -50,12 +102,16 @@ pub fn browser_callback_after_created(browser: CefBrowser) { browser.downcast().callback_executed.set(true); } -fn browser_host_create(client: CefClient, callback_executed: bool) -> CefBrowser { +fn browser_host_create(window_info: &cef_window_info_t, + client: CefClient, + callback_executed: bool) + -> CefBrowser { let mut urls = Vec::new(); urls.push("http://s27.postimg.org/vqbtrolyr/servo.jpg".to_string()); let mut opts = opts::default_opts(); opts.urls = urls; - let browser = ServoCefBrowser::new(client).as_cef_interface(); + let browser = ServoCefBrowser::new(window_info, client).as_cef_interface(); + browser.init(window_info); if callback_executed { browser_callback_after_created(browser.clone()); } @@ -73,32 +129,30 @@ fn browser_host_create(client: CefClient, callback_executed: bool) -> CefBrowser } cef_static_method_impls! { - fn cef_browser_host_create_browser(_window_info: *const cef_window_info_t, + fn cef_browser_host_create_browser(window_info: *const cef_window_info_t, client: *mut cef_client_t, _url: *const cef_string_t, _browser_settings: *const cef_browser_settings_t, _request_context: *mut cef_request_context_t) -> c_int { - let _window_info: &cef_window_info_t = _window_info; let client: CefClient = client; let _url: &[u16] = _url; let _browser_settings: &cef_browser_settings_t = _browser_settings; let _request_context: CefRequestContext = _request_context; - browser_host_create(client, false); + browser_host_create(window_info, client, false); 1i32 } - - fn cef_browser_host_create_browser_sync(_window_info: *const cef_window_info_t, + fn cef_browser_host_create_browser_sync(window_info: *const cef_window_info_t, client: *mut cef_client_t, _url: *const cef_string_t, _browser_settings: *const cef_browser_settings_t, _request_context: *mut cef_request_context_t) -> *mut cef_browser_t { - let _window_info: &cef_window_info_t = _window_info; let client: CefClient = client; let _url: &[u16] = _url; let _browser_settings: &cef_browser_settings_t = _browser_settings; let _request_context: CefRequestContext = _request_context; - browser_host_create(client, true) + browser_host_create(window_info, client, true) } } + diff --git a/ports/cef/browser_host.rs b/ports/cef/browser_host.rs new file mode 100644 index 00000000000..66ec82af343 --- /dev/null +++ b/ports/cef/browser_host.rs @@ -0,0 +1,158 @@ +/* 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 core; +use eutil::Downcast; +use interfaces::{CefBrowser, CefBrowserHost, CefClient, cef_browser_host_t, cef_client_t}; +use types::{KEYEVENT_CHAR, KEYEVENT_KEYDOWN, KEYEVENT_KEYUP, KEYEVENT_RAWKEYDOWN, cef_key_event}; +use types::{cef_mouse_button_type_t, cef_mouse_event, cef_rect_t}; + +use compositing::windowing::{InitializeCompositingWindowEvent, KeyEvent, MouseWindowClickEvent}; +use compositing::windowing::{MouseWindowEventClass, MouseWindowMouseUpEvent, PinchZoomWindowEvent}; +use compositing::windowing::{ResizeWindowEvent, ScrollWindowEvent}; +use geom::point::TypedPoint2D; +use geom::size::TypedSize2D; +use libc::{c_double, c_int}; +use servo_msg::constellation_msg::{mod, KeyModifiers, Pressed, Released, Repeated}; +use std::cell::RefCell; + +pub struct ServoCefBrowserHost { + /// A reference to the browser. + pub browser: RefCell<Option<CefBrowser>>, + /// A reference to the client. + pub client: CefClient, +} + +cef_class_impl! { + ServoCefBrowserHost : CefBrowserHost, cef_browser_host_t { + fn get_client(&this) -> *mut cef_client_t { + this.downcast().client.clone() + } + + fn was_resized(&this) -> () { + let mut rect = cef_rect_t::zero(); + this.get_client() + .get_render_handler() + .get_backing_rect(this.downcast().browser.borrow().clone().unwrap(), &mut rect); + let size = TypedSize2D(rect.width as uint, rect.height as uint); + core::send_window_event(ResizeWindowEvent(size)); + core::repaint_synchronously(); + } + + fn send_key_event(&_this, event: *const cef_key_event) -> () { + // FIXME(pcwalton): So awful. But it's nearly midnight here and I have to get + // Google working. + let event: &cef_key_event = event; + let key = match (*event).character as u8 { + b'a' | b'A' => constellation_msg::KeyA, + b'b' | b'B' => constellation_msg::KeyB, + b'c' | b'C' => constellation_msg::KeyC, + b'd' | b'D' => constellation_msg::KeyD, + b'e' | b'E' => constellation_msg::KeyE, + b'f' | b'F' => constellation_msg::KeyF, + b'g' | b'G' => constellation_msg::KeyG, + b'h' | b'H' => constellation_msg::KeyH, + b'i' | b'I' => constellation_msg::KeyI, + b'j' | b'J' => constellation_msg::KeyJ, + b'k' | b'K' => constellation_msg::KeyK, + b'l' | b'L' => constellation_msg::KeyL, + b'm' | b'M' => constellation_msg::KeyM, + b'n' | b'N' => constellation_msg::KeyN, + b'o' | b'O' => constellation_msg::KeyO, + b'p' | b'P' => constellation_msg::KeyP, + b'q' | b'Q' => constellation_msg::KeyQ, + b'r' | b'R' => constellation_msg::KeyR, + b's' | b'S' => constellation_msg::KeyS, + b't' | b'T' => constellation_msg::KeyT, + b'u' | b'U' => constellation_msg::KeyU, + b'v' | b'V' => constellation_msg::KeyV, + b'w' | b'W' => constellation_msg::KeyW, + b'x' | b'X' => constellation_msg::KeyX, + b'y' | b'Y' => constellation_msg::KeyY, + b'z' | b'Z' => constellation_msg::KeyZ, + b'0' => constellation_msg::Key0, + b'1' => constellation_msg::Key1, + b'2' => constellation_msg::Key2, + b'3' => constellation_msg::Key3, + b'4' => constellation_msg::Key4, + b'5' => constellation_msg::Key5, + b'6' => constellation_msg::Key6, + b'7' => constellation_msg::Key7, + b'8' => constellation_msg::Key8, + b'9' => constellation_msg::Key9, + b'\n' | b'\r' => constellation_msg::KeyEnter, + _ => constellation_msg::KeySpace, + }; + let key_state = match (*event).t { + KEYEVENT_RAWKEYDOWN => Pressed, + KEYEVENT_KEYDOWN | KEYEVENT_CHAR => Repeated, + KEYEVENT_KEYUP => Released, + }; + let key_modifiers = KeyModifiers::empty(); // TODO(pcwalton) + core::send_window_event(KeyEvent(key, key_state, key_modifiers)) + } + + fn send_mouse_click_event(&_this, + event: *const cef_mouse_event, + mouse_button_type: cef_mouse_button_type_t, + mouse_up: c_int, + _click_count: c_int) + -> () { + let event: &cef_mouse_event = event; + let button_type = mouse_button_type as uint; + let point = TypedPoint2D((*event).x as f32, (*event).y as f32); + if mouse_up != 0 { + core::send_window_event(MouseWindowEventClass(MouseWindowClickEvent(button_type, + point))) + } else { + core::send_window_event(MouseWindowEventClass(MouseWindowMouseUpEvent(button_type, + point))) + } + } + + fn send_mouse_wheel_event(&_this, + event: *const cef_mouse_event, + delta_x: c_int, + delta_y: c_int) + -> () { + let event: &cef_mouse_event = event; + let delta = TypedPoint2D(delta_x as f32, delta_y as f32); + let origin = TypedPoint2D((*event).x as i32, (*event).y as i32); + core::send_window_event(ScrollWindowEvent(delta, origin)) + } + + fn get_zoom_level(&_this) -> c_double { + core::pinch_zoom_level() as c_double + } + + fn set_zoom_level(&this, new_zoom_level: c_double) -> () { + let old_zoom_level = this.get_zoom_level(); + core::send_window_event(PinchZoomWindowEvent((new_zoom_level / old_zoom_level) as f32)) + } + + fn initialize_compositing(&_this) -> () { + core::send_window_event(InitializeCompositingWindowEvent); + } + } +} + +impl ServoCefBrowserHost { + pub fn new(client: CefClient) -> ServoCefBrowserHost { + ServoCefBrowserHost { + browser: RefCell::new(None), + client: client, + } + } +} + +pub trait ServoCefBrowserHostExtensions { + fn set_browser(&self, browser: CefBrowser); +} + +impl ServoCefBrowserHostExtensions for CefBrowserHost { + fn set_browser(&self, browser: CefBrowser) { + *self.downcast().browser.borrow_mut() = Some(browser) + } +} + diff --git a/ports/cef/command_line.rs b/ports/cef/command_line.rs index ed3578dfbcd..53655b28c79 100644 --- a/ports/cef/command_line.rs +++ b/ports/cef/command_line.rs @@ -46,7 +46,7 @@ pub fn command_line_init(argc: c_int, argv: *const *const u8) { #[no_mangle] pub extern "C" fn command_line_get_switch_value(cmd: *mut cef_command_line_t, name: *const cef_string_t) -> cef_string_userfree_t { if cmd.is_null() || name.is_null() { - return cef_string::empty_utf16_string() + return cef_string::empty_utf16_userfree_string() } unsafe { //technically cef_string_t can be any type of character size @@ -67,11 +67,11 @@ pub extern "C" fn command_line_get_switch_value(cmd: *mut cef_command_line_t, na &mut string, 1); }); - return string + return cef_string::string_to_userfree_string(string) } } } - return cef_string::empty_utf16_string() + return cef_string::empty_utf16_userfree_string() } #[no_mangle] diff --git a/ports/cef/core.rs b/ports/cef/core.rs index a3e6b90dd9a..8a895f76ebb 100644 --- a/ports/cef/core.rs +++ b/ports/cef/core.rs @@ -2,19 +2,43 @@ * 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 browser::{GLOBAL_BROWSERS, browser_callback_after_created}; use command_line::command_line_init; use interfaces::cef_app_t; -use eutil::Downcast; -use switches::{KPROCESSTYPE, KWAITFORDEBUGGER}; use types::{cef_main_args_t, cef_settings_t}; +use window; +use compositing::windowing::{IdleWindowEvent, WindowEvent}; +use geom::size::TypedSize2D; use glfw_app; -use libc::funcs::c95::string::strlen; use libc::{c_char, c_int, c_void}; use native; +use rustrt::local::Local; use servo::Browser; -use std::slice; +use servo_util::opts; +use servo_util::opts::OpenGL; +use std::c_str::CString; +use std::cell::RefCell; +use std::rc::Rc; +use std::rt; + +const MAX_RENDERING_THREADS: uint = 128; + +// TODO(pcwalton): Get the home page via the CEF API. +static HOME_URL: &'static str = "http://s27.postimg.org/vqbtrolyr/servo.jpg"; + +// TODO(pcwalton): Support multiple windows. +pub enum ServoCefGlobals { + OnScreenGlobals(RefCell<Rc<glfw_app::window::Window>>, + RefCell<Browser<glfw_app::window::Window>>), + OffScreenGlobals(RefCell<Rc<window::Window>>, RefCell<Browser<window::Window>>), +} + +local_data_key!(pub globals: ServoCefGlobals) + +local_data_key!(pub message_queue: RefCell<Vec<WindowEvent>>) + +// Copied from `libnative/lib.rs`. +static OS_DEFAULT_STACK_ESTIMATE: uint = 2 * (1 << 20); static CEF_API_HASH_UNIVERSAL: &'static [u8] = b"8efd129f4afc344bd04b2feb7f73a149b6c4e27f\0"; #[cfg(target_os="windows")] @@ -26,68 +50,109 @@ static CEF_API_HASH_PLATFORM: &'static [u8] = b"2bc564c3871965ef3a2531b528bda3e1 #[no_mangle] pub extern "C" fn cef_initialize(args: *const cef_main_args_t, - _settings: *mut cef_settings_t, + settings: *mut cef_settings_t, application: *mut cef_app_t, _windows_sandbox_info: *const c_void) -> c_int { if args.is_null() { return 0; } + unsafe { + rt::init((*args).argc as int, (*args).argv); command_line_init((*args).argc, (*args).argv); - (*application).get_browser_process_handler.map(|cb| { - let handler = cb(application); - if handler.is_not_null() { - (*handler).on_context_initialized.map(|hcb| hcb(handler)); - } - }); + + if !application.is_null() { + (*application).get_browser_process_handler.map(|cb| { + let handler = cb(application); + if handler.is_not_null() { + (*handler).on_context_initialized.map(|hcb| hcb(handler)); + } + }); + } } + + create_rust_task(); + + message_queue.replace(Some(RefCell::new(Vec::new()))); + + let urls = vec![HOME_URL.to_string()]; + opts::set_opts(opts::Opts { + urls: urls, + n_paint_threads: 1, + gpu_painting: false, + tile_size: 512, + device_pixels_per_px: None, + time_profiler_period: None, + memory_profiler_period: None, + enable_experimental: false, + nonincremental_layout: false, + layout_threads: unsafe { + if ((*settings).rendering_threads as uint) < 1 { + 1 + } else if (*settings).rendering_threads as uint > MAX_RENDERING_THREADS { + MAX_RENDERING_THREADS + } else { + (*settings).rendering_threads as uint + } + }, + output_file: None, + headless: false, + hard_fail: false, + bubble_inline_sizes_separately: false, + show_debug_borders: false, + show_debug_fragment_borders: false, + enable_text_antialiasing: true, + trace_layout: false, + devtools_port: None, + initial_window_size: TypedSize2D(800, 600), + profile_tasks: false, + user_agent: None, + dump_flow_tree: false, + validate_display_list_geometry: false, + render_api: OpenGL, + }); + return 1 } +// Copied from `libnative/lib.rs`. +fn create_rust_task() { + let something_around_the_top_of_the_stack = 1; + let addr = &something_around_the_top_of_the_stack as *const int; + let my_stack_top = addr as uint; + + // FIXME #11359 we just assume that this thread has a stack of a + // certain size, and estimate that there's at most 20KB of stack + // frames above our current position. + + let my_stack_bottom = my_stack_top + 20000 - OS_DEFAULT_STACK_ESTIMATE; + + let task = native::task::new((my_stack_bottom, my_stack_top), rt::thread::main_guard_page()); + Local::put(task); +} + #[no_mangle] pub extern "C" fn cef_shutdown() { } #[no_mangle] pub extern "C" fn cef_run_message_loop() { - native::start(0, 0 as *const *const u8, proc() { - GLOBAL_BROWSERS.get().map(|refcellbrowsers| { - let browsers = refcellbrowsers.borrow(); - let mut num = browsers.len(); - for active_browser in browsers.iter() { - *active_browser.downcast().window.borrow_mut() = - Some(glfw_app::create_window()); - *active_browser.downcast().servo_browser.borrow_mut() = - Some(Browser::new((*active_browser.downcast() - .window - .borrow()).clone())); - if !active_browser.downcast().callback_executed.get() { - browser_callback_after_created((*active_browser).clone()); - } - } - while num > 0 { - for active_browser in browsers.iter() - .filter(|&active_browser| { - active_browser.downcast() - .servo_browser - .borrow() - .is_some() - }) { - let ref mut browser = active_browser.downcast(); - let mut servobrowser = browser.servo_browser.borrow_mut().take().unwrap(); - if !servobrowser.handle_event(browser.window - .borrow_mut() - .as_ref() - .unwrap() - .wait_events()) { - servobrowser.shutdown(); - num -= 1; - } - } - } - }); - }); + let mut the_globals = globals.get(); + let the_globals = the_globals.as_mut().unwrap(); + match **the_globals { + OnScreenGlobals(ref window, ref browser) => { + while browser.borrow_mut().handle_event(window.borrow_mut().wait_events()) {} + } + OffScreenGlobals(ref window, ref browser) => { + while browser.borrow_mut().handle_event(window.borrow_mut().wait_events()) {} + } + } +} + +#[no_mangle] +pub extern "C" fn cef_do_message_loop_work() { + send_window_event(IdleWindowEvent) } #[no_mangle] @@ -95,33 +160,91 @@ pub extern "C" fn cef_quit_message_loop() { } #[no_mangle] -pub extern "C" fn cef_execute_process(args: *const cef_main_args_t, +pub extern "C" fn cef_execute_process(_args: *const cef_main_args_t, _app: *mut cef_app_t, _windows_sandbox_info: *mut c_void) -> c_int { - unsafe { - if args.is_null() { - println!("args must be passed"); - return -1; - } - for i in range(0u, (*args).argc as uint) { - let u = (*args).argv.offset(i as int) as *const u8; - slice::raw::buf_as_slice(u, strlen(u as *const i8) as uint, |s| { - if s.starts_with("--".as_bytes()) { - if s.slice_from(2) == KWAITFORDEBUGGER.as_bytes() { - //FIXME: this is NOT functionally equivalent to chromium! - - //this should be a pause() call with an installed signal - //handler callback, something which is impossible now in rust - } else if s.slice_from(2) == KPROCESSTYPE.as_bytes() { - //TODO: run other process now - } - } - }); + -1 +} + +pub fn send_window_event(event: WindowEvent) { + message_queue.get().as_mut().unwrap().borrow_mut().push(event); + + let mut the_globals = globals.get(); + let the_globals = match the_globals.as_mut() { + None => return, + Some(the_globals) => the_globals, + }; + loop { + match **the_globals { + OnScreenGlobals(_, ref browser) => { + match browser.try_borrow_mut() { + None => { + // We're trying to send an event while processing another one. This will + // cause general badness, so queue up that event instead of immediately + // processing it. + break + } + Some(ref mut browser) => { + let event = match message_queue.get() + .as_mut() + .unwrap() + .borrow_mut() + .pop() { + None => return, + Some(event) => event, + }; + browser.handle_event(event); + } + } + } + OffScreenGlobals(_, ref browser) => { + match browser.try_borrow_mut() { + None => { + // We're trying to send an event while processing another one. This will + // cause general badness, so queue up that event instead of immediately + // processing it. + break + } + Some(ref mut browser) => { + let event = match message_queue.get() + .as_mut() + .unwrap() + .borrow_mut() + .pop() { + None => return, + Some(event) => event, + }; + browser.handle_event(event); + } + } + } } } - //process type not specified, must be browser process (NOOP) - -1 +} + +macro_rules! browser_method_delegate( + ( $( fn $method:ident ( ) -> $return_type:ty ; )* ) => ( + $( + pub fn $method() -> $return_type { + let mut the_globals = globals.get(); + let the_globals = match the_globals.as_mut() { + None => panic!("{}: no globals created", stringify!($method)), + Some(the_globals) => the_globals, + }; + match **the_globals { + OnScreenGlobals(_, ref browser) => browser.borrow_mut().$method(), + OffScreenGlobals(_, ref browser) => browser.borrow_mut().$method(), + } + } + )* + ) +) + +browser_method_delegate! { + fn repaint_synchronously() -> (); + fn pinch_zoom_level() -> f32; + fn get_title_for_main_frame() -> (); } #[no_mangle] @@ -134,6 +257,16 @@ pub extern "C" fn cef_api_hash(entry: c_int) -> *const c_char { } #[no_mangle] +pub extern "C" fn cef_log(_file: *const c_char, + _line: c_int, + _severity: c_int, + message: *const c_char) { + unsafe { + println!("{}", CString::new(message, false)) + } +} + +#[no_mangle] pub extern "C" fn cef_get_min_log_level() -> c_int { 0 } diff --git a/ports/cef/frame.rs b/ports/cef/frame.rs new file mode 100644 index 00000000000..eb915fe9fe3 --- /dev/null +++ b/ports/cef/frame.rs @@ -0,0 +1,45 @@ +/* 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 eutil::Downcast; +use interfaces::{CefFrame, CefStringVisitor, cef_frame_t, cef_string_visitor_t}; +use types::{cef_string_t, cef_string_userfree_t}; + +use core; +use compositing::windowing::LoadUrlWindowEvent; +use std::cell::RefCell; + +pub struct ServoCefFrame { + pub title_visitor: RefCell<Option<CefStringVisitor>>, + pub url: RefCell<String>, +} + +impl ServoCefFrame { + pub fn new() -> ServoCefFrame { + ServoCefFrame { + title_visitor: RefCell::new(None), + url: RefCell::new(String::new()), + } + } +} + +cef_class_impl! { + ServoCefFrame : CefFrame, cef_frame_t { + fn load_url(&this, url: *const cef_string_t) -> () { + let this = this.downcast(); + *this.url.borrow_mut() = String::from_utf16(url).unwrap(); + core::send_window_event(LoadUrlWindowEvent(String::from_utf16(url).unwrap())); + } + fn get_url(&this) -> cef_string_userfree_t { + let this = this.downcast(); + (*this.url.borrow()).clone() + } + fn get_text(&this, visitor: *mut cef_string_visitor_t) -> () { + let this = this.downcast(); + *this.title_visitor.borrow_mut() = Some(visitor); + core::get_title_for_main_frame(); + } + } +} + diff --git a/ports/cef/lib.rs b/ports/cef/lib.rs index 28e3d33509a..3b44180ec11 100644 --- a/ports/cef/lib.rs +++ b/ports/cef/lib.rs @@ -14,11 +14,12 @@ extern crate log; extern crate "plugins" as servo_plugins; extern crate servo; +extern crate compositing; extern crate azure; -extern crate compositing; extern crate geom; extern crate gfx; +extern crate gleam; extern crate glfw; extern crate glfw_app; extern crate js; @@ -34,10 +35,15 @@ extern crate stb_image; extern crate green; extern crate native; +extern crate rustrt; extern crate libc; extern crate "url" as std_url; #[cfg(target_os="macos")] +extern crate cgl; +#[cfg(target_os="macos")] +extern crate cocoa; +#[cfg(target_os="macos")] extern crate core_graphics; #[cfg(target_os="macos")] extern crate core_text; @@ -46,14 +52,17 @@ extern crate core_text; pub mod macros; pub mod browser; +pub mod browser_host; pub mod command_line; pub mod cookie; pub mod core; pub mod drag_data; pub mod eutil; +pub mod frame; pub mod interfaces; pub mod print_settings; pub mod process_message; +pub mod render_handler; pub mod request; pub mod request_context; pub mod response; @@ -67,8 +76,9 @@ pub mod switches; pub mod task; pub mod types; pub mod urlrequest; -pub mod values; pub mod v8; +pub mod values; +pub mod window; pub mod wrappers; pub mod xml_reader; pub mod zip_reader; diff --git a/ports/cef/render_handler.rs b/ports/cef/render_handler.rs new file mode 100644 index 00000000000..13e277c061d --- /dev/null +++ b/ports/cef/render_handler.rs @@ -0,0 +1,19 @@ +/* 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 interfaces::{CefBrowser, CefRenderHandler}; +use types::PET_VIEW; + +use std::ptr; + +pub trait CefRenderHandlerExtensions { + fn paint(&self, browser: CefBrowser); +} + +impl CefRenderHandlerExtensions for CefRenderHandler { + fn paint(&self, browser: CefBrowser) { + self.on_paint(browser, PET_VIEW, 0, ptr::null(), &mut (), 0, 0) + } +} + diff --git a/ports/cef/string.rs b/ports/cef/string.rs index b4ead90f71d..9a817e97674 100644 --- a/ports/cef/string.rs +++ b/ports/cef/string.rs @@ -4,7 +4,7 @@ use eutil::slice_to_str; -use libc::{mod, size_t, c_int, c_ushort,c_void}; +use libc::{mod, size_t, c_int, c_ushort, c_void}; use libc::types::os::arch::c95::wchar_t; use std::char; use std::mem; @@ -54,7 +54,7 @@ pub extern "C" fn cef_string_userfree_utf8_free(cs: *mut cef_string_userfree_utf } #[no_mangle] -pub extern "C" fn cef_string_userfree_utf16_free(cs: *mut cef_string_userfree_utf16_t) { +pub extern "C" fn cef_string_userfree_utf16_free(cs: cef_string_userfree_utf16_t) { unsafe { cef_string_utf16_clear(cs); libc::free(cs as *mut c_void) @@ -269,19 +269,6 @@ pub extern "C" fn cef_string_utf8_to_wide(src: *const u8, src_len: size_t, outpu }) } -/// Wraps a borrowed reference to a UTF-16 CEF string. -pub struct CefStringRef<'a> { - pub c_object: &'a *const cef_string_utf16_t, -} - -impl<'a> CefStringRef<'a> { - pub unsafe fn from_c_object(c_object: &'a *const cef_string_utf16_t) -> CefStringRef<'a> { - CefStringRef { - c_object: c_object, - } - } -} - #[no_mangle] pub extern "C" fn cef_string_wide_to_utf8(src: *const wchar_t, src_len: size_t, output: *mut cef_string_utf8_t) -> c_int { if mem::size_of::<wchar_t>() == mem::size_of::<u16>() { @@ -321,3 +308,16 @@ pub fn empty_utf16_string() -> cef_string_utf16_t { } } +pub fn string_to_userfree_string(string: cef_string_utf16_t) -> cef_string_userfree_utf16_t { + unsafe { + let allocation: cef_string_userfree_utf16_t = + mem::transmute(libc::malloc(mem::size_of::<cef_string_utf16_t>() as size_t)); + ptr::write(allocation, string); + allocation + } +} + +pub fn empty_utf16_userfree_string() -> cef_string_userfree_utf16_t { + string_to_userfree_string(empty_utf16_string()) +} + diff --git a/ports/cef/string_multimap.rs b/ports/cef/string_multimap.rs index 88d83db9618..31974d5a95d 100644 --- a/ports/cef/string_multimap.rs +++ b/ports/cef/string_multimap.rs @@ -8,7 +8,8 @@ use std::collections::TreeMap; use std::iter::AdditiveIterator; use std::mem; use std::string::String; -use string::{cef_string_userfree_utf16_alloc,cef_string_userfree_utf16_free,cef_string_utf16_set}; +use string::{cef_string_userfree_utf16_alloc, cef_string_userfree_utf16_free}; +use string::{cef_string_utf16_set}; use types::{cef_string_multimap_t,cef_string_t}; fn string_multimap_to_treemap(smm: *mut cef_string_multimap_t) -> *mut TreeMap<String, Vec<*mut cef_string_t>> { diff --git a/ports/cef/types.rs b/ports/cef/types.rs index 66809dc0223..3f0d0752661 100644 --- a/ports/cef/types.rs +++ b/ports/cef/types.rs @@ -25,7 +25,6 @@ pub enum cef_response_t {} pub enum cef_urlrequest_client_t {} pub enum cef_domnode_t {} pub enum cef_load_handler_t {} -pub enum cef_request_context_t {} pub enum cef_browser_settings_t {} pub enum cef_v8context_t {} pub enum cef_v8exception_t {} @@ -68,7 +67,7 @@ pub type CefBrowserSettings = cef_browser_settings_t; pub type CefScreenInfo = cef_screen_info_t; pub type cef_string_t = cef_string_utf16; //FIXME: this is #defined... -pub type cef_string_userfree_t = cef_string_t; //FIXME: this is #defined... +pub type cef_string_userfree_t = *mut cef_string_t; //FIXME: this is #defined... pub struct cef_string_utf8 { pub str: *mut u8, @@ -79,7 +78,7 @@ pub type cef_string_utf8_t = cef_string_utf8; pub type cef_string_userfree_utf8_t = cef_string_utf8; pub type cef_string_utf16_t = cef_string_utf16; -pub type cef_string_userfree_utf16_t = cef_string_utf16; +pub type cef_string_userfree_utf16_t = *mut cef_string_utf16; pub struct cef_string_utf16 { pub str: *mut c_ushort, pub length: size_t, @@ -102,61 +101,61 @@ pub struct cef_main_args { pub type cef_color_t = c_uint; -/// +// // Existing thread IDs. -/// +// pub enum cef_thread_id_t { // BROWSER PROCESS THREADS -- Only available in the browser process. - /// + // // The main thread in the browser. This will be the same as the main // application thread if CefInitialize() is called with a // CefSettings.multi_threaded_message_loop value of false. - /// + // TID_UI, - /// + // // Used to interact with the database. - /// + // TID_DB, - /// + // // Used to interact with the file system. - /// + // TID_FILE, - /// + // // Used for file system operations that block user interactions. // Responsiveness of this thread affects users. - /// + // TID_FILE_USER_BLOCKING, - /// + // // Used to launch and terminate browser processes. - /// + // TID_PROCESS_LAUNCHER, - /// + // // Used to handle slow HTTP cache operations. - /// + // TID_CACHE, - /// + // // Used to process IPC and network messages. - /// + // TID_IO, // RENDER PROCESS THREADS -- Only available in the render process. - /// + // // The main thread in the renderer. Used for all WebKit and V8 interaction. - /// + // TID_RENDERER, } -/// +// // Navigation types. -/// +// pub enum cef_navigation_type_t { NAVIGATION_LINK_CLICKED = 0, NAVIGATION_FORM_SUBMITTED, @@ -166,138 +165,138 @@ pub enum cef_navigation_type_t { NAVIGATION_OTHER, } -/// +// // Mouse button types. -/// +// pub enum cef_mouse_button_type_t { MBT_LEFT = 0, MBT_MIDDLE, MBT_RIGHT, } -/// +// // Structure representing mouse event information. -/// +// pub type cef_mouse_event_t = cef_mouse_event; pub type CefMouseEvent = cef_mouse_event_t; pub struct cef_mouse_event { - /// + // // X coordinate relative to the left side of the view. - /// + // pub x: c_int, - /// + // // Y coordinate relative to the top side of the view. - /// + // pub y: c_int, - /// + // // Bit flags describing any pressed modifier keys. See // cef_event_flags_t for values. - /// + // pub modifiers: c_uint, } -/// +// // Post data elements may represent either bytes or files. -/// +// pub enum cef_postdataelement_type_t { PDE_TYPE_EMPTY = 0, PDE_TYPE_BYTES, PDE_TYPE_FILE, } -/// +// // Flags used to customize the behavior of CefURLRequest. -/// +// pub enum cef_urlrequest_flags_t { - /// + // // Default behavior. - /// + // UR_FLAG_NONE = 0, - /// + // // If set the cache will be skipped when handling the request. - /// + // UR_FLAG_SKIP_CACHE = 1 << 0, - /// + // // If set user name, password, and cookies may be sent with the request. - /// + // UR_FLAG_ALLOW_CACHED_CREDENTIALS = 1 << 1, - /// + // // If set cookies may be sent with the request and saved from the response. // UR_FLAG_ALLOW_CACHED_CREDENTIALS must also be set. - /// + // UR_FLAG_ALLOW_COOKIES = 1 << 2, - /// + // // If set upload progress events will be generated when a request has a body. - /// + // UR_FLAG_REPORT_UPLOAD_PROGRESS = 1 << 3, - /// + // // If set load timing info will be collected for the request. - /// + // UR_FLAG_REPORT_LOAD_TIMING = 1 << 4, - /// + // // If set the headers sent and received for the request will be recorded. - /// + // UR_FLAG_REPORT_RAW_HEADERS = 1 << 5, - /// + // // If set the CefURLRequestClient::OnDownloadData method will not be called. - /// + // UR_FLAG_NO_DOWNLOAD_DATA = 1 << 6, - /// + // // If set 5XX redirect errors will be propagated to the observer instead of // automatically re-tried. This currently only applies for requests // originated in the browser process. - /// + // UR_FLAG_NO_RETRY_ON_5XX = 1 << 7, } -/// +// // Flags that represent CefURLRequest status. -/// +// pub enum cef_urlrequest_status_t { - /// + // // Unknown status. - /// + // UR_UNKNOWN = 0, - /// + // // Request succeeded. - /// + // UR_SUCCESS, - /// + // // An IO request is pending, and the caller will be informed when it is // completed. - /// + // UR_IO_PENDING, - /// + // // Request was canceled programatically. - /// + // UR_CANCELED, - /// + // // Request failed for some reason. - /// + // UR_FAILED, } -/// +// // Supported error code values. See net\base\net_error_list.h for complete // descriptions of the error codes. -/// +// pub enum cef_errorcode_t { ERR_NONE = 0, ERR_FAILED = -2, @@ -351,9 +350,9 @@ pub enum cef_errorcode_t { } -/// +// // Key event types. -/// +// pub enum cef_key_event_type_t { KEYEVENT_RAWKEYDOWN = 0, KEYEVENT_KEYDOWN, @@ -361,74 +360,74 @@ pub enum cef_key_event_type_t { KEYEVENT_CHAR } -/// +// // Structure representing keyboard event information. -/// +// pub type cef_key_event_t = cef_key_event; pub struct cef_key_event { - /// + // // The type of keyboard event. - /// + // pub t: cef_key_event_type_t, - /// + // // Bit flags describing any pressed modifier keys. See // cef_event_flags_t for values. - /// + // pub modifiers: c_uint, - /// + // // The Windows key code for the key event. This value is used by the DOM // specification. Sometimes it comes directly from the event (i.e. on // Windows) and sometimes it's determined using a mapping function. See // WebCore/platform/chromium/KeyboardCodes.h for the list of values. - /// + // pub windows_key_code: c_int, - /// + // // The actual key code genenerated by the platform. - /// + // pub native_key_code: c_int, - /// + // // Indicates whether the event is considered a "system key" event (see // http://msdn.microsoft.com/en-us/library/ms646286(VS.85).aspx for details). // This value will always be false on non-Windows platforms. - /// + // pub is_system_key: c_int, - /// + // // The character generated by the keystroke. - /// + // pub character: c_ushort, //FIXME: can be wchar_t also? - /// + // // Same as |character| but unmodified by any concurrently-held modifiers // (except shift). This is useful for working out shortcut keys. - /// + // pub unmodified_character: c_ushort, //FIXME: can be wchar_t also? - /// + // // True if the focus is currently on an editable field on the page. This is // useful for determining if standard key events should be intercepted. - /// + // pub focus_on_editable_field: c_int, } pub type CefKeyEvent = cef_key_event_t; -/// +// // Structure representing a point. -/// +// pub type cef_point_t = cef_point; pub struct cef_point { pub x: c_int, pub y: c_int, } -/// +// // Structure representing a rectangle. -/// +// pub struct cef_rect { pub x: c_int, pub y: c_int, @@ -447,17 +446,17 @@ impl cef_rect { } } -/// +// // Paint element types. -/// +// pub enum cef_paint_element_type_t { PET_VIEW = 0, PET_POPUP, } -/// +// // DOM document types. -/// +// pub enum cef_dom_document_type_t { DOM_DOCUMENT_TYPE_UNKNOWN = 0, DOM_DOCUMENT_TYPE_HTML, @@ -465,30 +464,30 @@ pub enum cef_dom_document_type_t { DOM_DOCUMENT_TYPE_PLUGIN, } -/// +// // Supported file dialog modes. -/// +// pub enum cef_file_dialog_mode_t { - /// + // // Requires that the file exists before allowing the user to pick it. - /// + // FILE_DIALOG_OPEN = 0, - /// + // // Like Open, but allows picking multiple files to open. - /// + // FILE_DIALOG_OPEN_MULTIPLE, - /// + // // Allows picking a nonexistent file, and prompts to overwrite if the file // already exists. - /// + // FILE_DIALOG_SAVE, } -/// +// // Supported value types. -/// +// pub enum cef_value_type_t { VTYPE_INVALID = 0, VTYPE_NULL, @@ -503,229 +502,233 @@ pub enum cef_value_type_t { pub type CefValueType = cef_value_type_t; -/// +// // Existing process IDs. -/// +// pub enum cef_process_id_t { - /// + // // Browser process. - /// + // PID_BROWSER, - /// + // // Renderer process. - /// + // PID_RENDERER, } pub type CefProcessId = cef_process_id_t; -/// +// // Log severity levels. -/// +// pub enum cef_log_severity_t { - /// + // // Default logging (currently INFO logging). - /// + // LOGSEVERITY_DEFAULT, - /// + // // Verbose logging. - /// + // LOGSEVERITY_VERBOSE, - /// + // // INFO logging. - /// + // LOGSEVERITY_INFO, - /// + // // WARNING logging. - /// + // LOGSEVERITY_WARNING, - /// + // // ERROR logging. - /// + // LOGSEVERITY_ERROR, - /// + // // ERROR_REPORT logging. - /// + // LOGSEVERITY_ERROR_REPORT, - /// + // // Completely disable logging. - /// + // LOGSEVERITY_DISABLE = 99 } -/// +// // Initialization settings. Specify NULL or 0 to get the recommended default // values. Many of these and other settings can also configured using command- // line switches. -/// +// pub type cef_settings_t = cef_settings; + +#[repr(C)] pub struct cef_settings { - /// + // // Size of this structure. - /// + // pub size: size_t, - /// + // // Set to true (1) to use a single process for the browser and renderer. This // run mode is not officially supported by Chromium and is less stable than // the multi-process default. Also configurable using the "single-process" // command-line switch. - /// + // pub single_process: c_int, - /// + // // Set to true (1) to disable the sandbox for sub-processes. See // cef_sandbox_win.h for requirements to enable the sandbox on Windows. Also // configurable using the "no-sandbox" command-line switch. - /// + // pub no_sandbox: c_int, - /// + // // The path to a separate executable that will be launched for sub-processes. // By default the browser process executable is used. See the comments on // CefExecuteProcess() for details. Also configurable using the // "browser-subprocess-path" command-line switch. - /// + // pub browser_subprocess_path: cef_string_t, - /// + // // Set to true (1) to have the browser process message loop run in a separate // thread. If false (0) than the CefDoMessageLoopWork() function must be // called from your application message loop. - /// + // pub multi_threaded_message_loop: c_int, - /// + + // + // Set to true (1) to enable windowless (off-screen) rendering support. Do not + // enable this value if the application does not use windowless rendering as + // it may reduce rendering performance on some systems. + // + pub windowless_rendering_enabled: c_int, + + // // Set to true (1) to disable configuration of browser process features using // standard CEF and Chromium command-line arguments. Configuration can still // be specified using CEF data structures or via the // CefApp::OnBeforeCommandLineProcessing() method. - /// + // pub command_line_args_disabled: c_int, - /// + // // The location where cache data will be stored on disk. If empty an in-memory // cache will be used for some features and a temporary disk cache for others. // HTML5 databases such as localStorage will only persist across sessions if a // cache path is specified. - /// + // pub cache_path: cef_string_t, - /// + // // To persist session cookies (cookies without an expiry date or validity // interval) by default when using the global cookie manager set this value to // true. Session cookies are generally intended to be transient and most Web // browsers do not persist them. A |cache_path| value must also be specified to // enable this feature. Also configurable using the "persist-session-cookies" // command-line switch. - /// + // pub persist_session_cookies: c_int, - /// + // // Value that will be returned as the User-Agent HTTP header. If empty the // default User-Agent string will be used. Also configurable using the // "user-agent" command-line switch. - /// + // pub user_agent: cef_string_t, - /// + // // Value that will be inserted as the product portion of the default // User-Agent string. If empty the Chromium product version will be used. If // |userAgent| is specified this value will be ignored. Also configurable // using the "product-version" command-line switch. - /// + // pub product_version: cef_string_t, - /// + // // The locale string that will be passed to WebKit. If empty the default // locale of "en-US" will be used. This value is ignored on Linux where locale // is determined using environment variable parsing with the precedence order: // LANGUAGE, LC_ALL, LC_MESSAGES and LANG. Also configurable using the "lang" // command-line switch. - /// + // pub locale: cef_string_t, - /// + // // The directory and file name to use for the debug log. If empty, the // default name of "debug.log" will be used and the file will be written // to the application directory. Also configurable using the "log-file" // command-line switch. - /// + // pub log_file: cef_string_t, - /// + // // The log severity. Only messages of this severity level or higher will be // logged. Also configurable using the "log-severity" command-line switch with // a value of "verbose", "info", "warning", "error", "error-report" or // "disable". - /// + // pub log_severity: cef_log_severity_t, - /// - // Enable DCHECK in release mode to ease debugging. Also configurable using the - // "enable-release-dcheck" command-line switch. - /// - pub release_dcheck_enabled: c_int, - - /// + // // Custom flags that will be used when initializing the V8 JavaScript engine. // The consequences of using custom flags may not be well tested. Also // configurable using the "js-flags" command-line switch. - /// + // pub javascript_flags: cef_string_t, - /// + // // The fully qualified path for the resources directory. If this value is // empty the cef.pak and/or devtools_resources.pak files must be located in // the module directory on Windows/Linux or the app bundle Resources directory // on Mac OS X. Also configurable using the "resources-dir-path" command-line // switch. - /// + // pub resources_dir_path: cef_string_t, - /// + // // The fully qualified path for the locales directory. If this value is empty // the locales directory must be located in the module directory. This value // is ignored on Mac OS X where pack files are always loaded from the app // bundle Resources directory. Also configurable using the "locales-dir-path" // command-line switch. - /// + // pub locales_dir_path: cef_string_t, - /// + // // Set to true (1) to disable loading of pack files for resources and locales. // A resource bundle handler must be provided for the browser and render // processes via CefApp::GetResourceBundleHandler() if loading of pack files // is disabled. Also configurable using the "disable-pack-loading" command- // line switch. - /// + // pub pack_loading_disabled: c_int, - /// + // // Set to a value between 1024 and 65535 to enable remote debugging on the // specified port. For example, if 8080 is specified the remote debugging URL // will be http://localhost:8080. CEF can be remotely debugged from any CEF or // Chrome browser window. Also configurable using the "remote-debugging-port" // command-line switch. - /// + // pub remote_debugging_port: c_int, - /// + // // The number of stack trace frames to capture for uncaught exceptions. // Specify a positive value to enable the CefV8ContextHandler:: // OnUncaughtException() callback. Specify 0 (default value) and // OnUncaughtException() will not be called. Also configurable using the // "uncaught-exception-stack-size" command-line switch. - /// + // pub uncaught_exception_stack_size: c_int, - /// + // // By default CEF V8 references will be invalidated (the IsValid() method will // return false) after the owning context has been released. This reduces the // need for external record keeping and avoids crashes due to the use of V8 @@ -744,73 +747,124 @@ pub struct cef_settings { // // Also configurable using the "context-safety-implementation" command-line // switch. - /// + // pub context_safety_implementation: c_int, - /// + // // Set to true (1) to ignore errors related to invalid SSL certificates. // Enabling this setting can lead to potential security vulnerabilities like // "man in the middle" attacks. Applications that load content from the // internet should not enable this setting. Also configurable using the // "ignore-certificate-errors" command-line switch. - /// + // pub ignore_certificate_errors: c_int, - /// + // // Opaque background color used for accelerated content. By default the // background color will be white. Only the RGB compontents of the specified // value will be used. The alpha component must greater than 0 to enable use // of the background color but will be otherwise ignored. - /// + // pub background_color: cef_color_t, + + // + // Determines how many rendering threads are used. + // + pub rendering_threads: c_int, } -/// +// // Structure defining the reference count implementation functions. All // framework structures must include the cef_base_t structure first. -/// +// pub type cef_base_t = cef_base; pub struct cef_base { - /// + // // Size of the data structure. - /// + // pub size: size_t, - /// + // // Increment the reference count. - /// + // pub add_ref: Option<extern "C" fn(base: *mut cef_base) -> c_int>, - /// + // // Decrement the reference count. Delete this object when no references // remain. - /// + // pub release: Option<extern "C" fn(base: *mut cef_base) -> c_int>, - /// + // // Returns the current number of references. - /// + // pub get_refct: Option<extern "C" fn(base: *mut cef_base) -> c_int>, } pub type CefBase = *mut cef_base_t; -/// +// // Class representing window information. -/// +// pub type cef_window_info_t = cef_window_info; + +#[cfg(target_os="linux")] +pub struct cef_window_info { + pub x: c_uint, + pub y: c_uint, + pub width: c_uint, + pub height: c_uint, + + // + // Pointer for the parent window. + // + pub parent_window: cef_window_handle_t, + + // + // Set to true (1) to create the browser using windowless (off-screen) + // rendering. No window will be created for the browser and all rendering will + // occur via the CefRenderHandler interface. The |parent_window| value will be + // used to identify monitor info and to act as the parent window for dialogs, + // context menus, etc. If |parent_window| is not provided then the main screen + // monitor will be used and some functionality that requires a parent window + // may not function correctly. In order to create windowless browsers the + // CefSettings.windowless_rendering_enabled value must be set to true. + // + pub windowless_rendering_enabled: c_int, + + // + // Set to true (1) to enable transparent painting in combination with + // windowless rendering. When this value is true a transparent background + // color will be used (RGBA=0x00000000). When this value is false the + // background will be white and opaque. + // + pub transparent_painting_enabled: c_int, + + // + // Pointer for the new browser window. Only used with windowed rendering. + // + pub window: cef_window_handle_t +} + +#[cfg(target_os="macos")] pub struct cef_window_info { + pub window_name: cef_string_t, pub x: c_uint, pub y: c_uint, pub width: c_uint, pub height: c_uint, - /// + // + // Set to true (1) to create the view initially hidden. + // + pub hidden: c_int, + + // // Pointer for the parent window. - /// + // pub parent_window: cef_window_handle_t, - /// + // // Set to true (1) to create the browser using windowless (off-screen) // rendering. No window will be created for the browser and all rendering will // occur via the CefRenderHandler interface. The |parent_window| value will be @@ -819,28 +873,28 @@ pub struct cef_window_info { // monitor will be used and some functionality that requires a parent window // may not function correctly. In order to create windowless browsers the // CefSettings.windowless_rendering_enabled value must be set to true. - /// + // pub windowless_rendering_enabled: c_int, - /// + // // Set to true (1) to enable transparent painting in combination with // windowless rendering. When this value is true a transparent background // color will be used (RGBA=0x00000000). When this value is false the // background will be white and opaque. - /// + // pub transparent_painting_enabled: c_int, - /// + // // Pointer for the new browser window. Only used with windowed rendering. - /// + // pub window: cef_window_handle_t } pub type CefWindowInfo = cef_window_info_t; -/// +// // Supported menu item types. -/// +// pub enum cef_menu_item_type_t { MENUITEMTYPE_NONE, MENUITEMTYPE_COMMAND, @@ -850,73 +904,73 @@ pub enum cef_menu_item_type_t { MENUITEMTYPE_SUBMENU, } -/// +// // Supported context menu type flags. -/// +// pub enum cef_context_menu_type_flags_t { - /// + // // No node is selected. - /// + // CM_TYPEFLAG_NONE = 0, - /// + // // The top page is selected. - /// + // CM_TYPEFLAG_PAGE = 1 << 0, - /// + // // A subframe page is selected. - /// + // CM_TYPEFLAG_FRAME = 1 << 1, - /// + // // A link is selected. - /// + // CM_TYPEFLAG_LINK = 1 << 2, - /// + // // A media node is selected. - /// + // CM_TYPEFLAG_MEDIA = 1 << 3, - /// + // // There is a textual or mixed selection that is selected. - /// + // CM_TYPEFLAG_SELECTION = 1 << 4, - /// + // // An editable element is selected. - /// + // CM_TYPEFLAG_EDITABLE = 1 << 5, } -/// +// // Supported context menu media types. -/// +// pub enum cef_context_menu_media_type_t { - /// + // // No special node is in context. - /// + // CM_MEDIATYPE_NONE, - /// + // // An image node is selected. - /// + // CM_MEDIATYPE_IMAGE, - /// + // // A video node is selected. - /// + // CM_MEDIATYPE_VIDEO, - /// + // // An audio node is selected. - /// + // CM_MEDIATYPE_AUDIO, - /// + // // A file node is selected. - /// + // CM_MEDIATYPE_FILE, - /// + // // A plugin node is selected. - /// + // CM_MEDIATYPE_PLUGIN, } -/// +// // Supported context menu media state bit flags. -/// +// pub enum cef_context_menu_media_state_flags_t { CM_MEDIAFLAG_NONE = 0, CM_MEDIAFLAG_ERROR = 1 << 0, @@ -931,9 +985,9 @@ pub enum cef_context_menu_media_state_flags_t { CM_MEDIAFLAG_CAN_ROTATE = 1 << 9, } -/// +// // Supported context menu edit state bit flags. -/// +// pub enum cef_context_menu_edit_state_flags_t { CM_EDITFLAG_NONE = 0, CM_EDITFLAG_CAN_UNDO = 1 << 0, @@ -946,9 +1000,9 @@ pub enum cef_context_menu_edit_state_flags_t { CM_EDITFLAG_CAN_TRANSLATE = 1 << 7, } -/// +// // Supported event bit flags. -/// +// pub enum cef_event_flags_t { EVENTFLAG_NONE = 0, EVENTFLAG_CAPS_LOCK_ON = 1 << 0, @@ -966,9 +1020,9 @@ pub enum cef_event_flags_t { EVENTFLAG_IS_RIGHT = 1 << 11, } -/// +// // Time information. Values should always be in UTC. -/// +// #[repr(C)] pub struct _cef_time_t { year: c_int, // Four digit year "2007" @@ -984,9 +1038,9 @@ pub struct _cef_time_t { pub type cef_time_t = _cef_time_t; -/// +// // DOM event processing phases. -/// +// pub enum cef_dom_event_phase_t { DOM_EVENT_PHASE_UNKNOWN = 0, DOM_EVENT_PHASE_CAPTURING, @@ -994,9 +1048,9 @@ pub enum cef_dom_event_phase_t { DOM_EVENT_PHASE_BUBBLING, } -/// +// // DOM node types. -/// +// pub enum cef_dom_node_type_t { DOM_NODE_TYPE_UNSUPPORTED = 0, DOM_NODE_TYPE_ELEMENT, @@ -1010,32 +1064,32 @@ pub enum cef_dom_node_type_t { DOM_NODE_TYPE_DOCUMENT_FRAGMENT, } -/// +// // Focus sources. -/// +// pub enum cef_focus_source_t { - /// + // // The source is explicit navigation via the API (LoadURL(), etc). - /// + // FOCUS_SOURCE_NAVIGATION = 0, - /// + // // The source is a system-generated focus event. - /// + // FOCUS_SOURCE_SYSTEM, } -/// +// // Supported JavaScript dialog types. -/// +// pub enum cef_jsdialog_type_t { JSDIALOGTYPE_ALERT = 0, JSDIALOGTYPE_CONFIRM, JSDIALOGTYPE_PROMPT, } -/// +// // Structure representing a size. -/// +// pub struct _cef_size_t { pub width: c_int, pub height: c_int, @@ -1043,9 +1097,9 @@ pub struct _cef_size_t { pub type cef_size_t = _cef_size_t; -/// +// // Structure representing a print job page range. -/// +// pub struct _cef_page_range_t { pub from: c_int, pub to: c_int, @@ -1053,9 +1107,9 @@ pub struct _cef_page_range_t { pub type cef_page_range_t = _cef_page_range_t; -/// +// // Print job duplex mode values. -/// +// pub enum cef_duplex_mode_t { DUPLEX_MODE_UNKNOWN = -1, DUPLEX_MODE_SIMPLEX, @@ -1063,9 +1117,9 @@ pub enum cef_duplex_mode_t { DUPLEX_MODE_SHORT_EDGE, } -/// +// // Print job color mode values. -/// +// pub enum cef_color_model_t { COLOR_MODEL_UNKNOWN, COLOR_MODEL_GRAY, @@ -1090,216 +1144,216 @@ pub enum cef_color_model_t { COLOR_MODEL_PROCESSCOLORMODEL_RGB, // Used in canon printer ppds } -/// +// // Resource type for a request. -/// +// pub enum cef_resource_type_t { - /// + // // Top level page. - /// + // RT_MAIN_FRAME = 0, - /// + // // Frame or iframe. - /// + // RT_SUB_FRAME, - /// + // // CSS stylesheet. - /// + // RT_STYLESHEET, - /// + // // External script. - /// + // RT_SCRIPT, - /// + // // Image (jpg/gif/png/etc). - /// + // RT_IMAGE, - /// + // // Font. - /// + // RT_FONT_RESOURCE, - /// + // // Some other subresource. This is the default type if the actual type is // unknown. - /// + // RT_SUB_RESOURCE, - /// + // // Object (or embed) tag for a plugin, or a resource that a plugin requested. - /// + // RT_OBJECT, - /// + // // Media resource. - /// + // RT_MEDIA, - /// + // // Main resource of a dedicated worker. - /// + // RT_WORKER, - /// + // // Main resource of a shared worker. - /// + // RT_SHARED_WORKER, - /// + // // Explicitly requested prefetch. - /// + // RT_PREFETCH, - /// + // // Favicon. - /// + // RT_FAVICON, - /// + // // XMLHttpRequest. - /// + // RT_XHR, - /// + // // A request for a <ping> - /// + // RT_PING, - /// + // // Main resource of a service worker. - /// + // RT_SERVICE_WORKER, } -/// +// // Transition type for a request. Made up of one source value and 0 or more // qualifiers. -/// +// pub enum cef_transition_type_t { - /// + // // Source is a link click or the JavaScript window.open function. This is // also the default value for requests like sub-resource loads that are not // navigations. - /// + // TT_LINK = 0, - /// + // // Source is some other "explicit" navigation action such as creating a new // browser or using the LoadURL function. This is also the default value // for navigations where the actual type is unknown. - /// + // TT_EXPLICIT = 1, - /// + // // Source is a subframe navigation. This is any content that is automatically // loaded in a non-toplevel frame. For example, if a page consists of several // frames containing ads, those ad URLs will have this transition type. // The user may not even realize the content in these pages is a separate // frame, so may not care about the URL. - /// + // TT_AUTO_SUBFRAME = 3, - /// + // // Source is a subframe navigation explicitly requested by the user that will // generate new navigation entries in the back/forward list. These are // probably more important than frames that were automatically loaded in // the background because the user probably cares about the fact that this // link was loaded. - /// + // TT_MANUAL_SUBFRAME = 4, - /// + // // Source is a form submission by the user. NOTE: In some situations // submitting a form does not result in this transition type. This can happen // if the form uses a script to submit the contents. - /// + // TT_FORM_SUBMIT = 7, - /// + // // Source is a "reload" of the page via the Reload function or by re-visiting // the same URL. NOTE: This is distinct from the concept of whether a // particular load uses "reload semantics" (i.e. bypasses cached data). - /// + // TT_RELOAD = 8, - /// + // // General mask defining the bits used for the source values. - /// + // TT_SOURCE_MASK = 0xFF, // Qualifiers. // Any of the core values above can be augmented by one or more qualifiers. // These qualifiers further define the transition. - /// + // // Attempted to visit a URL but was blocked. - /// + // TT_BLOCKED_FLAG = 0x00800000, - /// + // // Used the Forward or Back function to navigate among browsing history. - /// + // TT_FORWARD_BACK_FLAG = 0x01000000, - /// + // // The beginning of a navigation chain. - /// + // TT_CHAIN_START_FLAG = 0x10000000, - /// + // // The last transition in a redirect chain. - /// + // TT_CHAIN_END_FLAG = 0x20000000, - /// + // // Redirects caused by JavaScript or a meta refresh tag on the page. - /// + // TT_CLIENT_REDIRECT_FLAG = 0x40000000, - /// + // // Redirects sent from the server by HTTP headers. - /// + // TT_SERVER_REDIRECT_FLAG = 0x80000000, - /// + // // Used to test whether a transition involves a redirect. - /// + // TT_IS_REDIRECT_MASK = 0xC0000000, - /// + // // General mask defining the bits used for the qualifiers. - /// + // TT_QUALIFIER_MASK = 0xFFFFFF00, } -/// +// // Process termination status values. -/// +// pub enum cef_termination_status_t { - /// + // // Non-zero exit status. - /// + // TS_ABNORMAL_TERMINATION, - /// + // // SIGKILL or task manager kill. - /// + // TS_PROCESS_WAS_KILLED, - /// + // // Segmentation fault. - /// + // TS_PROCESS_CRASHED, } -/// +// // V8 access control values. -/// +// pub enum cef_v8_accesscontrol_t { V8_ACCESS_CONTROL_DEFAULT = 0, V8_ACCESS_CONTROL_ALL_CAN_READ = 1, @@ -1307,9 +1361,9 @@ pub enum cef_v8_accesscontrol_t { V8_ACCESS_CONTROL_PROHIBITS_OVERWRITING = 1 << 2 } -/// +// // V8 property attribute values. -/// +// pub enum cef_v8_propertyattribute_t { V8_PROPERTY_ATTRIBUTE_NONE = 0, // Writeable, Enumerable, // Configurable @@ -1318,9 +1372,9 @@ pub enum cef_v8_propertyattribute_t { V8_PROPERTY_ATTRIBUTE_DONTDELETE = 1 << 2 // Not configurable } -/// +// // XML node types. -/// +// pub enum cef_xml_node_type_t { XML_NODE_UNSUPPORTED = 0, XML_NODE_PROCESSING_INSTRUCTION, @@ -1335,9 +1389,9 @@ pub enum cef_xml_node_type_t { XML_NODE_COMMENT, } -/// +// // Geoposition error codes. -/// +// pub enum cef_geoposition_error_code_t { GEOPOSITON_ERROR_NONE = 0, GEOPOSITON_ERROR_PERMISSION_DENIED, @@ -1345,61 +1399,61 @@ pub enum cef_geoposition_error_code_t { GEOPOSITON_ERROR_TIMEOUT, } -/// +// // Structure representing geoposition information. The properties of this // structure correspond to those of the JavaScript Position object although // their types may differ. -/// +// pub struct _cef_geoposition_t { - /// + // // Latitude in decimal degrees north (WGS84 coordinate frame). - /// + // pub latitude: c_double, - /// + // // Longitude in decimal degrees west (WGS84 coordinate frame). - /// + // pub longitude: c_double, - /// + // // Altitude in meters (above WGS84 datum). - /// + // pub altitude: c_double, - /// + // // Accuracy of horizontal position in meters. - /// + // pub accuracy: c_double, - /// + // // Accuracy of altitude in meters. - /// + // pub altitude_accuracy: c_double, - /// + // // Heading in decimal degrees clockwise from true north. - /// + // pub heading: c_double, - /// + // // Horizontal component of device velocity in meters per second. - /// + // pub speed: c_double, - /// + // // Time of position measurement in miliseconds since Epoch in UTC time. This // is taken from the host computer's system clock. - /// + // pub timestamp: cef_time_t, - /// + // // Error code, see enum above. - /// + // pub error_code: cef_geoposition_error_code_t, - /// + // // Human-readable error message. - /// + // pub error_message: cef_string_t, } @@ -1407,58 +1461,58 @@ pub type cef_geoposition_t = _cef_geoposition_t; pub type CefGeoposition = cef_geoposition_t; -/// +// // Cookie information. -/// +// pub struct _cef_cookie_t { - /// + // // The cookie name. - /// + // pub name: cef_string_t, - /// + // // The cookie value. - /// + // pub value: cef_string_t, - /// + // // If |domain| is empty a host cookie will be created instead of a domain // cookie. Domain cookies are stored with a leading "." and are visible to // sub-domains whereas host cookies are not. - /// + // pub domain: cef_string_t, - /// + // // If |path| is non-empty only URLs at or below the path will get the cookie // value. - /// + // pub path: cef_string_t, - /// + // // If |secure| is true the cookie will only be sent for HTTPS requests. - /// + // pub secure: c_int, - /// + // // If |httponly| is true the cookie will only be sent for HTTP requests. - /// + // pub httponly: c_int, - /// + // // The cookie creation date. This is automatically populated by the system on // cookie creation. - /// + // pub creation: cef_time_t, - /// + // // The cookie last access date. This is automatically populated by the system // on access. - /// + // pub last_access: cef_time_t, - /// + // // The cookie expiration date is only valid if |has_expires| is true. - /// + // pub has_expires: c_int, pub expires: cef_time_t, } @@ -1467,9 +1521,9 @@ pub type cef_cookie_t = _cef_cookie_t; pub type CefCookie = cef_cookie_t; -/// +// // Popup window features. -/// +// pub struct _cef_popup_features_t { pub x: c_int, pub x_set: c_int, diff --git a/ports/cef/window.rs b/ports/cef/window.rs new file mode 100644 index 00000000000..2e3ec5c34a1 --- /dev/null +++ b/ports/cef/window.rs @@ -0,0 +1,284 @@ +/* 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/. */ + +//! Off-screen windows. +//! +//! This is used for off-screen rendering mode only; on-screen windows (the default embedding mode) +//! are managed by a platform toolkit (GLFW or Glutin). + +use eutil::Downcast; +use interfaces::CefBrowser; +use render_handler::CefRenderHandlerExtensions; +use types::cef_rect_t; +use wrappers::Utf16Encoder; + +use compositing::compositor_task::{mod, CompositorProxy, CompositorReceiver}; +use compositing::windowing::{IdleWindowEvent, WindowEvent, WindowMethods}; +use geom::scale_factor::ScaleFactor; +use geom::size::TypedSize2D; +use gleam::gl; +use layers::geometry::DevicePixel; +use layers::platform::surface::NativeGraphicsMetadata; +use libc::{c_char, c_void}; +use servo_msg::compositor_msg::{Blank, FinishedLoading, Loading, PerformingLayout, PaintState}; +use servo_msg::compositor_msg::{ReadyState}; +use servo_msg::constellation_msg::LoadData; +use servo_util::geometry::ScreenPx; +use std::cell::RefCell; +use std::rc::Rc; + +#[cfg(target_os="macos")] +use std::ptr; + +/// The type of an off-screen window. +#[deriving(Clone)] +pub struct Window { + cef_browser: RefCell<Option<CefBrowser>>, +} + +impl Window { + /// Creates a new window. + pub fn new() -> Rc<Window> { + const RTLD_DEFAULT: *mut c_void = (-2) as *mut c_void; + + extern { + fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void; + } + + gl::load_with(|s| { + unsafe { + let c_str = s.to_c_str(); + dlsym(RTLD_DEFAULT, c_str.as_ptr()) as *const c_void + } + }); + + Rc::new(Window { + cef_browser: RefCell::new(None), + }) + } + + /// Sets the current browser. + pub fn set_browser(&self, browser: CefBrowser) { + *self.cef_browser.borrow_mut() = Some(browser) + } + + /// Currently unimplemented. + pub fn wait_events(&self) -> WindowEvent { + IdleWindowEvent + } +} + +impl WindowMethods for Window { + fn framebuffer_size(&self) -> TypedSize2D<DevicePixel,uint> { + let browser = self.cef_browser.borrow(); + match *browser { + None => TypedSize2D(400, 300), + Some(ref browser) => { + let mut rect = cef_rect_t::zero(); + browser.get_host() + .get_client() + .get_render_handler() + .get_backing_rect((*browser).clone(), &mut rect); + TypedSize2D(rect.width as uint, rect.height as uint) + } + } + } + + fn size(&self) -> TypedSize2D<ScreenPx,f32> { + let browser = self.cef_browser.borrow(); + match *browser { + None => TypedSize2D(400.0, 300.0), + Some(ref browser) => { + let mut rect = cef_rect_t::zero(); + browser.get_host() + .get_client() + .get_render_handler() + .get_view_rect((*browser).clone(), &mut rect); + TypedSize2D(rect.width as f32, rect.height as f32) + } + } + } + + fn present(&self) { + let browser = self.cef_browser.borrow(); + match *browser { + None => {} + Some(ref browser) => { + browser.get_host().get_client().get_render_handler().on_present(browser.clone()); + } + } + } + + fn set_ready_state(&self, ready_state: ReadyState) { + let browser = self.cef_browser.borrow(); + let browser = match *browser { + None => return, + Some(ref browser) => browser, + }; + let is_loading = match ready_state { + Blank | FinishedLoading => 0, + Loading | PerformingLayout => 1, + }; + browser.get_host() + .get_client() + .get_load_handler() + .on_loading_state_change(browser.clone(), is_loading, 1, 1); + } + + fn set_paint_state(&self, _: PaintState) { + // TODO(pcwalton) + } + + fn hidpi_factor(&self) -> ScaleFactor<ScreenPx,DevicePixel,f32> { + let browser = self.cef_browser.borrow(); + match *browser { + None => ScaleFactor(1.0), + Some(ref browser) => { + let mut view_rect = cef_rect_t::zero(); + browser.get_host() + .get_client() + .get_render_handler() + .get_view_rect((*browser).clone(), &mut view_rect); + let mut backing_rect = cef_rect_t::zero(); + browser.get_host() + .get_client() + .get_render_handler() + .get_backing_rect((*browser).clone(), &mut backing_rect); + ScaleFactor(backing_rect.width as f32 / view_rect.width as f32) + } + } + } + + #[cfg(target_os="macos")] + fn native_metadata(&self) -> NativeGraphicsMetadata { + use cgl::{CGLGetCurrentContext, CGLGetPixelFormat}; + + // FIXME(pcwalton) + unsafe { + NativeGraphicsMetadata { + pixel_format: CGLGetPixelFormat(CGLGetCurrentContext()), + } + } + } + + #[cfg(target_os="linux")] + fn native_metadata(&self) -> NativeGraphicsMetadata { + // TODO(pcwalton) + panic!() + } + + fn create_compositor_channel(_: &Option<Rc<Window>>) + -> (Box<CompositorProxy+Send>, Box<CompositorReceiver>) { + let (sender, receiver) = channel(); + (box CefCompositorProxy { + sender: sender, + } as Box<CompositorProxy+Send>, + box receiver as Box<CompositorReceiver>) + } + + fn prepare_for_composite(&self) -> bool { + let browser = self.cef_browser.borrow(); + match *browser { + None => {} + Some(ref browser) => { + browser.get_host().get_client().get_render_handler().paint(browser.clone()); + } + } + true + } + + fn load_end(&self) { + // FIXME(pcwalton): The status code 200 is a lie. + let browser = self.cef_browser.borrow(); + let browser = match *browser { + None => return, + Some(ref browser) => browser, + }; + browser.get_host() + .get_client() + .get_load_handler() + .on_load_end((*browser).clone(), browser.get_main_frame(), 200); + } + + fn set_page_title(&self, string: Option<String>) { + let browser = self.cef_browser.borrow(); + let browser = match *browser { + None => return, + Some(ref browser) => browser, + }; + let frame = browser.get_main_frame(); + let frame = frame.downcast(); + let mut title_visitor = frame.title_visitor.borrow_mut(); + match &mut *title_visitor { + &None => {} + &Some(ref mut visitor) => { + match string { + None => visitor.visit(&[]), + Some(string) => { + let utf16_chars: Vec<u16> = Utf16Encoder::new(string.chars()).collect(); + visitor.visit(utf16_chars.as_slice()) + } + } + } + } + } + + fn set_page_load_data(&self, load_data: LoadData) { + let browser = self.cef_browser.borrow(); + let browser = match *browser { + None => return, + Some(ref browser) => browser, + }; + let frame = browser.get_main_frame(); + let frame = frame.downcast(); + *frame.url.borrow_mut() = load_data.url.to_string() + } +} + +struct CefCompositorProxy { + sender: Sender<compositor_task::Msg>, +} + +impl CompositorProxy for CefCompositorProxy { + #[cfg(target_os="macos")] + fn send(&mut self, msg: compositor_task::Msg) { + use cocoa::appkit::{NSApp, NSApplication, NSApplicationDefined, NSAutoreleasePool}; + use cocoa::appkit::{NSEvent, NSPoint}; + use cocoa::base::nil; + + // Send a message and kick the OS event loop awake. + self.sender.send(msg); + + unsafe { + let pool = NSAutoreleasePool::new(nil); + let event = + NSEvent::otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2( + nil, + NSApplicationDefined, + NSPoint::new(0.0, 0.0), + 0, + 0.0, + 0, + ptr::null_mut(), + 0, + 0, + 0); + NSApp().postEvent_atStart_(event, false); + pool.drain(); + } + } + + #[cfg(target_os="linux")] + fn send(&mut self, msg: compositor_task::Msg) { + // FIXME(pcwalton): Kick the GTK event loop awake? + self.sender.send(msg); + } + + fn clone_compositor_proxy(&self) -> Box<CompositorProxy+Send> { + box CefCompositorProxy { + sender: self.sender.clone(), + } as Box<CompositorProxy+Send> + } +} + diff --git a/ports/cef/wrappers.rs b/ports/cef/wrappers.rs index ae871595743..8c650dad1dd 100644 --- a/ports/cef/wrappers.rs +++ b/ports/cef/wrappers.rs @@ -21,9 +21,9 @@ use types::{cef_load_handler_t, cef_menu_item_type_t, cef_mouse_button_type_t}; use types::{cef_mouse_event, cef_navigation_type_t}; use types::{cef_page_range_t, cef_paint_element_type_t, cef_point_t, cef_postdataelement_type_t}; use types::{cef_popup_features_t, cef_process_id_t}; -use types::{cef_rect_t, cef_request_context_t, cef_request_handler_t}; +use types::{cef_rect_t, cef_request_handler_t}; use types::{cef_resource_type_t}; -use types::{cef_screen_info_t, cef_size_t, cef_string_t}; +use types::{cef_screen_info_t, cef_size_t, cef_string_t, cef_string_userfree_t}; use types::{cef_string_list_t, cef_string_map_t, cef_string_multimap_t, cef_string_utf16}; use types::{cef_termination_status_t, cef_text_input_context_t, cef_thread_id_t}; use types::{cef_time_t, cef_transition_type_t, cef_urlrequest_status_t}; @@ -126,7 +126,6 @@ cef_noop_wrapper!(*mut cef_geolocation_handler_t) cef_noop_wrapper!(*mut cef_jsdialog_handler_t) cef_noop_wrapper!(*mut cef_keyboard_handler_t) cef_noop_wrapper!(*mut cef_load_handler_t) -cef_noop_wrapper!(*mut cef_request_context_t) cef_noop_wrapper!(*mut cef_request_handler_t) cef_noop_wrapper!(*mut cef_string_list_t) cef_noop_wrapper!(*mut cef_string_utf16) @@ -181,9 +180,8 @@ cef_unimplemented_wrapper!(cef_string_t, String) impl<'a> CefWrap<*const cef_string_t> for &'a [u16] { fn to_c(buffer: &'a [u16]) -> *const cef_string_t { unsafe { - let ptr: *mut c_ushort = - mem::transmute(libc::calloc(1, ((buffer.len() * 2) + 1) as u64)); - ptr::copy_memory(ptr, mem::transmute(buffer.as_ptr()), (buffer.len() * 2) as uint); + let ptr: *mut c_ushort = mem::transmute(libc::malloc(((buffer.len() + 1) * 2) as u64)); + ptr::copy_memory(ptr, mem::transmute(buffer.as_ptr()), buffer.len()); *ptr.offset(buffer.len() as int) = 0; // FIXME(pcwalton): This leaks!! We should instead have the caller pass some scratch @@ -240,6 +238,71 @@ impl<'a,'b> CefWrap<*mut *const c_char> for &'a mut &'b str { } } +impl<'a> CefWrap<cef_string_userfree_t> for String { + fn to_c(string: String) -> cef_string_userfree_t { + let utf16_chars: Vec<u16> = Utf16Encoder::new(string.chars()).collect(); + + let boxed_string; + unsafe { + let buffer = libc::malloc((mem::size_of::<c_ushort>() as u64) * + ((utf16_chars.len() + 1) as u64 + 1)) as *mut u16; + for (i, ch) in utf16_chars.iter().enumerate() { + *buffer.offset(i as int) = *ch + } + *buffer.offset(utf16_chars.len() as int) = 0; + + boxed_string = libc::malloc(mem::size_of::<cef_string_utf16>() as u64) as + *mut cef_string_utf16; + ptr::write(&mut (*boxed_string).str, buffer); + ptr::write(&mut (*boxed_string).length, utf16_chars.len() as u64); + ptr::write(&mut (*boxed_string).dtor, Some(free_utf16_buffer)); + mem::forget(utf16_chars); + } + boxed_string + } + unsafe fn to_rust(_: cef_string_userfree_t) -> String { + panic!("unimplemented CEF type conversion: cef_string_userfree_t") + } +} + +extern "C" fn free_utf16_buffer(buffer: *mut c_ushort) { + unsafe { + libc::free(buffer as *mut c_void) + } +} + +// TODO(pcwalton): Post Rust-upgrade, remove this and use `collections::str::Utf16Encoder`. +pub struct Utf16Encoder<I> { + chars: I, + extra: u16, +} + +impl<I> Utf16Encoder<I> { + pub fn new(chars: I) -> Utf16Encoder<I> where I: Iterator<char> { + Utf16Encoder { + chars: chars, + extra: 0, + } + } +} + +impl<I> Iterator<u16> for Utf16Encoder<I> where I: Iterator<char> { + fn next(&mut self) -> Option<u16> { + if self.extra != 0 { + return Some(mem::replace(&mut self.extra, 0)) + } + + let mut buf = [0u16, ..2]; + self.chars.next().map(|ch| { + let n = ch.encode_utf16(buf.as_mut_slice()).unwrap_or(0); + if n == 2 { + self.extra = buf[1] + } + buf[0] + }) + } +} + impl<'a> CefWrap<cef_string_t> for &'a mut String { fn to_c(_: &'a mut String) -> cef_string_t { panic!("unimplemented CEF type conversion: &'a mut String") diff --git a/ports/glfw/window.rs b/ports/glfw/window.rs index 422ea221c02..d391b274f03 100644 --- a/ports/glfw/window.rs +++ b/ports/glfw/window.rs @@ -23,9 +23,9 @@ use gleam::gl; use layers::geometry::DevicePixel; use layers::platform::surface::NativeGraphicsMetadata; use libc::c_int; -use msg::compositor_msg::{FinishedLoading, Blank, Loading, PerformingLayout, ReadyState}; -use msg::compositor_msg::{IdlePaintState, PaintState, PaintingPaintState}; -use msg::constellation_msg; +use msg::compositor_msg::{Blank, FinishedLoading, IdlePaintState, Loading, PaintState}; +use msg::compositor_msg::{PaintingPaintState, PerformingLayout, ReadyState}; +use msg::constellation_msg::{mod, LoadData}; use std::cell::{Cell, RefCell}; use std::comm::Receiver; use std::rc::Rc; @@ -97,6 +97,15 @@ impl Window { } pub fn wait_events(&self) -> WindowEvent { + self.wait_or_poll_events(|glfw| glfw.wait_events()) + } + + pub fn poll_events(&self) -> WindowEvent { + self.wait_or_poll_events(|glfw| glfw.poll_events()) + } + + /// Helper method to factor out functionality from `poll_events` and `wait_events`. + fn wait_or_poll_events(&self, callback: |glfw: &glfw::Glfw|) -> WindowEvent { { let mut event_queue = self.event_queue.borrow_mut(); if !event_queue.is_empty() { @@ -104,7 +113,7 @@ impl Window { } } - self.glfw.wait_events(); + callback(&self.glfw); for (_, event) in glfw::flush_messages(&self.events) { self.handle_window_event(&self.glfw_window, event); } @@ -196,6 +205,16 @@ impl WindowMethods for Window { } as Box<CompositorProxy+Send>, box receiver as Box<CompositorReceiver>) } + + fn prepare_for_composite(&self) -> bool { + true + } + + fn load_end(&self) {} + + fn set_page_title(&self, _: Option<String>) {} + + fn set_page_load_data(&self, _: LoadData) {} } impl Window { diff --git a/python/servo/build_commands.py b/python/servo/build_commands.py index e3e41ca38e2..cade77fbbd7 100644 --- a/python/servo/build_commands.py +++ b/python/servo/build_commands.py @@ -113,7 +113,7 @@ class MachCommands(CommandBase): build_start = time() with cd(path.join("ports", "cef")): - ret = subprocess.call(["cargo", "build"], + ret = subprocess.call(["cargo", "build"] + opts, env=self.build_env()) elapsed = time() - build_start |