diff options
author | bors-servo <metajack+bors@gmail.com> | 2015-03-22 21:36:51 -0600 |
---|---|---|
committer | bors-servo <metajack+bors@gmail.com> | 2015-03-22 21:36:51 -0600 |
commit | 1f682d878db99651bfd26b8a28b57895f2238f87 (patch) | |
tree | c74936efd24415d3603b9658b21f4aee44c68a9b | |
parent | dfb8929b001c8d0fb6d5e63f5a9d6dcc17cb388a (diff) | |
parent | bf9b8f705019e8d0bb2ff9ff18846b3e81d4b26f (diff) | |
download | servo-1f682d878db99651bfd26b8a28b57895f2238f87.tar.gz servo-1f682d878db99651bfd26b8a28b57895f2238f87.zip |
auto merge of #5281 : glennw/servo/mozbrowser, r=jdm
-rw-r--r-- | components/compositing/constellation.rs | 28 | ||||
-rw-r--r-- | components/compositing/pipeline.rs | 1 | ||||
-rw-r--r-- | components/msg/constellation_msg.rs | 2 | ||||
-rw-r--r-- | components/script/dom/document.rs | 23 | ||||
-rw-r--r-- | components/script/dom/htmliframeelement.rs | 119 | ||||
-rw-r--r-- | components/script/dom/webidls/BrowserElement.webidl | 138 | ||||
-rw-r--r-- | components/script/dom/webidls/HTMLIFrameElement.webidl | 7 | ||||
-rw-r--r-- | components/script/dom/window.rs | 16 | ||||
-rw-r--r-- | components/script/script_task.rs | 98 | ||||
-rw-r--r-- | components/script_traits/lib.rs | 3 |
10 files changed, 394 insertions, 41 deletions
diff --git a/components/compositing/constellation.rs b/components/compositing/constellation.rs index 307a4abae53..bf61a03e94b 100644 --- a/components/compositing/constellation.rs +++ b/components/compositing/constellation.rs @@ -375,6 +375,16 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> { debug!("constellation got get-pipeline-title message"); self.handle_get_pipeline_title_msg(pipeline_id); } + ConstellationMsg::MozBrowserEvent(pipeline_id, + subpage_id, + event_name, + event_detail) => { + debug!("constellation got mozbrowser event message"); + self.handle_mozbrowser_event_msg(pipeline_id, + subpage_id, + event_name, + event_detail); + } } true } @@ -627,6 +637,24 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> { } } + fn handle_mozbrowser_event_msg(&mut self, + pipeline_id: PipelineId, + subpage_id: SubpageId, + event_name: String, + event_detail: Option<String>) { + assert!(opts::experimental_enabled()); + + // Find the script channel for the given parent pipeline, + // and pass the event to that script task. + let pipeline = self.pipeline(pipeline_id); + let ScriptControlChan(ref script_channel) = pipeline.script_chan; + let event = ConstellationControlMsg::MozBrowserEvent(pipeline_id, + subpage_id, + event_name, + event_detail); + script_channel.send(event).unwrap(); + } + fn add_or_replace_pipeline_in_frame_tree(&mut self, frame_change: FrameChange) { let evicted_frames = match frame_change.old_pipeline_id { Some(old_pipeline_id) => { diff --git a/components/compositing/pipeline.rs b/components/compositing/pipeline.rs index 1c817cc845a..8e1381e5d90 100644 --- a/components/compositing/pipeline.rs +++ b/components/compositing/pipeline.rs @@ -84,6 +84,7 @@ impl Pipeline { let (script_chan, script_port) = channel(); ScriptTaskFactory::create(None::<&mut STF>, id, + parent_info, compositor_proxy.clone_compositor_proxy(), &layout_pair, ScriptControlChan(script_chan.clone()), diff --git a/components/msg/constellation_msg.rs b/components/msg/constellation_msg.rs index ce753f3c8d0..28528a6ff8e 100644 --- a/components/msg/constellation_msg.rs +++ b/components/msg/constellation_msg.rs @@ -212,6 +212,8 @@ pub enum Msg { GetPipelineTitle(PipelineId), /// Requests that the constellation inform the compositor of the a cursor change. SetCursor(Cursor), + /// Dispatch a mozbrowser event to a given iframe. Only available in experimental mode. + MozBrowserEvent(PipelineId, SubpageId, String, Option<String>), } /// Similar to net::resource_task::LoadData diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index d6e5f49b433..c11c22b67b6 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -60,13 +60,14 @@ use dom::window::{Window, WindowHelpers, ReflowReason}; use layout_interface::{HitTestResponse, MouseOverResponse}; use msg::compositor_msg::ScriptListener; -use msg::constellation_msg::{Key, KeyState, KeyModifiers}; +use msg::constellation_msg::Msg as ConstellationMsg; +use msg::constellation_msg::{ConstellationChan, 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::{opts, namespace}; use util::str::{DOMString, split_html_space_chars}; use layout_interface::{ReflowGoal, ReflowQueryType}; @@ -1450,7 +1451,23 @@ impl DocumentProgressHandler { event.r().fire(target); }); - window_ref.reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery, ReflowReason::DocumentLoaded); + if opts::experimental_enabled() { + // If this is a child frame, and experimental mode is enabled, + // send the mozbrowserloadend event. For details, see + // https://developer.mozilla.org/en-US/docs/Web/Events/mozbrowserloadend + if let Some((containing_pipeline_id, subpage_id)) = window_ref.parent_info() { + let ConstellationChan(ref chan) = window_ref.constellation_chan(); + let event = ConstellationMsg::MozBrowserEvent(containing_pipeline_id, + subpage_id, + "mozbrowserloadend".to_owned(), + None); + chan.send(event); + } + } + + window_ref.reflow(ReflowGoal::ForDisplay, + ReflowQueryType::NoQuery, + ReflowReason::DocumentLoaded); } } diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs index 61ce736bb7e..b35fb8695f8 100644 --- a/components/script/dom/htmliframeelement.rs +++ b/components/script/dom/htmliframeelement.rs @@ -8,12 +8,18 @@ use dom::attr::AttrHelpers; use dom::bindings::codegen::Bindings::HTMLIFrameElementBinding; use dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElementMethods; use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; -use dom::bindings::codegen::InheritTypes::{NodeCast, ElementCast}; -use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLIFrameElementDerived}; +use dom::bindings::codegen::InheritTypes::{NodeCast, ElementCast, EventCast}; +use dom::bindings::codegen::InheritTypes::{EventTargetCast, HTMLElementCast, HTMLIFrameElementDerived}; +use dom::bindings::conversions::ToJSValConvertible; +use dom::bindings::error::{ErrorResult, Fallible}; +use dom::bindings::error::Error::NotSupported; +use dom::bindings::global::GlobalRef; use dom::bindings::js::{JSRef, Temporary, OptionalRootable}; +use dom::customevent::CustomEvent; use dom::document::Document; use dom::element::Element; use dom::element::AttributeHandlers; +use dom::event::{Event, EventBubbles, EventCancelable, EventHelpers}; use dom::eventtarget::{EventTarget, EventTargetTypeId}; use dom::element::ElementTypeId; use dom::htmlelement::{HTMLElement, HTMLElementTypeId}; @@ -23,13 +29,15 @@ use dom::virtualmethods::VirtualMethods; use dom::window::{Window, WindowHelpers}; use page::IterablePage; -use msg::constellation_msg::{PipelineId, SubpageId, ConstellationChan}; +use msg::constellation_msg::{PipelineId, SubpageId, ConstellationChan, NavigationDirection}; use msg::constellation_msg::IFrameSandboxState::{IFrameSandboxed, IFrameUnsandboxed}; use msg::constellation_msg::Msg as ConstellationMsg; +use util::opts; use util::str::DOMString; use string_cache::Atom; use std::ascii::AsciiExt; +use std::borrow::ToOwned; use std::cell::Cell; use url::{Url, UrlParser}; @@ -64,6 +72,7 @@ pub trait HTMLIFrameElementHelpers { fn process_the_iframe_attributes(self); fn generate_new_subpage_id(self) -> (SubpageId, Option<SubpageId>); fn navigate_child_browsing_context(self, url: Url); + fn dispatch_mozbrowser_event(self, event_name: String, event_detail: Option<String>); } impl<'a> HTMLIFrameElementHelpers for JSRef<'a, HTMLIFrameElement> { @@ -112,6 +121,11 @@ impl<'a> HTMLIFrameElementHelpers for JSRef<'a, HTMLIFrameElement> { new_subpage_id, old_subpage_id, sandboxed)).unwrap(); + + if opts::experimental_enabled() { + // https://developer.mozilla.org/en-US/docs/Web/Events/mozbrowserloadstart + self.dispatch_mozbrowser_event("mozbrowserloadstart".to_owned(), None); + } } fn process_the_iframe_attributes(self) { @@ -122,6 +136,26 @@ impl<'a> HTMLIFrameElementHelpers for JSRef<'a, HTMLIFrameElement> { self.navigate_child_browsing_context(url); } + + fn dispatch_mozbrowser_event(self, event_name: String, event_detail: Option<String>) { + // TODO(gw): Support mozbrowser event types that have detail which is not a string. + // See https://developer.mozilla.org/en-US/docs/Web/API/Using_the_Browser_API + // for a list of mozbrowser events. + assert!(opts::experimental_enabled()); + + if self.Mozbrowser() { + let window = window_from_node(self).root(); + let cx = window.r().get_cx(); + let custom_event = CustomEvent::new(GlobalRef::Window(window.r()), + event_name.to_owned(), + true, + true, + event_detail.to_jsval(cx)).root(); + let target: JSRef<EventTarget> = EventTargetCast::from_ref(self); + let event: JSRef<Event> = EventCast::from_ref(custom_event.r()); + event.fire(target); + } + } } impl HTMLIFrameElement { @@ -199,6 +233,85 @@ impl<'a> HTMLIFrameElementMethods for JSRef<'a, HTMLIFrameElement> { } }) } + + // Experimental mozbrowser implementation is based on the webidl + // present in the gecko source tree, and the documentation here: + // https://developer.mozilla.org/en-US/docs/Web/API/Using_the_Browser_API + + // TODO(gw): Use experimental codegen when it is available to avoid + // exposing these APIs. See https://github.com/servo/servo/issues/5264. + + // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#attr-mozbrowser + fn Mozbrowser(self) -> bool { + if opts::experimental_enabled() { + let element: JSRef<Element> = ElementCast::from_ref(self); + element.has_attribute(&Atom::from_slice("mozbrowser")) + } else { + false + } + } + + fn SetMozbrowser(self, value: bool) -> ErrorResult { + if opts::experimental_enabled() { + let element: JSRef<Element> = ElementCast::from_ref(self); + element.set_bool_attribute(&Atom::from_slice("mozbrowser"), value); + } + Ok(()) + } + + // https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement/goBack + fn GoBack(self) -> Fallible<()> { + if self.Mozbrowser() { + let node: JSRef<Node> = NodeCast::from_ref(self); + if node.is_in_doc() { + let window = window_from_node(self).root(); + let window = window.r(); + + let pipeline_info = Some((self.containing_page_pipeline_id().unwrap(), + self.subpage_id().unwrap())); + let ConstellationChan(ref chan) = window.constellation_chan(); + let msg = ConstellationMsg::Navigate(pipeline_info, NavigationDirection::Back); + chan.send(msg).unwrap(); + } + + Ok(()) + } else { + debug!("this frame is not mozbrowser (or experimental_enabled is false)"); + Err(NotSupported) + } + } + + // https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement/goForward + fn GoForward(self) -> Fallible<()> { + if self.Mozbrowser() { + let node: JSRef<Node> = NodeCast::from_ref(self); + if node.is_in_doc() { + let window = window_from_node(self).root(); + let window = window.r(); + + let pipeline_info = Some((self.containing_page_pipeline_id().unwrap(), + self.subpage_id().unwrap())); + let ConstellationChan(ref chan) = window.constellation_chan(); + let msg = ConstellationMsg::Navigate(pipeline_info, NavigationDirection::Forward); + chan.send(msg).unwrap(); + } + + Ok(()) + } else { + debug!("this frame is not mozbrowser (or experimental_enabled is false)"); + Err(NotSupported) + } + } + + // https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement/reload + fn Reload(self, _hardReload: bool) -> Fallible<()> { + Err(NotSupported) + } + + // https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement/stop + fn Stop(self) -> Fallible<()> { + Err(NotSupported) + } } impl<'a> VirtualMethods for JSRef<'a, HTMLIFrameElement> { diff --git a/components/script/dom/webidls/BrowserElement.webidl b/components/script/dom/webidls/BrowserElement.webidl new file mode 100644 index 00000000000..a0db071972e --- /dev/null +++ b/components/script/dom/webidls/BrowserElement.webidl @@ -0,0 +1,138 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. + */ + +//callback BrowserElementNextPaintEventCallback = void (); + +//dictionary BrowserElementDownloadOptions { +// DOMString? filename; +//}; + +[NoInterfaceObject] +interface BrowserElement { +}; + +BrowserElement implements BrowserElementCommon; +BrowserElement implements BrowserElementPrivileged; + +[NoInterfaceObject] +interface BrowserElementCommon { + //[Throws, + // Pref="dom.mozBrowserFramesEnabled", + // CheckPermissions="browser embed-widgets"] + //void setVisible(boolean visible); + + //[Throws, + // Pref="dom.mozBrowserFramesEnabled", + // CheckPermissions="browser embed-widgets"] + //DOMRequest getVisible(); + + //[Throws, + // Pref="dom.mozBrowserFramesEnabled", + // CheckPermissions="browser embed-widgets"] + //void addNextPaintListener(BrowserElementNextPaintEventCallback listener); + + //[Throws, + // Pref="dom.mozBrowserFramesEnabled", + // CheckPermissions="browser embed-widgets"] + //void removeNextPaintListener(BrowserElementNextPaintEventCallback listener); +}; + +[NoInterfaceObject] +interface BrowserElementPrivileged { + //[Throws, + // Pref="dom.mozBrowserFramesEnabled", + // CheckPermissions="browser"] + //void sendMouseEvent(DOMString type, + // unsigned long x, + // unsigned long y, + // unsigned long button, + // unsigned long clickCount, + // unsigned long modifiers); + + //[Throws, + // Pref="dom.mozBrowserFramesEnabled", + // Func="TouchEvent::PrefEnabled", + // CheckPermissions="browser"] + //void sendTouchEvent(DOMString type, + // sequence<unsigned long> identifiers, + // sequence<long> x, + // sequence<long> y, + // sequence<unsigned long> rx, + // sequence<unsigned long> ry, + // sequence<float> rotationAngles, + // sequence<float> forces, + // unsigned long count, + // unsigned long modifiers); + + [Throws, + Pref="dom.mozBrowserFramesEnabled", + CheckPermissions="browser"] + void goBack(); + + [Throws, + Pref="dom.mozBrowserFramesEnabled", + CheckPermissions="browser"] + void goForward(); + + [Throws, + Pref="dom.mozBrowserFramesEnabled", + CheckPermissions="browser"] + void reload(optional boolean hardReload = false); + + [Throws, + Pref="dom.mozBrowserFramesEnabled", + CheckPermissions="browser"] + void stop(); + + //[Throws, + // Pref="dom.mozBrowserFramesEnabled", + // CheckPermissions="browser"] + //DOMRequest download(DOMString url, + // optional BrowserElementDownloadOptions options); + + //[Throws, + // Pref="dom.mozBrowserFramesEnabled", + // CheckPermissions="browser"] + //DOMRequest purgeHistory(); + + //[Throws, + // Pref="dom.mozBrowserFramesEnabled", + // CheckPermissions="browser"] + //DOMRequest getScreenshot([EnforceRange] unsigned long width, + // [EnforceRange] unsigned long height, + // optional DOMString mimeType=""); + + //[Throws, + // Pref="dom.mozBrowserFramesEnabled", + // CheckPermissions="browser"] + //void zoom(float zoom); + + //[Throws, + // Pref="dom.mozBrowserFramesEnabled", + // CheckPermissions="browser"] + //DOMRequest getCanGoBack(); + + //[Throws, + // Pref="dom.mozBrowserFramesEnabled", + // CheckPermissions="browser"] + //DOMRequest getCanGoForward(); + + //[Throws, + // Pref="dom.mozBrowserFramesEnabled", + // CheckPermissions="browser"] + //DOMRequest getContentDimensions(); + + //[Throws, + // Pref="dom.mozBrowserFramesEnabled", + // CheckPermissions="browser"] + //DOMRequest setInputMethodActive(boolean isActive); + + // Additional |nfc-manager| permission is required for setNFCFocus API + //[Throws, + // Pref="dom.mozBrowserFramesEnabled", + // CheckPermissions="browser"] + //void setNFCFocus(boolean isFocus); +}; diff --git a/components/script/dom/webidls/HTMLIFrameElement.webidl b/components/script/dom/webidls/HTMLIFrameElement.webidl index 7768065545c..558cdfd8030 100644 --- a/components/script/dom/webidls/HTMLIFrameElement.webidl +++ b/components/script/dom/webidls/HTMLIFrameElement.webidl @@ -31,3 +31,10 @@ partial interface HTMLIFrameElement { //[TreatNullAs=EmptyString] attribute DOMString marginHeight; //[TreatNullAs=EmptyString] attribute DOMString marginWidth; }; + +partial interface HTMLIFrameElement { + [ChromeOnly,SetterThrows] + attribute boolean mozbrowser; +}; + +HTMLIFrameElement implements BrowserElement; diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 47b5c1c3149..038ec358f73 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -114,7 +114,7 @@ pub struct Window { id: PipelineId, /// Subpage id associated with this page, if any. - subpage_id: Option<SubpageId>, + parent_info: Option<(PipelineId, SubpageId)>, /// Unique id for last reflow request; used for confirming completion reply. last_reflow_id: Cell<uint>, @@ -164,11 +164,15 @@ impl Window { } pub fn pipeline(&self) -> PipelineId { - self.id.clone() + self.id } pub fn subpage(&self) -> Option<SubpageId> { - self.subpage_id.clone() + self.parent_info.map(|p| p.1) + } + + pub fn parent_info(&self) -> Option<(PipelineId, SubpageId)> { + self.parent_info } pub fn control_chan<'a>(&'a self) -> &'a ScriptControlChan { @@ -549,7 +553,7 @@ impl<'a> WindowHelpers for JSRef<'a, Window> { let reflow = box Reflow { document_root: root.to_trusted_node_address(), url: self.get_url(), - iframe: self.subpage_id.is_some(), + iframe: self.parent_info.is_some(), goal: goal, window_size: window_size, script_chan: self.control_chan.clone(), @@ -768,7 +772,7 @@ impl Window { constellation_chan: ConstellationChan, layout_chan: LayoutChan, id: PipelineId, - subpage_id: Option<SubpageId>, + parent_info: Option<(PipelineId, SubpageId)>, window_size: Option<WindowSizeData>) -> Temporary<Window> { let layout_rpc: Box<LayoutRPC> = { @@ -797,7 +801,7 @@ impl Window { local_storage: Default::default(), timers: TimerManager::new(), id: id, - subpage_id: subpage_id, + parent_info: parent_info, dom_static: GlobalStaticData::new(), js_context: DOMRefCell::new(Some(js_context.clone())), resource_task: resource_task, diff --git a/components/script/script_task.rs b/components/script/script_task.rs index 19c8c96ead0..847da174eff 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -24,6 +24,7 @@ use dom::bindings::codegen::Bindings::DocumentBinding::{DocumentMethods, Documen use dom::bindings::codegen::InheritTypes::{ElementCast, EventTargetCast, HTMLIFrameElementCast, NodeCast, EventCast}; use dom::bindings::conversions::FromJSValConvertible; use dom::bindings::conversions::StringificationBehavior; +use dom::bindings::global::GlobalRef; use dom::bindings::js::{JS, JSRef, Temporary, OptionalRootable, RootedReference}; use dom::bindings::js::{RootCollection, RootCollectionPtr}; use dom::bindings::refcounted::{LiveDOMReferences, Trusted, TrustedReference}; @@ -116,7 +117,7 @@ struct InProgressLoad { /// The pipeline which requested this load. pipeline_id: PipelineId, /// The parent pipeline and child subpage associated with this load, if any. - subpage_id: Option<(PipelineId, SubpageId)>, + parent_info: Option<(PipelineId, SubpageId)>, /// The current window size associated with this pipeline. window_size: Option<WindowSizeData>, /// Channel to the layout task associated with this pipeline. @@ -130,13 +131,13 @@ struct InProgressLoad { impl InProgressLoad { /// Create a new InProgressLoad object. fn new(id: PipelineId, - subpage_id: Option<(PipelineId, SubpageId)>, + parent_info: Option<(PipelineId, SubpageId)>, layout_chan: LayoutChan, window_size: Option<WindowSizeData>, url: Url) -> InProgressLoad { InProgressLoad { pipeline_id: id, - subpage_id: subpage_id, + parent_info: parent_info, layout_chan: layout_chan, window_size: window_size, clip_rect: None, @@ -332,6 +333,7 @@ impl ScriptTaskFactory for ScriptTask { fn create<C>(_phantom: Option<&mut ScriptTask>, id: PipelineId, + parent_info: Option<(PipelineId, SubpageId)>, compositor: C, layout_chan: &OpaqueScriptLayoutChannel, control_chan: ScriptControlChan, @@ -365,7 +367,7 @@ impl ScriptTaskFactory for ScriptTask { }); let mut failsafe = ScriptMemoryFailsafe::new(&script_task); - let new_load = InProgressLoad::new(id, None, layout_chan, window_size, + let new_load = InProgressLoad::new(id, parent_info, layout_chan, window_size, load_data.url.clone()); script_task.start_page_load(new_load, load_data); @@ -641,7 +643,15 @@ impl ScriptTask { ConstellationControlMsg::Freeze(pipeline_id) => self.handle_freeze_msg(pipeline_id), ConstellationControlMsg::Thaw(pipeline_id) => - self.handle_thaw_msg(pipeline_id) + self.handle_thaw_msg(pipeline_id), + ConstellationControlMsg::MozBrowserEvent(parent_pipeline_id, + subpage_id, + event_name, + event_detail) => + self.handle_mozbrowser_event_msg(parent_pipeline_id, + subpage_id, + event_name, + event_detail), } } @@ -783,6 +793,30 @@ impl ScriptTask { window.r().thaw(); } + /// Handles a mozbrowser event, for example see: + /// https://developer.mozilla.org/en-US/docs/Web/Events/mozbrowserloadstart + fn handle_mozbrowser_event_msg(&self, + parent_pipeline_id: PipelineId, + subpage_id: SubpageId, + event_name: String, + event_detail: Option<String>) { + let borrowed_page = self.root_page(); + + let frame_element = borrowed_page.find(parent_pipeline_id).and_then(|page| { + let doc = page.document().root(); + let doc: JSRef<Node> = NodeCast::from_ref(doc.r()); + + doc.traverse_preorder() + .filter_map(HTMLIFrameElementCast::to_ref) + .find(|node| node.subpage_id() == Some(subpage_id)) + .map(Temporary::from_rooted) + }).root(); + + if let Some(frame_element) = frame_element { + frame_element.r().dispatch_mozbrowser_event(event_name, event_detail); + } + } + /// Handles a notification that reflow completed. fn handle_reflow_complete_msg(&self, pipeline_id: PipelineId, reflow_id: uint) { debug!("Script: Reflow {:?} complete for {:?}", reflow_id, pipeline_id); @@ -832,7 +866,7 @@ impl ScriptTask { response: LoadResponse) { // Any notification received should refer to an existing, in-progress load that is tracked. let idx = self.incomplete_loads.borrow().iter().position(|load| { - load.pipeline_id == id && load.subpage_id.map(|sub| sub.1) == subpage + load.pipeline_id == id && load.parent_info.map(|info| info.1) == subpage }).unwrap(); let load = self.incomplete_loads.borrow_mut().remove(idx); self.load(response, load); @@ -876,27 +910,31 @@ impl ScriptTask { // We should either be initializing a root page or loading a child page of an // existing one. let root_page_exists = self.page.borrow().is_some(); - assert!(incomplete.subpage_id.is_none() || root_page_exists); - - let frame_element = incomplete.subpage_id.and_then(|(parent_id, subpage_id)| { - let borrowed_page = self.root_page(); - // In the case a parent id exists but the matching page - // cannot be found, this means the page exists in a different - // script task (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.find(parent_id).and_then(|page| { - let doc = page.document().root(); - let doc: JSRef<Node> = NodeCast::from_ref(doc.r()); - doc.traverse_preorder() - .filter_map(HTMLIFrameElementCast::to_ref) - .find(|node| node.subpage_id() == Some(subpage_id)) - .map(ElementCast::from_ref) - .map(Temporary::from_rooted) - }) + 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 + // exists in a different script task. + let borrowed_page = self.page.borrow(); + + // In the case a parent id exists but the matching page + // cannot be found, this means the page exists in a different + // script task (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(); + let doc: JSRef<Node> = NodeCast::from_ref(doc.r()); + + doc.traverse_preorder() + .filter_map(HTMLIFrameElementCast::to_ref) + .find(|node| node.subpage_id() == Some(subpage_id)) + .map(ElementCast::from_ref) + .map(Temporary::from_rooted) + }) + }) }).root(); self.compositor.borrow_mut().set_ready_state(incomplete.pipeline_id, Loading); @@ -909,9 +947,11 @@ impl ScriptTask { if !root_page_exists { // We have a new root frame tree. *self.page.borrow_mut() = Some(page.clone()); - } else if let Some((parent, _)) = incomplete.subpage_id { + } 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 tasks + // between cross origin iframes in the same TLD. parent_page.find(parent).expect("received load for subpage with missing parent"); parent_page.children.borrow_mut().push(page.clone()); } @@ -972,7 +1012,7 @@ impl ScriptTask { self.constellation_chan.clone(), incomplete.layout_chan, incomplete.pipeline_id, - incomplete.subpage_id.map(|s| s.1), + incomplete.parent_info, incomplete.window_size).root(); let last_modified: Option<DOMString> = response.metadata.headers.as_ref().and_then(|headers| { @@ -1223,7 +1263,7 @@ impl ScriptTask { /// argument until a notification is received that the fetch is complete. fn start_page_load(&self, incomplete: InProgressLoad, mut load_data: LoadData) { let id = incomplete.pipeline_id.clone(); - let subpage = incomplete.subpage_id.clone().map(|p| p.1); + let subpage = incomplete.parent_info.clone().map(|p| p.1); let script_chan = self.chan.clone(); let resource_task = self.resource_task.clone(); diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index 58fa83dbf67..fbd64434b06 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -72,6 +72,8 @@ pub enum ConstellationControlMsg { Thaw(PipelineId), /// Notifies script task that a url should be loaded in this iframe. Navigate(PipelineId, SubpageId, LoadData), + /// Requests the script task forward a mozbrowser event to an iframe it owns + MozBrowserEvent(PipelineId, SubpageId, String, Option<String>), } unsafe impl Send for ConstellationControlMsg { @@ -99,6 +101,7 @@ pub struct ScriptControlChan(pub Sender<ConstellationControlMsg>); pub trait ScriptTaskFactory { fn create<C>(_phantom: Option<&mut Self>, id: PipelineId, + parent_info: Option<(PipelineId, SubpageId)>, compositor: C, layout_chan: &OpaqueScriptLayoutChannel, control_chan: ScriptControlChan, |