aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom
diff options
context:
space:
mode:
authorAlan Jeffrey <ajeffrey@mozilla.com>2016-11-30 16:54:12 -0600
committerAlan Jeffrey <ajeffrey@mozilla.com>2017-01-05 21:12:57 +0000
commit7c2de62124995c8c353756abdb1ec7c08973ae3a (patch)
treef6b54e6a181ee1498f0bf8fc06665f802078c5a0 /components/script/dom
parent143dfc879e609603839502d61bc064fba96cc80f (diff)
downloadservo-7c2de62124995c8c353756abdb1ec7c08973ae3a.tar.gz
servo-7c2de62124995c8c353756abdb1ec7c08973ae3a.zip
Implement browsing context discarding.
Diffstat (limited to 'components/script/dom')
-rw-r--r--components/script/dom/browsingcontext.rs39
-rw-r--r--components/script/dom/document.rs53
-rw-r--r--components/script/dom/htmliframeelement.rs61
-rw-r--r--components/script/dom/webidls/Window.webidl8
-rw-r--r--components/script/dom/window.rs47
5 files changed, 96 insertions, 112 deletions
diff --git a/components/script/dom/browsingcontext.rs b/components/script/dom/browsingcontext.rs
index 1a7f26253a0..9652445d96f 100644
--- a/components/script/dom/browsingcontext.rs
+++ b/components/script/dom/browsingcontext.rs
@@ -3,16 +3,13 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use dom::bindings::conversions::{ToJSValConvertible, root_from_handleobject};
-use dom::bindings::inheritance::Castable;
-use dom::bindings::js::{JS, MutNullableJS, Root, RootedReference};
+use dom::bindings::js::{JS, Root, RootedReference};
use dom::bindings::proxyhandler::{fill_property_descriptor, get_property_descriptor};
use dom::bindings::reflector::{DomObject, MutDomObject, Reflector};
use dom::bindings::trace::JSTraceable;
use dom::bindings::utils::WindowProxyHandler;
use dom::bindings::utils::get_array_index_from_id;
-use dom::document::Document;
use dom::element::Element;
-use dom::globalscope::GlobalScope;
use dom::window::Window;
use js::JSCLASS_IS_GLOBAL;
use js::glue::{CreateWrapperProxyHandler, ProxyTraps, NewWindowProxy};
@@ -26,7 +23,6 @@ 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::PipelineId;
use std::cell::Cell;
#[dom_struct]
@@ -40,10 +36,8 @@ pub struct BrowsingContext {
/// Indicates if reflow is required when reloading.
needs_reflow: Cell<bool>,
- /// The current active document.
- /// Note that the session history is stored in the constellation,
- /// in the script thread we just track the current active document.
- active_document: MutNullableJS<Document>,
+ /// 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>>,
@@ -54,7 +48,7 @@ impl BrowsingContext {
BrowsingContext {
reflector: Reflector::new(),
needs_reflow: Cell::new(true),
- active_document: Default::default(),
+ discarded: Cell::new(false),
frame_element: frame_element.map(JS::from_ref),
}
}
@@ -84,20 +78,12 @@ impl BrowsingContext {
}
}
- pub fn set_active_document(&self, document: &Document) {
- self.active_document.set(Some(document))
+ pub fn discard(&self) {
+ self.discarded.set(true);
}
- pub fn active_document(&self) -> Root<Document> {
- self.active_document.get().expect("No active document.")
- }
-
- pub fn maybe_active_document(&self) -> Option<Root<Document>> {
- self.active_document.get()
- }
-
- pub fn active_window(&self) -> Root<Window> {
- Root::from_ref(self.active_document().window())
+ pub fn is_discarded(&self) -> bool {
+ self.discarded.get()
}
pub fn frame_element(&self) -> Option<&Element> {
@@ -115,15 +101,6 @@ impl BrowsingContext {
self.needs_reflow.set(status);
old
}
-
- pub fn active_pipeline_id(&self) -> Option<PipelineId> {
- self.active_document.get()
- .map(|doc| doc.window().upcast::<GlobalScope>().pipeline_id())
- }
-
- pub fn unset_active_document(&self) {
- self.active_document.set(None)
- }
}
#[allow(unsafe_code)]
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs
index d6d967db6c4..bfc98d61b0f 100644
--- a/components/script/dom/document.rs
+++ b/components/script/dom/document.rs
@@ -190,6 +190,7 @@ pub struct Document {
last_modified: Option<String>,
encoding: Cell<EncodingRef>,
is_html_document: bool,
+ is_fully_active: Cell<bool>,
url: DOMRefCell<ServoUrl>,
quirks_mode: Cell<QuirksMode>,
/// Caches for the getElement methods
@@ -385,20 +386,17 @@ impl Document {
self.trigger_mozbrowser_event(MozBrowserEvent::SecurityChange(https_state));
}
- // https://html.spec.whatwg.org/multipage/#active-document
- pub fn is_active(&self) -> bool {
- self.browsing_context().map_or(false, |context| {
- self == &*context.active_document()
- })
- }
-
// https://html.spec.whatwg.org/multipage/#fully-active
pub fn is_fully_active(&self) -> bool {
- if !self.is_active() {
- return false;
- }
- // FIXME: It should also check whether the browser context is top-level or not
- true
+ self.is_fully_active.get()
+ }
+
+ pub fn fully_activate(&self) {
+ self.is_fully_active.set(true)
+ }
+
+ pub fn fully_deactivate(&self) {
+ self.is_fully_active.set(false)
}
pub fn origin(&self) -> &Origin {
@@ -1693,11 +1691,16 @@ impl Document {
self.current_parser.get()
}
- /// Find an iframe element in the document.
- pub fn find_iframe(&self, frame_id: FrameId) -> Option<Root<HTMLIFrameElement>> {
+ /// Iterate over all iframes in the document.
+ pub fn iter_iframes(&self) -> impl Iterator<Item=Root<HTMLIFrameElement>> {
self.upcast::<Node>()
.traverse_preorder()
.filter_map(Root::downcast::<HTMLIFrameElement>)
+ }
+
+ /// Find an iframe element in the document.
+ pub fn find_iframe(&self, frame_id: FrameId) -> Option<Root<HTMLIFrameElement>> {
+ self.iter_iframes()
.find(|node| node.frame_id() == frame_id)
}
@@ -1864,6 +1867,7 @@ impl Document {
// https://dom.spec.whatwg.org/#concept-document-encoding
encoding: Cell::new(UTF_8),
is_html_document: is_html_document == IsHTMLDocument::HTMLDocument,
+ is_fully_active: Cell::new(false),
id_map: DOMRefCell::new(HashMap::new()),
tag_map: DOMRefCell::new(HashMap::new()),
tagns_map: DOMRefCell::new(HashMap::new()),
@@ -2261,19 +2265,12 @@ impl DocumentMethods for Document {
// https://html.spec.whatwg.org/multipage/#dom-document-hasfocus
fn HasFocus(&self) -> bool {
- match self.browsing_context() {
- Some(browsing_context) => {
- // Step 2.
- let candidate = browsing_context.active_document();
- // Step 3.
- if &*candidate == self {
- true
- } else {
- false //TODO Step 4.
- }
- }
- None => false,
+ // Step 1-2.
+ if self.window().parent_info().is_none() && self.is_fully_active() {
+ return true;
}
+ // TODO Step 3.
+ false
}
// https://html.spec.whatwg.org/multipage/#relaxing-the-same-origin-restriction
@@ -3190,8 +3187,8 @@ impl DocumentMethods for Document {
// Step 2.
// TODO: handle throw-on-dynamic-markup-insertion counter.
-
- if !self.is_active() {
+ // FIXME: this should check for being active rather than fully active
+ if !self.is_fully_active() {
// Step 3.
return Ok(());
}
diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs
index 692598d5339..3a1c9daf1c9 100644
--- a/components/script/dom/htmliframeelement.rs
+++ b/components/script/dom/htmliframeelement.rs
@@ -33,7 +33,6 @@ use dom::eventtarget::EventTarget;
use dom::globalscope::GlobalScope;
use dom::htmlelement::HTMLElement;
use dom::node::{Node, NodeDamage, UnbindContext, document_from_node, window_from_node};
-use dom::urlhelper::UrlHelper;
use dom::virtualmethods::VirtualMethods;
use dom::window::{ReflowReason, Window};
use html5ever_atoms::LocalName;
@@ -709,42 +708,34 @@ impl VirtualMethods for HTMLIFrameElement {
LoadBlocker::terminate(&mut blocker);
// https://html.spec.whatwg.org/multipage/#a-browsing-context-is-discarded
- if let Some(pipeline_id) = self.pipeline_id.get() {
- debug!("Unbinding pipeline {} from frame {}.", pipeline_id, self.frame_id);
- let window = window_from_node(self);
-
- // The only reason we're waiting for the iframe to be totally
- // removed is to ensure the script thread can't add iframes faster
- // than the compositor can remove them.
- //
- // Since most of this cleanup doesn't happen on same-origin
- // iframes, and since that would cause a deadlock, don't do it.
- let same_origin = {
- // FIXME(#10968): this should probably match the origin check in
- // HTMLIFrameElement::contentDocument.
- let self_url = self.get_url();
- let win_url = window_from_node(self).get_url();
- UrlHelper::SameOrigin(&self_url, &win_url) || self_url.as_str() == "about:blank"
- };
- let (sender, receiver) = if same_origin {
- (None, None)
- } else {
- let (sender, receiver) = ipc::channel().unwrap();
- (Some(sender), Some(receiver))
- };
- let msg = ConstellationMsg::RemoveIFrame(pipeline_id, sender);
- window.upcast::<GlobalScope>().constellation_chan().send(msg).unwrap();
- if let Some(receiver) = receiver {
- receiver.recv().unwrap()
+ debug!("Unbinding frame {}.", self.frame_id);
+ let window = window_from_node(self);
+ let (sender, receiver) = ipc::channel().unwrap();
+
+ // Ask the constellation to remove the iframe, and tell us the
+ // pipeline ids of the closed pipelines.
+ let msg = ConstellationMsg::RemoveIFrame(self.frame_id, sender);
+ window.upcast::<GlobalScope>().constellation_chan().send(msg).unwrap();
+ let exited_pipeline_ids = receiver.recv().unwrap();
+
+ // The spec for discarding is synchronous,
+ // so we need to discard the browsing contexts now, rather than
+ // when the `PipelineExit` message arrives.
+ for exited_pipeline_id in exited_pipeline_ids {
+ if let Some(exited_document) = ScriptThread::find_document(exited_pipeline_id) {
+ exited_document.window().browsing_context().discard();
+ for exited_iframe in exited_document.iter_iframes() {
+ exited_iframe.pipeline_id.set(None);
+ }
}
-
- // Resetting the pipeline_id to None is required here so that
- // if this iframe is subsequently re-added to the document
- // the load doesn't think that it's a navigation, but instead
- // a new iframe. Without this, the constellation gets very
- // confused.
- self.pipeline_id.set(None);
}
+
+ // Resetting the pipeline_id to None is required here so that
+ // if this iframe is subsequently re-added to the document
+ // the load doesn't think that it's a navigation, but instead
+ // a new iframe. Without this, the constellation gets very
+ // confused.
+ self.pipeline_id.set(None);
}
}
diff --git a/components/script/dom/webidls/Window.webidl b/components/script/dom/webidls/Window.webidl
index dbc4b76591e..47c753f43b1 100644
--- a/components/script/dom/webidls/Window.webidl
+++ b/components/script/dom/webidls/Window.webidl
@@ -31,9 +31,13 @@
// other browsing contexts
[Replaceable] readonly attribute WindowProxy frames;
//[Replaceable] readonly attribute unsigned long length;
- [Unforgeable] readonly attribute WindowProxy top;
+ // Note that this can return null in the case that the browsing context has been discarded.
+ // https://github.com/whatwg/html/issues/2115
+ [Unforgeable] readonly attribute WindowProxy? top;
// attribute any opener;
- readonly attribute WindowProxy parent;
+ // Note that this can return null in the case that the browsing context has been discarded.
+ // https://github.com/whatwg/html/issues/2115
+ readonly attribute WindowProxy? parent;
readonly attribute Element? frameElement;
//WindowProxy open(optional DOMString url = "about:blank", optional DOMString target = "_blank",
// optional DOMString features = "", optional boolean replace = false);
diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs
index 37bf817ed7a..87d15ab1616 100644
--- a/components/script/dom/window.rs
+++ b/components/script/dom/window.rs
@@ -163,6 +163,7 @@ pub struct Window {
#[ignore_heap_size_of = "channels are hard"]
image_cache_chan: ImageCacheChan,
browsing_context: MutNullableJS<BrowsingContext>,
+ document: MutNullableJS<Document>,
history: MutNullableJS<History>,
performance: MutNullableJS<Performance>,
navigation_start: u64,
@@ -443,7 +444,7 @@ impl WindowMethods for Window {
// https://html.spec.whatwg.org/multipage/#dom-document-2
fn Document(&self) -> Root<Document> {
- self.browsing_context().active_document()
+ self.document.get().expect("Document accessed before initialization.")
}
// https://html.spec.whatwg.org/multipage/#dom-history
@@ -551,21 +552,32 @@ impl WindowMethods for Window {
}
// https://html.spec.whatwg.org/multipage/#dom-parent
- fn Parent(&self) -> Root<BrowsingContext> {
- match self.parent() {
- Some(window) => window.browsing_context(),
- None => self.Window()
+ fn GetParent(&self) -> Option<Root<BrowsingContext>> {
+ // Steps 1. and 2.
+ if self.browsing_context().is_discarded() {
+ return None;
}
- }
+ match self.parent() {
+ // Step 4.
+ Some(parent) => Some(parent.Window()),
+ // Step 5.
+ None => Some(self.Window())
+ }
+ }
// https://html.spec.whatwg.org/multipage/#dom-top
- fn Top(&self) -> Root<BrowsingContext> {
+ fn GetTop(&self) -> Option<Root<BrowsingContext>> {
+ // Steps 1. and 2.
+ if self.browsing_context().is_discarded() {
+ return None;
+ }
+ // Step 5.
let mut window = Root::from_ref(self);
while let Some(parent) = window.parent() {
window = parent;
}
- window.browsing_context()
- }
+ Some(window.Window())
+ }
// https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/
// NavigationTiming/Overview.html#sec-window.performance-attribute
@@ -1351,6 +1363,13 @@ impl Window {
unsafe { SetWindowProxy(cx, window, browsing_context.reflector().get_jsobject()); }
}
+ #[allow(unsafe_code)]
+ pub fn init_document(&self, document: &Document) {
+ assert!(self.document.get().is_none());
+ assert!(document.window() == self);
+ self.document.set(Some(&document));
+ }
+
/// Commence a new URL load which will either replace this window or scroll to a fragment.
pub fn load_url(&self, url: ServoUrl, replace: bool, force_reload: bool,
referrer_policy: Option<ReferrerPolicy>) {
@@ -1518,13 +1537,8 @@ impl Window {
return None;
}
- let browsing_context = self.browsing_context();
-
- browsing_context.frame_element().map(|frame_element| {
- let window = window_from_node(frame_element);
- let context = window.browsing_context();
- context.active_window()
- })
+ self.browsing_context().frame_element()
+ .map(|frame_element| window_from_node(frame_element))
}
/// Returns whether this window is mozbrowser.
@@ -1610,6 +1624,7 @@ impl Window {
image_cache_thread: image_cache_thread,
history: Default::default(),
browsing_context: Default::default(),
+ document: Default::default(),
performance: Default::default(),
navigation_start: (current_time.sec * 1000 + current_time.nsec as i64 / 1000000) as u64,
navigation_start_precise: time::precise_time_ns() as f64,