author | Alan Jeffrey <ajeffrey@mozilla.com> |
Fri, 17 Mar 2017 09:18:38 -0700 | |
changeset 348302 | 1374720a22135135edbad4b41d23ae9eb69158ec |
parent 348301 | b83e827c7b1f4f7be9a8ac23c21cbd7afca4a034 |
child 348303 | fcfacb180e4c909f5d787723c9117b99bb12e05c |
push id | 88187 |
push user | archaeopteryx@coole-files.de |
push date | Sat, 18 Mar 2017 15:27:00 +0000 |
treeherder | mozilla-inbound@0b1d3324cffe [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | nox |
milestone | 55.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
|
--- 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),