diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index eb0301e9208..50a47af45a4 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -4,12 +4,13 @@ use compositor_layer::{CompositorData, CompositorLayer, DoesntWantScrollEvents}; use compositor_layer::{ScrollPositionChanged, WantsScrollEvents}; -use compositor_task::{Msg, CompositorTask, Exit, ChangeReadyState, SetIds, LayerProperties}; -use compositor_task::{GetGraphicsMetadata, CreateOrUpdateRootLayer, CreateOrUpdateDescendantLayer}; -use compositor_task::{SetLayerOrigin, Paint, ScrollFragmentPoint, LoadComplete}; -use compositor_task::{ShutdownComplete, ChangeRenderState, RenderMsgDiscarded, ScrollTimeout}; -use compositor_task::{CompositorEventListener, CompositorProxy, CompositorReceiver}; -use constellation::SendableFrameTree; +use compositor_task::{ChangeReadyState, ChangeRenderState, CompositorEventListener}; +use compositor_task::{CompositorProxy, CompositorReceiver, CompositorTask}; +use compositor_task::{CreateOrUpdateDescendantLayer, CreateOrUpdateRootLayer, Exit}; +use compositor_task::{FrameTreeUpdateMsg, GetGraphicsMetadata, LayerProperties}; +use compositor_task::{LoadComplete, Msg, Paint, RenderMsgDiscarded, ScrollFragmentPoint}; +use compositor_task::{ScrollTimeout, SetIds, SetLayerOrigin, ShutdownComplete}; +use constellation::{SendableFrameTree, FrameTreeDiff}; use pipeline::CompositionPipeline; use scrolling::ScrollingTimerProxy; use windowing; @@ -267,6 +268,11 @@ impl IOCompositor { new_constellation_chan); } + (FrameTreeUpdateMsg(frame_tree_diff, response_channel), NotShuttingDown) => { + self.update_frame_tree(&frame_tree_diff); + response_channel.send(()); + } + (CreateOrUpdateRootLayer(layer_properties), NotShuttingDown) => { self.create_or_update_root_layer(layer_properties); } @@ -446,6 +452,34 @@ impl IOCompositor { return root_layer; } + fn update_frame_tree(&mut self, frame_tree_diff: &FrameTreeDiff) { + let layer_properties = LayerProperties { + pipeline_id: frame_tree_diff.pipeline.id, + epoch: Epoch(0), + id: LayerId::null(), + rect: Rect::zero(), + background_color: azure_hl::Color::new(0., 0., 0., 0.), + scroll_policy: Scrollable, + }; + let root_layer = CompositorData::new_layer(frame_tree_diff.pipeline.clone(), + layer_properties, + WantsScrollEvents, + opts::get().tile_size); + + match frame_tree_diff.rect { + Some(ref frame_rect) => { + *root_layer.masks_to_bounds.borrow_mut() = true; + + let frame_rect = frame_rect.to_untyped(); + *root_layer.bounds.borrow_mut() = Rect::from_untyped(&frame_rect); + } + None => {} + } + + let parent_layer = self.find_pipeline_root_layer(frame_tree_diff.parent_pipeline.id); + parent_layer.add_child(root_layer); + } + fn find_pipeline_root_layer(&self, pipeline_id: PipelineId) -> Rc> { match self.find_layer_with_pipeline_and_layer_id(pipeline_id, LayerId::null()) { Some(ref layer) => layer.clone(), diff --git a/components/compositing/compositor_task.rs b/components/compositing/compositor_task.rs index 5fe2c4bb57a..cfbb5acb2a6 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; +pub use constellation::{SendableFrameTree, FrameTreeDiff}; use compositor; use headless; @@ -189,6 +189,8 @@ pub enum Msg { RenderMsgDiscarded, /// Sets the channel to the current layout and render tasks, along with their id SetIds(SendableFrameTree, Sender<()>, ConstellationChan), + /// Sends an updated version of the frame tree. + FrameTreeUpdateMsg(FrameTreeDiff, Sender<()>), /// The load of a page for a given URL has completed. LoadComplete(PipelineId, Url), /// Indicates that the scrolling timeout with the given starting timestamp has happened and a @@ -211,6 +213,7 @@ impl Show for Msg { ChangeRenderState(..) => write!(f, "ChangeRenderState"), RenderMsgDiscarded(..) => write!(f, "RenderMsgDiscarded"), SetIds(..) => write!(f, "SetIds"), + FrameTreeUpdateMsg(..) => write!(f, "FrameTreeUpdateMsg"), LoadComplete(..) => write!(f, "LoadComplete"), ScrollTimeout(..) => write!(f, "ScrollTimeout"), } diff --git a/components/compositing/constellation.rs b/components/compositing/constellation.rs index 7a7204dfdaf..7bbf1ebb5fb 100644 --- a/components/compositing/constellation.rs +++ b/components/compositing/constellation.rs @@ -4,7 +4,7 @@ use pipeline::{Pipeline, CompositionPipeline}; -use compositor_task::{CompositorProxy, LoadComplete, ShutdownComplete, SetLayerOrigin, SetIds}; +use compositor_task::{CompositorProxy, FrameTreeUpdateMsg, LoadComplete, ShutdownComplete, SetLayerOrigin, SetIds}; use devtools_traits::DevtoolsControlChan; use geom::rect::{Rect, TypedRect}; use geom::scale_factor::ScaleFactor; @@ -18,9 +18,9 @@ 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::{IFrameSandboxState, IFrameUnsandboxed, InitLoadUrlMsg}; -use servo_msg::constellation_msg::{LoadCompleteMsg, LoadIframeUrlMsg, LoadUrlMsg, Msg}; -use servo_msg::constellation_msg::{LoadData, NavigateMsg, NavigationType, PipelineId}; -use servo_msg::constellation_msg::{RendererReadyMsg, ResizedWindowMsg, SubpageId, WindowSizeData}; +use servo_msg::constellation_msg::{LoadCompleteMsg, LoadUrlMsg, LoadData, Msg, NavigateMsg}; +use servo_msg::constellation_msg::{NavigationType, PipelineId, RendererReadyMsg, ResizedWindowMsg}; +use servo_msg::constellation_msg::{ScriptLoadedURLInIFrameMsg, SubpageId, WindowSizeData}; use servo_msg::constellation_msg; use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient}; use servo_net::resource_task::ResourceTask; @@ -29,7 +29,7 @@ use servo_util::geometry::{PagePx, ViewportPx}; use servo_util::opts; use servo_util::task::spawn_named; use servo_util::time::TimeProfilerChan; -use std::cell::RefCell; +use std::cell::{Cell, RefCell}; use std::collections::hashmap::{HashMap, HashSet}; use std::io; use std::mem::replace; @@ -83,6 +83,21 @@ struct FrameTree { pub pipeline: Rc, pub parent: RefCell>>, pub children: RefCell>, + pub has_compositor_layer: Cell, +} + +impl FrameTree { + fn new(pipeline: Rc, parent_pipeline: Option>) -> FrameTree { + FrameTree { + pipeline: pipeline.clone(), + parent: match parent_pipeline { + Some(ref pipeline) => RefCell::new(Some(pipeline.clone())), + None => RefCell::new(None), + }, + children: RefCell::new(vec!()), + has_compositor_layer: Cell::new(false), + } + } } #[deriving(Clone)] @@ -93,6 +108,15 @@ struct ChildFrameTree { pub rect: Option>, } +impl ChildFrameTree { + fn new(frame_tree: Rc, rect: Option>) -> ChildFrameTree { + ChildFrameTree { + frame_tree: frame_tree, + rect: rect, + } + } +} + pub struct SendableFrameTree { pub pipeline: CompositionPipeline, pub children: Vec, @@ -108,6 +132,16 @@ enum ReplaceResult { OriginalNode(Rc), } +/// A struct that triggers the addition of a new frame to a previously existing frame tree. +pub struct FrameTreeDiff { + /// The parent pipeline of the new frame. + pub parent_pipeline: CompositionPipeline, + /// The pipeline of the new frame itself. + pub pipeline: CompositionPipeline, + /// The frame rect of the new frame used for positioning its compositor layer. + pub rect: Option>, +} + impl FrameTree { fn to_sendable(&self) -> SendableFrameTree { let sendable_frame_tree = SendableFrameTree { @@ -382,9 +416,12 @@ impl Constellation { debug!("constellation got frame rect message"); self.handle_frame_rect_msg(pipeline_id, subpage_id, Rect::from_untyped(&rect)); } - LoadIframeUrlMsg(url, source_pipeline_id, subpage_id, sandbox) => { + ScriptLoadedURLInIFrameMsg(url, source_pipeline_id, subpage_id, sandbox) => { debug!("constellation got iframe URL load message"); - self.handle_load_iframe_url_msg(url, source_pipeline_id, subpage_id, sandbox); + self.handle_script_loaded_url_in_iframe_msg(url, + source_pipeline_id, + subpage_id, + sandbox); } // Load a new page, usually -- but not always -- from a mouse click or typed url // If there is already a pending page (self.pending_frames), it will not be overridden; @@ -478,11 +515,7 @@ impl Constellation { self.pending_frames.push(FrameChange{ before: Some(pipeline_id), - after: Rc::new(FrameTree { - pipeline: pipeline.clone(), - parent: RefCell::new(None), - children: RefCell::new(vec!()), - }), + after: Rc::new(FrameTree::new(pipeline.clone(), None)), navigation_type: constellation_msg::Load, }); @@ -495,11 +528,7 @@ impl Constellation { self.pending_frames.push(FrameChange { before: None, - after: Rc::new(FrameTree { - pipeline: pipeline.clone(), - parent: RefCell::new(None), - children: RefCell::new(vec!()), - }), + after: Rc::new(FrameTree::new(pipeline.clone(), None)), navigation_type: constellation_msg::Load, }); self.pipelines.insert(pipeline.id, pipeline); @@ -593,21 +622,20 @@ impl Constellation { } - fn handle_load_iframe_url_msg(&mut self, - url: Url, - source_pipeline_id: PipelineId, - subpage_id: SubpageId, - sandbox: IFrameSandboxState) { - // A message from the script associated with pipeline_id that it has - // parsed an iframe during html parsing. This iframe will result in a - // new pipeline being spawned and a frame tree being added to pipeline_id's - // frame tree's children. This message is never the result of a link clicked - // or a new url entered. - // Start by finding the frame trees matching the pipeline id, + // The script task associated with pipeline_id has loaded a URL in an iframe via script. This + // will result in a new pipeline being spawned and a frame tree being added to + // source_pipeline_id's frame tree's children. This message is never the result of a page + // navigation. + fn handle_script_loaded_url_in_iframe_msg(&mut self, + url: Url, + source_pipeline_id: PipelineId, + subpage_id: SubpageId, + sandbox: IFrameSandboxState) { + // Start by finding the frame trees matching the pipeline id, // and add the new pipeline to their sub frames. let frame_trees = self.find_all(source_pipeline_id); if frame_trees.is_empty() { - fail!("Constellation: source pipeline id of LoadIframeUrlMsg is not in + fail!("Constellation: source pipeline id of ScriptLoadedURLInIFrameMsg is not in navigation context, nor is it in a pending frame. This should be impossible."); } @@ -617,7 +645,7 @@ impl Constellation { // Compare the pipeline's url to the new url. If the origin is the same, // then reuse the script task in creating the new pipeline let source_pipeline = self.pipelines.find(&source_pipeline_id).expect("Constellation: - source Id of LoadIframeUrlMsg does have an associated pipeline in + source Id of ScriptLoadedURLInIFrameMsg does have an associated pipeline in constellation. This should be impossible.").clone(); let source_url = source_pipeline.load_data.url.clone(); @@ -626,7 +654,7 @@ impl Constellation { source_url.port() == url.port()) && sandbox == IFrameUnsandboxed; // FIXME(tkuehn): Need to follow the standardized spec for checking same-origin // Reuse the script task if the URL is same-origin - let new_pipeline = if same_script { + let script_pipeline = if same_script { debug!("Constellation: loading same-origin iframe at {:?}", url); Some(source_pipeline.clone()) } else { @@ -637,20 +665,15 @@ impl Constellation { let pipeline = self.new_pipeline( next_pipeline_id, Some(subpage_id), - new_pipeline, + script_pipeline, LoadData::new(url) ); let rect = self.pending_sizes.pop(&(source_pipeline_id, subpage_id)); for frame_tree in frame_trees.iter() { - frame_tree.children.borrow_mut().push(ChildFrameTree { - frame_tree: Rc::new(FrameTree { - pipeline: pipeline.clone(), - parent: RefCell::new(Some(source_pipeline.clone())), - children: RefCell::new(vec!()), - }), - rect: rect, - }); + frame_tree.children.borrow_mut().push(ChildFrameTree::new( + Rc::new(FrameTree::new(pipeline.clone(), Some(source_pipeline.clone()))), + rect)); } self.pipelines.insert(pipeline.id, pipeline); } @@ -684,13 +707,9 @@ impl Constellation { let pipeline = self.new_pipeline(next_pipeline_id, subpage_id, None, load_data); - self.pending_frames.push(FrameChange{ + self.pending_frames.push(FrameChange { before: Some(source_id), - after: Rc::new(FrameTree { - pipeline: pipeline.clone(), - parent: parent, - children: RefCell::new(vec!()), - }), + after: Rc::new(FrameTree::new(pipeline.clone(), parent.borrow().clone())), navigation_type: constellation_msg::Load, }); self.pipelines.insert(pipeline.id, pipeline); @@ -737,21 +756,22 @@ impl Constellation { } + fn pipeline_is_in_current_frame(&self, pipeline_id: PipelineId) -> bool { + self.current_frame().iter() + .any(|current_frame| current_frame.contains(pipeline_id)) + } + fn handle_renderer_ready_msg(&mut self, pipeline_id: PipelineId) { debug!("Renderer {:?} ready to send paint msg", pipeline_id); // This message could originate from a pipeline in the navigation context or // from a pending frame. The only time that we will grant paint permission is // when the message originates from a pending frame or the current frame. - for current_frame in self.current_frame().iter() { - // Messages originating in the current frame are not navigations; - // they may come from a page load in a subframe. - if current_frame.contains(pipeline_id) { - for frame in current_frame.iter() { - frame.pipeline.grant_paint_permission(); - } - return; - } + // Messages originating in the current frame are not navigations; + // they may come from a page load in a subframe. + if self.pipeline_is_in_current_frame(pipeline_id) { + self.create_compositor_layer_for_iframe_if_necessary(pipeline_id); + return; } // Find the pending frame change whose new pipeline id is pipeline_id. @@ -814,10 +834,8 @@ impl Constellation { let parent = next_frame_tree.find(parent.id).expect( "Constellation: pending frame has a parent frame that is not active. This is a bug."); - parent.children.borrow_mut().push(ChildFrameTree { - frame_tree: to_add.clone(), - rect: rect, - }); + parent.children.borrow_mut().push(ChildFrameTree::new(to_add.clone(), + rect)); } } } @@ -911,11 +929,65 @@ impl Constellation { Ok(()) => { let mut iter = frame_tree.iter(); for frame in iter { + frame.has_compositor_layer.set(true); frame.pipeline.grant_paint_permission(); } } Err(()) => {} // message has been discarded, probably shutting down } } + + fn find_child_parent_pair_in_frame_tree(&self, + frame_tree: Rc, + child_pipeline_id: PipelineId) + -> Option<(ChildFrameTree, Rc)> { + for child in frame_tree.children.borrow().iter() { + let child_frame_tree = child.frame_tree.clone(); + if child.frame_tree.pipeline.id == child_pipeline_id { + return Some((ChildFrameTree::new(child_frame_tree, child.rect), + frame_tree.clone())); + } + let result = self.find_child_parent_pair_in_frame_tree(child_frame_tree, + child_pipeline_id); + if result.is_some() { + return result; + } + } + None + } + + fn create_compositor_layer_for_iframe_if_necessary(&mut self, pipeline_id: PipelineId) { + let current_frame_tree = match self.current_frame() { + &Some(ref tree) => tree.clone(), + &None => return, + }; + + let (child, parent) = + match self.find_child_parent_pair_in_frame_tree(current_frame_tree, pipeline_id) { + Some(pair) => pair, + None => return, + }; + + if child.frame_tree.has_compositor_layer.get() { + child.frame_tree.pipeline.grant_paint_permission(); + return; + } + + let sendable_frame_tree_diff = FrameTreeDiff { + parent_pipeline: parent.pipeline.to_sendable(), + pipeline: child.frame_tree.pipeline.to_sendable(), + rect: child.rect, + }; + + let (chan, port) = channel(); + self.compositor_proxy.send(FrameTreeUpdateMsg(sendable_frame_tree_diff, chan)); + match port.recv_opt() { + Ok(()) => { + child.frame_tree.has_compositor_layer.set(true); + child.frame_tree.pipeline.grant_paint_permission(); + } + Err(()) => {} // The message has been discarded, we are probably shutting down. + } + } } diff --git a/components/compositing/headless.rs b/components/compositing/headless.rs index 648b71034e0..eaeb09eb7da 100644 --- a/components/compositing/headless.rs +++ b/components/compositing/headless.rs @@ -5,7 +5,7 @@ use compositor_task::{GetGraphicsMetadata, CreateOrUpdateRootLayer, CreateOrUpdateDescendantLayer}; use compositor_task::{Exit, ChangeReadyState, LoadComplete, Paint, ScrollFragmentPoint, SetIds}; use compositor_task::{SetLayerOrigin, ShutdownComplete, ChangeRenderState, RenderMsgDiscarded}; -use compositor_task::{CompositorEventListener, CompositorReceiver, ScrollTimeout}; +use compositor_task::{CompositorEventListener, CompositorReceiver, ScrollTimeout, FrameTreeUpdateMsg}; use windowing::WindowEvent; use geom::scale_factor::ScaleFactor; @@ -92,6 +92,10 @@ impl CompositorEventListener for NullCompositor { response_chan.send(()); } + FrameTreeUpdateMsg(_, response_channel) => { + response_channel.send(()); + } + // Explicitly list ignored messages so that when we add a new one, // we'll notice and think about whether it needs a response, like // SetIds. diff --git a/components/msg/constellation_msg.rs b/components/msg/constellation_msg.rs index 00357752ee1..fbdb437327c 100644 --- a/components/msg/constellation_msg.rs +++ b/components/msg/constellation_msg.rs @@ -58,7 +58,7 @@ pub enum Msg { LoadCompleteMsg(PipelineId, Url), FrameRectMsg(PipelineId, SubpageId, Rect), LoadUrlMsg(PipelineId, LoadData), - LoadIframeUrlMsg(Url, PipelineId, SubpageId, IFrameSandboxState), + ScriptLoadedURLInIFrameMsg(Url, PipelineId, SubpageId, IFrameSandboxState), NavigateMsg(NavigationDirection), RendererReadyMsg(PipelineId), ResizedWindowMsg(WindowSizeData), diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs index 737f3340ecf..3312925e2ff 100644 --- a/components/script/dom/htmliframeelement.rs +++ b/components/script/dom/htmliframeelement.rs @@ -4,7 +4,6 @@ use dom::attr::Attr; use dom::attr::AttrHelpers; -use dom::bindings::codegen::Bindings::DocumentBinding::{DocumentMethods, DocumentReadyStateValues}; use dom::bindings::codegen::Bindings::HTMLIFrameElementBinding; use dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElementMethods; use dom::bindings::codegen::InheritTypes::{NodeCast, ElementCast}; @@ -16,14 +15,14 @@ use dom::element::{HTMLIFrameElementTypeId, Element}; use dom::element::AttributeHandlers; use dom::eventtarget::{EventTarget, NodeTargetTypeId}; use dom::htmlelement::HTMLElement; -use dom::node::{Node, NodeHelpers, ElementNodeTypeId, window_from_node, document_from_node}; +use dom::node::{Node, NodeHelpers, ElementNodeTypeId, window_from_node}; use dom::virtualmethods::VirtualMethods; use dom::window::Window; use page::IterablePage; use servo_msg::constellation_msg::{PipelineId, SubpageId}; use servo_msg::constellation_msg::{IFrameSandboxed, IFrameUnsandboxed}; -use servo_msg::constellation_msg::{ConstellationChan, LoadIframeUrlMsg}; +use servo_msg::constellation_msg::{ConstellationChan, ScriptLoadedURLInIFrameMsg}; use servo_util::str::DOMString; use std::ascii::StrAsciiExt; @@ -120,13 +119,8 @@ impl<'a> HTMLIFrameElementHelpers for JSRef<'a, HTMLIFrameElement> { subpage_id: subpage_id, })); - let doc = document_from_node(self).root(); - if doc.ReadyState() == DocumentReadyStateValues::Loading { - // https://github.com/servo/servo/issues/3738 - // We can't handle dynamic frame tree changes in the compositor right now. - let ConstellationChan(ref chan) = page.constellation_chan; - chan.send(LoadIframeUrlMsg(url, page.id, subpage_id, sandboxed)); - } + let ConstellationChan(ref chan) = page.constellation_chan; + chan.send(ScriptLoadedURLInIFrameMsg(url, page.id, subpage_id, sandboxed)); } }