servo: Merge #15799 - Implement dissimilar-origin window.parent and window.top (from asajeffrey:script-window-xorigin-parent); r=nox
authorAlan Jeffrey <ajeffrey@mozilla.com>
Fri, 17 Mar 2017 09:18:38 -0700
changeset 348302 1374720a22135135edbad4b41d23ae9eb69158ec
parent 348301 b83e827c7b1f4f7be9a8ac23c21cbd7afca4a034
child 348303 fcfacb180e4c909f5d787723c9117b99bb12e05c
push id88187
push userarchaeopteryx@coole-files.de
push dateSat, 18 Mar 2017 15:27:00 +0000
treeherdermozilla-inbound@0b1d3324cffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnox
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
servo: Merge #15799 - Implement dissimilar-origin window.parent and window.top (from asajeffrey:script-window-xorigin-parent); r=nox <!-- Please describe your changes on the following line: --> This PR implements `window.parent` and `window.top` for dissimilar-origin windows. This PR builds on #15536, only the last commit is part of this PR. --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes fix #14996 and fix #11660. - [X] These changes do not require tests because there's already a parentage test in `mozilla/cross-origin-objects/cross-origin-objects.html`. <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Source-Repo: https://github.com/servo/servo Source-Revision: 0a3b373a6ae4fdd68ab9d2a7386733ab0bdebe4b
servo/components/constellation/constellation.rs
servo/components/script/dom/browsingcontext.rs
servo/components/script/dom/dissimilaroriginwindow.rs
servo/components/script/dom/window.rs
servo/components/script/script_thread.rs
servo/components/script_traits/script_msg.rs
--- a/servo/components/constellation/constellation.rs
+++ b/servo/components/constellation/constellation.rs
@@ -1087,17 +1087,28 @@ impl<Message, LTF, STF> Constellation<Me
 
             FromScriptMsg::SendKeyEvent(ch, key, key_state, key_modifiers) => {
                 self.compositor_proxy.send(ToCompositorMsg::KeyEvent(ch, key, key_state, key_modifiers))
             }
 
             FromScriptMsg::TouchEventProcessed(result) => {
                 self.compositor_proxy.send(ToCompositorMsg::TouchEventProcessed(result))
             }
-
+            FromScriptMsg::GetFrameId(pipeline_id, sender) => {
+                let result = self.pipelines.get(&pipeline_id).map(|pipeline| pipeline.frame_id);
+                if let Err(e) = sender.send(result) {
+                    warn!("Sending reply to get frame id failed ({:?}).", e);
+                }
+            }
+            FromScriptMsg::GetParentInfo(pipeline_id, sender) => {
+                let result = self.pipelines.get(&pipeline_id).and_then(|pipeline| pipeline.parent_info);
+                if let Err(e) = sender.send(result) {
+                    warn!("Sending reply to get parent info failed ({:?}).", e);
+                }
+            }
             FromScriptMsg::RegisterServiceWorker(scope_things, scope) => {
                 debug!("constellation got store registration scope message");
                 self.handle_register_serviceworker(scope_things, scope);
             }
             FromScriptMsg::ForwardDOMMessage(msg_vec, scope_url) => {
                 if let Some(ref mgr) = self.swmanager_chan {
                     let _ = mgr.send(ServiceWorkerMsg::ForwardDOMMessage(msg_vec, scope_url));
                 } else {
--- a/servo/components/script/dom/browsingcontext.rs
+++ b/servo/components/script/dom/browsingcontext.rs
@@ -58,52 +58,102 @@ pub struct BrowsingContext {
     /// the change, which could be expensive.
     currently_active: Cell<Option<PipelineId>>,
 
     /// Has this browsing context been discarded?
     discarded: Cell<bool>,
 
     /// The containing iframe element, if this is a same-origin iframe
     frame_element: Option<JS<Element>>,
+
+    /// The parent browsing context, if this is a nested browsing context
+    parent: Option<JS<BrowsingContext>>,
 }
 
 impl BrowsingContext {
     pub fn new_inherited(frame_id: FrameId,
-                         currently_active: PipelineId,
-                         frame_element: Option<&Element>)
+                         currently_active: Option<PipelineId>,
+                         frame_element: Option<&Element>,
+                         parent: Option<&BrowsingContext>)
                          -> BrowsingContext
     {
         BrowsingContext {
             reflector: Reflector::new(),
             frame_id: frame_id,
-            currently_active: Cell::new(Some(currently_active)),
+            currently_active: Cell::new(currently_active),
             discarded: Cell::new(false),
             frame_element: frame_element.map(JS::from_ref),
+            parent: parent.map(JS::from_ref),
         }
     }
 
     #[allow(unsafe_code)]
-    pub fn new(window: &Window, frame_id: FrameId, frame_element: Option<&Element>) -> Root<BrowsingContext> {
+    pub fn new(window: &Window,
+               frame_id: FrameId,
+               frame_element: Option<&Element>,
+               parent: Option<&BrowsingContext>)
+               -> Root<BrowsingContext>
+    {
         unsafe {
             let WindowProxyHandler(handler) = window.windowproxy_handler();
             assert!(!handler.is_null());
 
             let cx = window.get_cx();
             let window_jsobject = window.reflector().get_jsobject();
             assert!(!window_jsobject.get().is_null());
             assert!(((*get_object_class(window_jsobject.get())).flags & JSCLASS_IS_GLOBAL) != 0);
             let _ac = JSAutoCompartment::new(cx, window_jsobject.get());
 
             // Create a new window proxy.
             rooted!(in(cx) let window_proxy = NewWindowProxy(cx, window_jsobject, handler));
             assert!(!window_proxy.is_null());
 
             // Create a new browsing context.
-            let currently_active = window.global().pipeline_id();
-            let mut browsing_context = box BrowsingContext::new_inherited(frame_id, currently_active, frame_element);
+            let current = Some(window.global().pipeline_id());
+            let mut browsing_context = box BrowsingContext::new_inherited(frame_id, current, frame_element, parent);
+
+            // The window proxy owns the browsing context.
+            // When we finalize the window proxy, it drops the browsing context it owns.
+            SetProxyExtra(window_proxy.get(), 0, &PrivateValue(&*browsing_context as *const _ as *const _));
+
+            // Notify the JS engine about the new window proxy binding.
+            SetWindowProxy(cx, window_jsobject, window_proxy.handle());
+
+            // Set the reflector.
+            debug!("Initializing reflector of {:p} to {:p}.", browsing_context, window_proxy.get());
+            browsing_context.reflector.set_jsobject(window_proxy.get());
+            Root::from_ref(&*Box::into_raw(browsing_context))
+        }
+    }
+
+    #[allow(unsafe_code)]
+    pub fn new_dissimilar_origin(global_to_clone_from: &GlobalScope,
+                                 frame_id: FrameId,
+                                 parent: Option<&BrowsingContext>)
+                                 -> Root<BrowsingContext>
+    {
+        unsafe {
+            let handler = CreateWrapperProxyHandler(&XORIGIN_PROXY_HANDLER);
+            assert!(!handler.is_null());
+
+            let cx = global_to_clone_from.get_cx();
+
+            // Create a new browsing context.
+            let mut browsing_context = box BrowsingContext::new_inherited(frame_id, None, None, parent);
+
+            // Create a new dissimilar-origin window.
+            let window = DissimilarOriginWindow::new(global_to_clone_from, &*browsing_context);
+            let window_jsobject = window.reflector().get_jsobject();
+            assert!(!window_jsobject.get().is_null());
+            assert!(((*get_object_class(window_jsobject.get())).flags & JSCLASS_IS_GLOBAL) != 0);
+            let _ac = JSAutoCompartment::new(cx, window_jsobject.get());
+
+            // Create a new window proxy.
+            rooted!(in(cx) let window_proxy = NewWindowProxy(cx, window_jsobject, handler));
+            assert!(!window_proxy.is_null());
 
             // The window proxy owns the browsing context.
             // When we finalize the window proxy, it drops the browsing context it owns.
             SetProxyExtra(window_proxy.get(), 0, &PrivateValue(&*browsing_context as *const _ as *const _));
 
             // Notify the JS engine about the new window proxy binding.
             SetWindowProxy(cx, window_jsobject, window_proxy.handle());
 
@@ -125,16 +175,28 @@ impl BrowsingContext {
     pub fn frame_id(&self) -> FrameId {
         self.frame_id
     }
 
     pub fn frame_element(&self) -> Option<&Element> {
         self.frame_element.r()
     }
 
+    pub fn parent(&self) -> Option<&BrowsingContext> {
+        self.parent.r()
+    }
+
+    pub fn top(&self) -> &BrowsingContext {
+        let mut result = self;
+        while let Some(parent) = result.parent() {
+            result = parent;
+        }
+        result
+    }
+
     #[allow(unsafe_code)]
     /// Change the Window that this browsing context's WindowProxy resolves to.
     // TODO: support setting the window proxy to a dummy value,
     // to handle the case when the active document is in another script thread.
     fn set_window_proxy(&self, window: &GlobalScope, traps: &ProxyTraps) {
         unsafe {
             debug!("Setting window proxy of {:p}.", self);
             let handler = CreateWrapperProxyHandler(traps);
@@ -176,17 +238,18 @@ impl BrowsingContext {
 
     pub fn set_currently_active(&self, window: &Window) {
         let globalscope = window.upcast();
         self.set_window_proxy(&*globalscope, &PROXY_HANDLER);
         self.currently_active.set(Some(globalscope.pipeline_id()));
     }
 
     pub fn unset_currently_active(&self) {
-        let window = DissimilarOriginWindow::new(self);
+        let globalscope = self.global();
+        let window = DissimilarOriginWindow::new(&*globalscope, self);
         self.set_window_proxy(&*window.upcast(), &XORIGIN_PROXY_HANDLER);
         self.currently_active.set(None);
     }
 
     pub fn currently_active(&self) -> Option<PipelineId> {
         self.currently_active.get()
     }
 
--- a/servo/components/script/dom/dissimilaroriginwindow.rs
+++ b/servo/components/script/dom/dissimilaroriginwindow.rs
@@ -2,17 +2,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use dom::bindings::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;
 use dom_struct::dom_struct;
 use ipc_channel::ipc;
 use js::jsapi::{JSContext, HandleValue};
@@ -40,29 +39,28 @@ pub struct DissimilarOriginWindow {
     browsing_context: JS<BrowsingContext>,
 
     /// The location of this window, initialized lazily.
     location: MutNullableJS<DissimilarOriginLocation>,
 }
 
 impl DissimilarOriginWindow {
     #[allow(unsafe_code)]
-    pub fn new(browsing_context: &BrowsingContext) -> Root<DissimilarOriginWindow> {
-        let globalscope = browsing_context.global();
-        let cx = globalscope.get_cx();
+    pub fn new(global_to_clone_from: &GlobalScope, browsing_context: &BrowsingContext) -> Root<DissimilarOriginWindow> {
+        let cx = global_to_clone_from.get_cx();
         // Any timer events fired on this window are ignored.
         let (timer_event_chan, _) = ipc::channel().unwrap();
         let win = box DissimilarOriginWindow {
             globalscope: GlobalScope::new_inherited(PipelineId::new(),
-                                                    globalscope.devtools_chan().cloned(),
-                                                    globalscope.mem_profiler_chan().clone(),
-                                                    globalscope.time_profiler_chan().clone(),
-                                                    globalscope.constellation_chan().clone(),
-                                                    globalscope.scheduler_chan().clone(),
-                                                    globalscope.resource_threads().clone(),
+                                                    global_to_clone_from.devtools_chan().cloned(),
+                                                    global_to_clone_from.mem_profiler_chan().clone(),
+                                                    global_to_clone_from.time_profiler_chan().clone(),
+                                                    global_to_clone_from.constellation_chan().clone(),
+                                                    global_to_clone_from.scheduler_chan().clone(),
+                                                    global_to_clone_from.resource_threads().clone(),
                                                     timer_event_chan),
             browsing_context: JS::from_ref(browsing_context),
             location: MutNullableJS::new(None),
         };
         unsafe { DissimilarOriginWindowBinding::Wrap(cx, win) }
     }
 }
 
@@ -79,24 +77,36 @@ impl DissimilarOriginWindowMethods for D
 
     // https://html.spec.whatwg.org/multipage/#dom-frames
     fn Frames(&self) -> Root<BrowsingContext> {
         Root::from_ref(&*self.browsing_context)
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-parent
     fn GetParent(&self) -> Option<Root<BrowsingContext>> {
-        // TODO: implement window.parent correctly for x-origin windows.
+        // Steps 1-3.
+        if self.browsing_context.is_discarded() {
+            return None;
+        }
+        // Step 4.
+        if let Some(parent) = self.browsing_context.parent() {
+            return Some(Root::from_ref(parent));
+        }
+        // Step 5.
         Some(Root::from_ref(&*self.browsing_context))
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-top
     fn GetTop(&self) -> Option<Root<BrowsingContext>> {
-        // TODO: implement window.top correctly for x-origin windows.
-        Some(Root::from_ref(&*self.browsing_context))
+        // Steps 1-3.
+        if self.browsing_context.is_discarded() {
+            return None;
+        }
+        // Steps 4-5.
+        Some(Root::from_ref(self.browsing_context.top()))
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-length
     fn Length(&self) -> u32 {
         // TODO: Implement x-origin length
         0
     }
 
--- a/servo/components/script/dom/window.rs
+++ b/servo/components/script/dom/window.rs
@@ -37,17 +37,17 @@ use dom::element::Element;
 use dom::event::Event;
 use dom::globalscope::GlobalScope;
 use dom::history::History;
 use dom::htmliframeelement::build_mozbrowser_custom_event;
 use dom::location::Location;
 use dom::mediaquerylist::{MediaQueryList, WeakMediaQueryListVec};
 use dom::messageevent::MessageEvent;
 use dom::navigator::Navigator;
-use dom::node::{Node, NodeDamage, document_from_node, from_untrusted_node_address, window_from_node};
+use dom::node::{Node, NodeDamage, document_from_node, from_untrusted_node_address};
 use dom::performance::Performance;
 use dom::promise::Promise;
 use dom::screen::Screen;
 use dom::storage::Storage;
 use dom::testrunner::TestRunner;
 use dom_struct::dom_struct;
 use euclid::{Point2D, Rect, Size2D};
 use fetch;
@@ -626,41 +626,45 @@ impl WindowMethods for Window {
 
     // https://html.spec.whatwg.org/multipage/#dom-frames
     fn Frames(&self) -> Root<BrowsingContext> {
         self.browsing_context()
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-parent
     fn GetParent(&self) -> Option<Root<BrowsingContext>> {
-        // Steps 1. and 2.
-        if self.maybe_browsing_context().map_or(true, |c| c.is_discarded()) {
+        // Steps 1-3.
+        let context = match self.maybe_browsing_context() {
+            Some(context) => context,
+            None => return None,
+        };
+        if context.is_discarded() {
             return None;
         }
-        match self.parent() {
-            // Step 4.
-            Some(parent) => Some(parent.Window()),
-            // Step 5.
-            None => Some(self.Window())
+        // Step 4.
+        if let Some(parent) = context.parent() {
+            return Some(Root::from_ref(parent));
         }
-     }
+        // Step 5.
+        Some(context)
+    }
 
     // https://html.spec.whatwg.org/multipage/#dom-top
     fn GetTop(&self) -> Option<Root<BrowsingContext>> {
-        // Steps 1. and 2.
-        if self.maybe_browsing_context().map_or(true, |c| c.is_discarded()) {
+        // Steps 1-3.
+        let context = match self.maybe_browsing_context() {
+            Some(context) => context,
+            None => return None,
+        };
+        if context.is_discarded() {
             return None;
         }
-        // Step 5.
-        let mut window = Root::from_ref(self);
-        while let Some(parent) = window.parent() {
-            window = parent;
-        }
-        Some(window.Window())
-     }
+        // Steps 4-5.
+        Some(Root::from_ref(context.top()))
+    }
 
     // https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/
     // NavigationTiming/Overview.html#sec-window.performance-attribute
     fn Performance(&self) -> Root<Performance> {
         self.performance.or_init(|| {
             Performance::new(self, self.navigation_start,
                              self.navigation_start_precise)
         })
@@ -1652,26 +1656,16 @@ impl Window {
     // https://html.spec.whatwg.org/multipage/#top-level-browsing-context
     pub fn is_top_level(&self) -> bool {
         match self.parent_info {
             Some((_, FrameType::IFrame)) => false,
             _ => true,
         }
     }
 
-    // https://html.spec.whatwg.org/multipage/#parent-browsing-context
-    pub fn parent(&self) -> Option<Root<Window>> {
-        if self.is_top_level() {
-            return None;
-        }
-
-        self.browsing_context().frame_element()
-            .map(|frame_element| window_from_node(frame_element))
-    }
-
     /// Returns whether this window is mozbrowser.
     pub fn is_mozbrowser(&self) -> bool {
         PREFS.is_mozbrowser_enabled() && self.parent_info().is_none()
     }
 
     /// Returns whether mozbrowser is enabled and `obj` has been created
     /// in a top-level `Window` global.
     #[allow(unsafe_code)]
--- a/servo/components/script/script_thread.rs
+++ b/servo/components/script/script_thread.rs
@@ -1645,16 +1645,87 @@ impl ScriptThread {
     fn handle_frame_load_event(&self, parent_id: PipelineId, frame_id: FrameId, child_id: PipelineId) {
         let iframe = self.documents.borrow().find_iframe(parent_id, frame_id);
         match iframe {
             Some(iframe) => iframe.iframe_load_event_steps(child_id),
             None => warn!("Message sent to closed pipeline {}.", parent_id),
         }
     }
 
+    fn ask_constellation_for_frame_id(&self, pipeline_id: PipelineId) -> Option<FrameId> {
+        let (result_sender, result_receiver) = ipc::channel().unwrap();
+        let msg = ConstellationMsg::GetFrameId(pipeline_id, result_sender);
+        self.constellation_chan.send(msg).expect("Failed to send to constellation.");
+        result_receiver.recv().expect("Failed to get frame id from constellation.")
+    }
+
+    fn ask_constellation_for_parent_info(&self, pipeline_id: PipelineId) -> Option<(PipelineId, FrameType)> {
+        let (result_sender, result_receiver) = ipc::channel().unwrap();
+        let msg = ConstellationMsg::GetParentInfo(pipeline_id, result_sender);
+        self.constellation_chan.send(msg).expect("Failed to send to constellation.");
+        result_receiver.recv().expect("Failed to get frame id from constellation.")
+    }
+
+    // Get the browsing context for a pipeline that may exist in another
+    // script thread.  If the browsing context already exists in the
+    // `browsing_contexts` map, we return it, otherwise we recursively
+    // get the browsing context for the parent if there is one,
+    // construct a new dissimilar-origin browsing context, add it
+    // to the `browsing_contexts` map, and return it.
+    fn remote_browsing_context(&self,
+                               global_to_clone: &GlobalScope,
+                               pipeline_id: PipelineId)
+                               -> Option<Root<BrowsingContext>>
+    {
+        let frame_id = match self.ask_constellation_for_frame_id(pipeline_id) {
+            Some(frame_id) => frame_id,
+            None => return None,
+        };
+        if let Some(browsing_context) = self.browsing_contexts.borrow().get(&frame_id) {
+            return Some(Root::from_ref(browsing_context));
+        }
+        let parent = match self.ask_constellation_for_parent_info(pipeline_id) {
+            Some((parent_id, FrameType::IFrame)) => self.remote_browsing_context(global_to_clone, parent_id),
+            _ => None,
+        };
+        let browsing_context = BrowsingContext::new_dissimilar_origin(global_to_clone, frame_id, parent.r());
+        self.browsing_contexts.borrow_mut().insert(frame_id, JS::from_ref(&*browsing_context));
+        Some(browsing_context)
+    }
+
+    // Get the browsing context for a pipeline that exists in this
+    // script thread.  If the browsing context already exists in the
+    // `browsing_contexts` map, we return it, otherwise we recursively
+    // get the browsing context for the parent if there is one,
+    // construct a new similar-origin browsing context, add it
+    // to the `browsing_contexts` map, and return it.
+    fn local_browsing_context(&self,
+                              window: &Window,
+                              frame_id: FrameId,
+                              parent_info: Option<(PipelineId, FrameType)>)
+                              -> Root<BrowsingContext>
+    {
+        if let Some(browsing_context) = self.browsing_contexts.borrow().get(&frame_id) {
+            browsing_context.set_currently_active(&*window);
+            return Root::from_ref(browsing_context);
+        }
+        let iframe = match parent_info {
+            Some((parent_id, FrameType::IFrame)) => self.documents.borrow().find_iframe(parent_id, frame_id),
+            _ => None,
+        };
+        let parent = match (parent_info, iframe.as_ref()) {
+            (_, Some(iframe)) => Some(window_from_node(&**iframe).browsing_context()),
+            (Some((parent_id, FrameType::IFrame)), _) => self.remote_browsing_context(window.upcast(), parent_id),
+            _ => None,
+        };
+        let browsing_context = BrowsingContext::new(&window, frame_id, iframe.r().map(Castable::upcast), parent.r());
+        self.browsing_contexts.borrow_mut().insert(frame_id, JS::from_ref(&*browsing_context));
+        browsing_context
+    }
+
     /// The entry point to document loading. Defines bindings, sets up the window and document
     /// objects, parses HTML and CSS, and kicks off initial layout.
     fn load(&self, metadata: Metadata, incomplete: InProgressLoad) -> Root<ServoParser> {
         let final_url = metadata.final_url.clone();
         {
             // send the final url to the layout thread.
             incomplete.layout_chan
                       .send(message::Msg::SetFinalUrl(final_url.clone()))
@@ -1662,27 +1733,16 @@ impl ScriptThread {
 
             // update the pipeline url
             self.constellation_chan
                 .send(ConstellationMsg::SetFinalUrl(incomplete.pipeline_id, final_url.clone()))
                 .unwrap();
         }
         debug!("ScriptThread: loading {} on pipeline {:?}", incomplete.url, incomplete.pipeline_id);
 
-        let frame_element = incomplete.parent_info.and_then(|(parent_id, _)|
-            // In the case a parent id exists but the matching context
-            // cannot be found, this means the context exists in a different
-            // script thread (due to origin) so it shouldn't be returned.
-            // 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).
-            self.documents.borrow().find_iframe(parent_id, incomplete.frame_id)
-        );
-
         let MainThreadScriptChan(ref sender) = self.chan;
         let DOMManipulationTaskSource(ref dom_sender) = self.dom_manipulation_task_source;
         let UserInteractionTaskSource(ref user_sender) = self.user_interaction_task_source;
         let HistoryTraversalTaskSource(ref history_sender) = self.history_traversal_task_source;
 
         let (ipc_timer_event_chan, ipc_timer_event_port) = ipc::channel().unwrap();
         ROUTER.route_ipc_receiver_to_mpsc_sender(ipc_timer_event_port,
                                                  self.timer_event_chan.clone());
@@ -1706,30 +1766,20 @@ impl ScriptThread {
                                  self.control_chan.clone(),
                                  self.scheduler_chan.clone(),
                                  ipc_timer_event_chan,
                                  incomplete.layout_chan,
                                  incomplete.pipeline_id,
                                  incomplete.parent_info,
                                  incomplete.window_size,
                                  self.webvr_thread.clone());
-        let frame_element = frame_element.r().map(Castable::upcast);
 
-        match self.browsing_contexts.borrow_mut().entry(incomplete.frame_id) {
-            hash_map::Entry::Vacant(entry) => {
-                let browsing_context = BrowsingContext::new(&window, incomplete.frame_id, frame_element);
-                entry.insert(JS::from_ref(&*browsing_context));
-                window.init_browsing_context(&browsing_context);
-            },
-            hash_map::Entry::Occupied(entry) => {
-                let browsing_context = entry.get();
-                browsing_context.set_currently_active(&*window);
-                window.init_browsing_context(browsing_context);
-            },
-        }
+        // Initialize the browsing context for the window.
+        let browsing_context = self.local_browsing_context(&window, incomplete.frame_id, incomplete.parent_info);
+        window.init_browsing_context(&browsing_context);
 
         let last_modified = metadata.headers.as_ref().and_then(|headers| {
             headers.get().map(|&LastModified(HttpDate(ref tm))| dom_last_modified(tm))
         });
 
         let content_type = metadata.content_type
                                    .as_ref()
                                    .map(|&Serde(ContentType(ref mimetype))| DOMString::from(mimetype.to_string()));
--- a/servo/components/script_traits/script_msg.rs
+++ b/servo/components/script_traits/script_msg.rs
@@ -13,17 +13,17 @@ use MozBrowserEvent;
 use WorkerGlobalScopeInit;
 use WorkerScriptLoadOrigin;
 use canvas_traits::CanvasMsg;
 use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId};
 use euclid::point::Point2D;
 use euclid::size::{Size2D, TypedSize2D};
 use gfx_traits::ScrollRootId;
 use ipc_channel::ipc::IpcSender;
-use msg::constellation_msg::{FrameId, PipelineId, TraversalDirection};
+use msg::constellation_msg::{FrameId, FrameType, PipelineId, TraversalDirection};
 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;
@@ -81,16 +81,20 @@ pub enum ScriptMsg {
                            GLContextAttributes,
                            IpcSender<Result<(IpcSender<CanvasMsg>, GLLimits), String>>),
     /// Notifies the constellation that this frame has received focus.
     Focus(PipelineId),
     /// Forward an event that was sent to the parent window.
     ForwardEvent(PipelineId, CompositorEvent),
     /// Requests that the constellation retrieve the current contents of the clipboard
     GetClipboardContents(IpcSender<String>),
+    /// Get the frame id for a given pipeline.
+    GetFrameId(PipelineId, IpcSender<Option<FrameId>>),
+    /// Get the parent info for a given pipeline.
+    GetParentInfo(PipelineId, IpcSender<Option<(PipelineId, FrameType)>>),
     /// <head> tag finished parsing
     HeadParsed,
     /// All pending loads are complete, and the `load` event for this pipeline
     /// has been dispatched.
     LoadComplete(PipelineId),
     /// 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),