diff options
author | Connor Brewster <brewsterc@my.caspercollege.edu> | 2016-05-05 22:45:08 -0600 |
---|---|---|
committer | Connor Brewster <brewsterc@my.caspercollege.edu> | 2016-05-11 12:46:59 -0600 |
commit | cbc5ca65a8fed0542a74b8917b5d8d6450714478 (patch) | |
tree | 6e72f1dddbe6a1a69d92720e2e13615818f52fd6 | |
parent | 392135bd0c2f512a0d632a7d76e667bc9af8f4a7 (diff) | |
download | servo-cbc5ca65a8fed0542a74b8917b5d8d6450714478.tar.gz servo-cbc5ca65a8fed0542a74b8917b5d8d6450714478.zip |
remove page and move functionality to browing context
Allow for adding history items
Fixed nested iframe test failure
Cleanup and small refactors
fixup
-rw-r--r-- | components/script/devtools.rs | 59 | ||||
-rw-r--r-- | components/script/dom/browsingcontext.rs | 140 | ||||
-rw-r--r-- | components/script/dom/htmliframeelement.rs | 8 | ||||
-rw-r--r-- | components/script/dom/storage.rs | 8 | ||||
-rw-r--r-- | components/script/dom/window.rs | 8 | ||||
-rw-r--r-- | components/script/lib.rs | 1 | ||||
-rw-r--r-- | components/script/page.rs | 134 | ||||
-rw-r--r-- | components/script/script_thread.rs | 492 | ||||
-rw-r--r-- | components/script/webdriver_handlers.rs | 90 |
9 files changed, 461 insertions, 479 deletions
diff --git a/components/script/devtools.rs b/components/script/devtools.rs index 70bda654b80..08cae43283e 100644 --- a/components/script/devtools.rs +++ b/components/script/devtools.rs @@ -15,6 +15,7 @@ use dom::bindings::conversions::{FromJSValConvertible, jsstring_to_str}; use dom::bindings::global::GlobalRef; use dom::bindings::inheritance::Castable; use dom::bindings::js::Root; +use dom::browsingcontext::{BrowsingContext, IterableContext}; use dom::element::Element; use dom::node::Node; use dom::window::Window; @@ -22,10 +23,8 @@ use ipc_channel::ipc::IpcSender; use js::jsapi::{ObjectClassName, RootedObject, RootedValue}; use js::jsval::UndefinedValue; use msg::constellation_msg::PipelineId; -use page::{IterablePage, Page}; -use script_thread::get_page; +use script_thread::get_browsing_context; use std::ffi::CStr; -use std::rc::Rc; use std::str; use style::properties::longhands::{margin_top, margin_right, margin_bottom, margin_left}; use util::str::DOMString; @@ -66,28 +65,31 @@ pub fn handle_evaluate_js(global: &GlobalRef, eval: String, reply: IpcSender<Eva reply.send(result).unwrap(); } -pub fn handle_get_root_node(page: &Rc<Page>, pipeline: PipelineId, reply: IpcSender<NodeInfo>) { - let page = get_page(&*page, pipeline); - let document = page.document(); +pub fn handle_get_root_node(context: &Root<BrowsingContext>, pipeline: PipelineId, reply: IpcSender<NodeInfo>) { + let context = get_browsing_context(context, pipeline); + let document = context.active_document(); let node = document.upcast::<Node>(); reply.send(node.summarize()).unwrap(); } -pub fn handle_get_document_element(page: &Rc<Page>, +pub fn handle_get_document_element(context: &Root<BrowsingContext>, pipeline: PipelineId, reply: IpcSender<NodeInfo>) { - let page = get_page(&*page, pipeline); - let document = page.document(); + let context = get_browsing_context(context, pipeline); + let document = context.active_document(); let document_element = document.GetDocumentElement().unwrap(); let node = document_element.upcast::<Node>(); reply.send(node.summarize()).unwrap(); } -fn find_node_by_unique_id(page: &Rc<Page>, pipeline: PipelineId, node_id: String) -> Root<Node> { - let page = get_page(&*page, pipeline); - let document = page.document(); +fn find_node_by_unique_id(context: &Root<BrowsingContext>, + pipeline: PipelineId, + node_id: String) + -> Root<Node> { + let context = get_browsing_context(context, pipeline); + let document = context.active_document(); let node = document.upcast::<Node>(); for candidate in node.traverse_preorder() { @@ -99,29 +101,29 @@ fn find_node_by_unique_id(page: &Rc<Page>, pipeline: PipelineId, node_id: String panic!("couldn't find node with unique id {}", node_id) } -pub fn handle_get_children(page: &Rc<Page>, +pub fn handle_get_children(context: &Root<BrowsingContext>, pipeline: PipelineId, node_id: String, reply: IpcSender<Vec<NodeInfo>>) { - let parent = find_node_by_unique_id(&*page, pipeline, node_id); + let parent = find_node_by_unique_id(context, pipeline, node_id); let children = parent.children() .map(|child| child.summarize()) .collect(); reply.send(children).unwrap(); } -pub fn handle_get_layout(page: &Rc<Page>, +pub fn handle_get_layout(context: &Root<BrowsingContext>, pipeline: PipelineId, node_id: String, reply: IpcSender<ComputedNodeLayout>) { - let node = find_node_by_unique_id(&*page, pipeline, node_id); + let node = find_node_by_unique_id(context, pipeline, node_id); let elem = node.downcast::<Element>().expect("should be getting layout of element"); let rect = elem.GetBoundingClientRect(); let width = rect.Width() as f32; let height = rect.Height() as f32; - let window = page.window(); + let window = context.active_window(); let elem = node.downcast::<Element>().expect("should be getting layout of element"); let computed_style = window.r().GetComputedStyle(elem, None); @@ -200,11 +202,11 @@ pub fn handle_get_cached_messages(_pipeline_id: PipelineId, reply.send(messages).unwrap(); } -pub fn handle_modify_attribute(page: &Rc<Page>, +pub fn handle_modify_attribute(context: &Root<BrowsingContext>, pipeline: PipelineId, node_id: String, modifications: Vec<Modification>) { - let node = find_node_by_unique_id(&*page, pipeline, node_id); + let node = find_node_by_unique_id(context, pipeline, node_id); let elem = node.downcast::<Element>().expect("should be getting layout of element"); for modification in modifications { @@ -222,22 +224,25 @@ pub fn handle_wants_live_notifications(global: &GlobalRef, send_notifications: b global.set_devtools_wants_updates(send_notifications); } -pub fn handle_set_timeline_markers(page: &Rc<Page>, +pub fn handle_set_timeline_markers(context: &Root<BrowsingContext>, marker_types: Vec<TimelineMarkerType>, reply: IpcSender<TimelineMarker>) { - let window = page.window(); + let window = context.active_window(); window.set_devtools_timeline_markers(marker_types, reply); } -pub fn handle_drop_timeline_markers(page: &Rc<Page>, marker_types: Vec<TimelineMarkerType>) { - let window = page.window(); +pub fn handle_drop_timeline_markers(context: &Root<BrowsingContext>, + marker_types: Vec<TimelineMarkerType>) { + let window = context.active_window(); window.drop_devtools_timeline_markers(marker_types); } -pub fn handle_request_animation_frame(page: &Rc<Page>, id: PipelineId, actor_name: String) { - let page = page.find(id).expect("There is no such page"); - let doc = page.document(); - let devtools_sender = page.window().devtools_chan().unwrap(); +pub fn handle_request_animation_frame(context: &Root<BrowsingContext>, + id: PipelineId, + actor_name: String) { + let context = context.find(id).expect("There is no such context"); + let doc = context.active_document(); + let devtools_sender = context.active_window().devtools_chan().unwrap(); doc.request_animation_frame(box move |time| { let msg = ScriptToDevtoolsControlMsg::FramerateTick(actor_name, time); devtools_sender.send(msg).unwrap(); diff --git a/components/script/dom/browsingcontext.rs b/components/script/dom/browsingcontext.rs index f4f15cdf7c7..54b4c87e4e7 100644 --- a/components/script/dom/browsingcontext.rs +++ b/components/script/dom/browsingcontext.rs @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use dom::bindings::cell::DOMRefCell; +use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use dom::bindings::conversions::{ToJSValConvertible, root_from_handleobject}; use dom::bindings::js::{JS, Root, RootedReference}; use dom::bindings::proxyhandler::{fill_property_descriptor, get_property_descriptor}; @@ -22,27 +23,48 @@ use js::jsapi::{JS_ForwardGetPropertyTo, JS_ForwardSetPropertyTo, JS_GetClass, J use js::jsapi::{JS_GetOwnPropertyDescriptorById, JS_HasPropertyById, MutableHandle}; use js::jsapi::{MutableHandleValue, ObjectOpResult, RootedObject, RootedValue}; use js::jsval::{UndefinedValue, PrivateValue}; +use msg::constellation_msg::{PipelineId, SubpageId}; +use std::cell::Cell; +use url::Url; +use util::str::DOMString; #[dom_struct] 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 this context's session history history: DOMRefCell<Vec<SessionHistoryEntry>>, - active_index: usize, + + /// The index of the active session history entry + active_index: Cell<usize>, + + /// Stores the child browsing contexts (ex. iframe browsing context) + children: DOMRefCell<Vec<JS<BrowsingContext>>>, + frame_element: Option<JS<Element>>, } impl BrowsingContext { - pub fn new_inherited(frame_element: Option<&Element>) -> BrowsingContext { + pub fn new_inherited(frame_element: Option<&Element>, id: PipelineId) -> BrowsingContext { BrowsingContext { reflector: Reflector::new(), + id: id, + needs_reflow: Cell::new(true), history: DOMRefCell::new(vec![]), - active_index: 0, + active_index: Cell::new(0), + children: DOMRefCell::new(vec![]), frame_element: frame_element.map(JS::from_ref), } } #[allow(unsafe_code)] - pub fn new(window: &Window, frame_element: Option<&Element>) -> Root<BrowsingContext> { + pub fn new(window: &Window, frame_element: Option<&Element>, id: PipelineId) -> Root<BrowsingContext> { unsafe { let WindowProxyHandler(handler) = window.windowproxy_handler(); assert!(!handler.is_null()); @@ -57,7 +79,7 @@ impl BrowsingContext { NewWindowProxy(cx, parent, handler)); assert!(!window_proxy.ptr.is_null()); - let object = box BrowsingContext::new_inherited(frame_element); + let object = box BrowsingContext::new_inherited(frame_element, id); let raw = Box::into_raw(object); SetProxyExtra(window_proxy.ptr, 0, &PrivateValue(raw as *const _)); @@ -70,12 +92,20 @@ impl BrowsingContext { pub fn init(&self, document: &Document) { assert!(self.history.borrow().is_empty()); - assert_eq!(self.active_index, 0); - self.history.borrow_mut().push(SessionHistoryEntry::new(document)); + assert_eq!(self.active_index.get(), 0); + self.history.borrow_mut().push(SessionHistoryEntry::new(document, document.url().clone(), document.Title())); + } + + pub fn push_history(&self, document: &Document) { + let mut history = self.history.borrow_mut(); + // Clear all session history entries after the active index + history.drain((self.active_index.get() + 1)..); + history.push(SessionHistoryEntry::new(document, document.url().clone(), document.Title())); + self.active_index.set(self.active_index.get() + 1); } pub fn active_document(&self) -> Root<Document> { - Root::from_ref(&*self.history.borrow()[self.active_index].document) + Root::from_ref(&self.history.borrow()[self.active_index.get()].document) } pub fn active_window(&self) -> Root<Window> { @@ -91,6 +121,92 @@ impl BrowsingContext { assert!(!window_proxy.get().is_null()); 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(&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_subpage(&self, subpage_id: SubpageId) -> Option<Root<Window>> { + self.children.borrow().iter().find(|context| { + let window = context.active_window(); + window.subpage() == Some(subpage_id) + }).map(|context| context.active_window()) + } + + pub fn clear_session_history(&self) { + self.active_index.set(0); + self.history.borrow_mut().clear(); + } +} + +pub struct ContextIterator { + stack: Vec<Root<BrowsingContext>>, +} + +pub trait IterableContext { + fn iter(&self) -> ContextIterator; + fn find(&self, id: PipelineId) -> Option<Root<BrowsingContext>>; +} + +impl IterableContext for BrowsingContext { + fn iter(&self) -> ContextIterator { + ContextIterator { + stack: vec!(Root::from_ref(self)), + } + } + + 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() + } +} + +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() + .cloned() + .map(|ref c| Root::from_ref(&**c))); + } + popped + } } // This isn't a DOM struct, just a convenience struct @@ -100,14 +216,16 @@ impl BrowsingContext { #[derive(JSTraceable, HeapSizeOf)] pub struct SessionHistoryEntry { document: JS<Document>, - children: Vec<JS<BrowsingContext>>, + url: Url, + title: DOMString, } impl SessionHistoryEntry { - fn new(document: &Document) -> SessionHistoryEntry { + fn new(document: &Document, url: Url, title: DOMString) -> SessionHistoryEntry { SessionHistoryEntry { document: JS::from_ref(document), - children: vec![], + url: url, + title: title, } } } diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs index 42f5a8e17e7..59dc8673418 100644 --- a/components/script/dom/htmliframeelement.rs +++ b/components/script/dom/htmliframeelement.rs @@ -36,7 +36,6 @@ use layout_interface::ReflowQueryType; use msg::constellation_msg::{ConstellationChan, LoadData}; use msg::constellation_msg::{NavigationDirection, PipelineId, SubpageId}; use net_traits::response::HttpsState; -use page::IterablePage; use script_traits::IFrameSandboxState::{IFrameSandboxed, IFrameUnsandboxed}; use script_traits::{IFrameLoadInfo, MozBrowserEvent, ScriptMsg as ConstellationMsg}; use std::ascii::AsciiExt; @@ -418,11 +417,8 @@ impl HTMLIFrameElementMethods for HTMLIFrameElement { self.subpage_id.get().and_then(|subpage_id| { let window = window_from_node(self); let window = window.r(); - let children = window.page().children.borrow(); - children.iter().find(|page| { - let window = page.window(); - window.subpage() == Some(subpage_id) - }).map(|page| page.window()) + let browsing_context = window.browsing_context(); + browsing_context.find_child_by_subpage(subpage_id) }) } diff --git a/components/script/dom/storage.rs b/components/script/dom/storage.rs index ff77402ac1e..65416c0eb25 100644 --- a/components/script/dom/storage.rs +++ b/components/script/dom/storage.rs @@ -10,12 +10,12 @@ use dom::bindings::inheritance::Castable; use dom::bindings::js::{Root, RootedReference}; use dom::bindings::refcounted::Trusted; use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object}; +use dom::browsingcontext::IterableContext; use dom::event::{Event, EventBubbles, EventCancelable}; use dom::storageevent::StorageEvent; use dom::urlhelper::UrlHelper; use ipc_channel::ipc; use net_traits::storage_thread::{StorageThread, StorageThreadMsg, StorageType}; -use page::IterablePage; use script_runtime::ScriptChan; use script_thread::{MainThreadRunnable, ScriptThread}; use task_source::dom_manipulation::DOMManipulationTask; @@ -199,9 +199,9 @@ impl MainThreadRunnable for StorageEventRunnable { Some(storage) ); - let root_page = script_thread.root_page(); - for it_page in root_page.iter() { - let it_window_root = it_page.window(); + let root_context = script_thread.root_browsing_context(); + for it_context in root_context.iter() { + let it_window_root = it_context.active_window(); let it_window = it_window_root.r(); assert!(UrlHelper::SameOrigin(&ev_url, &it_window.get_url())); // TODO: Such a Document object is not necessarily fully active, but events fired on such diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index dc6fb8704e8..cb8f0c59639 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -51,7 +51,6 @@ use net_traits::bluetooth_thread::BluetoothMethodMsg; use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread}; use net_traits::storage_thread::{StorageThread, StorageType}; use num_traits::ToPrimitive; -use page::Page; use profile_traits::mem; use reporter::CSSErrorReporter; use rustc_serialize::base64::{FromBase64, STANDARD, ToBase64}; @@ -151,7 +150,6 @@ pub struct Window { #[ignore_heap_size_of = "TODO(#6911) newtypes containing unmeasurable types are hard"] compositor: IpcSender<ScriptToCompositorMsg>, browsing_context: MutNullableHeap<JS<BrowsingContext>>, - page: Rc<Page>, performance: MutNullableHeap<JS<Performance>>, navigation_start: u64, navigation_start_precise: f64, @@ -337,10 +335,6 @@ impl Window { self.browsing_context.get().unwrap() } - pub fn page(&self) -> &Page { - &*self.page - } - pub fn bluetooth_thread(&self) -> IpcSender<BluetoothMethodMsg> { self.bluetooth_thread.clone() } @@ -1420,7 +1414,6 @@ impl Window { impl Window { pub fn new(runtime: Rc<Runtime>, - page: Rc<Page>, script_chan: MainThreadScriptChan, dom_task_source: DOMManipulationTaskSource, user_task_source: UserInteractionTaskSource, @@ -1467,7 +1460,6 @@ impl Window { console: Default::default(), crypto: Default::default(), compositor: compositor, - page: page, navigator: Default::default(), image_cache_thread: image_cache_thread, mem_profiler_chan: mem_profiler_chan, diff --git a/components/script/lib.rs b/components/script/lib.rs index e8b22c353a0..54eab466906 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -95,7 +95,6 @@ pub mod layout_interface; mod mem; mod network_listener; pub mod origin; -pub mod page; pub mod parse; pub mod reporter; pub mod script_runtime; diff --git a/components/script/page.rs b/components/script/page.rs deleted file mode 100644 index bcfed37b764..00000000000 --- a/components/script/page.rs +++ /dev/null @@ -1,134 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -use dom::bindings::cell::DOMRefCell; -use dom::bindings::js::{JS, Root}; -use dom::document::Document; -use dom::window::Window; -use msg::constellation_msg::PipelineId; -use std::cell::Cell; -use std::rc::Rc; - -/// Encapsulates a handle to a frame in a frame tree. -#[derive(JSTraceable, HeapSizeOf)] -#[allow(unrooted_must_root)] // FIXME(#6687) this is wrong -pub struct Page { - /// Pipeline id associated with this page. - id: PipelineId, - - /// The outermost frame containing the document and window. - frame: DOMRefCell<Option<Frame>>, - - /// Indicates if reflow is required when reloading. - needs_reflow: Cell<bool>, - - // Child Pages. - pub children: DOMRefCell<Vec<Rc<Page>>>, -} - -pub struct PageIterator { - stack: Vec<Rc<Page>>, -} - -pub trait IterablePage { - fn iter(&self) -> PageIterator; - fn find(&self, id: PipelineId) -> Option<Rc<Page>>; -} - -impl IterablePage for Rc<Page> { - fn iter(&self) -> PageIterator { - PageIterator { - stack: vec!(self.clone()), - } - } - fn find(&self, id: PipelineId) -> Option<Rc<Page>> { - if self.id == id { - return Some(self.clone()); - } - - self.children.borrow() - .iter() - .filter_map(|p| p.find(id)) - .next() - } - -} - -impl Page { - pub fn new(id: PipelineId) -> Page { - Page { - id: id, - frame: DOMRefCell::new(None), - needs_reflow: Cell::new(true), - children: DOMRefCell::new(vec!()), - } - } - - pub fn pipeline(&self) -> PipelineId { - self.id - } - - pub fn window(&self) -> Root<Window> { - Root::from_ref(&*self.frame.borrow().as_ref().unwrap().window) - } - - pub fn document(&self) -> Root<Document> { - Root::from_ref(&*self.frame.borrow().as_ref().unwrap().document) - } - - // must handle root case separately - pub fn remove(&self, id: PipelineId) -> Option<Rc<Page>> { - let remove_idx = { - self.children - .borrow() - .iter() - .position(|page_tree| page_tree.id == id) - }; - match remove_idx { - Some(idx) => Some(self.children.borrow_mut().remove(idx)), - None => { - self.children - .borrow_mut() - .iter_mut() - .filter_map(|page_tree| page_tree.remove(id)) - .next() - } - } - } -} - -impl Iterator for PageIterator { - type Item = Rc<Page>; - - fn next(&mut self) -> Option<Rc<Page>> { - let popped = self.stack.pop(); - if let Some(ref page) = popped { - self.stack.extend(page.children.borrow().iter().cloned()); - } - popped - } -} - -impl Page { - pub fn set_reflow_status(&self, status: bool) -> bool { - let old = self.needs_reflow.get(); - self.needs_reflow.set(status); - old - } - - #[allow(unrooted_must_root)] - pub fn set_frame(&self, frame: Option<Frame>) { - *self.frame.borrow_mut() = frame; - } -} - -/// Information for one frame in the browsing context. -#[derive(JSTraceable, HeapSizeOf)] -#[must_root] -pub struct Frame { - /// The document for this frame. - pub document: JS<Document>, - /// The window object for this frame. - pub window: JS<Window>, -} diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 1d3bc4c4bcf..008af6ba040 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -32,7 +32,7 @@ use dom::bindings::js::{RootCollectionPtr, RootedReference}; use dom::bindings::refcounted::{LiveDOMReferences, Trusted}; use dom::bindings::trace::JSTraceable; use dom::bindings::utils::WRAP_CALLBACKS; -use dom::browsingcontext::BrowsingContext; +use dom::browsingcontext::{BrowsingContext, IterableContext}; use dom::document::{Document, DocumentProgressHandler, DocumentSource, FocusType, IsHTMLDocument}; use dom::element::Element; use dom::event::{Event, EventBubbles, EventCancelable}; @@ -69,7 +69,6 @@ use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheResult, ImageCach use net_traits::storage_thread::StorageThread; use net_traits::{AsyncResponseTarget, ControlMsg, LoadConsumer, LoadContext, Metadata, ResourceThread}; use network_listener::NetworkListener; -use page::{Frame, IterablePage, Page}; use parse::ParserRoot; use parse::html::{ParseContext, parse_html}; use parse::xml::{self, parse_xml}; @@ -304,7 +303,7 @@ impl OpaqueSender<CommonScriptMsg> for Sender<MainThreadScriptMsg> { #[allow(unrooted_must_root)] pub struct ScriptThread { /// A handle to the information pertaining to page layout - page: DOMRefCell<Option<Rc<Page>>>, + browsing_context: MutNullableHeap<JS<BrowsingContext>>, /// A list of data pertaining to loads that have not yet received a network response incomplete_loads: DOMRefCell<Vec<InProgressLoad>>, /// A handle to the image cache thread. @@ -409,12 +408,10 @@ impl<'a> Drop for ScriptMemoryFailsafe<'a> { fn drop(&mut self) { match self.owner { Some(owner) => { - unsafe { - let page = owner.page.borrow_for_script_deallocation(); - for page in page.iter() { - let window = page.window(); - window.clear_js_runtime_for_script_deallocation(); - } + let context = owner.browsing_context.get(); + for context in context.iter() { + let window = context.active_window(); + window.clear_js_runtime_for_script_deallocation(); } } None => (), @@ -550,7 +547,7 @@ impl ScriptThread { let control_port = ROUTER.route_ipc_receiver_to_new_mpsc_receiver(state.control_port); ScriptThread { - page: DOMRefCell::new(None), + browsing_context: MutNullableHeap::new(None), incomplete_loads: DOMRefCell::new(vec!()), image_cache_thread: state.image_cache_thread, @@ -593,19 +590,19 @@ impl ScriptThread { } } - // Return the root page in the frame tree. Panics if it doesn't exist. - pub fn root_page(&self) -> Rc<Page> { - self.page.borrow().as_ref().unwrap().clone() + // 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_page_exists(&self) -> bool { - self.page.borrow().is_some() + fn root_browsing_context_exists(&self) -> bool { + self.browsing_context.get().is_some() } - /// Find a child page of the root page by pipeline id. Returns `None` if the root page does - /// not exist or the subpage cannot be found. - fn find_subpage(&self, pipeline_id: PipelineId) -> Option<Rc<Page>> { - self.page.borrow().as_ref().and_then(|page| page.find(pipeline_id)) + /// 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 { @@ -629,11 +626,11 @@ impl ScriptThread { let mut resizes = vec!(); { - let page = self.page.borrow(); - if let Some(page) = page.as_ref() { - for page in page.iter() { + 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 = page.window(); + let window = context.active_window(); let resize_event = window.steal_resize_event(); match resize_event { Some(size) => resizes.push((window.pipeline(), size)), @@ -782,10 +779,10 @@ 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 page = self.page.borrow(); - if let Some(page) = page.as_ref() { - for page in page.iter() { - let window = page.window(); + 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, @@ -946,45 +943,45 @@ impl ScriptThread { TimerSource::FromWorker => panic!("Worker timeouts must not be sent to script thread"), }; - let page = self.root_page(); - let page = page.find(pipeline_id).expect("ScriptThread: received fire timer msg for a + 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 = page.window(); + let window = context.active_window(); window.handle_fire_timer(id); } fn handle_msg_from_devtools(&self, msg: DevtoolScriptControlMsg) { - let page = self.root_page(); + let context = self.root_browsing_context(); match msg { DevtoolScriptControlMsg::EvaluateJS(id, s, reply) => { - let window = get_page(&page, id).window(); + let window = get_browsing_context(&context, id).active_window(); let global_ref = GlobalRef::Window(window.r()); devtools::handle_evaluate_js(&global_ref, s, reply) }, DevtoolScriptControlMsg::GetRootNode(id, reply) => - devtools::handle_get_root_node(&page, id, reply), + devtools::handle_get_root_node(&context, id, reply), DevtoolScriptControlMsg::GetDocumentElement(id, reply) => - devtools::handle_get_document_element(&page, id, reply), + devtools::handle_get_document_element(&context, id, reply), DevtoolScriptControlMsg::GetChildren(id, node_id, reply) => - devtools::handle_get_children(&page, id, node_id, reply), + devtools::handle_get_children(&context, id, node_id, reply), DevtoolScriptControlMsg::GetLayout(id, node_id, reply) => - devtools::handle_get_layout(&page, id, node_id, reply), + devtools::handle_get_layout(&context, id, node_id, reply), DevtoolScriptControlMsg::GetCachedMessages(pipeline_id, message_types, reply) => devtools::handle_get_cached_messages(pipeline_id, message_types, reply), DevtoolScriptControlMsg::ModifyAttribute(id, node_id, modifications) => - devtools::handle_modify_attribute(&page, id, node_id, modifications), + devtools::handle_modify_attribute(&context, id, node_id, modifications), DevtoolScriptControlMsg::WantsLiveNotifications(id, to_send) => { - let window = get_page(&page, id).window(); + let window = get_browsing_context(&context, id).active_window(); let global_ref = GlobalRef::Window(window.r()); devtools::handle_wants_live_notifications(&global_ref, to_send) }, DevtoolScriptControlMsg::SetTimelineMarkers(_pipeline_id, marker_types, reply) => - devtools::handle_set_timeline_markers(&page, marker_types, reply), + devtools::handle_set_timeline_markers(&context, marker_types, reply), DevtoolScriptControlMsg::DropTimelineMarkers(_pipeline_id, marker_types) => - devtools::handle_drop_timeline_markers(&page, marker_types), + devtools::handle_drop_timeline_markers(&context, marker_types), DevtoolScriptControlMsg::RequestAnimationFrame(pipeline_id, name) => - devtools::handle_request_animation_frame(&page, pipeline_id, name), + devtools::handle_request_animation_frame(&context, pipeline_id, name), } } @@ -993,48 +990,48 @@ impl ScriptThread { } fn handle_webdriver_msg(&self, pipeline_id: PipelineId, msg: WebDriverScriptCommand) { - let page = self.root_page(); + let context = self.root_browsing_context(); match msg { WebDriverScriptCommand::ExecuteScript(script, reply) => - webdriver_handlers::handle_execute_script(&page, pipeline_id, script, reply), + webdriver_handlers::handle_execute_script(&context, pipeline_id, script, reply), WebDriverScriptCommand::FindElementCSS(selector, reply) => - webdriver_handlers::handle_find_element_css(&page, pipeline_id, selector, reply), + webdriver_handlers::handle_find_element_css(&context, pipeline_id, selector, reply), WebDriverScriptCommand::FindElementsCSS(selector, reply) => - webdriver_handlers::handle_find_elements_css(&page, pipeline_id, selector, reply), + webdriver_handlers::handle_find_elements_css(&context, pipeline_id, selector, reply), WebDriverScriptCommand::FocusElement(element_id, reply) => - webdriver_handlers::handle_focus_element(&page, pipeline_id, element_id, reply), + webdriver_handlers::handle_focus_element(&context, pipeline_id, element_id, reply), WebDriverScriptCommand::GetActiveElement(reply) => - webdriver_handlers::handle_get_active_element(&page, pipeline_id, reply), + webdriver_handlers::handle_get_active_element(&context, pipeline_id, reply), WebDriverScriptCommand::GetElementTagName(node_id, reply) => - webdriver_handlers::handle_get_name(&page, pipeline_id, node_id, reply), + webdriver_handlers::handle_get_name(&context, pipeline_id, node_id, reply), WebDriverScriptCommand::GetElementAttribute(node_id, name, reply) => - webdriver_handlers::handle_get_attribute(&page, pipeline_id, node_id, name, reply), + webdriver_handlers::handle_get_attribute(&context, pipeline_id, node_id, name, reply), WebDriverScriptCommand::GetElementCSS(node_id, name, reply) => - webdriver_handlers::handle_get_css(&page, pipeline_id, node_id, name, reply), + webdriver_handlers::handle_get_css(&context, pipeline_id, node_id, name, reply), WebDriverScriptCommand::GetElementRect(node_id, reply) => - webdriver_handlers::handle_get_rect(&page, pipeline_id, node_id, reply), + webdriver_handlers::handle_get_rect(&context, pipeline_id, node_id, reply), WebDriverScriptCommand::GetElementText(node_id, reply) => - webdriver_handlers::handle_get_text(&page, pipeline_id, node_id, reply), + webdriver_handlers::handle_get_text(&context, pipeline_id, node_id, reply), WebDriverScriptCommand::GetFrameId(frame_id, reply) => - webdriver_handlers::handle_get_frame_id(&page, pipeline_id, frame_id, reply), + webdriver_handlers::handle_get_frame_id(&context, pipeline_id, frame_id, reply), WebDriverScriptCommand::GetUrl(reply) => - webdriver_handlers::handle_get_url(&page, pipeline_id, reply), + webdriver_handlers::handle_get_url(&context, pipeline_id, reply), WebDriverScriptCommand::GetWindowSize(reply) => - webdriver_handlers::handle_get_window_size(&page, pipeline_id, reply), + webdriver_handlers::handle_get_window_size(&context, pipeline_id, reply), WebDriverScriptCommand::IsEnabled(element_id, reply) => - webdriver_handlers::handle_is_enabled(&page, pipeline_id, element_id, reply), + webdriver_handlers::handle_is_enabled(&context, pipeline_id, element_id, reply), WebDriverScriptCommand::IsSelected(element_id, reply) => - webdriver_handlers::handle_is_selected(&page, pipeline_id, element_id, reply), + webdriver_handlers::handle_is_selected(&context, pipeline_id, element_id, reply), WebDriverScriptCommand::GetTitle(reply) => - webdriver_handlers::handle_get_title(&page, pipeline_id, reply), + webdriver_handlers::handle_get_title(&context, pipeline_id, reply), WebDriverScriptCommand::ExecuteAsyncScript(script, reply) => - webdriver_handlers::handle_execute_async_script(&page, pipeline_id, script, reply), + webdriver_handlers::handle_execute_async_script(&context, pipeline_id, script, reply), } } fn handle_resize(&self, id: PipelineId, size: WindowSizeData, size_type: WindowSizeType) { - if let Some(ref page) = self.find_subpage(id) { - let window = page.window(); + if let Some(ref context) = self.find_child_context(id) { + let window = context.active_window(); window.set_resize_event(size, size_type); return; } @@ -1047,13 +1044,13 @@ impl ScriptThread { } fn handle_viewport(&self, id: PipelineId, rect: Rect<f32>) { - let page = self.page.borrow(); - if let Some(page) = page.as_ref() { - if let Some(ref inner_page) = page.find(id) { - let window = inner_page.window(); + 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) { - let page = get_page(page, id); - self.rebuild_and_force_reflow(&*page, ReflowReason::Viewport); + let context = get_browsing_context(&context, id); + self.rebuild_and_force_reflow(&context, ReflowReason::Viewport); } return; } @@ -1099,11 +1096,11 @@ impl ScriptThread { content_process_shutdown_chan: content_process_shutdown_chan, }; - let page = self.root_page(); - let parent_page = page.find(containing_pipeline_id).expect("ScriptThread: received a layout + let context = self.root_browsing_context(); + let parent_context = context.find(containing_pipeline_id).expect("ScriptThread: received a layout whose parent has a PipelineId which does not correspond to a pipeline in the script - thread's page tree. This is a bug."); - let parent_window = parent_page.window(); + thread's browsing context tree. This is a bug."); + let parent_window = parent_context.active_window(); // Tell layout to actually spawn the thread. parent_window.layout_chan() @@ -1119,8 +1116,8 @@ impl ScriptThread { } fn handle_loads_complete(&self, pipeline: PipelineId) { - let page = get_page(&self.root_page(), pipeline); - let doc = page.document(); + let context = get_browsing_context(&self.root_browsing_context(), pipeline); + let doc = context.active_document(); let doc = doc.r(); if doc.loader().is_blocked() { return; @@ -1141,14 +1138,14 @@ impl ScriptThread { let mut dom_tree_size = 0; let mut reports = vec![]; - if let Some(root_page) = self.page.borrow().as_ref() { - for it_page in root_page.iter() { - let current_url = it_page.document().url().to_string(); + 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 child in it_page.document().upcast::<Node>().traverse_preorder() { + for child in it_context.active_document().upcast::<Node>().traverse_preorder() { dom_tree_size += heap_size_of_self_and_children(&*child); } - let window = it_page.window(); + let window = it_context.active_window(); dom_tree_size += heap_size_of_self_and_children(&*window); reports.push(Report { @@ -1166,9 +1163,9 @@ impl ScriptThread { /// Handles freeze message fn handle_freeze_msg(&self, id: PipelineId) { - if let Some(root_page) = self.page.borrow().as_ref() { - if let Some(ref inner_page) = root_page.find(id) { - let window = inner_page.window(); + 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.freeze(); return; } @@ -1183,12 +1180,12 @@ impl ScriptThread { /// Handles thaw message fn handle_thaw_msg(&self, id: PipelineId) { - if let Some(ref inner_page) = self.root_page().find(id) { - let needed_reflow = inner_page.set_reflow_status(false); + 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_page, ReflowReason::CachedPageNeededReflow); + self.rebuild_and_force_reflow(&inner_context, ReflowReason::CachedPageNeededReflow); } - let window = inner_page.window(); + let window = inner_context.active_window(); window.thaw(); return; } @@ -1203,10 +1200,10 @@ impl ScriptThread { fn handle_focus_iframe_msg(&self, parent_pipeline_id: PipelineId, subpage_id: SubpageId) { - let borrowed_page = self.root_page(); - let page = borrowed_page.find(parent_pipeline_id).unwrap(); + let borrowed_context = self.root_browsing_context(); + let context = borrowed_context.find(parent_pipeline_id).unwrap(); - let doc = page.document(); + let doc = context.active_document(); let frame_element = doc.find_iframe(subpage_id); if let Some(ref frame_element) = frame_element { @@ -1219,13 +1216,13 @@ impl ScriptThread { fn handle_framed_content_changed(&self, parent_pipeline_id: PipelineId, subpage_id: SubpageId) { - let borrowed_page = self.root_page(); - let page = borrowed_page.find(parent_pipeline_id).unwrap(); - let doc = page.document(); + let root_context = self.root_browsing_context(); + let context = root_context.find(parent_pipeline_id).unwrap(); + let doc = context.active_document(); let frame_element = doc.find_iframe(subpage_id); if let Some(ref frame_element) = frame_element { frame_element.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage); - let window = page.window(); + let window = context.active_window(); window.reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery, ReflowReason::FramedContentChanged); @@ -1238,10 +1235,10 @@ impl ScriptThread { parent_pipeline_id: PipelineId, subpage_id: SubpageId, event: MozBrowserEvent) { - let borrowed_page = self.root_page(); + let borrowed_context = self.root_browsing_context(); - let frame_element = borrowed_page.find(parent_pipeline_id).and_then(|page| { - let doc = page.document(); + let frame_element = borrowed_context.find(parent_pipeline_id).and_then(|context| { + let doc = context.active_document(); doc.find_iframe(subpage_id) }); @@ -1255,10 +1252,10 @@ impl ScriptThread { old_subpage_id: SubpageId, new_subpage_id: SubpageId, new_pipeline_id: PipelineId) { - let borrowed_page = self.root_page(); + let borrowed_context = self.root_browsing_context(); - let frame_element = borrowed_page.find(containing_pipeline_id).and_then(|page| { - let doc = page.document(); + let frame_element = borrowed_context.find(containing_pipeline_id).and_then(|context| { + let doc = context.active_document(); doc.find_iframe(old_subpage_id) }); @@ -1267,12 +1264,12 @@ impl ScriptThread { /// 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 page = self.root_page(); - let page = page.find(id).expect("Received resize message for PipelineId not associated - with a page in the page tree. This is a bug."); - let window = page.window(); + 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(); window.set_window_size(new_size); - page.set_reflow_status(true); + context.set_reflow_status(true); } /// We have gotten a window.close from script, which we pass on to the compositor. @@ -1312,8 +1309,8 @@ impl ScriptThread { /// Handles a request for the window title. fn handle_get_title_msg(&self, pipeline_id: PipelineId) { - let page = get_page(&self.root_page(), pipeline_id); - let document = page.document(); + let context = get_browsing_context(&self.root_browsing_context(), pipeline_id); + let document = context.active_document(); document.send_title_to_compositor(); } @@ -1341,47 +1338,46 @@ impl ScriptThread { } let has_pending_loads = self.incomplete_loads.borrow().len() > 0; - let has_root_page = self.page.borrow().is_some(); + let has_root_context = self.root_browsing_context_exists(); - // Exit if no pending loads and no root page - return !has_pending_loads && !has_root_page; + // Exit if no pending loads and no root context + return !has_pending_loads && !has_root_context; } - // If root is being exited, shut down all pages - let page = self.root_page(); - let window = page.window(); + // If root is being exited, shut down all contexts + let context = self.root_browsing_context(); + let window = context.active_window(); if window.pipeline() == id { - debug!("shutting down layout for root page {:?}", id); - shut_down_layout(&page); + debug!("shutting down layout for root context {:?}", id); + shut_down_layout(&context); return true } - // otherwise find just the matching page and exit all sub-pages - if let Some(ref mut child_page) = page.remove(id) { - debug!("shutting down layout for child context {:?}", id); - shut_down_layout(&*child_page); + // 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); } false } /// Handles when layout thread finishes all animation in one tick fn handle_tick_all_animations(&self, id: PipelineId) { - let page = get_page(&self.root_page(), id); - let document = page.document(); + let context = get_browsing_context(&self.root_browsing_context(), id); + let document = context.active_document(); document.run_the_animation_frame_callbacks(); } /// 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(ref page) = self.find_subpage(pipeline_id) { - self.rebuild_and_force_reflow(page, ReflowReason::WebFontLoaded); + if let Some(context) = self.find_child_context(pipeline_id) { + self.rebuild_and_force_reflow(&context, ReflowReason::WebFontLoaded); } } /// Notify the containing document of a child frame that has completed loading. fn handle_frame_load_event(&self, containing_pipeline: PipelineId, id: PipelineId) { - let page = get_page(&self.root_page(), containing_pipeline); - let document = page.document(); + let context = get_browsing_context(&self.root_browsing_context(), containing_pipeline); + let document = context.active_document(); if let Some(iframe) = document.find_iframe_by_pipeline(id) { iframe.iframe_load_event_steps(id); } @@ -1400,84 +1396,28 @@ impl ScriptThread { let ConstellationChan(ref chan) = self.constellation_chan; chan.send(ConstellationMsg::SetFinalUrl(incomplete.pipeline_id, final_url.clone())).unwrap(); } - debug!("ScriptThread: loading {} on page {:?}", incomplete.url, incomplete.pipeline_id); + debug!("ScriptThread: loading {} on context {:?}", incomplete.url, incomplete.pipeline_id); let frame_element = incomplete.parent_info.and_then(|(parent_id, subpage_id)| { - // The root page may not exist yet, if the parent of this frame + // The root context may not exist yet, if the parent of this frame // exists in a different script thread. - let borrowed_page = self.page.borrow(); + let root_context = self.browsing_context.get(); - // In the case a parent id exists but the matching page - // cannot be found, this means the page exists in a different + // 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. // TODO: window.parent will continue to return self in that // 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). - borrowed_page.as_ref().and_then(|borrowed_page| { - borrowed_page.find(parent_id).and_then(|page| { - let doc = page.document(); + root_context.and_then(|root_context| { + root_context.find(parent_id).and_then(|context| { + let doc = context.active_document(); doc.find_iframe(subpage_id) }) }) }); - // Create a new frame tree entry. - let page = Rc::new(Page::new(incomplete.pipeline_id)); - if !self.root_page_exists() { - // We have a new root frame tree. - *self.page.borrow_mut() = Some(page.clone()); - } else if let Some((parent, _)) = incomplete.parent_info { - // We have a new child frame. - let parent_page = self.root_page(); - // TODO(gw): This find will fail when we are sharing script threads - // between cross origin iframes in the same TLD. - let parent_page = parent_page.find(parent) - .expect("received load for subpage with missing parent"); - parent_page.children.borrow_mut().push(page.clone()); - } - - enum PageToRemove { - Root, - Child(PipelineId), - } - struct AutoPageRemover<'a> { - page: PageToRemove, - script_thread: &'a ScriptThread, - neutered: bool, - } - impl<'a> AutoPageRemover<'a> { - fn new(script_thread: &'a ScriptThread, page: PageToRemove) -> AutoPageRemover<'a> { - AutoPageRemover { - page: page, - script_thread: script_thread, - neutered: false, - } - } - - fn neuter(&mut self) { - self.neutered = true; - } - } - impl<'a> Drop for AutoPageRemover<'a> { - fn drop(&mut self) { - if !self.neutered { - match self.page { - PageToRemove::Root => *self.script_thread.page.borrow_mut() = None, - PageToRemove::Child(id) => { - self.script_thread.root_page().remove(id).unwrap(); - } - } - } - } - } - - let page_to_remove = if !self.root_page_exists() { - PageToRemove::Root - } else { - PageToRemove::Child(incomplete.pipeline_id) - }; - let mut page_remover = AutoPageRemover::new(self, page_to_remove); let MainThreadScriptChan(ref sender) = self.chan; let DOMManipulationTaskSource(ref dom_sender) = self.dom_manipulation_task_source; let UserInteractionTaskSource(ref user_sender) = self.user_interaction_task_source; @@ -1491,7 +1431,6 @@ impl ScriptThread { // Create the window and document objects. let window = Window::new(self.js_runtime.clone(), - page.clone(), MainThreadScriptChan(sender.clone()), DOMManipulationTaskSource(dom_sender.clone()), UserInteractionTaskSource(user_sender.clone()), @@ -1514,10 +1453,74 @@ impl ScriptThread { incomplete.pipeline_id, incomplete.parent_info, incomplete.window_size); - let frame_element = frame_element.r().map(Castable::upcast); - let browsing_context = BrowsingContext::new(&window, frame_element); + + 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 mut using_new_context = true; + + 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 { + using_new_context = false; + (self.root_browsing_context(), ContextToRemove::None) + }; + 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)) @@ -1534,8 +1537,8 @@ impl ScriptThread { }); let loader = DocumentLoader::new_with_thread(self.resource_thread.clone(), - Some(page.pipeline()), - Some(incomplete.url.clone())); + Some(browsing_context.pipeline()), + Some(incomplete.url.clone())); let is_html_document = match metadata.content_type { Some(ContentType(Mime(TopLevel::Application, SubLevel::Xml, _))) | @@ -1552,20 +1555,18 @@ impl ScriptThread { last_modified, DocumentSource::FromParser, loader); - browsing_context.init(&document); + if using_new_context { + browsing_context.init(&document); + } else { + browsing_context.push_history(&document); + } document.set_ready_state(DocumentReadyState::Loading); - // Create the root frame - page.set_frame(Some(Frame { - document: JS::from_rooted(&document), - window: JS::from_rooted(&window), - })); - let ConstellationChan(ref chan) = self.constellation_chan; chan.send(ConstellationMsg::ActivateDocument(incomplete.pipeline_id)).unwrap(); // Notify devtools that a new script global exists. - self.notify_devtools(document.Title(), final_url.clone(), (page.pipeline(), None)); + self.notify_devtools(document.Title(), final_url.clone(), (browsing_context.pipeline(), None)); let is_javascript = incomplete.url.scheme() == "javascript"; let parse_input = if is_javascript { @@ -1625,7 +1626,7 @@ impl ScriptThread { window.freeze(); } - page_remover.neuter(); + context_remover.neuter(); document.get_current_parser().unwrap() } @@ -1664,8 +1665,8 @@ impl ScriptThread { } /// Reflows non-incrementally, rebuilding the entire layout tree in the process. - fn rebuild_and_force_reflow(&self, page: &Page, reason: ReflowReason) { - let document = page.document(); + fn rebuild_and_force_reflow(&self, context: &Root<BrowsingContext>, reason: ReflowReason) { + let document = context.active_document(); document.dirty_all_nodes(); let window = window_from_node(document.r()); window.reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery, reason); @@ -1676,8 +1677,8 @@ 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 page. - if !self.root_page_exists() { + // DOM events can only be handled if there's a root browsing context. + if !self.root_browsing_context_exists() { return; } @@ -1691,8 +1692,8 @@ impl ScriptThread { } MouseMoveEvent(point) => { - let page = get_page(&self.root_page(), pipeline_id); - let document = page.document(); + let context = get_browsing_context(&self.root_browsing_context(), pipeline_id); + let document = context.active_document(); // Get the previous target temporarily let prev_mouse_over_target = self.topmost_mouse_over_target.get(); @@ -1765,14 +1766,14 @@ impl ScriptThread { } TouchpadPressureEvent(point, pressure, phase) => { - let page = get_page(&self.root_page(), pipeline_id); - let document = page.document(); + let context = get_browsing_context(&self.root_browsing_context(), pipeline_id); + let document = context.active_document(); document.r().handle_touchpad_pressure_event(self.js_runtime.rt(), point, pressure, phase); } KeyEvent(key, state, modifiers) => { - let page = get_page(&self.root_page(), pipeline_id); - let document = page.document(); + let context = get_browsing_context(&self.root_browsing_context(), pipeline_id); + let document = context.active_document(); document.dispatch_key_event( key, state, modifiers, &mut self.compositor.borrow_mut()); } @@ -1784,8 +1785,8 @@ impl ScriptThread { mouse_event_type: MouseEventType, button: MouseButton, point: Point2D<f32>) { - let page = get_page(&self.root_page(), pipeline_id); - let document = page.document(); + let context = get_browsing_context(&self.root_browsing_context(), pipeline_id); + let document = context.active_document(); document.handle_mouse_event(self.js_runtime.rt(), button, point, mouse_event_type); } @@ -1795,8 +1796,8 @@ impl ScriptThread { identifier: TouchId, point: Point2D<f32>) -> bool { - let page = get_page(&self.root_page(), pipeline_id); - let document = page.document(); + let context = get_browsing_context(&self.root_browsing_context(), pipeline_id); + let document = context.active_document(); document.handle_touch_event(self.js_runtime.rt(), event_type, identifier, point) } @@ -1808,8 +1809,8 @@ impl ScriptThread { { let nurl = &load_data.url; if let Some(fragment) = nurl.fragment() { - let page = get_page(&self.root_page(), pipeline_id); - let document = page.document(); + let context = get_browsing_context(&self.root_browsing_context(), pipeline_id); + let document = context.active_document(); let document = document.r(); let url = document.url(); if &url[..Position::AfterQuery] == &nurl[..Position::AfterQuery] && @@ -1827,9 +1828,9 @@ impl ScriptThread { match subpage_id { Some(subpage_id) => { - let borrowed_page = self.root_page(); - let iframe = borrowed_page.find(pipeline_id).and_then(|page| { - let doc = page.document(); + let root_context = self.root_browsing_context(); + let iframe = root_context.find(pipeline_id).and_then(|context| { + let doc = context.active_document(); doc.find_iframe(subpage_id) }); if let Some(iframe) = iframe.r() { @@ -1844,14 +1845,14 @@ impl ScriptThread { } fn handle_resize_event(&self, pipeline_id: PipelineId, new_size: WindowSizeData, size_type: WindowSizeType) { - let page = get_page(&self.root_page(), pipeline_id); - let window = page.window(); + let context = get_browsing_context(&self.root_browsing_context(), pipeline_id); + let window = context.active_window(); window.set_window_size(new_size); window.force_reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery, ReflowReason::WindowResize); - let document = page.document(); + let document = context.active_document(); let fragment_node = window.steal_fragment_name() .and_then(|name| document.find_fragment_node(&*name)); match fragment_node { @@ -1911,13 +1912,13 @@ impl ScriptThread { } fn handle_parsing_complete(&self, id: PipelineId) { - let parent_page = self.root_page(); - let page = match parent_page.find(id) { - Some(page) => page, + let parent_context = self.root_browsing_context(); + let context = match parent_context.find(id) { + Some(context) => context, None => return, }; - let document = page.document(); + let document = context.active_document(); let final_url = document.url(); // https://html.spec.whatwg.org/multipage/#the-end step 1 @@ -1933,7 +1934,7 @@ impl ScriptThread { window.reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery, ReflowReason::FirstLoad); // No more reflow required - page.set_reflow_status(false); + context.set_reflow_status(false); // https://html.spec.whatwg.org/multipage/#the-end steps 3-4. document.process_deferred_scripts(); @@ -1943,13 +1944,13 @@ impl ScriptThread { fn handle_css_error_reporting(&self, pipeline_id: PipelineId, filename: String, line: usize, column: usize, msg: String) { - let parent_page = self.root_page(); - let page = match parent_page.find(pipeline_id) { - Some(page) => page, + let parent_context = self.root_browsing_context(); + let context = match parent_context.find(pipeline_id) { + Some(context) => context, None => return, }; - let document = page.document(); + let document = context.active_document(); let css_error = CSSError { filename: filename, line: line, @@ -1958,7 +1959,7 @@ impl ScriptThread { }; document.report_css_error(css_error.clone()); - let window = page.window(); + let window = context.active_window(); if window.live_devtools_updates() { if let Some(ref chan) = self.devtools_chan { @@ -1978,15 +1979,15 @@ impl Drop for ScriptThread { } } -/// Shuts down layout for the given page tree. -fn shut_down_layout(page_tree: &Rc<Page>) { +/// Shuts down layout for the given browsing context tree. +fn shut_down_layout(context_tree: &Root<BrowsingContext>) { let mut channels = vec!(); - for page in page_tree.iter() { + 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 = page.window(); + let window = context.active_window(); let LayoutChan(chan) = window.layout_chan().clone(); if chan.send(layout_interface::Msg::PrepareToExit(response_chan)).is_ok() { channels.push(chan); @@ -1995,11 +1996,12 @@ fn shut_down_layout(page_tree: &Rc<Page>) { } // Drop our references to the JSContext and DOM objects. - for page in page_tree.iter() { - let window = page.window(); + for context in context_tree.iter() { + let window = context.active_window(); window.clear_js_runtime(); + // Sever the connection between the global and the DOM tree - page.set_frame(None); + context.clear_session_history(); } // Destroy the layout thread. If there were node leaks, layout will now crash safely. @@ -2008,10 +2010,12 @@ fn shut_down_layout(page_tree: &Rc<Page>) { } } -pub fn get_page(page: &Rc<Page>, pipeline_id: PipelineId) -> Rc<Page> { - page.find(pipeline_id).expect("ScriptThread: received an event \ - message for a layout channel that is not associated with this script thread.\ - This is a bug.") +pub fn get_browsing_context(context: &Root<BrowsingContext>, + pipeline_id: PipelineId) + -> Root<BrowsingContext> { + context.find(pipeline_id).expect("ScriptThread: received an event \ + message for a layout channel that is not associated with this script thread.\ + This is a bug.") } fn dom_last_modified(tm: &Tm) -> String { diff --git a/components/script/webdriver_handlers.rs b/components/script/webdriver_handlers.rs index 142b8c79d8b..c46d69c2f99 100644 --- a/components/script/webdriver_handlers.rs +++ b/components/script/webdriver_handlers.rs @@ -15,6 +15,7 @@ use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use dom::bindings::conversions::{FromJSValConvertible, StringificationBehavior}; use dom::bindings::inheritance::Castable; use dom::bindings::js::Root; +use dom::browsingcontext::BrowsingContext; use dom::element::Element; use dom::htmlelement::HTMLElement; use dom::htmliframeelement::HTMLIFrameElement; @@ -31,15 +32,16 @@ use js::jsapi::{HandleValue, RootedValue}; use js::jsval::UndefinedValue; use msg::constellation_msg::{PipelineId, WindowSizeData}; use msg::webdriver_msg::{WebDriverFrameId, WebDriverJSError, WebDriverJSResult, WebDriverJSValue}; -use page::Page; -use script_thread::get_page; -use std::rc::Rc; +use script_thread::get_browsing_context; use url::Url; use util::str::DOMString; -fn find_node_by_unique_id(page: &Rc<Page>, pipeline: PipelineId, node_id: String) -> Option<Root<Node>> { - let page = get_page(&*page, pipeline); - let document = page.document(); +fn find_node_by_unique_id(context: &Root<BrowsingContext>, + pipeline: PipelineId, + node_id: String) + -> Option<Root<Node>> { + let context = get_browsing_context(&context, pipeline); + let document = context.active_document(); document.upcast::<Node>().traverse_preorder().find(|candidate| candidate.unique_id() == node_id) } @@ -63,12 +65,12 @@ pub unsafe fn jsval_to_webdriver(cx: *mut JSContext, val: HandleValue) -> WebDri } #[allow(unsafe_code)] -pub fn handle_execute_script(page: &Rc<Page>, +pub fn handle_execute_script(context: &Root<BrowsingContext>, pipeline: PipelineId, eval: String, reply: IpcSender<WebDriverJSResult>) { - let page = get_page(&*page, pipeline); - let window = page.window(); + let context = get_browsing_context(&context, pipeline); + let window = context.active_window(); let result = unsafe { let cx = window.get_cx(); let mut rval = RootedValue::new(cx, UndefinedValue()); @@ -78,19 +80,19 @@ pub fn handle_execute_script(page: &Rc<Page>, reply.send(result).unwrap(); } -pub fn handle_execute_async_script(page: &Rc<Page>, +pub fn handle_execute_async_script(context: &Root<BrowsingContext>, pipeline: PipelineId, eval: String, reply: IpcSender<WebDriverJSResult>) { - let page = get_page(&*page, pipeline); - let window = page.window(); + let context = get_browsing_context(&context, pipeline); + let window = context.active_window(); let cx = window.get_cx(); window.set_webdriver_script_chan(Some(reply)); let mut rval = RootedValue::new(cx, UndefinedValue()); window.evaluate_js_on_global_with_result(&eval, rval.handle_mut()); } -pub fn handle_get_frame_id(page: &Rc<Page>, +pub fn handle_get_frame_id(context: &Root<BrowsingContext>, pipeline: PipelineId, webdriver_frame_id: WebDriverFrameId, reply: IpcSender<Result<Option<PipelineId>, ()>>) { @@ -100,7 +102,7 @@ pub fn handle_get_frame_id(page: &Rc<Page>, Ok(None) }, WebDriverFrameId::Element(x) => { - match find_node_by_unique_id(page, pipeline, x) { + match find_node_by_unique_id(context, pipeline, x) { Some(ref node) => { match node.downcast::<HTMLIFrameElement>() { Some(ref elem) => Ok(elem.GetContentWindow()), @@ -111,7 +113,7 @@ pub fn handle_get_frame_id(page: &Rc<Page>, } }, WebDriverFrameId::Parent => { - let window = page.window(); + let window = context.active_window(); Ok(window.parent()) } }; @@ -120,9 +122,9 @@ pub fn handle_get_frame_id(page: &Rc<Page>, reply.send(frame_id).unwrap() } -pub fn handle_find_element_css(page: &Rc<Page>, _pipeline: PipelineId, selector: String, +pub fn handle_find_element_css(context: &Root<BrowsingContext>, _pipeline: PipelineId, selector: String, reply: IpcSender<Result<Option<String>, ()>>) { - reply.send(match page.document().QuerySelector(DOMString::from(selector)) { + reply.send(match context.active_document().QuerySelector(DOMString::from(selector)) { Ok(node) => { Ok(node.map(|x| x.upcast::<Node>().unique_id())) } @@ -130,11 +132,11 @@ pub fn handle_find_element_css(page: &Rc<Page>, _pipeline: PipelineId, selector: }).unwrap(); } -pub fn handle_find_elements_css(page: &Rc<Page>, +pub fn handle_find_elements_css(context: &Root<BrowsingContext>, _pipeline: PipelineId, selector: String, reply: IpcSender<Result<Vec<String>, ()>>) { - reply.send(match page.document().QuerySelectorAll(DOMString::from(selector)) { + 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() { @@ -150,11 +152,11 @@ pub fn handle_find_elements_css(page: &Rc<Page>, }).unwrap(); } -pub fn handle_focus_element(page: &Rc<Page>, +pub fn handle_focus_element(context: &Root<BrowsingContext>, pipeline: PipelineId, element_id: String, reply: IpcSender<Result<(), ()>>) { - reply.send(match find_node_by_unique_id(page, pipeline, element_id) { + reply.send(match find_node_by_unique_id(context, pipeline, element_id) { Some(ref node) => { match node.downcast::<HTMLElement>() { Some(ref elem) => { @@ -169,22 +171,22 @@ pub fn handle_focus_element(page: &Rc<Page>, }).unwrap(); } -pub fn handle_get_active_element(page: &Rc<Page>, +pub fn handle_get_active_element(context: &Root<BrowsingContext>, _pipeline: PipelineId, reply: IpcSender<Option<String>>) { - reply.send(page.document().GetActiveElement().map( + reply.send(context.active_document().GetActiveElement().map( |elem| elem.upcast::<Node>().unique_id())).unwrap(); } -pub fn handle_get_title(page: &Rc<Page>, _pipeline: PipelineId, reply: IpcSender<String>) { - reply.send(String::from(page.document().Title())).unwrap(); +pub fn handle_get_title(context: &Root<BrowsingContext>, _pipeline: PipelineId, reply: IpcSender<String>) { + reply.send(String::from(context.active_document().Title())).unwrap(); } -pub fn handle_get_rect(page: &Rc<Page>, +pub fn handle_get_rect(context: &Root<BrowsingContext>, pipeline: PipelineId, element_id: String, reply: IpcSender<Result<Rect<f64>, ()>>) { - reply.send(match find_node_by_unique_id(&*page, pipeline, element_id) { + reply.send(match find_node_by_unique_id(context, pipeline, element_id) { Some(elem) => { // https://w3c.github.io/webdriver/webdriver-spec.html#dfn-calculate-the-absolute-position match elem.downcast::<HTMLElement>() { @@ -218,11 +220,11 @@ pub fn handle_get_rect(page: &Rc<Page>, }).unwrap(); } -pub fn handle_get_text(page: &Rc<Page>, +pub fn handle_get_text(context: &Root<BrowsingContext>, pipeline: PipelineId, node_id: String, reply: IpcSender<Result<String, ()>>) { - reply.send(match find_node_by_unique_id(&*page, pipeline, node_id) { + reply.send(match find_node_by_unique_id(context, pipeline, node_id) { Some(ref node) => { Ok(node.GetTextContent().map_or("".to_owned(), String::from)) }, @@ -230,11 +232,11 @@ pub fn handle_get_text(page: &Rc<Page>, }).unwrap(); } -pub fn handle_get_name(page: &Rc<Page>, +pub fn handle_get_name(context: &Root<BrowsingContext>, pipeline: PipelineId, node_id: String, reply: IpcSender<Result<String, ()>>) { - reply.send(match find_node_by_unique_id(&*page, pipeline, node_id) { + reply.send(match find_node_by_unique_id(context, pipeline, node_id) { Some(node) => { Ok(String::from(node.downcast::<Element>().unwrap().TagName())) }, @@ -242,12 +244,12 @@ pub fn handle_get_name(page: &Rc<Page>, }).unwrap(); } -pub fn handle_get_attribute(page: &Rc<Page>, +pub fn handle_get_attribute(context: &Root<BrowsingContext>, pipeline: PipelineId, node_id: String, name: String, reply: IpcSender<Result<Option<String>, ()>>) { - reply.send(match find_node_by_unique_id(&*page, pipeline, node_id) { + reply.send(match find_node_by_unique_id(context, pipeline, node_id) { Some(node) => { Ok(node.downcast::<Element>().unwrap().GetAttribute(DOMString::from(name)) .map(String::from)) @@ -256,14 +258,14 @@ pub fn handle_get_attribute(page: &Rc<Page>, }).unwrap(); } -pub fn handle_get_css(page: &Rc<Page>, +pub fn handle_get_css(context: &Root<BrowsingContext>, pipeline: PipelineId, node_id: String, name: String, reply: IpcSender<Result<String, ()>>) { - reply.send(match find_node_by_unique_id(&*page, pipeline, node_id) { + reply.send(match find_node_by_unique_id(context, pipeline, node_id) { Some(node) => { - let window = page.window(); + let window = context.active_window(); let elem = node.downcast::<Element>().unwrap(); Ok(String::from( window.GetComputedStyle(&elem, None).GetPropertyValue(DOMString::from(name)))) @@ -272,27 +274,27 @@ pub fn handle_get_css(page: &Rc<Page>, }).unwrap(); } -pub fn handle_get_url(page: &Rc<Page>, +pub fn handle_get_url(context: &Root<BrowsingContext>, _pipeline: PipelineId, reply: IpcSender<Url>) { - let document = page.document(); + let document = context.active_document(); let url = document.url(); reply.send((*url).clone()).unwrap(); } -pub fn handle_get_window_size(page: &Rc<Page>, +pub fn handle_get_window_size(context: &Root<BrowsingContext>, _pipeline: PipelineId, reply: IpcSender<Option<WindowSizeData>>) { - let window = page.window(); + let window = context.active_window(); let size = window.window_size(); reply.send(size).unwrap(); } -pub fn handle_is_enabled(page: &Rc<Page>, +pub fn handle_is_enabled(context: &Root<BrowsingContext>, pipeline: PipelineId, element_id: String, reply: IpcSender<Result<bool, ()>>) { - reply.send(match find_node_by_unique_id(page, pipeline, element_id) { + reply.send(match find_node_by_unique_id(&context, pipeline, element_id) { Some(ref node) => { match node.downcast::<Element>() { Some(elem) => Ok(elem.enabled_state()), @@ -303,11 +305,11 @@ pub fn handle_is_enabled(page: &Rc<Page>, }).unwrap(); } -pub fn handle_is_selected(page: &Rc<Page>, +pub fn handle_is_selected(context: &Root<BrowsingContext>, pipeline: PipelineId, element_id: String, reply: IpcSender<Result<bool, ()>>) { - reply.send(match find_node_by_unique_id(page, pipeline, element_id) { + reply.send(match find_node_by_unique_id(context, pipeline, element_id) { Some(ref node) => { if let Some(input_element) = node.downcast::<HTMLInputElement>() { Ok(input_element.Checked()) |