aboutsummaryrefslogtreecommitdiffstats
path: root/components/script
diff options
context:
space:
mode:
Diffstat (limited to 'components/script')
-rw-r--r--components/script/devtools.rs108
-rw-r--r--components/script/dom/browsingcontext.rs83
-rw-r--r--components/script/dom/htmliframeelement.rs18
-rw-r--r--components/script/dom/nodelist.rs22
-rw-r--r--components/script/dom/storage.rs19
-rw-r--r--components/script/script_thread.rs705
-rw-r--r--components/script/webdriver_handlers.rs201
7 files changed, 489 insertions, 667 deletions
diff --git a/components/script/devtools.rs b/components/script/devtools.rs
index 4163ee426ed..9e6a0f1e528 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<E
reply.send(result).unwrap();
}
-pub fn handle_get_root_node(context: &BrowsingContext, pipeline: PipelineId, reply: IpcSender<Option<NodeInfo>>) {
- 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::<Node>();
- reply.send(Some(node.summarize())).unwrap();
+pub fn handle_get_root_node(documents: &Documents, pipeline: PipelineId, reply: IpcSender<Option<NodeInfo>>) {
+ let info = documents.find_document(pipeline)
+ .map(|document| document.upcast::<Node>().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<Option<NodeInfo>>) {
- 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::<Node>();
- reply.send(Some(node.summarize())).unwrap();
+ let info = documents.find_document(pipeline)
+ .and_then(|document| document.GetDocumentElement())
+ .map(|element| element.upcast::<Node>().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<Root<Node>> {
- let context = match context.find(pipeline) {
- Some(found_context) => found_context,
- None => return None
- };
-
- let document = context.active_document();
- let node = document.upcast::<Node>();
-
- node.traverse_preorder().find(|candidate| candidate.unique_id() == node_id)
+ documents.find_document(pipeline).and_then(|document|
+ document.upcast::<Node>().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<Option<Vec<NodeInfo>>>) {
- 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<Option<ComputedNodeLayout>>) {
- 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::<Element>().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<Modification>) {
- 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<TimelineMarkerType>,
reply: IpcSender<Option<TimelineMarker>>) {
- 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<TimelineMarkerType>) {
- 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::<GlobalScope>().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::<GlobalScope>().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 2ea6a1b78bd..1409c9c5dcf 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<bool>,
- /// Stores the child browsing contexts (ex. iframe browsing context)
- children: DOMRefCell<Vec<JS<BrowsingContext>>>,
-
/// 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<BrowsingContext> {
+ pub fn new(window: &Window, frame_element: Option<&Element>) -> Root<BrowsingContext> {
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<Root<BrowsingContext>> {
- 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<Root<Window>> {
- self.children.borrow().iter().find(|context| {
- let window = context.active_window();
- window.upcast::<GlobalScope>().pipeline_id() == pipeline_id
- }).map(|context| context.active_window())
+ pub fn active_pipeline_id(&self) -> Option<PipelineId> {
+ self.active_document.get()
+ .map(|doc| doc.window().upcast::<GlobalScope>().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<Root<BrowsingContext>> {
- 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<Root<BrowsingContext>>,
-}
-
-impl Iterator for ContextIterator {
- type Item = Root<BrowsingContext>;
-
- fn next(&mut self) -> Option<Root<BrowsingContext>> {
- 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 2b56b9d697c..0430a0f7d26 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<Root<Window>> {
- 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 3810f16b22c..391d21d5865 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<Node>;
+
+ fn next(&mut self) -> Option<Root<Node>> {
+ 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 3441f57a7e7..099c6c04fea 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::<GlobalScope>().pipeline_id() {
- storage_event.upcast::<Event>().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::<Event>().fire(document.window().upcast());
+ }
}
}
}
diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs
index 86d742680ee..b2d9e5307df 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<CommonScriptMsg> for Sender<MainThreadScriptMsg> {
}
}
-/// 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<PipelineId, JS<Document>>,
+}
+
+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<Root<Document>> {
+ 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<Root<Document>> {
+ self.map.get(&pipeline_id).map(|doc| Root::from_ref(&**doc))
+ }
+
+ pub fn find_window(&self, pipeline_id: PipelineId) -> Option<Root<Window>> {
+ self.find_document(pipeline_id).map(|doc| Root::from_ref(doc.window()))
+ }
+
+ pub fn find_global(&self, pipeline_id: PipelineId) -> Option<Root<GlobalScope>> {
+ self.find_window(pipeline_id).map(|window| Root::from_ref(window.upcast()))
+ }
+
+ pub fn find_iframe(&self, pipeline_id: PipelineId, frame_id: FrameId) -> Option<Root<HTMLIFrameElement>> {
+ 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<Document>>,
+}
+
+impl<'a> Iterator for DocumentsIter<'a> {
+ type Item = (PipelineId, Root<Document>);
+
+ fn next(&mut self) -> Option<(PipelineId, Root<Document>)> {
+ 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<JS<BrowsingContext>>,
+ /// The documents for pipelines managed by this thread
+ documents: DOMRefCell<Documents>,
/// A list of data pertaining to loads that have not yet received a network response
incomplete_loads: DOMRefCell<Vec<InProgressLoad>>,
/// 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<Root<Document>> {
+ 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<Documents> {
+ self.documents.borrow()
+ }
+
/// Creates a new script thread.
pub fn new(state: InitialScriptState,
port: Receiver<MainThreadScriptMsg>,
@@ -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<BrowsingContext> {
- 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<Root<BrowsingContext>> {
- 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::<GlobalScope>().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<f32>) {
- 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<f32>)]) {
- 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::<Node>().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::<Node>().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::<GlobalScope>().speed_up_timers();
- } else {
- window.upcast::<GlobalScope>().slow_down_timers();
- }
- return true;
+ if let Some(window) = self.documents.borrow().find_window(id) {
+ if visible {
+ window.upcast::<GlobalScope>().speed_up_timers();
+ } else {
+ window.upcast::<GlobalScope>().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::<GlobalScope>().suspend();
- return;
- }
+ if let Some(window) = self.documents.borrow().find_window(id) {
+ window.upcast::<GlobalScope>().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::<Node>().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<FrameId>,
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::<GlobalScope>().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<f32>) {
- 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<f32>)
-> 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::<GlobalScope>().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 bcb0186d8e6..331f0fbbf8c 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<Root<Node>> {
- let context = match context.find(pipeline) {
- Some(context) => context,
- None => return None
- };
-
- let document = context.active_document();
- document.upcast::<Node>().traverse_preorder().find(|candidate| candidate.unique_id() == node_id)
+ documents.find_document(pipeline).and_then(|document|
+ document.upcast::<Node>().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<WebDriverJSResult>) {
- 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<WebDriverJSResult>) {
- 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::<GlobalScope>().evaluate_js_on_global_with_result(
- &eval, rval.handle_mut());
+ window.upcast::<GlobalScope>().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<Result<Option<PipelineId>, ()>>) {
@@ -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::<HTMLIFrameElement>() {
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<Result<Option<String>, ()>>) {
- reply.send(match context.active_document().QuerySelector(DOMString::from(selector)) {
- Ok(node) => {
- Ok(node.map(|x| x.upcast::<Node>().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::<Node>().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<Result<Vec<String>, ()>>) {
- 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::<Node>().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<Result<(), ()>>) {
- 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::<HTMLElement>() {
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<Option<String>>) {
- reply.send(context.active_document().GetActiveElement().map(
- |elem| elem.upcast::<Node>().unique_id())).unwrap();
+ reply.send(documents.find_document(pipeline)
+ .and_then(|doc| doc.GetActiveElement())
+ .map(|elem| elem.upcast::<Node>().unique_id())).unwrap();
}
-pub fn handle_get_cookies(context: &BrowsingContext,
- _pipeline: PipelineId,
- reply: IpcSender<Vec<Serde<Cookie>>>) {
- let document = context.active_document();
- let url = document.url();
- let (sender, receiver) = ipc::channel().unwrap();
- let _ = document.window().upcast::<GlobalScope>().resource_threads().send(
- GetCookiesDataForUrl(url.clone(), sender, NonHTTP)
- );
- let cookies = receiver.recv().unwrap();
+pub fn handle_get_cookies(documents: &Documents,
+ pipeline: PipelineId,
+ reply: IpcSender<Vec<Serde<Cookie>>>) {
+ // TODO: Return an error if the pipeline doesn't exist?
+ let cookies: Vec<Serde<Cookie>> = match documents.find_document(pipeline) {
+ None => Vec::new(),
+ Some(document) => {
+ let url = document.url();
+ let (sender, receiver) = ipc::channel().unwrap();
+ let _ = document.window().upcast::<GlobalScope>().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<Vec<Serde<Cookie>>>) {
- let document = context.active_document();
- let url = document.url();
- let (sender, receiver) = ipc::channel().unwrap();
- let _ = document.window().upcast::<GlobalScope>().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<Serde<Cookie>> = match documents.find_document(pipeline) {
+ None => Vec::new(),
+ Some(document) => {
+ let url = document.url();
+ let (sender, receiver) = ipc::channel().unwrap();
+ let _ = document.window().upcast::<GlobalScope>().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<Result<(), WebDriverCookieError>>) {
- 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<String>) {
- reply.send(String::from(context.active_document().Title())).unwrap();
+pub fn handle_get_title(documents: &Documents, pipeline: PipelineId, reply: IpcSender<String>) {
+ // 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<Result<Rect<f64>, ()>>) {
- 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::<HTMLElement>() {
@@ -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<Result<String, ()>>) {
- 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<Result<String, ()>>) {
- 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::<Element>().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<Result<Option<String>, ()>>) {
- 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::<Element>().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<Result<String, ()>>) {
- 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::<Element>().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<Url>) {
- 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<Result<bool, ()>>) {
- 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::<Element>() {
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<Result<bool, ()>>) {
- 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::<HTMLInputElement>() {
Ok(input_element.Checked())