diff options
author | Josh Matthews <josh@joshmatthews.net> | 2015-02-19 13:08:50 -0500 |
---|---|---|
committer | Josh Matthews <josh@joshmatthews.net> | 2015-03-03 16:25:40 -0500 |
commit | e2c4f5ed6726ed7434197180b301f74a967d3ffc (patch) | |
tree | 543d4b085a38fdbc134c945d78fff2b2dec619b4 /components/script | |
parent | d9f04180a5d9146f4486ede6fabb9da638cccd41 (diff) | |
download | servo-e2c4f5ed6726ed7434197180b301f74a967d3ffc.tar.gz servo-e2c4f5ed6726ed7434197180b301f74a967d3ffc.zip |
Move everything unrelated to the frame tree out of Page and into Document or Window. Reduce the API surface of Page to a bare minimum to allow for easier future removal.
Diffstat (limited to 'components/script')
23 files changed, 669 insertions, 677 deletions
diff --git a/components/script/devtools.rs b/components/script/devtools.rs index 5333b9a4cee..051002c7478 100644 --- a/components/script/devtools.rs +++ b/components/script/devtools.rs @@ -11,7 +11,7 @@ use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use dom::bindings::codegen::Bindings::DOMRectBinding::{DOMRectMethods}; use dom::bindings::codegen::Bindings::ElementBinding::{ElementMethods}; use dom::node::{Node, NodeHelpers}; -use dom::window::{ScriptHelpers}; +use dom::window::{WindowHelpers, ScriptHelpers}; use dom::element::Element; use dom::document::DocumentHelpers; use page::Page; @@ -24,8 +24,7 @@ use std::rc::Rc; pub fn handle_evaluate_js(page: &Rc<Page>, pipeline: PipelineId, eval: String, reply: Sender<EvaluateJSReply>){ let page = get_page(&*page, pipeline); - let frame = page.frame(); - let window = frame.as_ref().unwrap().window.root(); + let window = page.window().root(); let cx = window.r().get_cx(); let rval = window.r().evaluate_js_on_global_with_result(eval.as_slice()); @@ -49,8 +48,7 @@ pub fn handle_evaluate_js(page: &Rc<Page>, pipeline: PipelineId, eval: String, r pub fn handle_get_root_node(page: &Rc<Page>, pipeline: PipelineId, reply: Sender<NodeInfo>) { let page = get_page(&*page, pipeline); - let frame = page.frame(); - let document = frame.as_ref().unwrap().document.root(); + let document = page.document().root(); let node: JSRef<Node> = NodeCast::from_ref(document.r()); reply.send(node.summarize()).unwrap(); @@ -58,8 +56,7 @@ pub fn handle_get_root_node(page: &Rc<Page>, pipeline: PipelineId, reply: Sender pub fn handle_get_document_element(page: &Rc<Page>, pipeline: PipelineId, reply: Sender<NodeInfo>) { let page = get_page(&*page, pipeline); - let frame = page.frame(); - let document = frame.as_ref().unwrap().document.root(); + let document = page.document().root(); let document_element = document.r().GetDocumentElement().root().unwrap(); let node: JSRef<Node> = NodeCast::from_ref(document_element.r()); @@ -68,8 +65,7 @@ pub fn handle_get_document_element(page: &Rc<Page>, pipeline: PipelineId, reply: fn find_node_by_unique_id(page: &Rc<Page>, pipeline: PipelineId, node_id: String) -> Temporary<Node> { let page = get_page(&*page, pipeline); - let frame = page.frame(); - let document = frame.as_ref().unwrap().document.root(); + let document = page.document().root(); let node: JSRef<Node> = NodeCast::from_ref(document.r()); for candidate in node.traverse_preorder() { @@ -110,5 +106,6 @@ pub fn handle_modify_attribute(page: &Rc<Page>, pipeline: PipelineId, node_id: S pub fn handle_wants_live_notifications(page: &Rc<Page>, pipeline_id: PipelineId, send_notifications: bool) { let page = get_page(&*page, pipeline_id); - page.devtools_wants_updates.set(send_notifications); + let window = page.window().root(); + window.r().set_devtools_wants_updates(send_notifications); } diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index 203bf60f340..9274c6cafb7 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -4643,7 +4643,6 @@ class CGBindingRoot(CGThing): 'dom::bindings::proxyhandler::{fill_property_descriptor, get_expando_object}', 'dom::bindings::proxyhandler::{get_property_descriptor}', 'dom::bindings::str::ByteString', - 'page::JSPageInfo', 'libc', 'util::str::DOMString', 'std::borrow::ToOwned', diff --git a/components/script/dom/bindings/global.rs b/components/script/dom/bindings/global.rs index dc42d0da6e0..3d17778c6b1 100644 --- a/components/script/dom/bindings/global.rs +++ b/components/script/dom/bindings/global.rs @@ -11,7 +11,7 @@ use dom::bindings::conversions::FromJSValConvertible; use dom::bindings::js::{JS, JSRef, Root, Unrooted}; use dom::bindings::utils::{Reflectable, Reflector}; use dom::workerglobalscope::{WorkerGlobalScope, WorkerGlobalScopeHelpers}; -use dom::window; +use dom::window::{self, WindowHelpers}; use script_task::ScriptChan; use net::resource_task::ResourceTask; @@ -84,7 +84,7 @@ impl<'a> GlobalRef<'a> { /// Get the `ResourceTask` for this global scope. pub fn resource_task(&self) -> ResourceTask { match *self { - GlobalRef::Window(ref window) => window.page().resource_task.clone(), + GlobalRef::Window(ref window) => window.resource_task().clone(), GlobalRef::Worker(ref worker) => worker.resource_task().clone(), } } diff --git a/components/script/dom/bindings/utils.rs b/components/script/dom/bindings/utils.rs index 56c9b58288d..706c228c0b2 100644 --- a/components/script/dom/bindings/utils.rs +++ b/components/script/dom/bindings/utils.rs @@ -45,6 +45,8 @@ use js::JSFUN_CONSTRUCTOR; use js; /// Proxy handler for a WindowProxy. +#[allow(raw_pointer_derive)] +#[derive(Copy)] pub struct WindowProxyHandler(pub *const libc::c_void); #[allow(raw_pointer_derive)] diff --git a/components/script/dom/browsercontext.rs b/components/script/dom/browsercontext.rs index e21533bbfe0..a3af0a8b02a 100644 --- a/components/script/dom/browsercontext.rs +++ b/components/script/dom/browsercontext.rs @@ -71,14 +71,12 @@ impl BrowserContext { fn create_window_proxy(&mut self) { let win = self.active_window().root(); let win = win.r(); - let page = win.page(); - let js_info = page.js_info(); - let WindowProxyHandler(handler) = js_info.as_ref().unwrap().dom_static.windowproxy_handler; + let WindowProxyHandler(handler) = win.windowproxy_handler(); assert!(!handler.is_null()); let parent = win.reflector().get_jsobject(); - let cx = js_info.as_ref().unwrap().js_context.ptr; + let cx = win.get_cx(); let wrapper = with_compartment(cx, parent, || unsafe { WrapperNew(cx, parent, handler) }); diff --git a/components/script/dom/console.rs b/components/script/dom/console.rs index 4dec1c3bd45..6308556ef2e 100644 --- a/components/script/dom/console.rs +++ b/components/script/dom/console.rs @@ -7,6 +7,7 @@ use dom::bindings::codegen::Bindings::ConsoleBinding::ConsoleMethods; use dom::bindings::global::{GlobalRef, GlobalField}; use dom::bindings::js::{JSRef, Temporary}; use dom::bindings::utils::{Reflector, reflect_dom_object}; +use dom::window::WindowHelpers; use devtools_traits::{DevtoolsControlMsg, ConsoleMessage}; use util::str::DOMString; @@ -66,8 +67,8 @@ fn propagate_console_msg(console: &JSRef<Console>, console_message: ConsoleMessa let global = console.global.root(); match global.r() { GlobalRef::Window(window_ref) => { - let pipelineId = window_ref.page().id; - console.global.root().r().as_window().page().devtools_chan.as_ref().map(|chan| { + let pipelineId = window_ref.pipeline(); + console.global.root().r().as_window().devtools_chan().as_ref().map(|chan| { chan.send(DevtoolsControlMsg::SendConsoleMessage( pipelineId, console_message.clone())).unwrap(); }); diff --git a/components/script/dom/cssstyledeclaration.rs b/components/script/dom/cssstyledeclaration.rs index 2337e6c061a..e1be98f99af 100644 --- a/components/script/dom/cssstyledeclaration.rs +++ b/components/script/dom/cssstyledeclaration.rs @@ -14,7 +14,7 @@ use dom::document::DocumentHelpers; use dom::element::{Element, ElementHelpers, StylePriority}; use dom::htmlelement::HTMLElement; use dom::node::{window_from_node, document_from_node, NodeDamage, Node}; -use dom::window::Window; +use dom::window::{Window, WindowHelpers}; use util::str::DOMString; use string_cache::Atom; use style::properties::{is_supported_property, longhands_from_shorthand, parse_style_attribute}; @@ -222,9 +222,8 @@ impl<'a> CSSStyleDeclarationMethods for JSRef<'a, CSSStyleDeclaration> { let owner = self.owner.root(); let window = window_from_node(owner.r()).root(); let window = window.r(); - let page = window.page(); let decl_block = parse_style_attribute(synthesized_declaration.as_slice(), - &page.get_url()); + &window.get_url()); // Step 7 if decl_block.normal.len() == 0 { @@ -271,9 +270,8 @@ impl<'a> CSSStyleDeclarationMethods for JSRef<'a, CSSStyleDeclaration> { let owner = self.owner.root(); let window = window_from_node(owner.r()).root(); let window = window.r(); - let page = window.page(); let decl_block = parse_style_attribute(property.as_slice(), - &page.get_url()); + &window.get_url()); let element: JSRef<Element> = ElementCast::from_ref(owner.r()); // Step 5 diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 6e5413ebdc8..1d611a6a643 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -11,7 +11,6 @@ use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull; use dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods; use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use dom::bindings::codegen::Bindings::NodeFilterBinding::NodeFilter; -use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use dom::bindings::codegen::InheritTypes::{DocumentDerived, EventCast, HTMLElementCast}; use dom::bindings::codegen::InheritTypes::{HTMLHeadElementCast, TextCast, ElementCast}; use dom::bindings::codegen::InheritTypes::{DocumentTypeCast, HTMLHtmlElementCast, NodeCast}; @@ -59,12 +58,14 @@ use dom::treewalker::TreeWalker; use dom::uievent::UIEvent; use dom::window::{Window, WindowHelpers}; +use layout_interface::{HitTestResponse, MouseOverResponse}; use msg::compositor_msg::ScriptListener; use msg::constellation_msg::{Key, KeyState, KeyModifiers}; use msg::constellation_msg::{SUPER, ALT, SHIFT, CONTROL}; use net::resource_task::ControlMsg::{SetCookiesForUrl, GetCookiesForUrl}; use net::cookie_storage::CookieSource::NonHTTP; use script_task::Runnable; +use script_traits::UntrustedNodeAddress; use util::namespace; use util::str::{DOMString, split_html_space_chars}; use layout_interface::{ReflowGoal, ReflowQueryType}; @@ -201,6 +202,8 @@ pub trait DocumentHelpers<'a> { fn register_named_element(self, element: JSRef<Element>, id: Atom); fn load_anchor_href(self, href: DOMString); fn find_fragment_node(self, fragid: DOMString) -> Option<Temporary<Element>>; + fn hit_test(self, point: &Point2D<f32>) -> Option<UntrustedNodeAddress>; + fn get_nodes_under_mouse(self, point: &Point2D<f32>) -> Vec<UntrustedNodeAddress>; fn set_ready_state(self, state: DocumentReadyState); fn get_focused_element(self) -> Option<Temporary<Element>>; fn begin_focus_transaction(self); @@ -264,7 +267,7 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { Quirks => { let window = self.window.root(); let window = window.r(); - let LayoutChan(ref layout_chan) = window.page().layout_chan; + let LayoutChan(ref layout_chan) = window.layout_chan(); layout_chan.send(Msg::SetQuirksMode).unwrap(); } NoQuirks | LimitedQuirks => {} @@ -377,6 +380,44 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { }) } + fn hit_test(self, point: &Point2D<f32>) -> Option<UntrustedNodeAddress> { + let root = self.GetDocumentElement().root(); + let root = match root.r() { + Some(root) => root, + None => return None, + }; + let root: JSRef<Node> = NodeCast::from_ref(root); + let win = self.window.root(); + let address = match win.r().layout().hit_test(root.to_trusted_node_address(), *point) { + Ok(HitTestResponse(node_address)) => { + Some(node_address) + } + Err(()) => { + debug!("layout query error"); + None + } + }; + address + } + + fn get_nodes_under_mouse(self, point: &Point2D<f32>) -> Vec<UntrustedNodeAddress> { + let root = self.GetDocumentElement().root(); + let root = match root.r() { + Some(root) => root, + None => return vec!(), + }; + let root: JSRef<Node> = NodeCast::from_ref(root); + let win = self.window.root(); + match win.r().layout().mouse_over(root.to_trusted_node_address(), *point) { + Ok(MouseOverResponse(node_address)) => { + node_address + } + Err(()) => { + vec!() + } + } + } + // https://html.spec.whatwg.org/multipage/dom.html#current-document-readiness fn set_ready_state(self, state: DocumentReadyState) { self.ready_state.set(state); @@ -416,7 +457,7 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { /// Sends this document's title to the compositor. fn send_title_to_compositor(self) { let window = self.window().root(); - window.r().page().send_title_to_compositor(); + window.r().compositor().set_title(window.r().pipeline(), Some(self.Title())); } fn dirty_all_nodes(self) { @@ -428,10 +469,7 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { fn handle_click_event(self, js_runtime: *mut JSRuntime, _button: uint, point: Point2D<f32>) { debug!("ClickEvent: clicked at {:?}", point); - let window = self.window.root(); - let window = window.r(); - let page = window.page(); - let node = match page.hit_test(&point) { + let node = match self.hit_test(&point) { Some(node_address) => { debug!("node address is {:?}", node_address.0); node::from_untrusted_node_address(js_runtime, node_address) @@ -460,48 +498,40 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { return; } - match *page.frame() { - Some(ref frame) => { - let window = frame.window.root(); - let doc = window.r().Document().root(); - doc.r().begin_focus_transaction(); - - // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#event-type-click - let x = point.x as i32; - let y = point.y as i32; - let event = MouseEvent::new(window.r(), - "click".to_owned(), - true, - true, - Some(window.r()), - 0i32, - x, y, x, y, - false, false, false, false, - 0i16, - None).root(); - let event: JSRef<Event> = EventCast::from_ref(event.r()); - - // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#trusted-events - event.set_trusted(true); - // https://html.spec.whatwg.org/multipage/interaction.html#run-authentic-click-activation-steps - el.authentic_click_activation(event); - - doc.r().commit_focus_transaction(); - window.r().flush_layout(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery); - } - None => {} - } + self.begin_focus_transaction(); + + let window = self.window.root(); + + // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#event-type-click + let x = point.x as i32; + let y = point.y as i32; + let event = MouseEvent::new(window.r(), + "click".to_owned(), + true, + true, + Some(window.r()), + 0i32, + x, y, x, y, + false, false, false, false, + 0i16, + None).root(); + let event: JSRef<Event> = EventCast::from_ref(event.r()); + + // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#trusted-events + event.set_trusted(true); + // https://html.spec.whatwg.org/multipage/interaction.html#run-authentic-click-activation-steps + el.authentic_click_activation(event); + + self.commit_focus_transaction(); + window.r().flush_layout(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery); } /// Return need force reflow or not fn handle_mouse_move_event(self, js_runtime: *mut JSRuntime, point: Point2D<f32>, prev_mouse_over_targets: &mut Vec<JS<Node>>) -> bool { - let window = self.window.root(); - let window = window.r(); - let page = window.page(); let mut needs_reflow = false; // Build a list of elements that are currently under the mouse. - let mouse_over_addresses = page.get_nodes_under_mouse(&point); + let mouse_over_addresses = self.get_nodes_under_mouse(&point); let mouse_over_targets: Vec<JS<Node>> = mouse_over_addresses.iter() .filter_map(|node_address| { let node = node::from_untrusted_node_address(js_runtime, *node_address); @@ -534,27 +564,24 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { let top_most_node = node::from_untrusted_node_address(js_runtime, mouse_over_addresses[0]).root(); - if let Some(ref frame) = *page.frame() { - let window = frame.window.root(); - - let x = point.x.to_i32().unwrap_or(0); - let y = point.y.to_i32().unwrap_or(0); - - let mouse_event = MouseEvent::new(window.r(), - "mousemove".to_owned(), - true, - true, - Some(window.r()), - 0i32, - x, y, x, y, - false, false, false, false, - 0i16, - None).root(); - - let event: JSRef<Event> = EventCast::from_ref(mouse_event.r()); - let target: JSRef<EventTarget> = EventTargetCast::from_ref(top_most_node.r()); - event.fire(target); - } + let x = point.x.to_i32().unwrap_or(0); + let y = point.y.to_i32().unwrap_or(0); + + let window = self.window.root(); + let mouse_event = MouseEvent::new(window.r(), + "mousemove".to_owned(), + true, + true, + Some(window.r()), + 0i32, + x, y, x, y, + false, false, false, false, + 0i16, + None).root(); + + let event: JSRef<Event> = EventCast::from_ref(mouse_event.r()); + let target: JSRef<EventTarget> = EventTargetCast::from_ref(top_most_node.r()); + event.fire(target); } // Store the current mouse over targets for next frame @@ -1259,7 +1286,7 @@ impl<'a> DocumentMethods for JSRef<'a, Document> { fn Location(self) -> Temporary<Location> { let window = self.window.root(); let window = window.r(); - self.location.or_init(|| Location::new(window, window.page_clone())) + self.location.or_init(|| Location::new(window)) } // http://dom.spec.whatwg.org/#dom-parentnode-children @@ -1298,10 +1325,8 @@ impl<'a> DocumentMethods for JSRef<'a, Document> { return Err(Security); } let window = self.window.root(); - let window = window.r(); - let page = window.page(); let (tx, rx) = channel(); - let _ = page.resource_task.send(GetCookiesForUrl(url, tx, NonHTTP)); + let _ = window.r().resource_task().send(GetCookiesForUrl(url, tx, NonHTTP)); let cookies = rx.recv().unwrap(); Ok(cookies.unwrap_or("".to_owned())) } @@ -1314,9 +1339,7 @@ impl<'a> DocumentMethods for JSRef<'a, Document> { return Err(Security); } let window = self.window.root(); - let window = window.r(); - let page = window.page(); - let _ = page.resource_task.send(SetCookiesForUrl(url, cookie, NonHTTP)); + let _ = window.r().resource_task().send(SetCookiesForUrl(url, cookie, NonHTTP)); Ok(()) } diff --git a/components/script/dom/domparser.rs b/components/script/dom/domparser.rs index 91d5d96a615..88429277f19 100644 --- a/components/script/dom/domparser.rs +++ b/components/script/dom/domparser.rs @@ -12,7 +12,7 @@ use dom::bindings::js::{JS, JSRef, Temporary}; use dom::bindings::utils::{Reflector, reflect_dom_object}; use dom::document::{Document, DocumentHelpers, IsHTMLDocument}; use dom::document::DocumentSource; -use dom::window::Window; +use dom::window::{Window, WindowHelpers}; use parse::html::{HTMLInput, parse_html}; use util::str::DOMString; diff --git a/components/script/dom/htmlbodyelement.rs b/components/script/dom/htmlbodyelement.rs index e609169ff05..eaf683ce3bd 100644 --- a/components/script/dom/htmlbodyelement.rs +++ b/components/script/dom/htmlbodyelement.rs @@ -16,6 +16,7 @@ use dom::eventtarget::{EventTarget, EventTargetTypeId, EventTargetHelpers}; use dom::htmlelement::{HTMLElement, HTMLElementTypeId}; use dom::node::{Node, NodeTypeId, window_from_node}; use dom::virtualmethods::VirtualMethods; +use dom::window::WindowHelpers; use cssparser::RGBA; use util::str::{self, DOMString}; diff --git a/components/script/dom/htmlelement.rs b/components/script/dom/htmlelement.rs index dd5cd1cb7e0..f3528185211 100644 --- a/components/script/dom/htmlelement.rs +++ b/components/script/dom/htmlelement.rs @@ -26,6 +26,7 @@ use dom::htmlmediaelement::HTMLMediaElementTypeId; use dom::htmltablecellelement::HTMLTableCellElementTypeId; use dom::node::{Node, NodeTypeId, window_from_node}; use dom::virtualmethods::VirtualMethods; +use dom::window::WindowHelpers; use util::str::DOMString; diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs index ddc8d42b661..9c2148334e2 100644 --- a/components/script/dom/htmlformelement.rs +++ b/components/script/dom/htmlformelement.rs @@ -215,7 +215,7 @@ impl<'a> HTMLFormElementHelpers for JSRef<'a, HTMLFormElement> { } // This is wrong. https://html.spec.whatwg.org/multipage/forms.html#planned-navigation - win.r().script_chan().send(ScriptMsg::TriggerLoad(win.r().page().id, load_data)).unwrap(); + win.r().script_chan().send(ScriptMsg::TriggerLoad(win.r().pipeline(), load_data)).unwrap(); } fn get_form_dataset<'b>(self, submitter: Option<FormSubmitter<'b>>) -> Vec<FormDatum> { diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs index b4f9b577b7b..3afcb2f4af2 100644 --- a/components/script/dom/htmliframeelement.rs +++ b/components/script/dom/htmliframeelement.rs @@ -20,8 +20,8 @@ use dom::htmlelement::{HTMLElement, HTMLElementTypeId}; use dom::node::{Node, NodeHelpers, NodeTypeId, window_from_node}; use dom::urlhelper::UrlHelper; use dom::virtualmethods::VirtualMethods; -use dom::window::Window; -use page::{IterablePage, Page}; +use dom::window::{Window, WindowHelpers}; +use page::IterablePage; use msg::constellation_msg::{PipelineId, SubpageId, ConstellationChan}; use msg::constellation_msg::IFrameSandboxState::{IFrameSandboxed, IFrameUnsandboxed}; @@ -62,7 +62,7 @@ pub trait HTMLIFrameElementHelpers { fn get_url(self) -> Option<Url>; /// http://www.whatwg.org/html/#process-the-iframe-attributes fn process_the_iframe_attributes(self); - fn generate_new_subpage_id(self, page: &Page) -> (SubpageId, Option<SubpageId>); + fn generate_new_subpage_id(self) -> (SubpageId, Option<SubpageId>); } impl<'a> HTMLIFrameElementHelpers for JSRef<'a, HTMLIFrameElement> { @@ -78,15 +78,16 @@ impl<'a> HTMLIFrameElementHelpers for JSRef<'a, HTMLIFrameElement> { None } else { let window = window_from_node(self).root(); - UrlParser::new().base_url(&window.r().page().get_url()) + UrlParser::new().base_url(&window.r().get_url()) .parse(url.as_slice()).ok() } }) } - fn generate_new_subpage_id(self, page: &Page) -> (SubpageId, Option<SubpageId>) { + fn generate_new_subpage_id(self) -> (SubpageId, Option<SubpageId>) { let old_subpage_id = self.subpage_id.get(); - let subpage_id = page.get_next_subpage_id(); + let win = window_from_node(self).root(); + let subpage_id = win.r().get_next_subpage_id(); self.subpage_id.set(Some(subpage_id)); (subpage_id, old_subpage_id) } @@ -105,14 +106,13 @@ impl<'a> HTMLIFrameElementHelpers for JSRef<'a, HTMLIFrameElement> { let window = window_from_node(self).root(); let window = window.r(); - let page = window.page(); - let (new_subpage_id, old_subpage_id) = self.generate_new_subpage_id(page); + let (new_subpage_id, old_subpage_id) = self.generate_new_subpage_id(); - self.containing_page_pipeline_id.set(Some(page.id)); + self.containing_page_pipeline_id.set(Some(window.pipeline())); - let ConstellationChan(ref chan) = page.constellation_chan; + let ConstellationChan(ref chan) = window.constellation_chan(); chan.send(ConstellationMsg::ScriptLoadedURLInIFrame(url, - page.id, + window.pipeline(), new_subpage_id, old_subpage_id, sandboxed)).unwrap(); @@ -172,14 +172,10 @@ impl<'a> HTMLIFrameElementMethods for JSRef<'a, HTMLIFrameElement> { let window = window_from_node(self).root(); let window = window.r(); let children = window.page().children.borrow(); - let child = children.iter().find(|child| { - child.subpage_id.unwrap() == subpage_id - }); - child.and_then(|page| { - page.frame.borrow().as_ref().map(|frame| { - Temporary::new(frame.window.clone()) - }) - }) + children.iter().find(|child| { + let window = child.window().root(); + window.r().subpage() == Some(subpage_id) + }).map(|page| page.window()) }) } @@ -189,7 +185,7 @@ impl<'a> HTMLIFrameElementMethods for JSRef<'a, HTMLIFrameElement> { Some(self_url) => self_url, None => return None, }; - let win_url = window_from_node(self).root().r().page().get_url(); + let win_url = window_from_node(self).root().r().get_url(); if UrlHelper::SameOrigin(&self_url, &win_url) { Some(window.r().Document()) diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs index 2f8d09bb759..6b2f0b3dc2f 100644 --- a/components/script/dom/htmlimageelement.rs +++ b/components/script/dom/htmlimageelement.rs @@ -17,6 +17,7 @@ use dom::element::ElementTypeId; use dom::htmlelement::{HTMLElement, HTMLElementTypeId}; use dom::node::{Node, NodeTypeId, NodeHelpers, NodeDamage, window_from_node}; use dom::virtualmethods::VirtualMethods; +use dom::window::WindowHelpers; use net::image_cache_task; use util::geometry::to_px; use util::str::DOMString; diff --git a/components/script/dom/htmllinkelement.rs b/components/script/dom/htmllinkelement.rs index 4f8f2aa14b8..ab0c45a22d5 100644 --- a/components/script/dom/htmllinkelement.rs +++ b/components/script/dom/htmllinkelement.rs @@ -17,6 +17,7 @@ use dom::element::ElementTypeId; use dom::htmlelement::{HTMLElement, HTMLElementTypeId}; use dom::node::{Node, NodeHelpers, NodeTypeId, window_from_node}; use dom::virtualmethods::VirtualMethods; +use dom::window::WindowHelpers; use layout_interface::{LayoutChan, Msg}; use util::str::{DOMString, HTML_SPACE_CHARACTERS}; @@ -130,9 +131,9 @@ impl<'a> PrivateHTMLLinkElementHelpers for JSRef<'a, HTMLLinkElement> { fn handle_stylesheet_url(self, href: &str) { let window = window_from_node(self).root(); let window = window.r(); - match UrlParser::new().base_url(&window.page().get_url()).parse(href) { + match UrlParser::new().base_url(&window.get_url()).parse(href) { Ok(url) => { - let LayoutChan(ref layout_chan) = window.page().layout_chan; + let LayoutChan(ref layout_chan) = window.layout_chan(); layout_chan.send(Msg::LoadStylesheet(url)).unwrap(); } Err(e) => debug!("Parsing url {} failed: {}", href, e) diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs index 853d6709fcb..e9809a6e2cb 100644 --- a/components/script/dom/htmlscriptelement.rs +++ b/components/script/dom/htmlscriptelement.rs @@ -25,7 +25,7 @@ use dom::element::ElementTypeId; use dom::htmlelement::{HTMLElement, HTMLElementTypeId}; use dom::node::{Node, NodeHelpers, NodeTypeId, document_from_node, window_from_node, CloneChildrenFlag}; use dom::virtualmethods::VirtualMethods; -use dom::window::ScriptHelpers; +use dom::window::{WindowHelpers, ScriptHelpers}; use script_task::{ScriptMsg, Runnable}; use encoding::all::UTF_8; @@ -214,8 +214,7 @@ impl<'a> HTMLScriptElementHelpers for JSRef<'a, HTMLScriptElement> { // Step 14. let window = window_from_node(self).root(); let window = window.r(); - let page = window.page(); - let base_url = page.get_url(); + let base_url = window.get_url(); let load = match element.get_attribute(ns!(""), &atom!("src")).root() { // Step 14. @@ -243,7 +242,7 @@ impl<'a> HTMLScriptElementHelpers for JSRef<'a, HTMLScriptElement> { // state of the element's `crossorigin` content attribute, the origin being // the origin of the script element's node document, and the default origin // behaviour set to taint. - ScriptOrigin::External(load_whole_resource(&page.resource_task, url)) + ScriptOrigin::External(load_whole_resource(&window.resource_task(), url)) } } }, diff --git a/components/script/dom/htmlstyleelement.rs b/components/script/dom/htmlstyleelement.rs index 6fc3e1a2039..945831a35b1 100644 --- a/components/script/dom/htmlstyleelement.rs +++ b/components/script/dom/htmlstyleelement.rs @@ -12,6 +12,7 @@ use dom::element::ElementTypeId; use dom::htmlelement::{HTMLElement, HTMLElementTypeId}; use dom::node::{Node, NodeHelpers, NodeTypeId, window_from_node}; use dom::virtualmethods::VirtualMethods; +use dom::window::WindowHelpers; use layout_interface::{LayoutChan, Msg}; use util::str::DOMString; use style::stylesheets::{Origin, Stylesheet}; @@ -52,11 +53,11 @@ impl<'a> StyleElementHelpers for JSRef<'a, HTMLStyleElement> { let win = window_from_node(node).root(); let win = win.r(); - let url = win.page().get_url(); + let url = win.get_url(); let data = node.GetTextContent().expect("Element.textContent must be a string"); let sheet = Stylesheet::from_str(data.as_slice(), url, Origin::Author); - let LayoutChan(ref layout_chan) = win.page().layout_chan; + let LayoutChan(ref layout_chan) = win.layout_chan(); layout_chan.send(Msg::AddStylesheet(sheet)).unwrap(); } } diff --git a/components/script/dom/location.rs b/components/script/dom/location.rs index 614b623e5b9..13cc1a05c65 100644 --- a/components/script/dom/location.rs +++ b/components/script/dom/location.rs @@ -5,33 +5,31 @@ use dom::bindings::codegen::Bindings::LocationBinding; use dom::bindings::codegen::Bindings::LocationBinding::LocationMethods; use dom::bindings::global::GlobalRef; -use dom::bindings::js::{JSRef, Temporary}; +use dom::bindings::js::{JS, JSRef, Temporary}; use dom::bindings::utils::{Reflector, reflect_dom_object}; use dom::urlhelper::UrlHelper; use dom::window::Window; use dom::window::WindowHelpers; -use page::Page; use util::str::DOMString; - -use std::rc::Rc; +use url::Url; #[dom_struct] pub struct Location { reflector_: Reflector, - page: Rc<Page>, + window: JS<Window>, } impl Location { - fn new_inherited(page: Rc<Page>) -> Location { + fn new_inherited(window: JSRef<Window>) -> Location { Location { reflector_: Reflector::new(), - page: page + window: JS::from_rooted(window) } } - pub fn new(window: JSRef<Window>, page: Rc<Page>) -> Temporary<Location> { - reflect_dom_object(box Location::new_inherited(page), + pub fn new(window: JSRef<Window>) -> Temporary<Location> { + reflect_dom_object(box Location::new_inherited(window), GlobalRef::Window(window), LocationBinding::Wrap) } @@ -40,11 +38,11 @@ impl Location { impl<'a> LocationMethods for JSRef<'a, Location> { // https://html.spec.whatwg.org/multipage/browsers.html#dom-location-assign fn Assign(self, url: DOMString) { - self.page.frame().as_ref().unwrap().window.root().r().load_url(url); + self.window.root().r().load_url(url); } fn Href(self) -> DOMString { - UrlHelper::Href(&self.page.get_url()) + UrlHelper::Href(&self.get_url()) } fn Stringify(self) -> DOMString { @@ -52,11 +50,21 @@ impl<'a> LocationMethods for JSRef<'a, Location> { } fn Search(self) -> DOMString { - UrlHelper::Search(&self.page.get_url()) + UrlHelper::Search(&self.get_url()) } fn Hash(self) -> DOMString { - UrlHelper::Hash(&self.page.get_url()) + UrlHelper::Hash(&self.get_url()) } } +trait PrivateLocationHelpers { + fn get_url(self) -> Url; +} + +impl<'a> PrivateLocationHelpers for JSRef<'a, Location> { + fn get_url(self) -> Url { + let window = self.window.root(); + window.r().get_url() + } +} diff --git a/components/script/dom/macros.rs b/components/script/dom/macros.rs index d00aa9961b1..b1cba9ccd3e 100644 --- a/components/script/dom/macros.rs +++ b/components/script/dom/macros.rs @@ -80,6 +80,7 @@ macro_rules! make_url_or_base_getter( fn $attr(self) -> DOMString { use dom::element::{Element, AttributeHandlers}; use dom::bindings::codegen::InheritTypes::ElementCast; + use dom::window::WindowHelpers; #[allow(unused_imports)] use std::ascii::AsciiExt; let element: JSRef<Element> = ElementCast::from_ref(self); diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index a7b5e5484d6..7526c2490d2 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -41,7 +41,7 @@ use dom::nodelist::NodeList; use dom::processinginstruction::ProcessingInstruction; use dom::text::Text; use dom::virtualmethods::{VirtualMethods, vtable_for}; -use dom::window::Window; +use dom::window::{Window, WindowHelpers}; use geom::rect::Rect; use layout_interface::{LayoutChan, Msg}; use devtools_traits::NodeInfo; @@ -737,11 +737,11 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> { } fn get_bounding_content_box(self) -> Rect<Au> { - window_from_node(self).root().r().page().content_box_query(self.to_trusted_node_address()) + window_from_node(self).root().r().content_box_query(self.to_trusted_node_address()) } fn get_content_boxes(self) -> Vec<Rect<Au>> { - window_from_node(self).root().r().page().content_boxes_query(self.to_trusted_node_address()) + window_from_node(self).root().r().content_boxes_query(self.to_trusted_node_address()) } // http://dom.spec.whatwg.org/#dom-parentnode-queryselector diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 27b297e21f2..30b1e6baaa0 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -8,50 +8,59 @@ use dom::bindings::codegen::Bindings::FunctionBinding::Function; use dom::bindings::codegen::Bindings::WindowBinding; use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; -use dom::bindings::codegen::InheritTypes::EventTargetCast; +use dom::bindings::codegen::InheritTypes::{NodeCast, EventTargetCast}; use dom::bindings::global::global_object_for_js_object; use dom::bindings::error::{report_pending_exception, Fallible}; use dom::bindings::error::Error::InvalidCharacter; use dom::bindings::global::GlobalRef; -use dom::bindings::js::{MutNullableJS, JSRef, Temporary}; -use dom::bindings::utils::Reflectable; +use dom::bindings::js::{MutNullableJS, JSRef, Temporary, OptionalRootable, RootedReference}; +use dom::bindings::utils::{GlobalStaticData, Reflectable, WindowProxyHandler}; use dom::browsercontext::BrowserContext; use dom::console::Console; -use dom::document::Document; +use dom::document::{Document, DocumentHelpers}; use dom::element::Element; use dom::eventtarget::{EventTarget, EventTargetHelpers, EventTargetTypeId}; use dom::location::Location; use dom::navigator::Navigator; -use dom::node::window_from_node; +use dom::node::{window_from_node, Node, TrustedNodeAddress, NodeHelpers}; use dom::performance::Performance; use dom::screen::Screen; use dom::storage::Storage; -use layout_interface::{ReflowGoal, ReflowQueryType}; +use layout_interface::{ReflowGoal, ReflowQueryType, LayoutRPC, LayoutChan, Reflow, Msg}; +use layout_interface::{ContentBoxResponse, ContentBoxesResponse}; use page::Page; use script_task::{TimerSource, ScriptChan}; use script_task::ScriptMsg; use script_traits::ScriptControlChan; use timers::{IsInterval, TimerId, TimerManager, TimerCallback}; +use devtools_traits::DevtoolsControlChan; use msg::compositor_msg::ScriptListener; -use msg::constellation_msg::LoadData; +use msg::constellation_msg::{LoadData, PipelineId, SubpageId, ConstellationChan, WindowSizeData}; use net::image_cache_task::ImageCacheTask; +use net::resource_task::ResourceTask; use net::storage_task::StorageTask; +use util::geometry::{self, Au, MAX_RECT}; use util::str::{DOMString,HTML_SPACE_CHARACTERS}; +use geom::{Point2D, Rect, Size2D}; use js::jsapi::JS_EvaluateUCScript; use js::jsapi::JSContext; use js::jsapi::{JS_GC, JS_GetRuntime}; use js::jsval::{JSVal, UndefinedValue}; -use js::rust::with_compartment; +use js::rust::{Cx, with_compartment}; use url::{Url, UrlParser}; use libc; use rustc_serialize::base64::{FromBase64, ToBase64, STANDARD}; -use std::cell::{Ref, RefMut}; +use std::cell::{Cell, Ref, RefMut}; use std::default::Default; use std::ffi::CString; +use std::mem::replace; +use std::num::Float; use std::rc::Rc; +use std::sync::mpsc::{channel, Receiver}; +use std::sync::mpsc::TryRecvError::{Empty, Disconnected}; use time; #[dom_struct] @@ -71,18 +80,80 @@ pub struct Window { screen: MutNullableJS<Screen>, session_storage: MutNullableJS<Storage>, timers: TimerManager, + + /// For providing instructions to an optional devtools server. + devtools_chan: Option<DevtoolsControlChan>, + + /// A flag to indicate whether the developer tools have requested live updates of + /// page changes. + devtools_wants_updates: Cell<bool>, + + next_subpage_id: Cell<SubpageId>, + + /// Pending resize event, if any. + resize_event: Cell<Option<WindowSizeData>>, + + /// Pipeline id associated with this page. + id: PipelineId, + + /// Subpage id associated with this page, if any. + subpage_id: Option<SubpageId>, + + /// Unique id for last reflow request; used for confirming completion reply. + last_reflow_id: Cell<uint>, + + /// Global static data related to the DOM. + dom_static: GlobalStaticData, + + /// The JavaScript context. + js_context: DOMRefCell<Option<Rc<Cx>>>, + + /// A handle for communicating messages to the layout task. + layout_chan: LayoutChan, + + /// A handle to perform RPC calls into the layout, quickly. + layout_rpc: Box<LayoutRPC+'static>, + + /// The port that we will use to join layout. If this is `None`, then layout is not running. + layout_join_port: DOMRefCell<Option<Receiver<()>>>, + + /// The current size of the window, in pixels. + window_size: Cell<WindowSizeData>, + + /// Associated resource task for use by DOM objects like XMLHttpRequest + resource_task: ResourceTask, + + /// A handle for communicating messages to the storage task. + storage_task: StorageTask, + + /// A handle for communicating messages to the constellation task. + constellation_chan: ConstellationChan, + + /// Pending scroll to fragment event, if any + fragment_name: DOMRefCell<Option<String>>, + + /// An enlarged rectangle around the page contents visible in the viewport, used + /// to prevent creating display list items for content that is far away from the viewport. + page_clip_rect: Cell<Rect<Au>>, } impl Window { pub fn get_cx(&self) -> *mut JSContext { - let js_info = self.page().js_info(); - (*js_info.as_ref().unwrap().js_context).ptr + self.js_context.borrow().as_ref().unwrap().ptr } pub fn script_chan(&self) -> Box<ScriptChan+Send> { self.script_chan.clone() } + pub fn pipeline(&self) -> PipelineId { + self.id.clone() + } + + pub fn subpage(&self) -> Option<SubpageId> { + self.subpage_id.clone() + } + pub fn control_chan<'a>(&'a self) -> &'a ScriptControlChan { &self.control_chan } @@ -103,16 +174,8 @@ impl Window { &*self.page } - pub fn page_clone(&self) -> Rc<Page> { - self.page.clone() - } - - pub fn get_url(&self) -> Url { - self.page().get_url() - } - pub fn storage_task(&self) -> StorageTask { - self.page().storage_task.clone() + self.storage_task.clone() } } @@ -197,12 +260,11 @@ impl<'a> WindowMethods for JSRef<'a, Window> { } fn Close(self) { - self.script_chan.send(ScriptMsg::ExitWindow(self.page.id.clone())).unwrap(); + self.script_chan.send(ScriptMsg::ExitWindow(self.id.clone())).unwrap(); } fn Document(self) -> Temporary<Document> { - let frame = self.page().frame(); - Temporary::new(frame.as_ref().unwrap().document.clone()) + self.browser_context().as_ref().unwrap().active_document() } fn Location(self) -> Temporary<Location> { @@ -230,7 +292,7 @@ impl<'a> WindowMethods for JSRef<'a, Window> { args, timeout, IsInterval::NonInterval, - TimerSource::FromWindow(self.page.id.clone()), + TimerSource::FromWindow(self.id.clone()), self.script_chan.clone()) } @@ -239,7 +301,7 @@ impl<'a> WindowMethods for JSRef<'a, Window> { args, timeout, IsInterval::NonInterval, - TimerSource::FromWindow(self.page.id.clone()), + TimerSource::FromWindow(self.id.clone()), self.script_chan.clone()) } @@ -252,7 +314,7 @@ impl<'a> WindowMethods for JSRef<'a, Window> { args, timeout, IsInterval::Interval, - TimerSource::FromWindow(self.page.id.clone()), + TimerSource::FromWindow(self.id.clone()), self.script_chan.clone()) } @@ -261,7 +323,7 @@ impl<'a> WindowMethods for JSRef<'a, Window> { args, timeout, IsInterval::Interval, - TimerSource::FromWindow(self.page.id.clone()), + TimerSource::FromWindow(self.id.clone()), self.script_chan.clone()) } @@ -330,10 +392,35 @@ impl<'a> WindowMethods for JSRef<'a, Window> { } pub trait WindowHelpers { + fn clear_js_context(self); + fn clear_js_context_for_script_deallocation(self); fn flush_layout(self, goal: ReflowGoal, query: ReflowQueryType); fn init_browser_context(self, doc: JSRef<Document>, frame_element: Option<JSRef<Element>>); fn load_url(self, href: DOMString); fn handle_fire_timer(self, timer_id: TimerId); + fn reflow(self, goal: ReflowGoal, query_type: ReflowQueryType); + fn join_layout(self); + fn layout(&self) -> &LayoutRPC; + fn content_box_query(self, content_box_request: TrustedNodeAddress) -> Rect<Au>; + fn content_boxes_query(self, content_boxes_request: TrustedNodeAddress) -> Vec<Rect<Au>>; + fn handle_reflow_complete_msg(self, reflow_id: uint); + fn handle_resize_inactive_msg(self, new_size: WindowSizeData); + fn set_fragment_name(self, fragment: Option<String>); + fn steal_fragment_name(self) -> Option<String>; + fn set_window_size(self, size: WindowSizeData); + fn window_size(self) -> WindowSizeData; + fn get_url(self) -> Url; + fn resource_task(self) -> ResourceTask; + fn devtools_chan(self) -> Option<DevtoolsControlChan>; + fn layout_chan(self) -> LayoutChan; + fn constellation_chan(self) -> ConstellationChan; + fn windowproxy_handler(self) -> WindowProxyHandler; + fn get_next_subpage_id(self) -> SubpageId; + fn layout_is_idle(self) -> bool; + fn set_resize_event(self, event: WindowSizeData); + fn steal_resize_event(self) -> Option<WindowSizeData>; + fn set_page_clip_rect_with_new_viewport(self, viewport: Rect<f32>) -> bool; + fn set_devtools_wants_updates(self, value: bool); fn IndexedGetter(self, _index: u32, _found: &mut bool) -> Option<Temporary<Window>>; fn thaw(self); fn freeze(self); @@ -373,8 +460,132 @@ impl<'a, T: Reflectable> ScriptHelpers for JSRef<'a, T> { } impl<'a> WindowHelpers for JSRef<'a, Window> { + fn clear_js_context(self) { + *self.js_context.borrow_mut() = None; + *self.browser_context.borrow_mut() = None; + } + + #[allow(unsafe_blocks)] + fn clear_js_context_for_script_deallocation(self) { + unsafe { + *self.js_context.borrow_for_script_deallocation() = None; + *self.browser_context.borrow_for_script_deallocation() = None; + } + } + fn flush_layout(self, goal: ReflowGoal, query: ReflowQueryType) { - self.page().flush_layout(goal, query); + self.reflow(goal, query); + } + + /// Reflows the page if it's possible to do so and the page is dirty. This method will wait + /// for the layout thread to complete (but see the `TODO` below). If there is no window size + /// yet, the page is presumed invisible and no reflow is performed. + /// + /// TODO(pcwalton): Only wait for style recalc, since we have off-main-thread layout. + fn reflow(self, goal: ReflowGoal, query_type: ReflowQueryType) { + let document = self.Document().root(); + let root = document.r().GetDocumentElement().root(); + let root = match root.r() { + Some(root) => root, + None => return, + }; + + debug!("script: performing reflow for goal {:?}", goal); + + let root: JSRef<Node> = NodeCast::from_ref(root); + if !root.get_has_dirty_descendants() { + debug!("root has no dirty descendants; avoiding reflow"); + return + } + + debug!("script: performing reflow for goal {:?}", goal); + + // Layout will let us know when it's done. + let (join_chan, join_port) = channel(); + + { + let mut layout_join_port = self.layout_join_port.borrow_mut(); + *layout_join_port = Some(join_port); + } + + let last_reflow_id = &self.last_reflow_id; + last_reflow_id.set(last_reflow_id.get() + 1); + + let window_size = self.window_size.get(); + + // Send new document and relevant styles to layout. + let reflow = box Reflow { + document_root: root.to_trusted_node_address(), + url: self.get_url(), + iframe: self.subpage_id.is_some(), + goal: goal, + window_size: window_size, + script_chan: self.control_chan.clone(), + script_join_chan: join_chan, + id: last_reflow_id.get(), + query_type: query_type, + page_clip_rect: self.page_clip_rect.get(), + }; + + let LayoutChan(ref chan) = self.layout_chan; + chan.send(Msg::Reflow(reflow)).unwrap(); + + debug!("script: layout forked"); + + self.join_layout(); + } + + // FIXME(cgaebel): join_layout is racey. What if the compositor triggers a + // reflow between the "join complete" message and returning from this + // function? + + /// Sends a ping to layout and waits for the response. The response will arrive when the + /// layout task has finished any pending request messages. + fn join_layout(self) { + let mut layout_join_port = self.layout_join_port.borrow_mut(); + if let Some(join_port) = replace(&mut *layout_join_port, None) { + match join_port.try_recv() { + Err(Empty) => { + info!("script: waiting on layout"); + join_port.recv().unwrap(); + } + Ok(_) => {} + Err(Disconnected) => { + panic!("Layout task failed while script was waiting for a result."); + } + } + + debug!("script: layout joined") + } + } + + fn layout(&self) -> &LayoutRPC { + &*self.layout_rpc + } + + fn content_box_query(self, content_box_request: TrustedNodeAddress) -> Rect<Au> { + self.flush_layout(ReflowGoal::ForScriptQuery, ReflowQueryType::ContentBoxQuery(content_box_request)); + self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough? + let ContentBoxResponse(rect) = self.layout_rpc.content_box(); + rect + } + + fn content_boxes_query(self, content_boxes_request: TrustedNodeAddress) -> Vec<Rect<Au>> { + self.flush_layout(ReflowGoal::ForScriptQuery, ReflowQueryType::ContentBoxesQuery(content_boxes_request)); + self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough? + let ContentBoxesResponse(rects) = self.layout_rpc.content_boxes(); + rects + } + + fn handle_reflow_complete_msg(self, reflow_id: uint) { + let last_reflow_id = self.last_reflow_id.get(); + if last_reflow_id == reflow_id { + *self.layout_join_port.borrow_mut() = None; + } + } + + fn handle_resize_inactive_msg(self, new_size: WindowSizeData) { + self.window_size.set(new_size); } fn init_browser_context(self, doc: JSRef<Document>, frame_element: Option<JSRef<Element>>) { @@ -383,17 +594,17 @@ impl<'a> WindowHelpers for JSRef<'a, Window> { /// Commence a new URL load which will either replace this window or scroll to a fragment. fn load_url(self, href: DOMString) { - let base_url = self.page().get_url(); + let base_url = self.get_url(); debug!("current page url is {}", base_url); let url = UrlParser::new().base_url(&base_url).parse(href.as_slice()); // FIXME: handle URL parse errors more gracefully. let url = url.unwrap(); match url.fragment { Some(fragment) => { - self.script_chan.send(ScriptMsg::TriggerFragment(self.page.id, fragment)).unwrap(); + self.script_chan.send(ScriptMsg::TriggerFragment(self.id, fragment)).unwrap(); }, None => { - self.script_chan.send(ScriptMsg::TriggerLoad(self.page.id, LoadData::new(url))).unwrap(); + self.script_chan.send(ScriptMsg::TriggerLoad(self.id, LoadData::new(url))).unwrap(); } } } @@ -403,6 +614,97 @@ impl<'a> WindowHelpers for JSRef<'a, Window> { self.flush_layout(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery); } + fn set_fragment_name(self, fragment: Option<String>) { + *self.fragment_name.borrow_mut() = fragment; + } + + fn steal_fragment_name(self) -> Option<String> { + self.fragment_name.borrow_mut().take() + } + + fn set_window_size(self, size: WindowSizeData) { + self.window_size.set(size); + } + + fn window_size(self) -> WindowSizeData { + self.window_size.get() + } + + fn get_url(self) -> Url { + let doc = self.Document().root(); + doc.r().url() + } + + fn resource_task(self) -> ResourceTask { + self.resource_task.clone() + } + + fn devtools_chan(self) -> Option<DevtoolsControlChan> { + self.devtools_chan.clone() + } + + fn layout_chan(self) -> LayoutChan { + self.layout_chan.clone() + } + + fn constellation_chan(self) -> ConstellationChan { + self.constellation_chan.clone() + } + + fn windowproxy_handler(self) -> WindowProxyHandler { + self.dom_static.windowproxy_handler + } + + fn get_next_subpage_id(self) -> SubpageId { + let subpage_id = self.next_subpage_id.get(); + let SubpageId(id_num) = subpage_id; + self.next_subpage_id.set(SubpageId(id_num + 1)); + subpage_id + } + + fn layout_is_idle(self) -> bool { + self.layout_join_port.borrow().is_none() + } + + fn set_resize_event(self, event: WindowSizeData) { + self.resize_event.set(Some(event)); + } + + fn steal_resize_event(self) -> Option<WindowSizeData> { + let event = self.resize_event.get(); + self.resize_event.set(None); + event + } + + fn set_page_clip_rect_with_new_viewport(self, viewport: Rect<f32>) -> bool { + // We use a clipping rectangle that is five times the size of the of the viewport, + // so that we don't collect display list items for areas too far outside the viewport, + // but also don't trigger reflows every time the viewport changes. + static VIEWPORT_EXPANSION: f32 = 2.0; // 2 lengths on each side plus original length is 5 total. + let proposed_clip_rect = geometry::f32_rect_to_au_rect( + viewport.inflate(viewport.size.width * VIEWPORT_EXPANSION, + viewport.size.height * VIEWPORT_EXPANSION)); + let clip_rect = self.page_clip_rect.get(); + if proposed_clip_rect == clip_rect { + return false; + } + + let had_clip_rect = clip_rect != MAX_RECT; + if had_clip_rect && !should_move_clip_rect(clip_rect, viewport) { + return false; + } + + self.page_clip_rect.set(proposed_clip_rect); + + // If we didn't have a clip rect, the previous display doesn't need rebuilding + // because it was built for infinite clip (MAX_RECT). + had_clip_rect + } + + fn set_devtools_wants_updates(self, value: bool) { + self.devtools_wants_updates.set(value); + } + // https://html.spec.whatwg.org/multipage/browsers.html#accessing-other-browsing-contexts fn IndexedGetter(self, _index: u32, _found: &mut bool) -> Option<Temporary<Window>> { None @@ -419,13 +721,28 @@ impl<'a> WindowHelpers for JSRef<'a, Window> { } impl Window { - pub fn new(cx: *mut JSContext, + pub fn new(js_context: Rc<Cx>, page: Rc<Page>, script_chan: Box<ScriptChan+Send>, control_chan: ScriptControlChan, compositor: Box<ScriptListener+'static>, - image_cache_task: ImageCacheTask) + image_cache_task: ImageCacheTask, + resource_task: ResourceTask, + storage_task: StorageTask, + devtools_chan: Option<DevtoolsControlChan>, + constellation_chan: ConstellationChan, + layout_chan: LayoutChan, + id: PipelineId, + subpage_id: Option<SubpageId>, + window_size: WindowSizeData) -> Temporary<Window> { + let layout_rpc: Box<LayoutRPC> = { + let (rpc_send, rpc_recv) = channel(); + let LayoutChan(ref lchan) = layout_chan; + lchan.send(Msg::GetRPC(rpc_send)).unwrap(); + rpc_recv.recv().unwrap() + }; + let win = box Window { eventtarget: EventTarget::new_inherited(EventTargetTypeId::Window), script_chan: script_chan, @@ -435,6 +752,7 @@ impl Window { page: page, navigator: Default::default(), image_cache_task: image_cache_task, + devtools_chan: devtools_chan, browser_context: DOMRefCell::new(None), performance: Default::default(), navigation_start: time::get_time().sec as u64, @@ -442,8 +760,43 @@ impl Window { screen: Default::default(), session_storage: Default::default(), timers: TimerManager::new(), + id: id, + subpage_id: subpage_id, + dom_static: GlobalStaticData::new(), + js_context: DOMRefCell::new(Some(js_context.clone())), + resource_task: resource_task, + storage_task: storage_task, + constellation_chan: constellation_chan, + page_clip_rect: Cell::new(MAX_RECT), + fragment_name: DOMRefCell::new(None), + last_reflow_id: Cell::new(0), + resize_event: Cell::new(None), + next_subpage_id: Cell::new(SubpageId(0)), + layout_chan: layout_chan, + layout_rpc: layout_rpc, + layout_join_port: DOMRefCell::new(None), + window_size: Cell::new(window_size), + devtools_wants_updates: Cell::new(false), }; - WindowBinding::Wrap(cx, win) + WindowBinding::Wrap(js_context.ptr, win) } } + +fn should_move_clip_rect(clip_rect: Rect<Au>, new_viewport: Rect<f32>) -> bool{ + let clip_rect = Rect(Point2D(geometry::to_frac_px(clip_rect.origin.x) as f32, + geometry::to_frac_px(clip_rect.origin.y) as f32), + Size2D(geometry::to_frac_px(clip_rect.size.width) as f32, + geometry::to_frac_px(clip_rect.size.height) as f32)); + + // We only need to move the clip rect if the viewport is getting near the edge of + // our preexisting clip rect. We use half of the size of the viewport as a heuristic + // for "close." + static VIEWPORT_SCROLL_MARGIN_SIZE: f32 = 0.5; + let viewport_scroll_margin = new_viewport.size * VIEWPORT_SCROLL_MARGIN_SIZE; + + (clip_rect.origin.x - new_viewport.origin.x).abs() <= viewport_scroll_margin.width || + (clip_rect.max_x() - new_viewport.max_x()).abs() <= viewport_scroll_margin.width || + (clip_rect.origin.y - new_viewport.origin.y).abs() <= viewport_scroll_margin.height || + (clip_rect.max_y() - new_viewport.max_y()).abs() <= viewport_scroll_margin.height +} diff --git a/components/script/page.rs b/components/script/page.rs index 3800b5b3950..c080be4d16c 100644 --- a/components/script/page.rs +++ b/components/script/page.rs @@ -3,39 +3,13 @@ * 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::codegen::InheritTypes::NodeCast; -use dom::bindings::js::{JS, JSRef, Temporary, OptionalRootable}; -use dom::bindings::utils::GlobalStaticData; +use dom::bindings::js::{JS, Temporary, Unrooted}; use dom::document::{Document, DocumentHelpers}; -use dom::element::Element; -use dom::node::{Node, NodeHelpers}; +use dom::node::NodeHelpers; use dom::window::Window; -use devtools_traits::DevtoolsControlChan; -use layout_interface::{ - ContentBoxResponse, ContentBoxesResponse, - HitTestResponse, LayoutChan, LayoutRPC, MouseOverResponse, Msg, Reflow, - ReflowGoal, ReflowQueryType, - TrustedNodeAddress -}; -use script_traits::{UntrustedNodeAddress, ScriptControlChan}; -use geom::{Point2D, Rect, Size2D}; -use js::rust::Cx; -use msg::compositor_msg::ScriptListener; -use msg::constellation_msg::{ConstellationChan, WindowSizeData}; use msg::constellation_msg::{PipelineId, SubpageId}; -use net::resource_task::ResourceTask; -use net::storage_task::StorageTask; -use util::geometry::{Au, MAX_RECT}; -use util::geometry; -use util::str::DOMString; use util::smallvec::SmallVec; -use std::cell::{Cell, Ref, RefMut}; -use std::sync::mpsc::{channel, Receiver}; -use std::sync::mpsc::TryRecvError::{Empty, Disconnected}; -use std::mem::replace; -use std::num::Float; use std::rc::Rc; use url::Url; @@ -43,30 +17,13 @@ use url::Url; #[jstraceable] pub struct Page { /// Pipeline id associated with this page. - pub id: PipelineId, + id: PipelineId, /// Subpage id associated with this page, if any. - pub subpage_id: Option<SubpageId>, + subpage_id: Option<SubpageId>, - /// Unique id for last reflow request; used for confirming completion reply. - pub last_reflow_id: Cell<uint>, - - /// The outermost frame containing the document, window, and page URL. - pub frame: DOMRefCell<Option<Frame>>, - - /// A handle for communicating messages to the layout task. - pub layout_chan: LayoutChan, - - /// A handle to perform RPC calls into the layout, quickly. - layout_rpc: Box<LayoutRPC+'static>, - - /// The port that we will use to join layout. If this is `None`, then layout is not running. - pub layout_join_port: DOMRefCell<Option<Receiver<()>>>, - - /// The current size of the window, in pixels. - pub window_size: Cell<WindowSizeData>, - - js_info: DOMRefCell<Option<JSPageInfo>>, + /// The outermost frame containing the document and window. + frame: DOMRefCell<Option<Frame>>, /// Cached copy of the most recent url loaded by the script, after all redirections. /// TODO(tkuehn): this currently does not follow any particular caching policy @@ -74,36 +31,8 @@ pub struct Page { /// when reloading. url: DOMRefCell<(Url, bool)>, - next_subpage_id: Cell<SubpageId>, - - /// Pending resize event, if any. - pub resize_event: Cell<Option<WindowSizeData>>, - - /// Pending scroll to fragment event, if any - pub fragment_name: DOMRefCell<Option<String>>, - - /// Associated resource task for use by DOM objects like XMLHttpRequest - pub resource_task: ResourceTask, - - /// A handle for communicating messages to the storage task. - pub storage_task: StorageTask, - - /// A handle for communicating messages to the constellation task. - pub constellation_chan: ConstellationChan, - // Child Pages. pub children: DOMRefCell<Vec<Rc<Page>>>, - - /// An enlarged rectangle around the page contents visible in the viewport, used - /// to prevent creating display list items for content that is far away from the viewport. - pub page_clip_rect: Cell<Rect<Au>>, - - /// A flag to indicate whether the developer tools have requested live updates of - /// page changes. - pub devtools_wants_updates: Cell<bool>, - - /// For providing instructions to an optional devtools server. - pub devtools_chan: Option<DevtoolsControlChan>, } pub struct PageIterator { @@ -133,71 +62,26 @@ impl IterablePage for Rc<Page> { } impl Page { - pub fn new(id: PipelineId, subpage_id: Option<SubpageId>, - layout_chan: LayoutChan, - window_size: WindowSizeData, - resource_task: ResourceTask, - storage_task: StorageTask, - constellation_chan: ConstellationChan, - js_context: Rc<Cx>, - devtools_chan: Option<DevtoolsControlChan>, - url: Url) -> Page { - let js_info = JSPageInfo { - dom_static: GlobalStaticData::new(), - js_context: js_context, - }; - let layout_rpc: Box<LayoutRPC> = { - let (rpc_send, rpc_recv) = channel(); - let LayoutChan(ref lchan) = layout_chan; - lchan.send(Msg::GetRPC(rpc_send)).unwrap(); - rpc_recv.recv().unwrap() - }; + pub fn new(id: PipelineId, subpage_id: Option<SubpageId>, url: Url) -> Page { Page { id: id, subpage_id: subpage_id, frame: DOMRefCell::new(None), - layout_chan: layout_chan, - layout_rpc: layout_rpc, - layout_join_port: DOMRefCell::new(None), - window_size: Cell::new(window_size), - js_info: DOMRefCell::new(Some(js_info)), url: DOMRefCell::new((url, true)), - next_subpage_id: Cell::new(SubpageId(0)), - resize_event: Cell::new(None), - fragment_name: DOMRefCell::new(None), - last_reflow_id: Cell::new(0), - resource_task: resource_task, - storage_task: storage_task, - constellation_chan: constellation_chan, children: DOMRefCell::new(vec!()), - page_clip_rect: Cell::new(MAX_RECT), - devtools_wants_updates: Cell::new(false), - devtools_chan: devtools_chan, } } - pub fn flush_layout(&self, goal: ReflowGoal, query: ReflowQueryType) { - let frame = self.frame(); - let window = frame.as_ref().unwrap().window.root(); - self.reflow(goal, window.r().control_chan().clone(), &mut **window.r().compositor(), query); - } - - pub fn layout(&self) -> &LayoutRPC { - &*self.layout_rpc + pub fn window(&self) -> Temporary<Window> { + Temporary::new(self.frame.borrow().as_ref().unwrap().window.clone()) } - pub fn content_box_query(&self, content_box_request: TrustedNodeAddress) -> Rect<Au> { - self.flush_layout(ReflowGoal::ForScriptQuery, ReflowQueryType::ContentBoxQuery(content_box_request)); - self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough? - let ContentBoxResponse(rect) = self.layout_rpc.content_box(); - rect + pub fn window_for_script_dealloation(&self) -> Unrooted<Window> { + Unrooted::from_js(self.frame.borrow().as_ref().unwrap().window) } - pub fn content_boxes_query(&self, content_boxes_request: TrustedNodeAddress) -> Vec<Rect<Au>> { - self.flush_layout(ReflowGoal::ForScriptQuery, ReflowQueryType::ContentBoxesQuery(content_boxes_request)); - self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough? - let ContentBoxesResponse(rects) = self.layout_rpc.content_boxes(); - rects + pub fn document(&self) -> Temporary<Document> { + Temporary::new(self.frame.borrow().as_ref().unwrap().document.clone()) } // must handle root case separately @@ -219,49 +103,6 @@ impl Page { } } } - - pub fn set_page_clip_rect_with_new_viewport(&self, viewport: Rect<f32>) -> bool { - // We use a clipping rectangle that is five times the size of the of the viewport, - // so that we don't collect display list items for areas too far outside the viewport, - // but also don't trigger reflows every time the viewport changes. - static VIEWPORT_EXPANSION: f32 = 2.0; // 2 lengths on each side plus original length is 5 total. - let proposed_clip_rect = geometry::f32_rect_to_au_rect( - viewport.inflate(viewport.size.width * VIEWPORT_EXPANSION, - viewport.size.height * VIEWPORT_EXPANSION)); - let clip_rect = self.page_clip_rect.get(); - if proposed_clip_rect == clip_rect { - return false; - } - - let had_clip_rect = clip_rect != MAX_RECT; - if had_clip_rect && !should_move_clip_rect(clip_rect, viewport) { - return false; - } - - self.page_clip_rect.set(proposed_clip_rect); - - // If we didn't have a clip rect, the previous display doesn't need rebuilding - // because it was built for infinite clip (MAX_RECT). - had_clip_rect - } - - pub fn send_title_to_compositor(&self) { - match *self.frame() { - None => {} - Some(ref frame) => { - let window = frame.window.root(); - let document = frame.document.root(); - window.r().compositor().set_title(self.id, Some(document.r().Title())); - } - } - } - - pub fn dirty_all_nodes(&self) { - match *self.frame.borrow() { - None => {} - Some(ref frame) => frame.document.root().r().dirty_all_nodes(), - } - } } impl Iterator for PageIterator { @@ -281,200 +122,17 @@ impl Iterator for PageIterator { } impl Page { - pub fn mut_js_info<'a>(&'a self) -> RefMut<'a, Option<JSPageInfo>> { - self.js_info.borrow_mut() - } - - pub unsafe fn unsafe_mut_js_info<'a>(&'a self) -> &'a mut Option<JSPageInfo> { - self.js_info.borrow_for_script_deallocation() - } - - pub fn js_info<'a>(&'a self) -> Ref<'a, Option<JSPageInfo>> { - self.js_info.borrow() - } - - pub fn url<'a>(&'a self) -> Ref<'a, (Url, bool)> { - self.url.borrow() - } - - pub fn mut_url<'a>(&'a self) -> RefMut<'a, (Url, bool)> { - self.url.borrow_mut() - } - - pub fn frame<'a>(&'a self) -> Ref<'a, Option<Frame>> { - self.frame.borrow() - } - - pub fn mut_frame<'a>(&'a self) -> RefMut<'a, Option<Frame>> { - self.frame.borrow_mut() - } - - pub fn get_next_subpage_id(&self) -> SubpageId { - let subpage_id = self.next_subpage_id.get(); - let SubpageId(id_num) = subpage_id; - self.next_subpage_id.set(SubpageId(id_num + 1)); - subpage_id + pub fn set_reflow_status(&self, status: bool) -> bool { + let old = (*self.url.borrow()).1; + (*self.url.borrow_mut()).1 = status; + old } - pub fn get_url(&self) -> Url { - self.url().0.clone() - } - - // FIXME(cgaebel): join_layout is racey. What if the compositor triggers a - // reflow between the "join complete" message and returning from this - // function? - - /// Sends a ping to layout and waits for the response. The response will arrive when the - /// layout task has finished any pending request messages. - fn join_layout(&self) { - let mut layout_join_port = self.layout_join_port.borrow_mut(); - if let Some(join_port) = replace(&mut *layout_join_port, None) { - match join_port.try_recv() { - Err(Empty) => { - info!("script: waiting on layout"); - join_port.recv().unwrap(); - } - Ok(_) => {} - Err(Disconnected) => { - panic!("Layout task failed while script was waiting for a result."); - } - } - - debug!("script: layout joined") - } - } - - /// Reflows the page if it's possible to do so and the page is dirty. This method will wait - /// for the layout thread to complete (but see the `TODO` below). If there is no window size - /// yet, the page is presumed invisible and no reflow is performed. - /// - /// TODO(pcwalton): Only wait for style recalc, since we have off-main-thread layout. - pub fn reflow(&self, - goal: ReflowGoal, - script_chan: ScriptControlChan, - _: &mut ScriptListener, - query_type: ReflowQueryType) { - let root = match *self.frame() { - None => return, - Some(ref frame) => { - frame.document.root().r().GetDocumentElement() - } - }; - - let root = match root.root() { - None => return, - Some(root) => root, - }; - - debug!("script: performing reflow for goal {:?}", goal); - - let root: JSRef<Node> = NodeCast::from_ref(root.r()); - if !root.get_has_dirty_descendants() { - debug!("root has no dirty descendants; avoiding reflow"); - return - } - - debug!("script: performing reflow for goal {:?}", goal); - - // Layout will let us know when it's done. - let (join_chan, join_port) = channel(); - - { - let mut layout_join_port = self.layout_join_port.borrow_mut(); - *layout_join_port = Some(join_port); - } - - let last_reflow_id = &self.last_reflow_id; - last_reflow_id.set(last_reflow_id.get() + 1); - - let window_size = self.window_size.get(); - - // Send new document and relevant styles to layout. - let reflow = box Reflow { - document_root: root.to_trusted_node_address(), - url: self.get_url(), - iframe: self.subpage_id.is_some(), - goal: goal, - window_size: window_size, - script_chan: script_chan, - script_join_chan: join_chan, - id: last_reflow_id.get(), - query_type: query_type, - page_clip_rect: self.page_clip_rect.get(), - }; - - let LayoutChan(ref chan) = self.layout_chan; - chan.send(Msg::Reflow(reflow)).unwrap(); - - debug!("script: layout forked"); - - self.join_layout(); - } - - /// Attempt to find a named element in this page's document. - pub fn find_fragment_node(&self, fragid: DOMString) -> Option<Temporary<Element>> { - let document = self.frame().as_ref().unwrap().document.root(); - document.r().find_fragment_node(fragid) - } - - pub fn hit_test(&self, point: &Point2D<f32>) -> Option<UntrustedNodeAddress> { - let frame = self.frame(); - let document = frame.as_ref().unwrap().document.root(); - let root = match document.r().GetDocumentElement().root() { - None => return None, - Some(root) => root, - }; - let root: JSRef<Node> = NodeCast::from_ref(root.r()); - let address = match self.layout().hit_test(root.to_trusted_node_address(), *point) { - Ok(HitTestResponse(node_address)) => { - Some(node_address) - } - Err(()) => { - debug!("layout query error"); - None - } - }; - address - } - - pub fn get_nodes_under_mouse(&self, point: &Point2D<f32>) -> Vec<UntrustedNodeAddress> { - let mut results = vec!(); - let frame = self.frame(); - let document = frame.as_ref().unwrap().document.root(); - match document.r().GetDocumentElement().root() { - Some(root) => { - let root: JSRef<Node> = NodeCast::from_ref(root.r()); - match self.layout().mouse_over(root.to_trusted_node_address(), *point) { - Ok(MouseOverResponse(node_addresses)) => { - results = node_addresses; - } - Err(()) => {} - }; - } - None => {} - } - results + pub fn set_frame(&self, frame: Option<Frame>) { + *self.frame.borrow_mut() = frame; } } -fn should_move_clip_rect(clip_rect: Rect<Au>, new_viewport: Rect<f32>) -> bool{ - let clip_rect = Rect(Point2D(geometry::to_frac_px(clip_rect.origin.x) as f32, - geometry::to_frac_px(clip_rect.origin.y) as f32), - Size2D(geometry::to_frac_px(clip_rect.size.width) as f32, - geometry::to_frac_px(clip_rect.size.height) as f32)); - - // We only need to move the clip rect if the viewport is getting near the edge of - // our preexisting clip rect. We use half of the size of the viewport as a heuristic - // for "close." - static VIEWPORT_SCROLL_MARGIN_SIZE: f32 = 0.5; - let viewport_scroll_margin = new_viewport.size * VIEWPORT_SCROLL_MARGIN_SIZE; - - (clip_rect.origin.x - new_viewport.origin.x).abs() <= viewport_scroll_margin.width || - (clip_rect.max_x() - new_viewport.max_x()).abs() <= viewport_scroll_margin.width || - (clip_rect.origin.y - new_viewport.origin.y).abs() <= viewport_scroll_margin.height || - (clip_rect.max_y() - new_viewport.max_y()).abs() <= viewport_scroll_margin.height -} - /// Information for one frame in the browsing context. #[jstraceable] #[must_root] @@ -484,12 +142,3 @@ pub struct Frame { /// The window object for this frame. pub window: JS<Window>, } - -/// Encapsulation of the javascript information associated with each frame. -#[jstraceable] -pub struct JSPageInfo { - /// Global static data related to the DOM. - pub dom_static: GlobalStaticData, - /// The JavaScript context. - pub js_context: Rc<Cx>, -} diff --git a/components/script/script_task.rs b/components/script/script_task.rs index 28a2e6437c0..de9f36fab24 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -23,7 +23,7 @@ use dom::element::{Element, AttributeHandlers}; use dom::event::{Event, EventHelpers}; use dom::uievent::UIEvent; use dom::eventtarget::EventTarget; -use dom::node::{self, Node, NodeHelpers, NodeDamage}; +use dom::node::{self, Node, NodeHelpers, NodeDamage, window_from_node}; use dom::window::{Window, WindowHelpers, ScriptHelpers}; use dom::worker::{Worker, TrustedWorkerAddress}; use parse::html::{HTMLInput, parse_html}; @@ -74,7 +74,6 @@ use libc; use std::any::Any; use std::borrow::ToOwned; use std::cell::Cell; -use std::mem::replace; use std::num::ToPrimitive; use std::rc::Rc; use std::result::Result; @@ -275,7 +274,8 @@ impl<'a> Drop for ScriptMemoryFailsafe<'a> { unsafe { let page = owner.page.borrow_for_script_deallocation(); for page in page.iter() { - *page.unsafe_mut_js_info() = None; + let window = page.window().root(); + window.r().clear_js_context_for_script_deallocation(); } *owner.js_context.borrow_for_script_deallocation() = None; } @@ -475,14 +475,13 @@ impl ScriptTask { if let Some(ref page) = page.as_ref() { for page in page.iter() { // Only process a resize if layout is idle. - let layout_join_port = page.layout_join_port.borrow(); - if layout_join_port.is_none() { - let mut resize_event = page.resize_event.get(); - match resize_event.take() { - Some(size) => resizes.push((page.id, size)), + let window = page.window().root(); + if window.r().layout_is_idle() { + let resize_event = window.r().steal_resize_event(); + match resize_event { + Some(size) => resizes.push((window.r().pipeline(), size)), None => () } - page.resize_event.set(None); } } } @@ -654,7 +653,8 @@ impl ScriptTask { let page = self.page.borrow(); if let Some(ref page) = page.as_ref() { if let Some(ref page) = page.find(id) { - page.resize_event.set(Some(size)); + let window = page.window().root(); + window.r().set_resize_event(size); return; } } @@ -670,7 +670,8 @@ impl ScriptTask { let page = self.page.borrow(); if let Some(page) = page.as_ref() { if let Some(ref inner_page) = page.find(id) { - if inner_page.set_page_clip_rect_with_new_viewport(rect) { + let window = inner_page.window().root(); + if window.r().set_page_clip_rect_with_new_viewport(rect) { let page = get_page(page, id); self.force_reflow(&*page); } @@ -699,10 +700,11 @@ impl ScriptTask { whose parent has a PipelineId which does not correspond to a pipeline in the script task's page tree. This is a bug."); + let parent_window = parent_page.window().root(); let chan = layout_chan.downcast_ref::<Sender<layout_interface::Msg>>().unwrap(); let layout_chan = LayoutChan(chan.clone()); let new_load = InProgressLoad::new(new_pipeline_id, Some((old_pipeline_id, subpage_id)), - layout_chan, parent_page.window_size.get(), + layout_chan, parent_window.r().window_size(), load_data.url.clone()); self.start_page_load(new_load, load_data); } @@ -712,8 +714,7 @@ impl ScriptTask { let page = self.root_page(); let page = page.find(id).expect("ScriptTask: received fire timer msg for a pipeline ID not associated with this script task. This is a bug."); - let frame = page.frame(); - let window = frame.as_ref().unwrap().window.root(); + let window = page.window().root(); window.r().handle_fire_timer(timer_id); } @@ -722,8 +723,7 @@ impl ScriptTask { let page = self.root_page(); let page = page.find(id).expect("ScriptTask: received freeze msg for a pipeline ID not associated with this script task. This is a bug."); - let frame = page.frame(); - let window = frame.as_ref().unwrap().window.root(); + let window = page.window().root(); window.r().freeze(); } @@ -732,8 +732,7 @@ impl ScriptTask { let page = self.root_page(); let page = page.find(id).expect("ScriptTask: received thaw msg for a pipeline ID not associated with this script task. This is a bug."); - let frame = page.frame(); - let window = frame.as_ref().unwrap().window.root(); + let window = page.window().root(); window.r().thaw(); } @@ -742,12 +741,7 @@ impl ScriptTask { // should exist. let page = self.root_page().find(pipeline_id).unwrap(); - // Pull out the `needs_reflow` flag explicitly because `reflow` can ask for the - // page's URL, and we can't be holding a borrow on that URL (#4402). - let needed_reflow = { - let &mut (_, ref mut needs_reflow) = &mut *page.mut_url(); - replace(needs_reflow, false) - }; + let needed_reflow = page.set_reflow_status(false); if needed_reflow { self.force_reflow(&*page); } @@ -760,13 +754,10 @@ impl ScriptTask { let page = page.find(pipeline_id).expect( "ScriptTask: received a load message for a layout channel that is not associated \ with this script task. This is a bug."); - let last_reflow_id = page.last_reflow_id.get(); - if last_reflow_id == reflow_id { - let mut layout_join_port = page.layout_join_port.borrow_mut(); - *layout_join_port = None; - } + let window = page.window().root(); + window.r().handle_reflow_complete_msg(reflow_id); - let doc = page.frame().as_ref().unwrap().document.root(); + let doc = page.document().root(); let html_element = doc.r().GetDocumentElement().root(); let reftest_wait = html_element.r().map_or(false, |elem| elem.has_class(&Atom::from_slice("reftest-wait"))); @@ -780,10 +771,9 @@ impl ScriptTask { 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."); - page.window_size.set(new_size); - match &mut *page.mut_url() { - &mut (_, ref mut needs_reflow) => *needs_reflow = true, - } + let window = page.window().root(); + window.r().set_window_size(new_size); + page.set_reflow_status(true); } /// We have gotten a window.close from script, which we pass on to the compositor. @@ -811,7 +801,9 @@ impl ScriptTask { /// Handles a request for the window title. fn handle_get_title_msg(&self, pipeline_id: PipelineId) { - get_page(&self.root_page(), pipeline_id).send_title_to_compositor(); + let page = get_page(&self.root_page(), pipeline_id); + let document = page.document().root(); + document.r().send_title_to_compositor(); } /// Handles a request to exit the script task and shut down layout. @@ -819,7 +811,8 @@ impl ScriptTask { fn handle_exit_pipeline_msg(&self, id: PipelineId, exit_type: PipelineExitType) -> bool { // If root is being exited, shut down all pages let page = self.root_page(); - if page.id == id { + let window = page.window().root(); + if window.r().pipeline() == id { debug!("shutting down layout for root page {:?}", id); *self.js_context.borrow_mut() = None; shut_down_layout(&page, (*self.js_runtime).ptr, exit_type); @@ -827,18 +820,10 @@ impl ScriptTask { } // otherwise find just the matching page and exit all sub-pages - match page.find(id) { - Some(ref mut page) => { - shut_down_layout(&*page, (*self.js_runtime).ptr, exit_type); - page.remove(id); - false - } - // TODO(tkuehn): pipeline closing is currently duplicated across - // script and constellation, which can cause this to happen. Constellation - // needs to be smarter about exiting pipelines. - None => false, + if let Some(ref mut child_page) = page.remove(id) { + shut_down_layout(&*child_page, (*self.js_runtime).ptr, exit_type); } - + return false; } /// The entry point to document loading. Defines bindings, sets up the window and document @@ -860,7 +845,7 @@ impl ScriptTask { // denies access to most properties (per // https://github.com/servo/servo/issues/3939#issuecomment-62287025). borrowed_page.find(parent_id).and_then(|page| { - let doc = page.frame().as_ref().unwrap().document.root(); + let doc = page.document().root(); let doc: JSRef<Node> = NodeCast::from_ref(doc.r()); doc.traverse_preorder() @@ -881,10 +866,7 @@ impl ScriptTask { let cx = cx.as_ref().unwrap(); let page = Rc::new(Page::new(incomplete.pipeline_id, incomplete.subpage_id.map(|p| p.1), - incomplete.layout_chan, incomplete.window_size, - self.resource_task.clone(), self.storage_task.clone(), - self.constellation_chan.clone(), cx.clone(), - self.devtools_chan.clone(), final_url.clone())); + final_url.clone())); if root_page_exists { *self.page.borrow_mut() = Some(page.clone()); } else if let Some((parent, _)) = incomplete.subpage_id { @@ -894,12 +876,20 @@ impl ScriptTask { } // Create the window and document objects. - let window = Window::new(cx.ptr, + let window = Window::new(cx.clone(), page.clone(), self.chan.clone(), self.control_chan.clone(), self.compositor.borrow_mut().dup(), - self.image_cache_task.clone()).root(); + self.image_cache_task.clone(), + self.resource_task.clone(), + self.storage_task.clone(), + self.devtools_chan.clone(), + self.constellation_chan.clone(), + incomplete.layout_chan, + incomplete.pipeline_id, + incomplete.subpage_id.map(|s| s.1), + incomplete.window_size).root(); let document = Document::new(window.r(), Some(final_url.clone()), IsHTMLDocument::HTMLDocument, None, @@ -910,14 +900,11 @@ impl ScriptTask { window.r().init_browser_context(document.r(), frame_element.r()); - { - // Create the root frame. - let mut frame = page.mut_frame(); - *frame = Some(Frame { - document: JS::from_rooted(document.r()), - window: JS::from_rooted(window.r()), - }); - } + // Create the root frame + page.set_frame(Some(Frame { + document: JS::from_rooted(document.r()), + window: JS::from_rooted(window.r()), + })); let is_javascript = incomplete.url.scheme.as_slice() == "javascript"; let parse_input = if is_javascript { @@ -941,11 +928,8 @@ impl ScriptTask { NodeDamage::OtherNodeDamage); window.r().flush_layout(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery); - { - // No more reflow required - let mut page_url = page.mut_url(); - (*page_url).1 = false; - } + // No more reflow required + page.set_reflow_status(false); // https://html.spec.whatwg.org/multipage/#the-end step 4 let addr: Trusted<Document> = Trusted::new(self.get_cx(), document.r(), self.chan.clone()); @@ -960,7 +944,7 @@ impl ScriptTask { let handler = Box::new(DocumentProgressHandler::new(addr, DocumentProgressTask::Load)); self.chan.send(ScriptMsg::RunnableMsg(handler)).unwrap(); - *page.fragment_name.borrow_mut() = final_url.fragment.clone(); + window.r().set_fragment_name(final_url.fragment.clone()); let ConstellationChan(ref chan) = self.constellation_chan; chan.send(ConstellationMsg::LoadComplete).unwrap(); @@ -994,11 +978,10 @@ impl ScriptTask { /// Reflows non-incrementally. fn force_reflow(&self, page: &Page) { - page.dirty_all_nodes(); - page.reflow(ReflowGoal::ForDisplay, - self.control_chan.clone(), - &mut **self.compositor.borrow_mut(), - ReflowQueryType::NoQuery); + let document = page.document().root(); + document.r().dirty_all_nodes(); + let window = window_from_node(document.r()).root(); + window.r().reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery); } /// This is the main entry point for receiving and dispatching DOM events. @@ -1025,8 +1008,7 @@ impl ScriptTask { let node_to_dirty = node::from_untrusted_node_address(self.js_runtime.ptr, *node).root(); let page = get_page(&self.root_page(), pipeline_id); - let frame = page.frame(); - let document = frame.as_ref().unwrap().document.root(); + let document = page.document().root(); document.r().content_changed(node_to_dirty.r(), NodeDamage::OtherNodeDamage); } @@ -1036,8 +1018,7 @@ impl ScriptTask { ClickEvent(_button, point) => { let page = get_page(&self.root_page(), pipeline_id); - let frame = page.frame(); - let document = frame.as_ref().unwrap().document.root(); + let document = page.document().root(); document.r().handle_click_event(self.js_runtime.ptr, _button, point); } @@ -1045,8 +1026,7 @@ impl ScriptTask { MouseUpEvent(..) => {} MouseMoveEvent(point) => { let page = get_page(&self.root_page(), pipeline_id); - let frame = page.frame(); - let document = frame.as_ref().unwrap().document.root(); + let document = page.document().root(); let mouse_over_targets = &mut *self.mouse_over_targets.borrow_mut(); if document.r().handle_mouse_move_event(self.js_runtime.ptr, point, mouse_over_targets) { @@ -1056,8 +1036,7 @@ impl ScriptTask { KeyEvent(key, state, modifiers) => { let page = get_page(&self.root_page(), pipeline_id); - let frame = page.frame(); - let document = frame.as_ref().unwrap().document.root(); + let document = page.document().root(); document.r().dispatch_key_event( key, state, modifiers, &mut *self.compositor.borrow_mut()); } @@ -1075,7 +1054,8 @@ impl ScriptTask { /// for the given pipeline. fn trigger_fragment(&self, pipeline_id: PipelineId, fragment: String) { let page = get_page(&self.root_page(), pipeline_id); - match page.find_fragment_node(fragment).root() { + let document = page.document().root(); + match document.r().find_fragment_node(fragment).root() { Some(node) => { self.scroll_fragment_point(pipeline_id, node.r()); } @@ -1085,53 +1065,36 @@ impl ScriptTask { fn handle_resize_event(&self, pipeline_id: PipelineId, new_size: WindowSizeData) { - let window = { - let page = get_page(&self.root_page(), pipeline_id); - page.window_size.set(new_size); - - let frame = page.frame(); - if frame.is_some() { - self.force_reflow(&*page); - } - - let fragment_node = - page.fragment_name - .borrow_mut() - .take() - .and_then(|name| page.find_fragment_node(name)) - .root(); - match fragment_node { - Some(node) => self.scroll_fragment_point(pipeline_id, node.r()), - None => {} - } + let page = get_page(&self.root_page(), pipeline_id); + let window = page.window().root(); + window.r().set_window_size(new_size); + self.force_reflow(&*page); + + let document = page.document().root(); + let fragment_node = window.r().steal_fragment_name() + .and_then(|name| document.r().find_fragment_node(name)) + .root(); + match fragment_node { + Some(node) => self.scroll_fragment_point(pipeline_id, node.r()), + None => {} + } - frame.as_ref().map(|frame| Temporary::new(frame.window.clone())) - }; + // http://dev.w3.org/csswg/cssom-view/#resizing-viewports + // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#event-type-resize + let uievent = UIEvent::new(window.r(), + "resize".to_owned(), false, + false, Some(window.r()), + 0i32).root(); + let event: JSRef<Event> = EventCast::from_ref(uievent.r()); - match window.root() { - Some(window) => { - // http://dev.w3.org/csswg/cssom-view/#resizing-viewports - // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#event-type-resize - let uievent = UIEvent::new(window.r(), - "resize".to_owned(), false, - false, Some(window.r()), - 0i32).root(); - let event: JSRef<Event> = EventCast::from_ref(uievent.r()); - - let wintarget: JSRef<EventTarget> = EventTargetCast::from_ref(window.r()); - event.fire(wintarget); - } - None => () - } + let wintarget: JSRef<EventTarget> = EventTargetCast::from_ref(window.r()); + event.fire(wintarget); } fn handle_reflow_event(&self, pipeline_id: PipelineId) { debug!("script got reflow event"); let page = get_page(&self.root_page(), pipeline_id); - let frame = page.frame(); - if frame.is_some() { - self.force_reflow(&*page); - } + self.force_reflow(&*page); } fn start_page_load(&self, incomplete: InProgressLoad, mut load_data: LoadData) { @@ -1167,24 +1130,25 @@ impl ScriptTask { /// Shuts down layout for the given page tree. fn shut_down_layout(page_tree: &Rc<Page>, rt: *mut JSRuntime, exit_type: PipelineExitType) { + let mut channels = vec!(); + for page in page_tree.iter() { // Tell the layout task to begin shutting down, and wait until it // processed this message. let (response_chan, response_port) = channel(); - let LayoutChan(ref chan) = page.layout_chan; + let window = page.window().root(); + let LayoutChan(chan) = window.r().layout_chan(); if chan.send(layout_interface::Msg::PrepareToExit(response_chan)).is_ok() { - response_port.recv().unwrap(); + channels.push(chan); + response_port.recv().unwrap(); } } - // Remove our references to the DOM objects in this page tree. - for page in page_tree.iter() { - *page.mut_frame() = None; - } - - // Drop our references to the JSContext, potentially triggering a GC. + // Drop our references to the JSContext and DOM objects, potentially triggering a GC. for page in page_tree.iter() { - *page.mut_js_info() = None; + let window = page.window().root(); + window.r().clear_js_context(); + page.set_frame(None); } // Force a GC to make sure that our DOM reflectors are released before we tell @@ -1194,8 +1158,7 @@ fn shut_down_layout(page_tree: &Rc<Page>, rt: *mut JSRuntime, exit_type: Pipelin } // Destroy the layout task. If there were node leaks, layout will now crash safely. - for page in page_tree.iter() { - let LayoutChan(ref chan) = page.layout_chan; + for chan in channels.into_iter() { chan.send(layout_interface::Msg::ExitNow(exit_type)).ok(); } } |