diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index 99b0ac0b0a51..7c33641f5fb5 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -1623,12 +1623,12 @@ impl Constellation pipeline_id: PipelineId, event: MozBrowserEvent) { assert!(PREFS.is_mozbrowser_enabled()); - 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. // If the pipeline lookup fails, it is because we have torn down the pipeline, // so it is reasonable to silently ignore the event. + let frame_id = self.pipelines.get(&pipeline_id).map(|pipeline| pipeline.frame_id); match self.pipelines.get(&parent_pipeline_id) { Some(pipeline) => pipeline.trigger_mozbrowser_event(frame_id, event), None => warn!("Pipeline {:?} handling mozbrowser event after closure.", parent_pipeline_id), @@ -2206,6 +2206,7 @@ impl Constellation // Close a frame (and all children) fn close_frame(&mut self, frame_id: FrameId, exit_mode: ExitPipelineMode) { + debug!("Closing frame {:?}.", frame_id); // Store information about the pipelines to be closed. Then close the // pipelines, before removing ourself from the frames hash map. This // ordering is vital - so that if close_pipeline() ends up closing @@ -2241,10 +2242,12 @@ impl Constellation }; parent_pipeline.remove_child(frame_id); } + debug!("Closed frame {:?}.", frame_id); } // Close all pipelines at and beneath a given frame fn close_pipeline(&mut self, pipeline_id: PipelineId, exit_mode: ExitPipelineMode) { + debug!("Closing pipeline {:?}.", pipeline_id); // Store information about the frames to be closed. Then close the // frames, before removing ourself from the pipelines hash map. This // ordering is vital - so that if close_frames() ends up closing @@ -2284,6 +2287,7 @@ impl Constellation ExitPipelineMode::Normal => pipeline.exit(), ExitPipelineMode::Force => pipeline.force_exit(), } + debug!("Closed pipeline {:?}.", pipeline_id); } // Randomly close a pipeline -if --random-pipeline-closure-probability is set diff --git a/components/script/devtools.rs b/components/script/devtools.rs index 4163ee426ed4..9e6a0f1e5286 100644 --- a/components/script/devtools.rs +++ b/components/script/devtools.rs @@ -17,15 +17,15 @@ use dom::bindings::inheritance::Castable; use dom::bindings::js::Root; use dom::bindings::reflector::Reflectable; use dom::bindings::str::DOMString; -use dom::browsingcontext::BrowsingContext; use dom::element::Element; use dom::globalscope::GlobalScope; -use dom::node::Node; +use dom::node::{Node, window_from_node}; use dom::window::Window; use ipc_channel::ipc::IpcSender; use js::jsapi::{JSAutoCompartment, ObjectClassName}; use js::jsval::UndefinedValue; use msg::constellation_msg::PipelineId; +use script_thread::Documents; use std::ffi::CStr; use std::str; use style::properties::longhands::{margin_bottom, margin_left, margin_right, margin_top}; @@ -72,53 +72,35 @@ pub fn handle_evaluate_js(global: &GlobalScope, eval: String, reply: IpcSender>) { - let context = match context.find(pipeline) { - Some(found_context) => found_context, - None => return reply.send(None).unwrap() - }; - - let document = context.active_document(); - - let node = document.upcast::(); - reply.send(Some(node.summarize())).unwrap(); +pub fn handle_get_root_node(documents: &Documents, pipeline: PipelineId, reply: IpcSender>) { + let info = documents.find_document(pipeline) + .map(|document| document.upcast::().summarize()); + reply.send(info).unwrap(); } -pub fn handle_get_document_element(context: &BrowsingContext, +pub fn handle_get_document_element(documents: &Documents, pipeline: PipelineId, reply: IpcSender>) { - let context = match context.find(pipeline) { - Some(found_context) => found_context, - None => return reply.send(None).unwrap() - }; - - let document = context.active_document(); - let document_element = document.GetDocumentElement().unwrap(); - - let node = document_element.upcast::(); - reply.send(Some(node.summarize())).unwrap(); + let info = documents.find_document(pipeline) + .and_then(|document| document.GetDocumentElement()) + .map(|element| element.upcast::().summarize()); + reply.send(info).unwrap(); } -fn find_node_by_unique_id(context: &BrowsingContext, +fn find_node_by_unique_id(documents: &Documents, pipeline: PipelineId, node_id: &str) -> Option> { - let context = match context.find(pipeline) { - Some(found_context) => found_context, - None => return None - }; - - let document = context.active_document(); - let node = document.upcast::(); - - node.traverse_preorder().find(|candidate| candidate.unique_id() == node_id) + documents.find_document(pipeline).and_then(|document| + document.upcast::().traverse_preorder().find(|candidate| candidate.unique_id() == node_id) + ) } -pub fn handle_get_children(context: &BrowsingContext, +pub fn handle_get_children(documents: &Documents, pipeline: PipelineId, node_id: String, reply: IpcSender>>) { - match find_node_by_unique_id(context, pipeline, &*node_id) { + match find_node_by_unique_id(documents, pipeline, &*node_id) { None => return reply.send(None).unwrap(), Some(parent) => { let children = parent.children() @@ -130,11 +112,11 @@ pub fn handle_get_children(context: &BrowsingContext, }; } -pub fn handle_get_layout(context: &BrowsingContext, +pub fn handle_get_layout(documents: &Documents, pipeline: PipelineId, node_id: String, reply: IpcSender>) { - let node = match find_node_by_unique_id(context, pipeline, &*node_id) { + let node = match find_node_by_unique_id(documents, pipeline, &*node_id) { None => return reply.send(None).unwrap(), Some(found_node) => found_node }; @@ -144,7 +126,7 @@ pub fn handle_get_layout(context: &BrowsingContext, let width = rect.Width() as f32; let height = rect.Height() as f32; - let window = context.active_window(); + let window = window_from_node(&*node); let elem = node.downcast::().expect("should be getting layout of element"); let computed_style = window.GetComputedStyle(elem, None); @@ -223,11 +205,11 @@ pub fn handle_get_cached_messages(_pipeline_id: PipelineId, reply.send(messages).unwrap(); } -pub fn handle_modify_attribute(context: &BrowsingContext, +pub fn handle_modify_attribute(documents: &Documents, pipeline: PipelineId, node_id: String, modifications: Vec) { - let node = match find_node_by_unique_id(context, pipeline, &*node_id) { + let node = match find_node_by_unique_id(documents, pipeline, &*node_id) { None => return warn!("node id {} for pipeline id {} is not found", &node_id, &pipeline), Some(found_node) => found_node }; @@ -249,49 +231,39 @@ pub fn handle_wants_live_notifications(global: &GlobalScope, send_notifications: global.set_devtools_wants_updates(send_notifications); } -pub fn handle_set_timeline_markers(context: &BrowsingContext, +pub fn handle_set_timeline_markers(documents: &Documents, pipeline: PipelineId, marker_types: Vec, reply: IpcSender>) { - match context.find(pipeline) { + match documents.find_window(pipeline) { None => reply.send(None).unwrap(), - Some(context) => context.active_window().set_devtools_timeline_markers(marker_types, reply), + Some(window) => window.set_devtools_timeline_markers(marker_types, reply), } } -pub fn handle_drop_timeline_markers(context: &BrowsingContext, +pub fn handle_drop_timeline_markers(documents: &Documents, pipeline: PipelineId, marker_types: Vec) { - if let Some(context) = context.find(pipeline) { - context.active_window().drop_devtools_timeline_markers(marker_types); + if let Some(window) = documents.find_window(pipeline) { + window.drop_devtools_timeline_markers(marker_types); } } -pub fn handle_request_animation_frame(context: &BrowsingContext, +pub fn handle_request_animation_frame(documents: &Documents, id: PipelineId, actor_name: String) { - let context = match context.find(id) { - None => return warn!("context for pipeline id {} is not found", id), - Some(found_node) => found_node - }; - - let doc = context.active_document(); - let devtools_sender = - context.active_window().upcast::().devtools_chan().unwrap().clone(); - doc.request_animation_frame(box move |time| { - let msg = ScriptToDevtoolsControlMsg::FramerateTick(actor_name, time); - devtools_sender.send(msg).unwrap(); - }); + if let Some(doc) = documents.find_document(id) { + let devtools_sender = doc.window().upcast::().devtools_chan().unwrap().clone(); + doc.request_animation_frame(box move |time| { + let msg = ScriptToDevtoolsControlMsg::FramerateTick(actor_name, time); + devtools_sender.send(msg).unwrap(); + }); + } } -pub fn handle_reload(context: &BrowsingContext, +pub fn handle_reload(documents: &Documents, id: PipelineId) { - let context = match context.find(id) { - None => return warn!("context for pipeline id {} is not found", id), - Some(found_node) => found_node - }; - - let win = context.active_window(); - let location = win.Location(); - location.Reload(); + if let Some(win) = documents.find_window(id) { + win.Location().Reload(); + } } diff --git a/components/script/dom/browsingcontext.rs b/components/script/dom/browsingcontext.rs index 2ea6a1b78bdd..1409c9c5dcf7 100644 --- a/components/script/dom/browsingcontext.rs +++ b/components/script/dom/browsingcontext.rs @@ -2,7 +2,6 @@ * 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 dom::bindings::cell::DOMRefCell; use dom::bindings::conversions::{ToJSValConvertible, root_from_handleobject}; use dom::bindings::inheritance::Castable; use dom::bindings::js::{JS, MutNullableHeap, Root, RootedReference}; @@ -37,15 +36,9 @@ use std::cell::Cell; pub struct BrowsingContext { reflector: Reflector, - /// Pipeline id associated with this context. - id: PipelineId, - /// Indicates if reflow is required when reloading. needs_reflow: Cell, - /// Stores the child browsing contexts (ex. iframe browsing context) - children: DOMRefCell>>, - /// The current active document. /// Note that the session history is stored in the constellation, /// in the script thread we just track the current active document. @@ -56,19 +49,17 @@ pub struct BrowsingContext { } impl BrowsingContext { - pub fn new_inherited(frame_element: Option<&Element>, id: PipelineId) -> BrowsingContext { + pub fn new_inherited(frame_element: Option<&Element>) -> BrowsingContext { BrowsingContext { reflector: Reflector::new(), - id: id, needs_reflow: Cell::new(true), - children: DOMRefCell::new(vec![]), active_document: Default::default(), frame_element: frame_element.map(JS::from_ref), } } #[allow(unsafe_code)] - pub fn new(window: &Window, frame_element: Option<&Element>, id: PipelineId) -> Root { + pub fn new(window: &Window, frame_element: Option<&Element>) -> Root { unsafe { let WindowProxyHandler(handler) = window.windowproxy_handler(); assert!(!handler.is_null()); @@ -81,7 +72,7 @@ impl BrowsingContext { rooted!(in(cx) let window_proxy = NewWindowProxy(cx, parent, handler)); assert!(!window_proxy.is_null()); - let object = box BrowsingContext::new_inherited(frame_element, id); + let object = box BrowsingContext::new_inherited(frame_element); let raw = Box::into_raw(object); SetProxyExtra(window_proxy.get(), 0, &PrivateValue(raw as *const _)); @@ -118,82 +109,20 @@ impl BrowsingContext { window_proxy.get() } - pub fn remove(&self, id: PipelineId) -> Option> { - let remove_idx = self.children - .borrow() - .iter() - .position(|context| context.id == id); - match remove_idx { - Some(idx) => Some(Root::from_ref(&*self.children.borrow_mut().remove(idx))), - None => { - self.children - .borrow_mut() - .iter_mut() - .filter_map(|context| context.remove(id)) - .next() - } - } - } - pub fn set_reflow_status(&self, status: bool) -> bool { let old = self.needs_reflow.get(); self.needs_reflow.set(status); old } - pub fn pipeline_id(&self) -> PipelineId { - self.id - } - - pub fn push_child_context(&self, context: &BrowsingContext) { - self.children.borrow_mut().push(JS::from_ref(&context)); - } - - pub fn find_child_by_id(&self, pipeline_id: PipelineId) -> Option> { - self.children.borrow().iter().find(|context| { - let window = context.active_window(); - window.upcast::().pipeline_id() == pipeline_id - }).map(|context| context.active_window()) + pub fn active_pipeline_id(&self) -> Option { + self.active_document.get() + .map(|doc| doc.window().upcast::().pipeline_id()) } pub fn unset_active_document(&self) { self.active_document.set(None) } - - pub fn iter(&self) -> ContextIterator { - ContextIterator { - stack: vec!(Root::from_ref(self)), - } - } - - pub fn find(&self, id: PipelineId) -> Option> { - if self.id == id { - return Some(Root::from_ref(self)); - } - - self.children.borrow() - .iter() - .filter_map(|c| c.find(id)) - .next() - } -} - -pub struct ContextIterator { - stack: Vec>, -} - -impl Iterator for ContextIterator { - type Item = Root; - - fn next(&mut self) -> Option> { - let popped = self.stack.pop(); - if let Some(ref context) = popped { - self.stack.extend(context.children.borrow() - .iter() - .map(|c| Root::from_ref(&**c))); - } - popped - } } #[allow(unsafe_code)] diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs index 2b56b9d697c2..0430a0f7d260 100644 --- a/components/script/dom/htmliframeelement.rs +++ b/components/script/dom/htmliframeelement.rs @@ -42,6 +42,7 @@ use js::jsval::{NullValue, UndefinedValue}; use msg::constellation_msg::{FrameType, FrameId, PipelineId, TraversalDirection}; use net_traits::response::HttpsState; use script_layout_interface::message::ReflowQueryType; +use script_thread::ScriptThread; use script_traits::{IFrameLoadInfo, LoadData, MozBrowserEvent, ScriptMsg as ConstellationMsg}; use script_traits::IFrameSandboxState::{IFrameSandboxed, IFrameUnsandboxed}; use servo_atoms::Atom; @@ -235,7 +236,7 @@ impl HTMLIFrameElement { 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()); + if Some(loaded_pipeline) != self.pipeline_id() { return; } // TODO A cross-origin child document would not be easily accessible // from this script thread. It's unclear how to implement @@ -268,13 +269,16 @@ impl HTMLIFrameElement { } pub fn get_content_window(&self) -> Option> { - self.pipeline_id.get().and_then(|pipeline_id| { - let window = window_from_node(self); - let browsing_context = window.browsing_context(); - browsing_context.find_child_by_id(pipeline_id) - }) + self.pipeline_id.get() + .and_then(|pipeline_id| ScriptThread::find_document(pipeline_id)) + .and_then(|document| { + if self.global().get_url().origin() == document.global().get_url().origin() { + Some(Root::from_ref(document.window())) + } else { + None + } + }) } - } pub trait HTMLIFrameElementLayoutMethods { diff --git a/components/script/dom/nodelist.rs b/components/script/dom/nodelist.rs index 3810f16b22c5..391d21d58659 100644 --- a/components/script/dom/nodelist.rs +++ b/components/script/dom/nodelist.rs @@ -97,6 +97,13 @@ impl NodeList { panic!("called as_simple_list() on a children node list") } } + + pub fn iter(&self) -> NodeListIterator { + NodeListIterator { + nodes: self, + offset: 0, + } + } } #[derive(JSTraceable, HeapSizeOf)] @@ -277,3 +284,18 @@ impl ChildrenList { self.last_index.set(0u32); } } + +pub struct NodeListIterator<'a> { + nodes: &'a NodeList, + offset: u32, +} + +impl<'a> Iterator for NodeListIterator<'a> { + type Item = Root; + + fn next(&mut self) -> Option> { + let result = self.nodes.Item(self.offset); + self.offset = self.offset + 1; + result + } +} diff --git a/components/script/dom/storage.rs b/components/script/dom/storage.rs index 3441f57a7e70..099c6c04fea7 100644 --- a/components/script/dom/storage.rs +++ b/components/script/dom/storage.rs @@ -13,7 +13,6 @@ use dom::bindings::str::DOMString; use dom::event::{Event, EventBubbles, EventCancelable}; use dom::globalscope::GlobalScope; use dom::storageevent::StorageEvent; -use dom::urlhelper::UrlHelper; use ipc_channel::ipc::{self, IpcSender}; use net_traits::IpcSend; use net_traits::storage_thread::{StorageThreadMsg, StorageType}; @@ -193,14 +192,16 @@ impl Runnable for StorageEventRunnable { Some(&storage) ); - let root_context = script_thread.root_browsing_context(); - for it_context in root_context.iter() { - let it_window = it_context.active_window(); - assert!(UrlHelper::SameOrigin(&ev_url, &it_window.get_url())); - // TODO: Such a Document object is not necessarily fully active, but events fired on such - // objects are ignored by the event loop until the Document becomes fully active again. - if global.pipeline_id() != it_window.upcast::().pipeline_id() { - storage_event.upcast::().fire(it_window.upcast()); + // TODO: This is only iterating over documents in the current script + // thread, so we are not firing events to other script threads. + // NOTE: once that is fixed, we can remove borrow_documents from ScriptThread. + for (id, document) in script_thread.borrow_documents().iter() { + if ev_url.origin() == document.window().get_url().origin() { + // TODO: Such a Document object is not necessarily fully active, but events fired on such + // objects are ignored by the event loop until the Document becomes fully active again. + if global.pipeline_id() != id { + storage_event.upcast::().fire(document.window().upcast()); + } } } } diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 86d742680eec..b2d9e5307dfa 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -46,6 +46,7 @@ use dom::element::Element; use dom::event::{Event, EventBubbles, EventCancelable}; use dom::globalscope::GlobalScope; use dom::htmlanchorelement::HTMLAnchorElement; +use dom::htmliframeelement::HTMLIFrameElement; use dom::node::{Node, NodeDamage, window_from_node}; use dom::serviceworker::TrustedServiceWorkerAddress; use dom::serviceworkerregistration::ServiceWorkerRegistration; @@ -91,8 +92,8 @@ use script_traits::CompositorEvent::{KeyEvent, MouseButtonEvent, MouseMoveEvent, use script_traits::CompositorEvent::{TouchEvent, TouchpadPressureEvent}; use script_traits::webdriver_msg::WebDriverScriptCommand; use std::borrow::ToOwned; -use std::cell::Cell; -use std::collections::{HashMap, HashSet}; +use std::cell::{Cell, Ref}; +use std::collections::{hash_map, HashMap, HashSet}; use std::option::Option; use std::ptr; use std::rc::Rc; @@ -325,14 +326,75 @@ impl OpaqueSender for Sender { } } -/// Information for an entire page. Pages are top-level browsing contexts and can contain multiple -/// frames. +/// The set of all documents managed by this script thread. +#[derive(JSTraceable)] +#[must_root] +pub struct Documents { + map: HashMap>, +} + +impl Documents { + pub fn new() -> Documents { + Documents { + map: HashMap::new(), + } + } + + pub fn insert(&mut self, pipeline_id: PipelineId, doc: &Document) { + self.map.insert(pipeline_id, JS::from_ref(doc)); + } + + pub fn remove(&mut self, pipeline_id: PipelineId) -> Option> { + self.map.remove(&pipeline_id).map(|ref doc| Root::from_ref(&**doc)) + } + + pub fn is_empty(&self) -> bool { + self.map.is_empty() + } + + pub fn find_document(&self, pipeline_id: PipelineId) -> Option> { + self.map.get(&pipeline_id).map(|doc| Root::from_ref(&**doc)) + } + + pub fn find_window(&self, pipeline_id: PipelineId) -> Option> { + self.find_document(pipeline_id).map(|doc| Root::from_ref(doc.window())) + } + + pub fn find_global(&self, pipeline_id: PipelineId) -> Option> { + self.find_window(pipeline_id).map(|window| Root::from_ref(window.upcast())) + } + + pub fn find_iframe(&self, pipeline_id: PipelineId, frame_id: FrameId) -> Option> { + self.find_document(pipeline_id).and_then(|doc| doc.find_iframe(frame_id)) + } + + pub fn iter<'a>(&'a self) -> DocumentsIter<'a> { + DocumentsIter { + iter: self.map.iter(), + } + } +} + +#[allow(unrooted_must_root)] +pub struct DocumentsIter<'a> { + iter: hash_map::Iter<'a, PipelineId, JS>, +} + +impl<'a> Iterator for DocumentsIter<'a> { + type Item = (PipelineId, Root); + + fn next(&mut self) -> Option<(PipelineId, Root)> { + self.iter.next().map(|(id, doc)| (*id, Root::from_ref(&**doc))) + } +} + + #[derive(JSTraceable)] // ScriptThread instances are rooted on creation, so this is okay #[allow(unrooted_must_root)] pub struct ScriptThread { - /// A handle to the information pertaining to page layout - browsing_context: MutNullableHeap>, + /// The documents for pipelines managed by this thread + documents: DOMRefCell, /// A list of data pertaining to loads that have not yet received a network response incomplete_loads: DOMRefCell>, /// A map to store service worker registrations for a given origin @@ -433,12 +495,8 @@ impl<'a> Drop for ScriptMemoryFailsafe<'a> { fn drop(&mut self) { match self.owner { Some(owner) => { - let context = owner.browsing_context.get(); - for context in context.iter() { - if let Some(document) = context.maybe_active_document() { - let window = document.window(); - window.clear_js_runtime_for_script_deallocation(); - } + for (_, document) in owner.documents.borrow().iter() { + document.window().clear_js_runtime_for_script_deallocation(); } } None => (), @@ -542,6 +600,19 @@ impl ScriptThread { }); } + pub fn find_document(id: PipelineId) -> Option> { + SCRIPT_THREAD_ROOT.with(|root| root.get().and_then(|script_thread| { + let script_thread = unsafe { &*script_thread }; + script_thread.documents.borrow().find_document(id) + })) + } + + // TODO: This method is only needed for storage, and can be removed + // once storage event dispatch is moved to the constellation. + pub fn borrow_documents(&self) -> Ref { + self.documents.borrow() + } + /// Creates a new script thread. pub fn new(state: InitialScriptState, port: Receiver, @@ -572,7 +643,7 @@ impl ScriptThread { let boxed_script_sender = MainThreadScriptChan(chan.clone()).clone(); ScriptThread { - browsing_context: MutNullableHeap::new(None), + documents: DOMRefCell::new(Documents::new()), incomplete_loads: DOMRefCell::new(vec!()), registration_map: DOMRefCell::new(HashMap::new()), @@ -616,21 +687,6 @@ impl ScriptThread { } } - // Return the root browsing context in the frame tree. Panics if it doesn't exist. - pub fn root_browsing_context(&self) -> Root { - self.browsing_context.get().unwrap() - } - - fn root_browsing_context_exists(&self) -> bool { - self.browsing_context.get().is_some() - } - - /// Find a child browsing context of the root context by pipeline id. Returns `None` if the - /// root context does not exist or the child context cannot be found. - fn find_child_context(&self, pipeline_id: PipelineId) -> Option> { - self.browsing_context.get().and_then(|context| context.find(pipeline_id)) - } - pub fn get_cx(&self) -> *mut JSContext { self.js_runtime.cx() } @@ -638,9 +694,11 @@ impl ScriptThread { /// Starts the script thread. After calling this method, the script thread will loop receiving /// messages on its port. pub fn start(&self) { + debug!("Starting script thread."); while self.handle_msgs() { // Go on... } + debug!("Stopped script thread."); } /// Handle incoming control messages. @@ -652,20 +710,16 @@ impl ScriptThread { // Gather them first to avoid a double mut borrow on self. let mut resizes = vec!(); - let context = self.browsing_context.get(); - if let Some(context) = context { - for context in context.iter() { - // Only process a resize if layout is idle. - let window = context.active_window(); - let resize_event = window.steal_resize_event(); - match resize_event { - Some(size) => resizes.push((window.upcast::().pipeline_id(), size)), - None => () - } + for (id, document) in self.documents.borrow().iter() { + // Only process a resize if layout is idle. + let resize_event = document.window().steal_resize_event(); + match resize_event { + Some((size, size_type)) => resizes.push((id, size, size_type)), + None => () } } - for (id, (size, size_type)) in resizes { + for (id, size, size_type) in resizes { self.handle_event(id, ResizeEvent(size, size_type)); } @@ -809,24 +863,21 @@ impl ScriptThread { // Issue batched reflows on any pages that require it (e.g. if images loaded) // TODO(gw): In the future we could probably batch other types of reflows // into this loop too, but for now it's only images. - let context = self.browsing_context.get(); - if let Some(context) = context { - for context in context.iter() { - let window = context.active_window(); - let pending_reflows = window.get_pending_reflow_count(); - if pending_reflows > 0 { - window.reflow(ReflowGoal::ForDisplay, - ReflowQueryType::NoQuery, - ReflowReason::ImageLoaded); - } else { - // Reflow currently happens when explicitly invoked by code that - // knows the document could have been modified. This should really - // be driven by the compositor on an as-needed basis instead, to - // minimize unnecessary work. - window.reflow(ReflowGoal::ForDisplay, - ReflowQueryType::NoQuery, - ReflowReason::MissingExplicitReflow); - } + for (_, document) in self.documents.borrow().iter() { + let window = document.window(); + let pending_reflows = window.get_pending_reflow_count(); + if pending_reflows > 0 { + window.reflow(ReflowGoal::ForDisplay, + ReflowQueryType::NoQuery, + ReflowReason::ImageLoaded); + } else { + // Reflow currently happens when explicitly invoked by code that + // knows the document could have been modified. This should really + // be driven by the compositor on an as-needed basis instead, to + // minimize unnecessary work. + window.reflow(ReflowGoal::ForDisplay, + ReflowQueryType::NoQuery, + ReflowReason::MissingExplicitReflow); } } @@ -980,51 +1031,47 @@ impl ScriptThread { TimerSource::FromWorker => panic!("Worker timeouts must not be sent to script thread"), }; - let context = self.root_browsing_context(); - let context = context.find(pipeline_id).expect("ScriptThread: received fire timer msg for a - pipeline ID not associated with this script thread. This is a bug."); - let window = context.active_window(); + let window = self.documents.borrow().find_window(pipeline_id) + .expect("ScriptThread: received fire timer msg for a pipeline not in this script thread. This is a bug."); window.handle_fire_timer(id); } fn handle_msg_from_devtools(&self, msg: DevtoolScriptControlMsg) { - let context = self.root_browsing_context(); + let documents = self.documents.borrow(); match msg { DevtoolScriptControlMsg::EvaluateJS(id, s, reply) => { - let window = match context.find(id) { - Some(browsing_context) => browsing_context.active_window(), + match documents.find_window(id) { + Some(window) => devtools::handle_evaluate_js(window.upcast(), s, reply), None => return warn!("Message sent to closed pipeline {}.", id), - }; - devtools::handle_evaluate_js(window.upcast(), s, reply) + } }, DevtoolScriptControlMsg::GetRootNode(id, reply) => - devtools::handle_get_root_node(&context, id, reply), + devtools::handle_get_root_node(&*documents, id, reply), DevtoolScriptControlMsg::GetDocumentElement(id, reply) => - devtools::handle_get_document_element(&context, id, reply), + devtools::handle_get_document_element(&*documents, id, reply), DevtoolScriptControlMsg::GetChildren(id, node_id, reply) => - devtools::handle_get_children(&context, id, node_id, reply), + devtools::handle_get_children(&*documents, id, node_id, reply), DevtoolScriptControlMsg::GetLayout(id, node_id, reply) => - devtools::handle_get_layout(&context, id, node_id, reply), + devtools::handle_get_layout(&*documents, id, node_id, reply), DevtoolScriptControlMsg::GetCachedMessages(id, message_types, reply) => devtools::handle_get_cached_messages(id, message_types, reply), DevtoolScriptControlMsg::ModifyAttribute(id, node_id, modifications) => - devtools::handle_modify_attribute(&context, id, node_id, modifications), + devtools::handle_modify_attribute(&*documents, id, node_id, modifications), DevtoolScriptControlMsg::WantsLiveNotifications(id, to_send) => { - let window = match context.find(id) { - Some(browsing_context) => browsing_context.active_window(), + match documents.find_window(id) { + Some(window) => devtools::handle_wants_live_notifications(window.upcast(), to_send), None => return warn!("Message sent to closed pipeline {}.", id), - }; - devtools::handle_wants_live_notifications(window.upcast(), to_send) + } }, DevtoolScriptControlMsg::SetTimelineMarkers(id, marker_types, reply) => - devtools::handle_set_timeline_markers(&context, id, marker_types, reply), + devtools::handle_set_timeline_markers(&*documents, id, marker_types, reply), DevtoolScriptControlMsg::DropTimelineMarkers(id, marker_types) => - devtools::handle_drop_timeline_markers(&context, id, marker_types), + devtools::handle_drop_timeline_markers(&*documents, id, marker_types), DevtoolScriptControlMsg::RequestAnimationFrame(id, name) => - devtools::handle_request_animation_frame(&context, id, name), + devtools::handle_request_animation_frame(&*documents, id, name), DevtoolScriptControlMsg::Reload(id) => - devtools::handle_reload(&context, id), + devtools::handle_reload(&*documents, id), } } @@ -1033,55 +1080,51 @@ impl ScriptThread { } fn handle_webdriver_msg(&self, pipeline_id: PipelineId, msg: WebDriverScriptCommand) { - let context = self.root_browsing_context(); + let documents = self.documents.borrow(); match msg { WebDriverScriptCommand::AddCookie(params, reply) => - webdriver_handlers::handle_add_cookie(&context, pipeline_id, params, reply), + webdriver_handlers::handle_add_cookie(&*documents, pipeline_id, params, reply), WebDriverScriptCommand::ExecuteScript(script, reply) => - webdriver_handlers::handle_execute_script(&context, pipeline_id, script, reply), + webdriver_handlers::handle_execute_script(&*documents, pipeline_id, script, reply), WebDriverScriptCommand::FindElementCSS(selector, reply) => - webdriver_handlers::handle_find_element_css(&context, pipeline_id, selector, reply), + webdriver_handlers::handle_find_element_css(&*documents, pipeline_id, selector, reply), WebDriverScriptCommand::FindElementsCSS(selector, reply) => - webdriver_handlers::handle_find_elements_css(&context, pipeline_id, selector, reply), + webdriver_handlers::handle_find_elements_css(&*documents, pipeline_id, selector, reply), WebDriverScriptCommand::FocusElement(element_id, reply) => - webdriver_handlers::handle_focus_element(&context, pipeline_id, element_id, reply), + webdriver_handlers::handle_focus_element(&*documents, pipeline_id, element_id, reply), WebDriverScriptCommand::GetActiveElement(reply) => - webdriver_handlers::handle_get_active_element(&context, pipeline_id, reply), + webdriver_handlers::handle_get_active_element(&*documents, pipeline_id, reply), WebDriverScriptCommand::GetCookies(reply) => - webdriver_handlers::handle_get_cookies(&context, pipeline_id, reply), + webdriver_handlers::handle_get_cookies(&*documents, pipeline_id, reply), WebDriverScriptCommand::GetCookie(name, reply) => - webdriver_handlers::handle_get_cookie(&context, pipeline_id, name, reply), + webdriver_handlers::handle_get_cookie(&*documents, pipeline_id, name, reply), WebDriverScriptCommand::GetElementTagName(node_id, reply) => - webdriver_handlers::handle_get_name(&context, pipeline_id, node_id, reply), + webdriver_handlers::handle_get_name(&*documents, pipeline_id, node_id, reply), WebDriverScriptCommand::GetElementAttribute(node_id, name, reply) => - webdriver_handlers::handle_get_attribute(&context, pipeline_id, node_id, name, reply), + webdriver_handlers::handle_get_attribute(&*documents, pipeline_id, node_id, name, reply), WebDriverScriptCommand::GetElementCSS(node_id, name, reply) => - webdriver_handlers::handle_get_css(&context, pipeline_id, node_id, name, reply), + webdriver_handlers::handle_get_css(&*documents, pipeline_id, node_id, name, reply), WebDriverScriptCommand::GetElementRect(node_id, reply) => - webdriver_handlers::handle_get_rect(&context, pipeline_id, node_id, reply), + webdriver_handlers::handle_get_rect(&*documents, pipeline_id, node_id, reply), WebDriverScriptCommand::GetElementText(node_id, reply) => - webdriver_handlers::handle_get_text(&context, pipeline_id, node_id, reply), + webdriver_handlers::handle_get_text(&*documents, pipeline_id, node_id, reply), WebDriverScriptCommand::GetFrameId(frame_id, reply) => - webdriver_handlers::handle_get_frame_id(&context, pipeline_id, frame_id, reply), + webdriver_handlers::handle_get_frame_id(&*documents, pipeline_id, frame_id, reply), WebDriverScriptCommand::GetUrl(reply) => - webdriver_handlers::handle_get_url(&context, pipeline_id, reply), + webdriver_handlers::handle_get_url(&*documents, pipeline_id, reply), WebDriverScriptCommand::IsEnabled(element_id, reply) => - webdriver_handlers::handle_is_enabled(&context, pipeline_id, element_id, reply), + webdriver_handlers::handle_is_enabled(&*documents, pipeline_id, element_id, reply), WebDriverScriptCommand::IsSelected(element_id, reply) => - webdriver_handlers::handle_is_selected(&context, pipeline_id, element_id, reply), + webdriver_handlers::handle_is_selected(&*documents, pipeline_id, element_id, reply), WebDriverScriptCommand::GetTitle(reply) => - webdriver_handlers::handle_get_title(&context, pipeline_id, reply), + webdriver_handlers::handle_get_title(&*documents, pipeline_id, reply), WebDriverScriptCommand::ExecuteAsyncScript(script, reply) => - webdriver_handlers::handle_execute_async_script(&context, pipeline_id, script, reply), + webdriver_handlers::handle_execute_async_script(&*documents, pipeline_id, script, reply), } } fn handle_resize(&self, id: PipelineId, size: WindowSizeData, size_type: WindowSizeType) { - if let Some(ref context) = self.find_child_context(id) { - let window = match context.find(id) { - Some(browsing_context) => browsing_context.active_window(), - None => return warn!("Message sent to closed pipeline {}.", id), - }; + if let Some(ref window) = self.documents.borrow().find_window(id) { window.set_resize_event(size, size_type); return; } @@ -1094,15 +1137,11 @@ impl ScriptThread { } fn handle_viewport(&self, id: PipelineId, rect: Rect) { - let context = self.browsing_context.get(); - if let Some(context) = context { - if let Some(inner_context) = context.find(id) { - let window = inner_context.active_window(); - if window.set_page_clip_rect_with_new_viewport(rect) { - self.rebuild_and_force_reflow(&inner_context, ReflowReason::Viewport); - } - return; + if let Some(document) = self.documents.borrow().find_document(id) { + if document.window().set_page_clip_rect_with_new_viewport(rect) { + self.rebuild_and_force_reflow(&document, ReflowReason::Viewport); } + return; } let mut loads = self.incomplete_loads.borrow_mut(); if let Some(ref mut load) = loads.iter_mut().find(|load| load.pipeline_id == id) { @@ -1115,15 +1154,8 @@ impl ScriptThread { fn handle_set_scroll_state(&self, id: PipelineId, scroll_states: &[(UntrustedNodeAddress, Point2D)]) { - let window = match self.browsing_context.get() { - Some(context) => { - match context.find(id) { - Some(inner_context) => inner_context.active_window(), - None => { - panic!("Set scroll state message sent to nonexistent pipeline: {:?}", id) - } - } - } + let window = match self.documents.borrow().find_window(id) { + Some(window) => window, None => return warn!("Set scroll state message sent to nonexistent pipeline: {:?}", id), }; @@ -1168,11 +1200,8 @@ impl ScriptThread { layout_threads: layout_threads, }; - let context = self.root_browsing_context(); - let parent_context = context.find(parent_pipeline_id).expect("ScriptThread: received a layout - whose parent has a PipelineId which does not correspond to a pipeline in the script - thread's browsing context tree. This is a bug."); - let parent_window = parent_context.active_window(); + let parent_window = self.documents.borrow().find_window(parent_pipeline_id) + .expect("ScriptThread: received a layout for a parent pipeline not in this script thread. This is a bug."); // Tell layout to actually spawn the thread. parent_window.layout_chan() @@ -1187,8 +1216,8 @@ impl ScriptThread { } fn handle_loads_complete(&self, pipeline: PipelineId) { - let doc = match self.root_browsing_context().find(pipeline) { - Some(browsing_context) => browsing_context.active_document(), + let doc = match self.documents.borrow().find_document(pipeline) { + Some(doc) => doc, None => return warn!("Message sent to closed pipeline {}.", pipeline), }; if doc.loader().is_blocked() { @@ -1222,29 +1251,31 @@ impl ScriptThread { } fn collect_reports(&self, reports_chan: ReportsChan) { - let mut urls = vec![]; + let mut path_seg = String::from("url("); let mut dom_tree_size = 0; let mut reports = vec![]; - if let Some(root_context) = self.browsing_context.get() { - for it_context in root_context.iter() { - let current_url = it_context.active_document().url().to_string(); + for (_, document) in self.documents.borrow().iter() { + let current_url = document.url().as_str(); - for child in it_context.active_document().upcast::().traverse_preorder() { - dom_tree_size += heap_size_of_self_and_children(&*child); - } - let window = it_context.active_window(); - dom_tree_size += heap_size_of_self_and_children(&*window); - - reports.push(Report { - path: path![format!("url({})", current_url), "dom-tree"], - kind: ReportKind::ExplicitJemallocHeapSize, - size: dom_tree_size, - }); - urls.push(current_url); + for child in document.upcast::().traverse_preorder() { + dom_tree_size += heap_size_of_self_and_children(&*child); } + dom_tree_size += heap_size_of_self_and_children(document.window()); + + if reports.len() > 0 { + path_seg.push_str(", "); + } + path_seg.push_str(current_url); + + reports.push(Report { + path: path![format!("url({})", current_url), "dom-tree"], + kind: ReportKind::ExplicitJemallocHeapSize, + size: dom_tree_size, + }); } - let path_seg = format!("url({})", urls.join(", ")); + + path_seg.push_str(")"); reports.extend(get_reports(self.get_cx(), path_seg)); reports_chan.send(reports); } @@ -1252,28 +1283,21 @@ impl ScriptThread { /// To slow/speed up timers and manage any other script thread resource based on visibility. /// Returns true if successful. fn alter_resource_utilization(&self, id: PipelineId, visible: bool) -> bool { - if let Some(root_context) = self.browsing_context.get() { - if let Some(ref inner_context) = root_context.find(id) { - let window = inner_context.active_window(); - if visible { - window.upcast::().speed_up_timers(); - } else { - window.upcast::().slow_down_timers(); - } - return true; + if let Some(window) = self.documents.borrow().find_window(id) { + if visible { + window.upcast::().speed_up_timers(); + } else { + window.upcast::().slow_down_timers(); } + return true; } false } /// Updates iframe element after a change in visibility fn handle_visibility_change_complete_msg(&self, parent_pipeline_id: PipelineId, id: FrameId, visible: bool) { - if let Some(root_context) = self.browsing_context.get() { - if let Some(ref inner_context) = root_context.find(parent_pipeline_id) { - if let Some(iframe) = inner_context.active_document().find_iframe(id) { - iframe.change_visibility_status(visible); - } - } + if let Some(iframe) = self.documents.borrow().find_iframe(parent_pipeline_id, id) { + iframe.change_visibility_status(visible); } } @@ -1300,12 +1324,9 @@ impl ScriptThread { /// Handles freeze message fn handle_freeze_msg(&self, id: PipelineId) { - if let Some(root_context) = self.browsing_context.get() { - if let Some(ref inner_context) = root_context.find(id) { - let window = inner_context.active_window(); - window.upcast::().suspend(); - return; - } + if let Some(window) = self.documents.borrow().find_window(id) { + window.upcast::().suspend(); + return; } let mut loads = self.incomplete_loads.borrow_mut(); if let Some(ref mut load) = loads.iter_mut().find(|load| load.pipeline_id == id) { @@ -1317,13 +1338,14 @@ impl ScriptThread { /// Handles thaw message fn handle_thaw_msg(&self, id: PipelineId) { - if let Some(inner_context) = self.root_browsing_context().find(id) { - let needed_reflow = inner_context.set_reflow_status(false); - if needed_reflow { - self.rebuild_and_force_reflow(&inner_context, ReflowReason::CachedPageNeededReflow); + if let Some(document) = self.documents.borrow().find_document(id) { + if let Some(context) = document.browsing_context() { + let needed_reflow = context.set_reflow_status(false); + if needed_reflow { + self.rebuild_and_force_reflow(&document, ReflowReason::CachedPageNeededReflow); + } } - let window = inner_context.active_window(); - window.thaw(); + document.window().thaw(); return; } let mut loads = self.incomplete_loads.borrow_mut(); @@ -1337,10 +1359,7 @@ impl ScriptThread { fn handle_focus_iframe_msg(&self, parent_pipeline_id: PipelineId, frame_id: FrameId) { - let borrowed_context = self.root_browsing_context(); - let context = borrowed_context.find(parent_pipeline_id).unwrap(); - - let doc = context.active_document(); + let doc = self.documents.borrow().find_document(parent_pipeline_id).unwrap(); let frame_element = doc.find_iframe(frame_id); if let Some(ref frame_element) = frame_element { @@ -1353,13 +1372,11 @@ impl ScriptThread { fn handle_framed_content_changed(&self, parent_pipeline_id: PipelineId, frame_id: FrameId) { - let root_context = self.root_browsing_context(); - let context = root_context.find(parent_pipeline_id).unwrap(); - let doc = context.active_document(); + let doc = self.documents.borrow().find_document(parent_pipeline_id).unwrap(); let frame_element = doc.find_iframe(frame_id); if let Some(ref frame_element) = frame_element { frame_element.upcast::().dirty(NodeDamage::OtherNodeDamage); - let window = context.active_window(); + let window = doc.window(); window.reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery, ReflowReason::FramedContentChanged); @@ -1372,11 +1389,11 @@ impl ScriptThread { parent_pipeline_id: PipelineId, frame_id: Option, event: MozBrowserEvent) { - match self.root_browsing_context().find(parent_pipeline_id) { + match self.documents.borrow().find_document(parent_pipeline_id) { None => warn!("Mozbrowser event after pipeline {:?} closed.", parent_pipeline_id), - Some(context) => match frame_id { - None => context.active_window().dispatch_mozbrowser_event(event), - Some(frame_id) => match context.active_document().find_iframe(frame_id) { + Some(doc) => match frame_id { + None => doc.window().dispatch_mozbrowser_event(event), + Some(frame_id) => match doc.find_iframe(frame_id) { None => warn!("Mozbrowser event after iframe {:?}/{:?} closed.", parent_pipeline_id, frame_id), Some(frame_element) => frame_element.dispatch_mozbrowser_event(event), }, @@ -1388,24 +1405,17 @@ impl ScriptThread { parent_pipeline_id: PipelineId, frame_id: FrameId, new_pipeline_id: PipelineId) { - let borrowed_context = self.root_browsing_context(); - - let frame_element = borrowed_context.find(parent_pipeline_id).and_then(|context| { - let doc = context.active_document(); - doc.find_iframe(frame_id) - }); - - frame_element.unwrap().update_pipeline_id(new_pipeline_id); + if let Some(frame_element) = self.documents.borrow().find_iframe(parent_pipeline_id, frame_id) { + frame_element.update_pipeline_id(new_pipeline_id); + } } /// Window was resized, but this script was not active, so don't reflow yet fn handle_resize_inactive_msg(&self, id: PipelineId, new_size: WindowSizeData) { - let context = self.root_browsing_context(); - let context = context.find(id).expect("Received resize message for PipelineId not associated - with a browsing context in the browsing context tree. This is a bug."); - let window = context.active_window(); + let window = self.documents.borrow().find_window(id) + .expect("ScriptThread: received a resize msg for a pipeline not in this script thread. This is a bug."); window.set_window_size(new_size); - context.set_reflow_status(true); + window.browsing_context().set_reflow_status(true); } /// We have gotten a window.close from script, which we pass on to the compositor. @@ -1459,10 +1469,9 @@ impl ScriptThread { Some(r) => r, None => return }; - if let Some(context) = self.root_browsing_context().find(pipeline_id) { + if let Some(window) = self.documents.borrow().find_window(pipeline_id) { let script_url = maybe_registration.get_installed().get_script_url(); - let scope_things = ServiceWorkerRegistration::create_scope_things( - context.active_window().upcast(), script_url); + let scope_things = ServiceWorkerRegistration::create_scope_things(window.upcast(), script_url); let _ = self.constellation_chan.send(ConstellationMsg::RegisterServiceWorker(scope_things, scope)); } else { warn!("Registration failed for {}", scope); @@ -1471,8 +1480,8 @@ impl ScriptThread { /// Handles a request for the window title. fn handle_get_title_msg(&self, pipeline_id: PipelineId) { - let document = match self.root_browsing_context().find(pipeline_id) { - Some(browsing_context) => browsing_context.active_document(), + let document = match self.documents.borrow().find_document(pipeline_id) { + Some(document) => document, None => return warn!("Message sent to closed pipeline {}.", pipeline_id), }; document.send_title_to_compositor(); @@ -1481,6 +1490,8 @@ impl ScriptThread { /// Handles a request to exit the script thread and shut down layout. /// Returns true if the script thread should shut down and false otherwise. fn handle_exit_pipeline_msg(&self, id: PipelineId) -> bool { + debug!("Exiting pipeline {:?}.", id); + self.closed_pipelines.borrow_mut().insert(id); // Check if the exit message is for an in progress load. @@ -1500,36 +1511,26 @@ impl ScriptThread { response_port.recv().unwrap(); chan.send(message::Msg::ExitNow).ok(); } - - let has_pending_loads = self.incomplete_loads.borrow().len() > 0; - let has_root_context = self.root_browsing_context_exists(); - - // Exit if no pending loads and no root context - return !has_pending_loads && !has_root_context; } - // If root is being exited, shut down all contexts - let context = self.root_browsing_context(); - let window = context.active_window(); - if window.upcast::().pipeline_id() == id { - debug!("shutting down layout for root context {:?}", id); - shut_down_layout(&context); + if let Some(document) = self.documents.borrow_mut().remove(id) { + shut_down_layout(document.window()); let _ = self.constellation_chan.send(ConstellationMsg::PipelineExited(id)); - return true } - // otherwise find just the matching context and exit all sub-contexts - if let Some(ref mut child_context) = context.remove(id) { - shut_down_layout(&child_context); - } - let _ = self.constellation_chan.send(ConstellationMsg::PipelineExited(id)); - false + let no_pending_loads = self.incomplete_loads.borrow().is_empty(); + let no_remaining_contexts = self.documents.borrow().is_empty(); + + debug!("Exited pipeline {:?} ({}&{}).", id, no_pending_loads, no_remaining_contexts); + + // Exit if no pending loads and no remaining contexts + no_pending_loads && no_remaining_contexts } /// Handles when layout thread finishes all animation in one tick fn handle_tick_all_animations(&self, id: PipelineId) { - let document = match self.root_browsing_context().find(id) { - Some(browsing_context) => browsing_context.active_document(), + let document = match self.documents.borrow().find_document(id) { + Some(document) => document, None => return warn!("Message sent to closed pipeline {}.", id), }; document.run_the_animation_frame_callbacks(); @@ -1569,21 +1570,16 @@ impl ScriptThread { /// Handles a Web font being loaded. Does nothing if the page no longer exists. fn handle_web_font_loaded(&self, pipeline_id: PipelineId) { - if let Some(context) = self.find_child_context(pipeline_id) { - self.rebuild_and_force_reflow(&context, ReflowReason::WebFontLoaded); + if let Some(document) = self.documents.borrow().find_document(pipeline_id) { + self.rebuild_and_force_reflow(&document, ReflowReason::WebFontLoaded); } } /// Notify the containing document of a child frame that has completed loading. 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_id), - }; - if let Some(iframe) = document.find_iframe(frame_id) { - if iframe.pipeline_id() == Some(child_id) { - iframe.iframe_load_event_steps(child_id); - } + match self.documents.borrow().find_iframe(parent_id, frame_id) { + Some(iframe) => iframe.iframe_load_event_steps(child_id), + None => warn!("Message sent to closed pipeline {}.", parent_id), } } @@ -1604,11 +1600,7 @@ impl ScriptThread { } debug!("ScriptThread: loading {} on pipeline {:?}", incomplete.url, incomplete.pipeline_id); - let frame_element = incomplete.parent_info.and_then(|(parent_id, _)| { - // The root context may not exist yet, if the parent of this frame - // exists in a different script thread. - let root_context = self.browsing_context.get(); - + let frame_element = incomplete.parent_info.and_then(|(parent_id, _)| // In the case a parent id exists but the matching context // cannot be found, this means the context exists in a different // script thread (due to origin) so it shouldn't be returned. @@ -1616,13 +1608,8 @@ impl ScriptThread { // case, which is wrong. We should be returning an object that // denies access to most properties (per // https://github.com/servo/servo/issues/3939#issuecomment-62287025). - root_context.and_then(|root_context| { - root_context.find(parent_id).and_then(|context| { - let doc = context.active_document(); - doc.find_iframe(incomplete.frame_id) - }) - }) - }); + self.documents.borrow().find_iframe(parent_id, incomplete.frame_id) + ); let MainThreadScriptChan(ref sender) = self.chan; let DOMManipulationTaskSource(ref dom_sender) = self.dom_manipulation_task_source; @@ -1659,69 +1646,8 @@ impl ScriptThread { incomplete.window_size); let frame_element = frame_element.r().map(Castable::upcast); - enum ContextToRemove { - Root, - Child(PipelineId), - None, - } - struct AutoContextRemover<'a> { - context: ContextToRemove, - script_thread: &'a ScriptThread, - neutered: bool, - } - impl<'a> AutoContextRemover<'a> { - fn new(script_thread: &'a ScriptThread, context: ContextToRemove) -> AutoContextRemover<'a> { - AutoContextRemover { - context: context, - script_thread: script_thread, - neutered: false, - } - } - - fn neuter(&mut self) { - self.neutered = true; - } - } - - impl<'a> Drop for AutoContextRemover<'a> { - fn drop(&mut self) { - if !self.neutered { - match self.context { - ContextToRemove::Root => { - self.script_thread.browsing_context.set(None) - }, - ContextToRemove::Child(id) => { - self.script_thread.root_browsing_context().remove(id).unwrap(); - }, - ContextToRemove::None => {}, - } - } - } - } - - let (browsing_context, context_to_remove) = if !self.root_browsing_context_exists() { - // Create a new context tree entry. This will become the root context. - let new_context = BrowsingContext::new(&window, frame_element, incomplete.pipeline_id); - // We have a new root frame tree. - self.browsing_context.set(Some(&new_context)); - (new_context, ContextToRemove::Root) - } else if let Some((parent, _)) = incomplete.parent_info { - // Create a new context tree entry. This will be a child context. - let new_context = BrowsingContext::new(&window, frame_element, incomplete.pipeline_id); - - let root_context = self.root_browsing_context(); - // TODO(gw): This find will fail when we are sharing script threads - // between cross origin iframes in the same TLD. - let parent_context = root_context.find(parent) - .expect("received load for child context with missing parent"); - parent_context.push_child_context(&*new_context); - (new_context, ContextToRemove::Child(incomplete.pipeline_id)) - } else { - (self.root_browsing_context(), ContextToRemove::None) - }; - + let browsing_context = BrowsingContext::new(&window, frame_element); window.init_browsing_context(&browsing_context); - let mut context_remover = AutoContextRemover::new(self, context_to_remove); let last_modified = metadata.headers.as_ref().and_then(|headers| { headers.get().map(|&LastModified(HttpDate(ref tm))| dom_last_modified(tm)) @@ -1781,15 +1707,18 @@ impl ScriptThread { loader, referrer, referrer_policy); - browsing_context.set_active_document(&document); document.set_ready_state(DocumentReadyState::Loading); + self.documents.borrow_mut().insert(incomplete.pipeline_id, &*document); + + browsing_context.set_active_document(&document); + self.constellation_chan .send(ConstellationMsg::ActivateDocument(incomplete.pipeline_id)) .unwrap(); // Notify devtools that a new script global exists. - self.notify_devtools(document.Title(), final_url.clone(), (browsing_context.pipeline_id(), None)); + self.notify_devtools(document.Title(), final_url.clone(), (incomplete.pipeline_id, None)); let is_javascript = incomplete.url.scheme() == "javascript"; let parse_input = if is_javascript { @@ -1854,11 +1783,9 @@ impl ScriptThread { } if !incomplete.is_visible { - self.alter_resource_utilization(browsing_context.pipeline_id(), false); + self.alter_resource_utilization(incomplete.pipeline_id, false); } - context_remover.neuter(); - document.get_current_parser().unwrap() } @@ -1896,10 +1823,9 @@ impl ScriptThread { } /// Reflows non-incrementally, rebuilding the entire layout tree in the process. - fn rebuild_and_force_reflow(&self, context: &BrowsingContext, reason: ReflowReason) { - let document = context.active_document(); - document.dirty_all_nodes(); + fn rebuild_and_force_reflow(&self, document: &Document, reason: ReflowReason) { let window = window_from_node(&*document); + document.dirty_all_nodes(); window.reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery, reason); } @@ -1907,11 +1833,6 @@ impl ScriptThread { /// /// TODO: Actually perform DOM event dispatch. fn handle_event(&self, pipeline_id: PipelineId, event: CompositorEvent) { - // DOM events can only be handled if there's a root browsing context. - if !self.root_browsing_context_exists() { - return; - } - match event { ResizeEvent(new_size, size_type) => { self.handle_resize_event(pipeline_id, new_size, size_type); @@ -1922,8 +1843,8 @@ impl ScriptThread { } MouseMoveEvent(point) => { - let document = match self.root_browsing_context().find(pipeline_id) { - Some(browsing_context) => browsing_context.active_document(), + let document = match self.documents.borrow().find_document(pipeline_id) { + Some(document) => document, None => return warn!("Message sent to closed pipeline {}.", pipeline_id), }; @@ -1994,19 +1915,17 @@ impl ScriptThread { } TouchpadPressureEvent(point, pressure, phase) => { - let document = match self.root_browsing_context().find(pipeline_id) { - Some(browsing_context) => browsing_context.active_document(), - None => return warn!("Message sent to closed pipeline {}.", pipeline_id), - }; - document.handle_touchpad_pressure_event(self.js_runtime.rt(), point, pressure, phase); + match self.documents.borrow().find_document(pipeline_id) { + Some(doc) => doc.handle_touchpad_pressure_event(self.js_runtime.rt(), point, pressure, phase), + None => warn!("Message sent to closed pipeline {}.", pipeline_id), + } } KeyEvent(ch, key, state, modifiers) => { - let document = match self.root_browsing_context().find(pipeline_id) { - Some(browsing_context) => browsing_context.active_document(), - None => return warn!("Message sent to closed pipeline {}.", pipeline_id), - }; - document.dispatch_key_event(ch, key, state, modifiers, &self.constellation_chan); + match self.documents.borrow().find_document(pipeline_id) { + Some(document) => document.dispatch_key_event(ch, key, state, modifiers, &self.constellation_chan), + None => warn!("Message sent to closed pipeline {}.", pipeline_id), + } } } } @@ -2016,11 +1935,10 @@ impl ScriptThread { mouse_event_type: MouseEventType, button: MouseButton, point: Point2D) { - let document = match self.root_browsing_context().find(pipeline_id) { - Some(browsing_context) => browsing_context.active_document(), - None => return warn!("Message sent to closed pipeline {}.", pipeline_id), - }; - document.handle_mouse_event(self.js_runtime.rt(), button, point, mouse_event_type); + match self.documents.borrow().find_document(pipeline_id) { + Some(document) => document.handle_mouse_event(self.js_runtime.rt(), button, point, mouse_event_type), + None => warn!("Message sent to closed pipeline {}.", pipeline_id), + } } fn handle_touch_event(&self, @@ -2029,14 +1947,13 @@ impl ScriptThread { identifier: TouchId, point: Point2D) -> TouchEventResult { - let document = match self.root_browsing_context().find(pipeline_id) { - Some(browsing_context) => browsing_context.active_document(), + match self.documents.borrow().find_document(pipeline_id) { + Some(document) => document.handle_touch_event(self.js_runtime.rt(), event_type, identifier, point), None => { warn!("Message sent to closed pipeline {}.", pipeline_id); - return TouchEventResult::Processed(true) + TouchEventResult::Processed(true) }, - }; - document.handle_touch_event(self.js_runtime.rt(), event_type, identifier, point) + } } /// https://html.spec.whatwg.org/multipage/#navigating-across-documents @@ -2050,8 +1967,8 @@ impl ScriptThread { { let nurl = &load_data.url; if let Some(fragment) = nurl.fragment() { - let document = match self.root_browsing_context().find(parent_pipeline_id) { - Some(browsing_context) => browsing_context.active_document(), + let document = match self.documents.borrow().find_document(parent_pipeline_id) { + Some(document) => document, None => return warn!("Message sent to closed pipeline {}.", parent_pipeline_id), }; let url = document.url(); @@ -2065,12 +1982,7 @@ impl ScriptThread { match frame_id { Some(frame_id) => { - let root_context = self.root_browsing_context(); - let iframe = root_context.find(parent_pipeline_id).and_then(|context| { - let doc = context.active_document(); - doc.find_iframe(frame_id) - }); - if let Some(iframe) = iframe.r() { + if let Some(iframe) = self.documents.borrow().find_iframe(parent_pipeline_id, frame_id) { iframe.navigate_or_reload_child_browsing_context(Some(load_data), replace); } } @@ -2083,17 +1995,17 @@ impl ScriptThread { } fn handle_resize_event(&self, pipeline_id: PipelineId, new_size: WindowSizeData, size_type: WindowSizeType) { - let context = match self.root_browsing_context().find(pipeline_id) { - Some(browsing_context) => browsing_context, + let document = match self.documents.borrow().find_document(pipeline_id) { + Some(document) => document, None => return warn!("Message sent to closed pipeline {}.", pipeline_id), }; - let window = context.active_window(); + + let window = document.window(); window.set_window_size(new_size); window.force_reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery, ReflowReason::WindowResize); - let document = context.active_document(); let fragment_node = window.steal_fragment_name() .and_then(|name| document.find_fragment_node(&*name)); match fragment_node { @@ -2156,13 +2068,11 @@ impl ScriptThread { } fn handle_parsing_complete(&self, id: PipelineId) { - let parent_context = self.root_browsing_context(); - let context = match parent_context.find(id) { - Some(context) => context, + let document = match self.documents.borrow().find_document(id) { + Some(document) => document, None => return, }; - let document = context.active_document(); let final_url = document.url(); // https://html.spec.whatwg.org/multipage/#the-end step 1 @@ -2178,7 +2088,7 @@ impl ScriptThread { window.reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery, ReflowReason::FirstLoad); // No more reflow required - context.set_reflow_status(false); + window.browsing_context().set_reflow_status(false); // https://html.spec.whatwg.org/multipage/#the-end steps 3-4. document.process_deferred_scripts(); @@ -2193,30 +2103,23 @@ impl ScriptThread { None => return, }; - let parent_context = self.root_browsing_context(); - let context = match parent_context.find(pipeline_id) { - Some(context) => context, - None => return, - }; - - let window = context.active_window(); - if window.upcast::().live_devtools_updates() { - let css_error = CSSError { - filename: filename, - line: line, - column: column, - msg: msg - }; - let message = ScriptToDevtoolsControlMsg::ReportCSSError(pipeline_id, css_error); - sender.send(message).unwrap(); + if let Some(global) = self.documents.borrow().find_global(pipeline_id) { + if global.live_devtools_updates() { + let css_error = CSSError { + filename: filename, + line: line, + column: column, + msg: msg + }; + let message = ScriptToDevtoolsControlMsg::ReportCSSError(pipeline_id, css_error); + sender.send(message).unwrap(); + } } } fn handle_reload(&self, pipeline_id: PipelineId) { - if let Some(context) = self.find_child_context(pipeline_id) { - let win = context.active_window(); - let location = win.Location(); - location.Reload(); + if let Some(window) = self.documents.borrow().find_window(pipeline_id) { + window.Location().Reload(); } } @@ -2236,11 +2139,7 @@ impl ScriptThread { } fn do_flush_promise_jobs(&self) { - self.promise_job_queue.flush_promise_jobs(|id| { - self.find_child_context(id).map(|context| { - Root::upcast(context.active_window()) - }) - }); + self.promise_job_queue.flush_promise_jobs(|id| self.documents.borrow().find_global(id)) } } @@ -2259,35 +2158,27 @@ impl Drop for ScriptThread { } } -/// Shuts down layout for the given browsing context tree. -fn shut_down_layout(context_tree: &BrowsingContext) { - let mut channels = vec!(); - - for context in context_tree.iter() { - // Tell the layout thread to begin shutting down, and wait until it - // processed this message. - let (response_chan, response_port) = channel(); - let window = context.active_window(); - let chan = window.layout_chan().clone(); - if chan.send(message::Msg::PrepareToExit(response_chan)).is_ok() { - channels.push(chan); - let _ = response_port.recv(); - } +/// Shuts down layout for the given window. +fn shut_down_layout(window: &Window) { + // Tell the layout thread to begin shutting down, and wait until it + // processed this message. + let (response_chan, response_port) = channel(); + let chan = window.layout_chan().clone(); + if chan.send(message::Msg::PrepareToExit(response_chan)).is_ok() { + let _ = response_port.recv(); } + // The browsing context is cleared by window.clear_js_runtime(), so we need to save a copy + let browsing_context = window.browsing_context(); + // Drop our references to the JSContext and DOM objects. - for context in context_tree.iter() { - let window = context.active_window(); - window.clear_js_runtime(); + window.clear_js_runtime(); - // Sever the connection between the global and the DOM tree - context.unset_active_document(); - } + // Sever the connection between the global and the DOM tree + browsing_context.unset_active_document(); // Destroy the layout thread. If there were node leaks, layout will now crash safely. - for chan in channels { - chan.send(message::Msg::ExitNow).ok(); - } + chan.send(message::Msg::ExitNow).ok(); } fn dom_last_modified(tm: &Tm) -> String { diff --git a/components/script/webdriver_handlers.rs b/components/script/webdriver_handlers.rs index bcb0186d8e63..331f0fbbf8c4 100644 --- a/components/script/webdriver_handlers.rs +++ b/components/script/webdriver_handlers.rs @@ -10,20 +10,18 @@ use dom::bindings::codegen::Bindings::HTMLElementBinding::HTMLElementMethods; use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods; use dom::bindings::codegen::Bindings::HTMLOptionElementBinding::HTMLOptionElementMethods; use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; -use dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods; use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use dom::bindings::conversions::{ConversionResult, FromJSValConvertible, StringificationBehavior}; use dom::bindings::inheritance::Castable; use dom::bindings::js::Root; use dom::bindings::str::DOMString; -use dom::browsingcontext::BrowsingContext; use dom::element::Element; use dom::globalscope::GlobalScope; use dom::htmlelement::HTMLElement; use dom::htmliframeelement::HTMLIFrameElement; use dom::htmlinputelement::HTMLInputElement; use dom::htmloptionelement::HTMLOptionElement; -use dom::node::Node; +use dom::node::{Node, window_from_node}; use euclid::point::Point2D; use euclid::rect::Rect; use euclid::size::Size2D; @@ -35,21 +33,18 @@ use msg::constellation_msg::PipelineId; use net_traits::CookieSource::{HTTP, NonHTTP}; use net_traits::CoreResourceMsg::{GetCookiesDataForUrl, SetCookiesForUrlWithData}; use net_traits::IpcSend; +use script_thread::Documents; use script_traits::webdriver_msg::{WebDriverFrameId, WebDriverJSError, WebDriverJSResult, WebDriverJSValue}; use script_traits::webdriver_msg::WebDriverCookieError; use url::Url; -fn find_node_by_unique_id(context: &BrowsingContext, +fn find_node_by_unique_id(documents: &Documents, pipeline: PipelineId, node_id: String) -> Option> { - let context = match context.find(pipeline) { - Some(context) => context, - None => return None - }; - - let document = context.active_document(); - document.upcast::().traverse_preorder().find(|candidate| candidate.unique_id() == node_id) + documents.find_document(pipeline).and_then(|document| + document.upcast::().traverse_preorder().find(|candidate| candidate.unique_id() == node_id) + ) } #[allow(unsafe_code)] @@ -79,16 +74,15 @@ pub unsafe fn jsval_to_webdriver(cx: *mut JSContext, val: HandleValue) -> WebDri } #[allow(unsafe_code)] -pub fn handle_execute_script(context: &BrowsingContext, +pub fn handle_execute_script(documents: &Documents, pipeline: PipelineId, eval: String, reply: IpcSender) { - let context = match context.find(pipeline) { - Some(context) => context, + let window = match documents.find_window(pipeline) { + Some(window) => window, None => return reply.send(Err(WebDriverJSError::BrowsingContextNotFound)).unwrap() }; - let window = context.active_window(); let result = unsafe { let cx = window.get_cx(); rooted!(in(cx) let mut rval = UndefinedValue()); @@ -96,27 +90,26 @@ pub fn handle_execute_script(context: &BrowsingContext, &eval, rval.handle_mut()); jsval_to_webdriver(cx, rval.handle()) }; + reply.send(result).unwrap(); } -pub fn handle_execute_async_script(context: &BrowsingContext, +pub fn handle_execute_async_script(documents: &Documents, pipeline: PipelineId, eval: String, reply: IpcSender) { - let context = match context.find(pipeline) { - Some(context) => context, + let window = match documents.find_window(pipeline) { + Some(window) => window, None => return reply.send(Err(WebDriverJSError::BrowsingContextNotFound)).unwrap() - }; + }; - let window = context.active_window(); let cx = window.get_cx(); window.set_webdriver_script_chan(Some(reply)); rooted!(in(cx) let mut rval = UndefinedValue()); - window.upcast::().evaluate_js_on_global_with_result( - &eval, rval.handle_mut()); + window.upcast::().evaluate_js_on_global_with_result(&eval, rval.handle_mut()); } -pub fn handle_get_frame_id(context: &BrowsingContext, +pub fn handle_get_frame_id(documents: &Documents, pipeline: PipelineId, webdriver_frame_id: WebDriverFrameId, reply: IpcSender, ()>>) { @@ -126,7 +119,7 @@ pub fn handle_get_frame_id(context: &BrowsingContext, Ok(None) }, WebDriverFrameId::Element(x) => { - match find_node_by_unique_id(context, pipeline, x) { + match find_node_by_unique_id(documents, pipeline, x) { Some(ref node) => { match node.downcast::() { Some(ref elem) => Ok(elem.get_content_window()), @@ -137,8 +130,7 @@ pub fn handle_get_frame_id(context: &BrowsingContext, } }, WebDriverFrameId::Parent => { - let window = context.active_window(); - Ok(window.parent()) + documents.find_window(pipeline).map(|window| window.parent()).ok_or(()) } }; @@ -146,41 +138,31 @@ pub fn handle_get_frame_id(context: &BrowsingContext, reply.send(frame_id).unwrap() } -pub fn handle_find_element_css(context: &BrowsingContext, _pipeline: PipelineId, selector: String, +pub fn handle_find_element_css(documents: &Documents, pipeline: PipelineId, selector: String, reply: IpcSender, ()>>) { - reply.send(match context.active_document().QuerySelector(DOMString::from(selector)) { - Ok(node) => { - Ok(node.map(|x| x.upcast::().unique_id())) - } - Err(_) => Err(()) - }).unwrap(); + let node_id = documents.find_document(pipeline) + .ok_or(()) + .and_then(|doc| doc.QuerySelector(DOMString::from(selector)).map_err(|_| ())) + .map(|node| node.map(|x| x.upcast::().unique_id())); + reply.send(node_id).unwrap(); } -pub fn handle_find_elements_css(context: &BrowsingContext, - _pipeline: PipelineId, +pub fn handle_find_elements_css(documents: &Documents, + pipeline: PipelineId, selector: String, reply: IpcSender, ()>>) { - reply.send(match context.active_document().QuerySelectorAll(DOMString::from(selector)) { - Ok(ref nodes) => { - let mut result = Vec::with_capacity(nodes.Length() as usize); - for i in 0..nodes.Length() { - if let Some(ref node) = nodes.Item(i) { - result.push(node.unique_id()); - } - } - Ok(result) - }, - Err(_) => { - Err(()) - } - }).unwrap(); + let node_ids = documents.find_document(pipeline) + .ok_or(()) + .and_then(|doc| doc.QuerySelectorAll(DOMString::from(selector)).map_err(|_| ())) + .map(|nodes| nodes.iter().map(|x| x.upcast::().unique_id()).collect()); + reply.send(node_ids).unwrap(); } -pub fn handle_focus_element(context: &BrowsingContext, +pub fn handle_focus_element(documents: &Documents, pipeline: PipelineId, element_id: String, reply: IpcSender>) { - reply.send(match find_node_by_unique_id(context, pipeline, element_id) { + reply.send(match find_node_by_unique_id(documents, pipeline, element_id) { Some(ref node) => { match node.downcast::() { Some(ref elem) => { @@ -195,47 +177,62 @@ pub fn handle_focus_element(context: &BrowsingContext, }).unwrap(); } -pub fn handle_get_active_element(context: &BrowsingContext, - _pipeline: PipelineId, +pub fn handle_get_active_element(documents: &Documents, + pipeline: PipelineId, reply: IpcSender>) { - reply.send(context.active_document().GetActiveElement().map( - |elem| elem.upcast::().unique_id())).unwrap(); + reply.send(documents.find_document(pipeline) + .and_then(|doc| doc.GetActiveElement()) + .map(|elem| elem.upcast::().unique_id())).unwrap(); } -pub fn handle_get_cookies(context: &BrowsingContext, - _pipeline: PipelineId, - reply: IpcSender>>) { - let document = context.active_document(); - let url = document.url(); - let (sender, receiver) = ipc::channel().unwrap(); - let _ = document.window().upcast::().resource_threads().send( - GetCookiesDataForUrl(url.clone(), sender, NonHTTP) - ); - let cookies = receiver.recv().unwrap(); +pub fn handle_get_cookies(documents: &Documents, + pipeline: PipelineId, + reply: IpcSender>>) { + // TODO: Return an error if the pipeline doesn't exist? + let cookies: Vec> = match documents.find_document(pipeline) { + None => Vec::new(), + Some(document) => { + let url = document.url(); + let (sender, receiver) = ipc::channel().unwrap(); + let _ = document.window().upcast::().resource_threads().send( + GetCookiesDataForUrl(url.clone(), sender, NonHTTP) + ); + receiver.recv().unwrap() + }, + }; reply.send(cookies).unwrap(); } // https://w3c.github.io/webdriver/webdriver-spec.html#get-cookie -pub fn handle_get_cookie(context: &BrowsingContext, - _pipeline: PipelineId, +pub fn handle_get_cookie(documents: &Documents, + pipeline: PipelineId, name: String, reply: IpcSender>>) { - let document = context.active_document(); - let url = document.url(); - let (sender, receiver) = ipc::channel().unwrap(); - let _ = document.window().upcast::().resource_threads().send( - GetCookiesDataForUrl(url.clone(), sender, NonHTTP) - ); - let cookies = receiver.recv().unwrap(); + // TODO: Return an error if the pipeline doesn't exist? + let cookies: Vec> = match documents.find_document(pipeline) { + None => Vec::new(), + Some(document) => { + let url = document.url(); + let (sender, receiver) = ipc::channel().unwrap(); + let _ = document.window().upcast::().resource_threads().send( + GetCookiesDataForUrl(url.clone(), sender, NonHTTP) + ); + receiver.recv().unwrap() + }, + }; reply.send(cookies.into_iter().filter(|c| c.name == &*name).collect()).unwrap(); } // https://w3c.github.io/webdriver/webdriver-spec.html#add-cookie -pub fn handle_add_cookie(context: &BrowsingContext, - _pipeline: PipelineId, +pub fn handle_add_cookie(documents: &Documents, + pipeline: PipelineId, cookie: Cookie, reply: IpcSender>) { - let document = context.active_document(); + // TODO: Return a different error if the pipeline doesn't exist? + let document = match documents.find_document(pipeline) { + Some(document) => document, + None => return reply.send(Err(WebDriverCookieError::UnableToSetCookie)).unwrap(), + }; let url = document.url(); let method = if cookie.httponly { HTTP @@ -262,15 +259,19 @@ pub fn handle_add_cookie(context: &BrowsingContext, }).unwrap(); } -pub fn handle_get_title(context: &BrowsingContext, _pipeline: PipelineId, reply: IpcSender) { - reply.send(String::from(context.active_document().Title())).unwrap(); +pub fn handle_get_title(documents: &Documents, pipeline: PipelineId, reply: IpcSender) { + // TODO: Return an error if the pipeline doesn't exist. + let title = documents.find_document(pipeline) + .map(|doc| String::from(doc.Title())) + .unwrap_or_default(); + reply.send(title).unwrap(); } -pub fn handle_get_rect(context: &BrowsingContext, +pub fn handle_get_rect(documents: &Documents, pipeline: PipelineId, element_id: String, reply: IpcSender, ()>>) { - reply.send(match find_node_by_unique_id(context, pipeline, element_id) { + reply.send(match find_node_by_unique_id(documents, pipeline, element_id) { Some(elem) => { // https://w3c.github.io/webdriver/webdriver-spec.html#dfn-calculate-the-absolute-position match elem.downcast::() { @@ -304,11 +305,11 @@ pub fn handle_get_rect(context: &BrowsingContext, }).unwrap(); } -pub fn handle_get_text(context: &BrowsingContext, +pub fn handle_get_text(documents: &Documents, pipeline: PipelineId, node_id: String, reply: IpcSender>) { - reply.send(match find_node_by_unique_id(context, pipeline, node_id) { + reply.send(match find_node_by_unique_id(documents, pipeline, node_id) { Some(ref node) => { Ok(node.GetTextContent().map_or("".to_owned(), String::from)) }, @@ -316,11 +317,11 @@ pub fn handle_get_text(context: &BrowsingContext, }).unwrap(); } -pub fn handle_get_name(context: &BrowsingContext, +pub fn handle_get_name(documents: &Documents, pipeline: PipelineId, node_id: String, reply: IpcSender>) { - reply.send(match find_node_by_unique_id(context, pipeline, node_id) { + reply.send(match find_node_by_unique_id(documents, pipeline, node_id) { Some(node) => { Ok(String::from(node.downcast::().unwrap().TagName())) }, @@ -328,12 +329,12 @@ pub fn handle_get_name(context: &BrowsingContext, }).unwrap(); } -pub fn handle_get_attribute(context: &BrowsingContext, +pub fn handle_get_attribute(documents: &Documents, pipeline: PipelineId, node_id: String, name: String, reply: IpcSender, ()>>) { - reply.send(match find_node_by_unique_id(context, pipeline, node_id) { + reply.send(match find_node_by_unique_id(documents, pipeline, node_id) { Some(node) => { Ok(node.downcast::().unwrap().GetAttribute(DOMString::from(name)) .map(String::from)) @@ -342,14 +343,14 @@ pub fn handle_get_attribute(context: &BrowsingContext, }).unwrap(); } -pub fn handle_get_css(context: &BrowsingContext, +pub fn handle_get_css(documents: &Documents, pipeline: PipelineId, node_id: String, name: String, reply: IpcSender>) { - reply.send(match find_node_by_unique_id(context, pipeline, node_id) { + reply.send(match find_node_by_unique_id(documents, pipeline, node_id) { Some(node) => { - let window = context.active_window(); + let window = window_from_node(&*node); let elem = node.downcast::().unwrap(); Ok(String::from( window.GetComputedStyle(&elem, None).GetPropertyValue(DOMString::from(name)))) @@ -358,19 +359,21 @@ pub fn handle_get_css(context: &BrowsingContext, }).unwrap(); } -pub fn handle_get_url(context: &BrowsingContext, - _pipeline: PipelineId, +pub fn handle_get_url(documents: &Documents, + pipeline: PipelineId, reply: IpcSender) { - let document = context.active_document(); - let url = document.url(); - reply.send((*url).clone()).unwrap(); + // TODO: Return an error if the pipeline doesn't exist. + let url = documents.find_document(pipeline) + .map(|document| document.url().clone()) + .unwrap_or_else(|| Url::parse("about:blank").expect("infallible")); + reply.send(url).unwrap(); } -pub fn handle_is_enabled(context: &BrowsingContext, +pub fn handle_is_enabled(documents: &Documents, pipeline: PipelineId, element_id: String, reply: IpcSender>) { - reply.send(match find_node_by_unique_id(&context, pipeline, element_id) { + reply.send(match find_node_by_unique_id(&documents, pipeline, element_id) { Some(ref node) => { match node.downcast::() { Some(elem) => Ok(elem.enabled_state()), @@ -381,11 +384,11 @@ pub fn handle_is_enabled(context: &BrowsingContext, }).unwrap(); } -pub fn handle_is_selected(context: &BrowsingContext, +pub fn handle_is_selected(documents: &Documents, pipeline: PipelineId, element_id: String, reply: IpcSender>) { - reply.send(match find_node_by_unique_id(context, pipeline, element_id) { + reply.send(match find_node_by_unique_id(documents, pipeline, element_id) { Some(ref node) => { if let Some(input_element) = node.downcast::() { Ok(input_element.Checked())