diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index 55127bd64fe..dca6d253e56 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -739,6 +739,7 @@ impl IOCompositor { fn set_frame_tree(&mut self, frame_tree: &SendableFrameTree, response_chan: IpcSender<()>) { + debug!("Setting the frame tree for pipeline {}", frame_tree.pipeline.id); if let Err(e) = response_chan.send(()) { warn!("Sending reponse to set frame tree failed ({}).", e); } diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index d257e38dd3e..c854a48b618 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -610,6 +610,10 @@ impl Constellation assert!(!self.pipelines.contains_key(&pipeline_id)); self.pipelines.insert(pipeline_id, pipeline); + + if !self.frames.contains_key(&frame_id) { + self.new_frame(frame_id, pipeline_id); + } } // Get an iterator for the current frame tree. Specify self.root_frame_id to @@ -668,13 +672,16 @@ impl Constellation // Create a new frame and update the internal bookkeeping. fn new_frame(&mut self, frame_id: FrameId, pipeline_id: PipelineId) { let frame = Frame::new(frame_id, pipeline_id); - - match self.pipelines.get_mut(&pipeline_id) { - Some(pipeline) => pipeline.is_mature = true, - None => return warn!("Pipeline {} matured after closure.", pipeline_id), - }; - self.frames.insert(frame_id, frame); + + // If a child frame, add it to the parent pipeline. + let parent_info = self.pipelines.get(&pipeline_id) + .and_then(|pipeline| pipeline.parent_info); + if let Some((parent_id, _)) = parent_info { + if let Some(parent) = self.pipelines.get_mut(&parent_id) { + parent.add_child(frame_id); + } + } } /// Handles loading pages, navigation, and granting access to the compositor @@ -777,6 +784,7 @@ impl Constellation } FromCompositorMsg::IsReadyToSaveImage(pipeline_states) => { let is_ready = self.handle_is_ready_to_save_image(pipeline_states); + debug!("Ready to save image {:?}.", is_ready); if opts::get().is_running_problem_test { println!("got ready to save image query, result is {:?}", is_ready); } @@ -1022,8 +1030,31 @@ impl Constellation self.shutting_down = true; // TODO: exit before the root frame is initialized? + debug!("Removing root frame."); let root_frame_id = self.root_frame_id; self.close_frame(root_frame_id, ExitPipelineMode::Normal); + + // Close any pending frames and pipelines + while let Some(pending) = self.pending_frames.pop() { + debug!("Removing pending frame {}.", pending.frame_id); + self.close_frame(pending.frame_id, ExitPipelineMode::Normal); + debug!("Removing pending pipeline {}.", pending.new_pipeline_id); + self.close_pipeline(pending.new_pipeline_id, ExitPipelineMode::Normal); + } + + // In case there are frames which weren't attached to the frame tree, we close them. + let frame_ids: Vec = self.frames.keys().cloned().collect(); + for frame_id in frame_ids { + debug!("Removing detached frame {}.", frame_id); + self.close_frame(frame_id, ExitPipelineMode::Normal); + } + + // In case there are pipelines which weren't attached to the pipeline tree, we close them. + let pipeline_ids: Vec = self.pipelines.keys().cloned().collect(); + for pipeline_id in pipeline_ids { + debug!("Removing detached pipeline {}.", pipeline_id); + self.close_pipeline(pipeline_id, ExitPipelineMode::Normal); + } } fn handle_shutdown(&mut self) { @@ -1219,6 +1250,7 @@ impl Constellation let msg = ConstellationControlMsg::DispatchFrameLoadEvent { target: frame_id, parent: subframe_parent_id, + child: pipeline_id, }; let result = match self.pipelines.get(&subframe_parent_id) { Some(pipeline) => pipeline.script_chan.send(msg), @@ -1592,13 +1624,10 @@ impl Constellation fn handle_mozbrowser_event_msg(&mut self, parent_pipeline_id: PipelineId, - pipeline_id: Option, + pipeline_id: PipelineId, event: MozBrowserEvent) { assert!(PREFS.is_mozbrowser_enabled()); - - let frame_id = pipeline_id - .and_then(|pipeline_id| self.pipelines.get(&pipeline_id)) - .map(|pipeline| pipeline.frame_id); + let frame_id = self.pipelines.get(&pipeline_id).map(|pipeline| pipeline.frame_id); // Find the script channel for the given parent pipeline, // and pass the event to that script thread. @@ -1922,11 +1951,6 @@ impl Constellation } if self.frames.contains_key(&frame_change.frame_id) { - // Mature the new pipeline, and return frames evicted from history. - if let Some(ref mut pipeline) = self.pipelines.get_mut(&frame_change.new_pipeline_id) { - pipeline.is_mature = true; - } - if frame_change.replace { let evicted = self.frames.get_mut(&frame_change.frame_id).map(|frame| { frame.replace_current(frame_change.new_pipeline_id) @@ -1942,16 +1966,6 @@ impl Constellation } else { // The new pipeline is in a new frame with no history self.new_frame(frame_change.frame_id, frame_change.new_pipeline_id); - - // If a child frame, add it to the parent pipeline. Otherwise - // it must surely be the root frame being created! - let parent_info = self.pipelines.get(&frame_change.new_pipeline_id) - .and_then(|pipeline| pipeline.parent_info); - if let Some((parent_id, _)) = parent_info { - if let Some(parent) = self.pipelines.get_mut(&parent_id) { - parent.add_child(frame_change.frame_id); - } - } } if !frame_change.replace { @@ -1970,49 +1984,25 @@ impl Constellation fn handle_activate_document_msg(&mut self, pipeline_id: PipelineId) { debug!("Document ready to activate {:?}", pipeline_id); - if let Some(ref child_pipeline) = self.pipelines.get(&pipeline_id) { - if let Some(ref parent_info) = child_pipeline.parent_info { - if let Some(parent_pipeline) = self.pipelines.get(&parent_info.0) { - let _ = parent_pipeline.script_chan - .send(ConstellationControlMsg::FramedContentChanged( - parent_info.0, - child_pipeline.frame_id)); + // Notify the parent (if there is one). + if let Some(pipeline) = self.pipelines.get(&pipeline_id) { + if let Some((parent_pipeline_id, _)) = pipeline.parent_info { + if let Some(parent_pipeline) = self.pipelines.get(&parent_pipeline_id) { + let msg = ConstellationControlMsg::FramedContentChanged(parent_pipeline_id, pipeline.frame_id); + let _ = parent_pipeline.script_chan.send(msg); } } } - // If this pipeline is already part of the current frame tree, - // we don't need to do anything. - if self.pipeline_is_in_current_frame(pipeline_id) { - return; - } - // Find the pending frame change whose new pipeline id is pipeline_id. - // If it is found, mark this pending frame as ready to be enabled. let pending_index = self.pending_frames.iter().rposition(|frame_change| { frame_change.new_pipeline_id == pipeline_id }); - if let Some(pending_index) = pending_index { - self.pending_frames[pending_index].document_ready = true; - } - // This is a bit complex. We need to loop through pending frames and find - // ones that can be swapped. A frame can be swapped (enabled) once it is - // ready to layout (has document_ready set), and also is mature - // (i.e. the pipeline it is replacing has been enabled and now has a frame). - // The outer loop is required because any time a pipeline is enabled, that - // may affect whether other pending frames are now able to be enabled. On the - // other hand, if no frames can be enabled after looping through all pending - // frames, we can safely exit the loop, knowing that we will need to wait on - // a dependent pipeline to be ready to paint. - while let Some(valid_frame_change) = self.pending_frames.iter().rposition(|frame_change| { - let frame_is_mature = frame_change.old_pipeline_id - .and_then(|old_pipeline_id| self.pipelines.get(&old_pipeline_id)) - .map(|old_pipeline| old_pipeline.is_mature) - .unwrap_or(true); - frame_change.document_ready && frame_is_mature - }) { - let frame_change = self.pending_frames.swap_remove(valid_frame_change); + // If it is found, remove it from the pending frames, and make it + // the active document of its frame. + if let Some(pending_index) = pending_index { + let frame_change = self.pending_frames.swap_remove(pending_index); self.add_or_replace_pipeline_in_frame_tree(frame_change); } } @@ -2112,6 +2102,7 @@ impl Constellation // screenshot can safely be written. for frame in self.current_frame_tree_iter(self.root_frame_id) { let pipeline_id = frame.current.pipeline_id; + debug!("Checking readiness of frame {}, pipeline {}.", frame.id, pipeline_id); let pipeline = match self.pipelines.get(&pipeline_id) { None => { @@ -2277,7 +2268,9 @@ impl Constellation self.close_frame(*child_frame, exit_mode); } - let pipeline = match self.pipelines.get_mut(&pipeline_id) { + // Note, we don't remove the pipeline now, we wait for the message to come back from + // the pipeline. + let pipeline = match self.pipelines.get(&pipeline_id) { Some(pipeline) => pipeline, None => return warn!("Closing pipeline {:?} twice.", pipeline_id), }; @@ -2349,6 +2342,7 @@ impl Constellation // Note that this function can panic, due to ipc-channel creation failure. // avoiding this panic would require a mechanism for dealing // with low-resource scenarios. + debug!("Sending frame tree for frame {}.", self.root_frame_id); if let Some(frame_tree) = self.frame_to_sendable(self.root_frame_id) { let (chan, port) = ipc::channel().expect("Failed to create IPC channel!"); self.compositor_proxy.send(ToCompositorMsg::SetFrameTree(frame_tree, diff --git a/components/constellation/pipeline.rs b/components/constellation/pipeline.rs index b7a172af5aa..2fb52909516 100644 --- a/components/constellation/pipeline.rs +++ b/components/constellation/pipeline.rs @@ -69,9 +69,6 @@ pub struct Pipeline { /// Whether this pipeline should be treated as visible for the purposes of scheduling and /// resource management. pub visible: bool, - /// Whether this pipeline is has matured or not. - /// A pipeline is considered mature when it has an associated frame. - pub is_mature: bool, } /// Initial setup data needed to construct a pipeline. @@ -283,7 +280,6 @@ impl Pipeline { running_animations: false, visible: visible, is_private: is_private, - is_mature: false, } } diff --git a/components/net/http_loader.rs b/components/net/http_loader.rs index 885ce794b6d..5b15166b9dd 100644 --- a/components/net/http_loader.rs +++ b/components/net/http_loader.rs @@ -784,7 +784,7 @@ pub fn obtain_response(request_factory: &HttpRequestFactory, } if log_enabled!(log::LogLevel::Info) { - info!("{}", method); + info!("{} {}", method, connection_url); for header in headers.iter() { info!(" - {}", header); } diff --git a/components/script/document_loader.rs b/components/script/document_loader.rs index 837660109c3..04b9e258573 100644 --- a/components/script/document_loader.rs +++ b/components/script/document_loader.rs @@ -94,6 +94,7 @@ impl DocumentLoader { pub fn new_with_threads(resource_threads: ResourceThreads, initial_load: Option) -> DocumentLoader { + debug!("Initial blocking load {:?}.", initial_load); let initial_loads = initial_load.into_iter().map(LoadType::PageSource).collect(); DocumentLoader { @@ -105,6 +106,7 @@ impl DocumentLoader { /// Add a load to the list of blocking loads. fn add_blocking_load(&mut self, load: LoadType) { + debug!("Adding blocking load {:?} ({}).", load, self.blocking_loads.len()); self.blocking_loads.push(load); } @@ -119,6 +121,7 @@ impl DocumentLoader { /// Mark an in-progress network request complete. pub fn finish_load(&mut self, load: &LoadType) { + debug!("Removing blocking load {:?} ({}).", load, self.blocking_loads.len()); let idx = self.blocking_loads.iter().position(|unfinished| *unfinished == *load); self.blocking_loads.remove(idx.expect(&format!("unknown completed load {:?}", load))); } diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 470e4eb5e9a..9f4ffa1b2ac 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -1399,7 +1399,7 @@ impl Document { if let Some((parent_pipeline_id, _)) = self.window.parent_info() { let global_scope = self.window.upcast::(); let event = ConstellationMsg::MozBrowserEvent(parent_pipeline_id, - Some(global_scope.pipeline_id()), + global_scope.pipeline_id(), event); global_scope.constellation_chan().send(event).unwrap(); } @@ -1512,8 +1512,10 @@ impl Document { } } - let loader = self.loader.borrow(); - if !loader.is_blocked() && !loader.events_inhibited() { + if !self.loader.borrow().is_blocked() && !self.loader.borrow().events_inhibited() { + // Schedule a task to fire a "load" event (if no blocking loads have arrived in the mean time) + // NOTE: we can end up executing this code more than once, in case more blocking loads arrive. + debug!("Document loads are complete."); let win = self.window(); let msg = MainThreadScriptMsg::DocumentLoadsComplete( win.upcast::().pipeline_id()); diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs index d76ddc08152..761cc05a202 100644 --- a/components/script/dom/htmliframeelement.rs +++ b/components/script/dom/htmliframeelement.rs @@ -231,7 +231,11 @@ impl HTMLIFrameElement { } /// https://html.spec.whatwg.org/multipage/#iframe-load-event-steps steps 1-4 - pub fn iframe_load_event_steps(&self) { + pub fn iframe_load_event_steps(&self, loaded_pipeline: PipelineId) { + // TODO(#9592): assert that the load blocker is present at all times when we + // can guarantee that it's created for the case of iframe.reload(). + assert_eq!(loaded_pipeline, self.pipeline_id().unwrap()); + // TODO A cross-origin child document would not be easily accessible // from this script thread. It's unclear how to implement // steps 2, 3, and 5 efficiently in this case. diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 9919374db45..c3b4f912d31 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -932,8 +932,8 @@ impl ScriptThread { ConstellationControlMsg::WebFontLoaded(pipeline_id) => self.handle_web_font_loaded(pipeline_id), ConstellationControlMsg::DispatchFrameLoadEvent { - target: frame_id, parent: parent_pipeline_id } => - self.handle_frame_load_event(parent_pipeline_id, frame_id), + target: frame_id, parent: parent_id, child: child_id } => + self.handle_frame_load_event(parent_id, frame_id, child_id), ConstellationControlMsg::FramedContentChanged(parent_pipeline_id, frame_id) => self.handle_framed_content_changed(parent_pipeline_id, frame_id), ConstellationControlMsg::ReportCSSError(pipeline_id, filename, line, column, msg) => @@ -1193,12 +1193,15 @@ impl ScriptThread { None => return warn!("Message sent to closed pipeline {}.", pipeline), }; if doc.loader().is_blocked() { + debug!("Script thread got loads complete while loader is blocked."); return; } doc.mut_loader().inhibit_events(); // https://html.spec.whatwg.org/multipage/#the-end step 7 + // Schedule a task to fire a "load" event (if no blocking loads have arrived in the mean time) + // NOTE: we can end up executing this code more than once, in case more blocking loads arrive. let handler = box DocumentProgressHandler::new(Trusted::new(&doc)); self.dom_manipulation_task_source.queue(handler, doc.window().upcast()).unwrap(); @@ -1573,13 +1576,15 @@ impl ScriptThread { } /// Notify the containing document of a child frame that has completed loading. - fn handle_frame_load_event(&self, parent_pipeline_id: PipelineId, id: FrameId) { - let document = match self.root_browsing_context().find(parent_pipeline_id) { + fn handle_frame_load_event(&self, parent_id: PipelineId, frame_id: FrameId, child_id: PipelineId) { + let document = match self.root_browsing_context().find(parent_id) { Some(browsing_context) => browsing_context.active_document(), - None => return warn!("Message sent to closed pipeline {}.", parent_pipeline_id), + None => return warn!("Message sent to closed pipeline {}.", parent_id), }; - if let Some(iframe) = document.find_iframe(id) { - iframe.iframe_load_event_steps(); + if let Some(iframe) = document.find_iframe(frame_id) { + if iframe.pipeline_id() == Some(child_id) { + iframe.iframe_load_event_steps(child_id); + } } } diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index 55b4ba1052f..e5f881dc200 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -237,6 +237,8 @@ pub enum ConstellationControlMsg { target: FrameId, /// The pipeline that contains a frame loading the target pipeline. parent: PipelineId, + /// The pipeline that has completed loading. + child: PipelineId, }, /// Notifies a parent pipeline that one of its child frames is now active. /// PipelineId is for the parent, FrameId is the child frame. diff --git a/components/script_traits/script_msg.rs b/components/script_traits/script_msg.rs index 4ef3c0c92d5..b73be67c023 100644 --- a/components/script_traits/script_msg.rs +++ b/components/script_traits/script_msg.rs @@ -83,10 +83,9 @@ pub enum ScriptMsg { /// A new load has been requested, with an option to replace the current entry once loaded /// instead of adding a new entry. LoadUrl(PipelineId, LoadData, bool), - /// Dispatch a mozbrowser event to a given iframe, - /// or to the window if no subpage id is provided. - /// First PipelineId is for the parent, second PipelineId is in the frame. - MozBrowserEvent(PipelineId, Option, MozBrowserEvent), + /// Dispatch a mozbrowser event to the parent of this pipeline. + /// The first PipelineId is for the parent, the second is for the originating pipeline. + MozBrowserEvent(PipelineId, PipelineId, MozBrowserEvent), /// HTMLIFrameElement Forward or Back traversal. TraverseHistory(Option, TraversalDirection), /// Gets the length of the joint session history from the constellation.