diff options
12 files changed, 191 insertions, 13 deletions
diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index 213ae5f6ffd..124731f4bf8 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -108,7 +108,7 @@ use servo_config::opts; use servo_config::prefs::PREFS; use servo_rand::{Rng, SeedableRng, ServoRng, random}; use servo_remutex::ReentrantMutex; -use servo_url::{Host, ServoUrl}; +use servo_url::{Host, ImmutableOrigin, ServoUrl}; use std::borrow::ToOwned; use std::collections::{HashMap, VecDeque}; use std::iter::once; @@ -980,6 +980,10 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> warn!("constellation got set final url message for dead pipeline"); } } + FromScriptMsg::PostMessage(frame_id, origin, data) => { + debug!("constellation got postMessage message"); + self.handle_post_message_msg(frame_id, origin, data); + } FromScriptMsg::MozBrowserEvent(parent_pipeline_id, pipeline_id, event) => { debug!("constellation got mozbrowser event message"); self.handle_mozbrowser_event_msg(parent_pipeline_id, @@ -1795,6 +1799,21 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> } } + fn handle_post_message_msg(&mut self, frame_id: FrameId, origin: Option<ImmutableOrigin>, data: Vec<u8>) { + let pipeline_id = match self.frames.get(&frame_id) { + None => return warn!("postMessage to closed frame {}.", frame_id), + Some(frame) => frame.pipeline_id, + }; + let msg = ConstellationControlMsg::PostMessage(pipeline_id, origin, data); + let result = match self.pipelines.get(&pipeline_id) { + Some(pipeline) => pipeline.event_loop.send(msg), + None => return warn!("postMessage to closed pipeline {}.", pipeline_id), + }; + if let Err(e) = result { + self.handle_send_error(pipeline_id, e); + } + } + fn handle_mozbrowser_event_msg(&mut self, parent_pipeline_id: PipelineId, pipeline_id: PipelineId, diff --git a/components/script/dom/browsingcontext.rs b/components/script/dom/browsingcontext.rs index 77c6eac2941..6363e08095f 100644 --- a/components/script/dom/browsingcontext.rs +++ b/components/script/dom/browsingcontext.rs @@ -29,6 +29,7 @@ use js::jsapi::{MutableHandle, MutableHandleObject, MutableHandleValue}; use js::jsapi::{ObjectOpResult, PropertyDescriptor}; use js::jsval::{UndefinedValue, PrivateValue}; use js::rust::get_object_class; +use msg::constellation_msg::FrameId; use msg::constellation_msg::PipelineId; use std::cell::Cell; use std::ptr; @@ -45,6 +46,11 @@ pub struct BrowsingContext { /// changes Window. reflector: Reflector, + /// The frame id of the browsing context. + /// In the case that this is a nested browsing context, this is the frame id + /// of the container. + frame_id: FrameId, + /// The pipeline id of the currently active document. /// May be None, when the currently active document is in another script thread. /// We do not try to keep the pipeline id for documents in other threads, @@ -60,9 +66,14 @@ pub struct BrowsingContext { } impl BrowsingContext { - pub fn new_inherited(currently_active: PipelineId, frame_element: Option<&Element>) -> BrowsingContext { + pub fn new_inherited(frame_id: FrameId, + currently_active: PipelineId, + frame_element: Option<&Element>) + -> BrowsingContext + { BrowsingContext { reflector: Reflector::new(), + frame_id: frame_id, currently_active: Cell::new(Some(currently_active)), discarded: Cell::new(false), frame_element: frame_element.map(JS::from_ref), @@ -70,7 +81,7 @@ impl BrowsingContext { } #[allow(unsafe_code)] - pub fn new(window: &Window, frame_element: Option<&Element>) -> Root<BrowsingContext> { + pub fn new(window: &Window, frame_id: FrameId, frame_element: Option<&Element>) -> Root<BrowsingContext> { unsafe { let WindowProxyHandler(handler) = window.windowproxy_handler(); assert!(!handler.is_null()); @@ -87,7 +98,7 @@ impl BrowsingContext { // Create a new browsing context. let currently_active = window.global().pipeline_id(); - let mut browsing_context = box BrowsingContext::new_inherited(currently_active, frame_element); + let mut browsing_context = box BrowsingContext::new_inherited(frame_id, currently_active, frame_element); // The window proxy owns the browsing context. // When we finalize the window proxy, it drops the browsing context it owns. @@ -111,6 +122,10 @@ impl BrowsingContext { self.discarded.get() } + pub fn frame_id(&self) -> FrameId { + self.frame_id + } + pub fn frame_element(&self) -> Option<&Element> { self.frame_element.r() } diff --git a/components/script/dom/dissimilaroriginwindow.rs b/components/script/dom/dissimilaroriginwindow.rs index 9867e642906..96171ef3bf1 100644 --- a/components/script/dom/dissimilaroriginwindow.rs +++ b/components/script/dom/dissimilaroriginwindow.rs @@ -4,9 +4,12 @@ use dom::bindings::codegen::Bindings::DissimilarOriginWindowBinding; use dom::bindings::codegen::Bindings::DissimilarOriginWindowBinding::DissimilarOriginWindowMethods; +use dom::bindings::error::{Error, ErrorResult}; +use dom::bindings::inheritance::Castable; use dom::bindings::js::{JS, MutNullableJS, Root}; use dom::bindings::reflector::DomObject; use dom::bindings::str::DOMString; +use dom::bindings::structuredclone::StructuredCloneData; use dom::browsingcontext::BrowsingContext; use dom::dissimilaroriginlocation::DissimilarOriginLocation; use dom::globalscope::GlobalScope; @@ -15,6 +18,9 @@ use ipc_channel::ipc; use js::jsapi::{JSContext, HandleValue}; use js::jsval::{JSVal, UndefinedValue}; use msg::constellation_msg::PipelineId; +use script_traits::ScriptMsg as ConstellationMsg; +use servo_url::ImmutableOrigin; +use servo_url::ServoUrl; /// Represents a dissimilar-origin `Window` that exists in another script thread. /// @@ -107,8 +113,27 @@ impl DissimilarOriginWindowMethods for DissimilarOriginWindow { #[allow(unsafe_code)] // https://html.spec.whatwg.org/multipage/#dom-window-postmessage - unsafe fn PostMessage(&self, _: *mut JSContext, _: HandleValue, _: DOMString) { - // TODO: Implement x-origin postMessage + unsafe fn PostMessage(&self, cx: *mut JSContext, message: HandleValue, origin: DOMString) -> ErrorResult { + // Step 3-5. + let origin = match &origin[..] { + "*" => None, + "/" => { + // TODO: Should be the origin of the incumbent settings object. + None + }, + url => match ServoUrl::parse(&url) { + Ok(url) => Some(url.origin()), + Err(_) => return Err(Error::Syntax), + } + }; + + // Step 1-2, 6-8. + // TODO(#12717): Should implement the `transfer` argument. + let data = try!(StructuredCloneData::write(cx, message)); + + // Step 9. + self.post_message(origin, data); + Ok(()) } #[allow(unsafe_code)] @@ -139,3 +164,10 @@ impl DissimilarOriginWindowMethods for DissimilarOriginWindow { self.location.or_init(|| DissimilarOriginLocation::new(self)) } } + +impl DissimilarOriginWindow { + pub fn post_message(&self, origin: Option<ImmutableOrigin>, data: StructuredCloneData) { + let msg = ConstellationMsg::PostMessage(self.browsing_context.frame_id(), origin, data.move_to_arraybuffer()); + let _ = self.upcast::<GlobalScope>().constellation_chan().send(msg); + } +} diff --git a/components/script/dom/webidls/DissimilarOriginWindow.webidl b/components/script/dom/webidls/DissimilarOriginWindow.webidl index 6aeb5c7d1b2..a1f3a2f8b6d 100644 --- a/components/script/dom/webidls/DissimilarOriginWindow.webidl +++ b/components/script/dom/webidls/DissimilarOriginWindow.webidl @@ -25,7 +25,7 @@ interface DissimilarOriginWindow : GlobalScope { void close(); readonly attribute boolean closed; - void postMessage(any message, DOMString targetOrigin); + [Throws] void postMessage(any message, DOMString targetOrigin); attribute any opener; void blur(); void focus(); diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 50a2a57c41e..e778b451102 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -718,10 +718,7 @@ impl WindowMethods for Window { let data = try!(StructuredCloneData::write(cx, message)); // Step 9. - let runnable = PostMessageHandler::new(self, origin, data); - let msg = CommonScriptMsg::RunnableMsg(ScriptThreadEventCategory::DomEvent, box runnable); - // TODO(#12718): Use the "posted message task source". - let _ = self.script_chan.send(msg); + self.post_message(origin, data); Ok(()) } @@ -1910,3 +1907,12 @@ impl Runnable for PostMessageHandler { message.handle()); } } + +impl Window { + pub fn post_message(&self, origin: Option<ImmutableOrigin>, data: StructuredCloneData) { + let runnable = PostMessageHandler::new(self, origin, data); + let msg = CommonScriptMsg::RunnableMsg(ScriptThreadEventCategory::DomEvent, box runnable); + // TODO(#12718): Use the "posted message task source". + let _ = self.script_chan.send(msg); + } +} diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 97d1c36d637..0e8e4ceadca 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -36,6 +36,7 @@ use dom::bindings::js::{RootCollectionPtr, RootedReference}; use dom::bindings::num::Finite; use dom::bindings::reflector::DomObject; use dom::bindings::str::DOMString; +use dom::bindings::structuredclone::StructuredCloneData; use dom::bindings::trace::JSTraceable; use dom::bindings::utils::WRAP_CALLBACKS; use dom::browsingcontext::BrowsingContext; @@ -93,7 +94,7 @@ use script_traits::WebVREventMsg; use script_traits::webdriver_msg::WebDriverScriptCommand; use serviceworkerjob::{Job, JobQueue, AsyncJobHandler}; use servo_config::opts; -use servo_url::{MutableOrigin, ServoUrl}; +use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl}; use std::cell::Cell; use std::collections::{hash_map, HashMap, HashSet}; use std::default::Default; @@ -1018,6 +1019,8 @@ impl ScriptThread { self.handle_visibility_change_msg(pipeline_id, visible), ConstellationControlMsg::NotifyVisibilityChange(parent_pipeline_id, frame_id, visible) => self.handle_visibility_change_complete_msg(parent_pipeline_id, frame_id, visible), + ConstellationControlMsg::PostMessage(pipeline_id, origin, data) => + self.handle_post_message_msg(pipeline_id, origin, data), ConstellationControlMsg::MozBrowserEvent(parent_pipeline_id, frame_id, event) => @@ -1396,6 +1399,13 @@ impl ScriptThread { } } + fn handle_post_message_msg(&self, pipeline_id: PipelineId, origin: Option<ImmutableOrigin>, data: Vec<u8>) { + match { self.documents.borrow().find_window(pipeline_id) } { + None => return warn!("postMessage after pipeline {} closed.", pipeline_id), + Some(window) => window.post_message(origin, StructuredCloneData::Vector(data)), + } + } + /// Handles a mozbrowser event, for example see: /// https://developer.mozilla.org/en-US/docs/Web/Events/mozbrowserloadstart fn handle_mozbrowser_event_msg(&self, @@ -1705,7 +1715,7 @@ impl ScriptThread { match self.browsing_contexts.borrow_mut().entry(incomplete.frame_id) { hash_map::Entry::Vacant(entry) => { - let browsing_context = BrowsingContext::new(&window, frame_element); + let browsing_context = BrowsingContext::new(&window, incomplete.frame_id, frame_element); entry.insert(JS::from_ref(&*browsing_context)); window.init_browsing_context(&browsing_context); }, diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index a1d97b8b232..96042083d39 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -63,6 +63,7 @@ use net_traits::storage_thread::StorageType; use profile_traits::mem; use profile_traits::time as profile_time; use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use servo_url::ImmutableOrigin; use servo_url::ServoUrl; use std::collections::HashMap; use std::fmt; @@ -239,6 +240,8 @@ pub enum ConstellationControlMsg { /// Notifies script thread that a url should be loaded in this iframe. /// PipelineId is for the parent, FrameId is for the actual frame. Navigate(PipelineId, FrameId, LoadData, bool), + /// Post a message to a given window. + PostMessage(PipelineId, Option<ImmutableOrigin>, Vec<u8>), /// Requests the script thread forward a mozbrowser event to an iframe it owns, /// or to the window if no child frame id is provided. MozBrowserEvent(PipelineId, Option<FrameId>, MozBrowserEvent), @@ -297,6 +300,7 @@ impl fmt::Debug for ConstellationControlMsg { ChangeFrameVisibilityStatus(..) => "ChangeFrameVisibilityStatus", NotifyVisibilityChange(..) => "NotifyVisibilityChange", Navigate(..) => "Navigate", + PostMessage(..) => "PostMessage", MozBrowserEvent(..) => "MozBrowserEvent", UpdatePipelineId(..) => "UpdatePipelineId", FocusIFrame(..) => "FocusIFrame", diff --git a/components/script_traits/script_msg.rs b/components/script_traits/script_msg.rs index 8aa606c02c5..2fd70174368 100644 --- a/components/script_traits/script_msg.rs +++ b/components/script_traits/script_msg.rs @@ -23,6 +23,7 @@ use msg::constellation_msg::{Key, KeyModifiers, KeyState}; use net_traits::CoreResourceMsg; use net_traits::storage_thread::StorageType; use offscreen_gl_context::{GLContextAttributes, GLLimits}; +use servo_url::ImmutableOrigin; use servo_url::ServoUrl; use style_traits::CSSPixel; use style_traits::cursor::Cursor; @@ -93,6 +94,8 @@ pub enum ScriptMsg { /// A new load has been requested, with an option to replace the current entry once loaded /// instead of adding a new entry. LoadUrl(PipelineId, LoadData, bool), + /// Post a message to the currently active window of a given browsing context. + PostMessage(FrameId, Option<ImmutableOrigin>, Vec<u8>), /// Dispatch a mozbrowser event to the parent of this pipeline. /// The first PipelineId is for the parent, the second is for the originating pipeline. MozBrowserEvent(PipelineId, PipelineId, MozBrowserEvent), diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index 366654cac25..5d7438c4e04 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -9378,6 +9378,16 @@ {} ] ], + "mozilla/cross-origin-objects/cross-origin-postMessage-child1.html": [ + [ + {} + ] + ], + "mozilla/cross-origin-objects/cross-origin-postMessage-child2.html": [ + [ + {} + ] + ], "mozilla/details_ui_closed_ref.html": [ [ {} @@ -12532,6 +12542,12 @@ } ] ], + "mozilla/cross-origin-objects/cross-origin-postMessage.html": [ + [ + "/_mozilla/mozilla/cross-origin-objects/cross-origin-postMessage.html", + {} + ] + ], "mozilla/deterministic-raf.html": [ [ "/_mozilla/mozilla/deterministic-raf.html", @@ -25101,6 +25117,18 @@ "5d5a3ba4099dfabddbed1ea98ad8fe1f5c00a3d3", "testharness" ], + "mozilla/cross-origin-objects/cross-origin-postMessage-child1.html": [ + "6669b133609e17e368a124ddfc52ace940e74156", + "support" + ], + "mozilla/cross-origin-objects/cross-origin-postMessage-child2.html": [ + "a37cd21178fd25a829be30b0367bd0812db4c211", + "support" + ], + "mozilla/cross-origin-objects/cross-origin-postMessage.html": [ + "0163758c92997c6723c9d21be49ef7f51d8b3daf", + "testharness" + ], "mozilla/details_ui_closed.html": [ "2acbe3afbec267bad4dd986803e636740a707507", "reftest" diff --git a/tests/wpt/mozilla/tests/mozilla/cross-origin-objects/cross-origin-postMessage-child1.html b/tests/wpt/mozilla/tests/mozilla/cross-origin-objects/cross-origin-postMessage-child1.html new file mode 100644 index 00000000000..6097799bb60 --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/cross-origin-objects/cross-origin-postMessage-child1.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<html> +<head> +<title> postMessage to dissimilar-origin iframe </title> +</head> +<body> +<script> + window.addEventListener("message", function(e) { + window.location.href = e.data; + }); +</script> +</body> +</html> diff --git a/tests/wpt/mozilla/tests/mozilla/cross-origin-objects/cross-origin-postMessage-child2.html b/tests/wpt/mozilla/tests/mozilla/cross-origin-objects/cross-origin-postMessage-child2.html new file mode 100644 index 00000000000..a1395ad2b51 --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/cross-origin-objects/cross-origin-postMessage-child2.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<html> +<head> +<title> postMessage to dissimilar-origin iframe </title> +</head> +<body> +<script> + window.parent.postMessage("OK", "*"); +</script> +</body> +</html> diff --git a/tests/wpt/mozilla/tests/mozilla/cross-origin-objects/cross-origin-postMessage.html b/tests/wpt/mozilla/tests/mozilla/cross-origin-objects/cross-origin-postMessage.html new file mode 100644 index 00000000000..143240c97aa --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/cross-origin-objects/cross-origin-postMessage.html @@ -0,0 +1,37 @@ +<!DOCTYPE html> +<html> +<head> +<title> postMessage to dissimilar-origin iframe </title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +</head> +<body> +<div id=log></div> +<iframe id="child"></iframe> + +<script> + async_test(function(t) { + // child1 is a dissimilar-origin document + var childURL1 = new URL("cross-origin-postMessage-child1.html", document.location); + childURL1.hostname = "127.0.0.1"; + // child2 is a same-origin document + var childURL2 = new URL("cross-origin-postMessage-child2.html", document.location); + // Load child1 into the iframe + var childIframe = document.getElementById("child"); + childIframe.src = childURL1; + // Once child1 has loaded, post a message to it, asking it to navigate to child2 + childIframe.addEventListener("load", t.step_func(function() { + childIframe.contentWindow.postMessage(childURL2.toString(), "*"); + })); + // Wait for child2 to post an OK message back to us. + // (We don't yet have support for event.source or window.parent, + // so we have to wait for the child to become same-origin.) + window.addEventListener("message", t.step_func(function(e) { + assert_equals(e.data, "OK"); + assert_equals(childIframe.contentWindow.location.href, childURL2.toString()); + t.done(); + })); + }); +</script> +</body> +</html> |