aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbors-servo <metajack+bors@gmail.com>2014-10-23 09:18:37 -0600
committerbors-servo <metajack+bors@gmail.com>2014-10-23 09:18:37 -0600
commit2df236376a443d8d031ee7a72379f336f2cd8cc4 (patch)
tree8f4278428198189993dd703945ada5189ddaf764
parent470d27a6681b4647de64c085654403820d48f7af (diff)
parent539c21f38022b6d0be6ad01be94bc2e1217d5ccb (diff)
downloadservo-2df236376a443d8d031ee7a72379f336f2cd8cc4.tar.gz
servo-2df236376a443d8d031ee7a72379f336f2cd8cc4.zip
auto merge of #3759 : jdm/servo/readystate, r=Ms2ger
...itor after the initial parse. Fixes #1720. Fixes #3738. r? @Ms2ger or @Manishearth
-rw-r--r--components/script/dom/document.rs56
-rw-r--r--components/script/dom/domimplementation.rs6
-rw-r--r--components/script/dom/domparser.rs9
-rw-r--r--components/script/dom/htmliframeelement.rs12
-rw-r--r--components/script/dom/node.rs4
-rw-r--r--components/script/dom/webidls/Document.webidl17
-rw-r--r--components/script/script_task.rs20
-rw-r--r--tests/content/test_document_readystate.html15
-rw-r--r--tests/wpt/metadata/html/dom/documents/resource-metadata-management/document-readyState.html.ini3
-rw-r--r--tests/wpt/metadata/html/dom/interfaces.html.ini12
10 files changed, 113 insertions, 41 deletions
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs
index d5b52a851db..f383d66c649 100644
--- a/components/script/dom/document.rs
+++ b/components/script/dom/document.rs
@@ -5,8 +5,10 @@
use dom::attr::AttrHelpers;
use dom::bindings::cell::{DOMRefCell, Ref};
use dom::bindings::codegen::Bindings::DocumentBinding;
-use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
+use dom::bindings::codegen::Bindings::DocumentBinding::{DocumentMethods, DocumentReadyState};
+use dom::bindings::codegen::Bindings::DocumentBinding::DocumentReadyStateValues;
use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
+use dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods;
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::codegen::Bindings::NodeFilterBinding::NodeFilter;
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
@@ -34,7 +36,7 @@ use dom::domimplementation::DOMImplementation;
use dom::element::{Element, AttributeHandlers, get_attribute_parts};
use dom::element::{HTMLHtmlElementTypeId, HTMLHeadElementTypeId, HTMLTitleElementTypeId};
use dom::element::{HTMLBodyElementTypeId, HTMLFrameSetElementTypeId};
-use dom::event::Event;
+use dom::event::{Event, DoesNotBubble, NotCancelable};
use dom::eventtarget::{EventTarget, NodeTargetTypeId, EventTargetHelpers};
use dom::htmlanchorelement::HTMLAnchorElement;
use dom::htmlcollection::{HTMLCollection, CollectionFilter};
@@ -93,6 +95,7 @@ pub struct Document {
scripts: MutNullableJS<HTMLCollection>,
anchors: MutNullableJS<HTMLCollection>,
applets: MutNullableJS<HTMLCollection>,
+ ready_state: Cell<DocumentReadyState>,
}
impl DocumentDerived for EventTarget {
@@ -171,6 +174,7 @@ pub trait DocumentHelpers<'a> {
fn register_named_element(self, element: JSRef<Element>, id: Atom);
fn load_anchor_href(self, href: DOMString);
fn find_fragment_node(self, fragid: DOMString) -> Option<Temporary<Element>>;
+ fn set_ready_state(self, state: DocumentReadyState);
}
impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> {
@@ -288,15 +292,39 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> {
.map(|node| Temporary::from_rooted(ElementCast::from_ref(node)))
})
}
+
+ // https://html.spec.whatwg.org/multipage/dom.html#current-document-readiness
+ fn set_ready_state(self, state: DocumentReadyState) {
+ self.ready_state.set(state);
+
+ let window = self.window.root();
+ let event = Event::new(&global::Window(*window), "readystatechange".to_string(),
+ DoesNotBubble, NotCancelable).root();
+ let target: JSRef<EventTarget> = EventTargetCast::from_ref(self);
+ let _ = target.DispatchEvent(*event);
+ }
+}
+
+#[deriving(PartialEq)]
+pub enum DocumentSource {
+ FromParser,
+ NotFromParser,
}
impl Document {
fn new_inherited(window: JSRef<Window>,
- url: Option<Url>,
- is_html_document: IsHTMLDocument,
- content_type: Option<DOMString>) -> Document {
+ url: Option<Url>,
+ is_html_document: IsHTMLDocument,
+ content_type: Option<DOMString>,
+ source: DocumentSource) -> Document {
let url = url.unwrap_or_else(|| Url::parse("about:blank").unwrap());
+ let ready_state = if source == FromParser {
+ DocumentReadyStateValues::Loading
+ } else {
+ DocumentReadyStateValues::Complete
+ };
+
Document {
node: Node::new_without_doc(DocumentNodeTypeId),
window: JS::from_rooted(window),
@@ -325,16 +353,22 @@ impl Document {
scripts: Default::default(),
anchors: Default::default(),
applets: Default::default(),
+ ready_state: Cell::new(ready_state),
}
}
// http://dom.spec.whatwg.org/#dom-document
pub fn Constructor(global: &GlobalRef) -> Fallible<Temporary<Document>> {
- Ok(Document::new(global.as_window(), None, NonHTMLDocument, None))
+ Ok(Document::new(global.as_window(), None, NonHTMLDocument, None, NotFromParser))
}
- pub fn new(window: JSRef<Window>, url: Option<Url>, doctype: IsHTMLDocument, content_type: Option<DOMString>) -> Temporary<Document> {
- let document = reflect_dom_object(box Document::new_inherited(window, url, doctype, content_type),
+ pub fn new(window: JSRef<Window>,
+ url: Option<Url>,
+ doctype: IsHTMLDocument,
+ content_type: Option<DOMString>,
+ source: DocumentSource) -> Temporary<Document> {
+ let document = reflect_dom_object(box Document::new_inherited(window, url, doctype,
+ content_type, source),
&global::Window(window),
DocumentBinding::Wrap).root();
@@ -888,6 +922,12 @@ impl<'a> DocumentMethods for JSRef<'a, Document> {
root.query_selector_all(selectors)
}
+ // https://html.spec.whatwg.org/multipage/dom.html#dom-document-readystate
+ fn ReadyState(self) -> DocumentReadyState {
+ self.ready_state.get()
+ }
+
event_handler!(click, GetOnclick, SetOnclick)
event_handler!(load, GetOnload, SetOnload)
+ event_handler!(readystatechange, GetOnreadystatechange, SetOnreadystatechange)
}
diff --git a/components/script/dom/domimplementation.rs b/components/script/dom/domimplementation.rs
index e2761ea5456..364729721b9 100644
--- a/components/script/dom/domimplementation.rs
+++ b/components/script/dom/domimplementation.rs
@@ -12,7 +12,7 @@ use dom::bindings::global::Window;
use dom::bindings::js::{JS, JSRef, Root, Temporary, OptionalRootable};
use dom::bindings::utils::{Reflector, Reflectable, reflect_dom_object};
use dom::bindings::utils::{QName, Name, InvalidXMLName, xml_name_type};
-use dom::document::{Document, HTMLDocument, NonHTMLDocument};
+use dom::document::{Document, HTMLDocument, NonHTMLDocument, NotFromParser};
use dom::documenttype::DocumentType;
use dom::htmlbodyelement::HTMLBodyElement;
use dom::htmlheadelement::HTMLHeadElement;
@@ -74,7 +74,7 @@ impl<'a> DOMImplementationMethods for JSRef<'a, DOMImplementation> {
let win = doc.window().root();
// Step 1.
- let doc = Document::new(*win, None, NonHTMLDocument, None).root();
+ let doc = Document::new(*win, None, NonHTMLDocument, None, NotFromParser).root();
// Step 2-3.
let maybe_elem = if qname.is_empty() {
None
@@ -119,7 +119,7 @@ impl<'a> DOMImplementationMethods for JSRef<'a, DOMImplementation> {
let win = document.window().root();
// Step 1-2.
- let doc = Document::new(*win, None, HTMLDocument, None).root();
+ let doc = Document::new(*win, None, HTMLDocument, None, NotFromParser).root();
let doc_node: JSRef<Node> = NodeCast::from_ref(*doc);
{
diff --git a/components/script/dom/domparser.rs b/components/script/dom/domparser.rs
index 0ce5487159a..30bbebf77df 100644
--- a/components/script/dom/domparser.rs
+++ b/components/script/dom/domparser.rs
@@ -10,7 +10,7 @@ use dom::bindings::global::GlobalRef;
use dom::bindings::global;
use dom::bindings::js::{JS, JSRef, Temporary};
use dom::bindings::utils::{Reflector, Reflectable, reflect_dom_object};
-use dom::document::{Document, HTMLDocument, NonHTMLDocument};
+use dom::document::{Document, HTMLDocument, NonHTMLDocument, NotFromParser};
use dom::window::Window;
use servo_util::str::DOMString;
@@ -44,12 +44,15 @@ impl<'a> DOMParserMethods for JSRef<'a, DOMParser> {
ty: DOMParserBinding::SupportedType)
-> Fallible<Temporary<Document>> {
let window = self.window.root();
+ //FIXME: these should probably be FromParser when we actually parse the string (#3756).
match ty {
Text_html => {
- Ok(Document::new(*window, None, HTMLDocument, Some("text/html".to_string())))
+ Ok(Document::new(*window, None, HTMLDocument, Some("text/html".to_string()),
+ NotFromParser))
}
Text_xml => {
- Ok(Document::new(*window, None, NonHTMLDocument, Some("text/xml".to_string())))
+ Ok(Document::new(*window, None, NonHTMLDocument, Some("text/xml".to_string()),
+ NotFromParser))
}
_ => {
Err(FailureUnknown)
diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs
index 05b5ccaf46c..52ee5c56c7b 100644
--- a/components/script/dom/htmliframeelement.rs
+++ b/components/script/dom/htmliframeelement.rs
@@ -4,6 +4,7 @@
use dom::attr::Attr;
use dom::attr::AttrHelpers;
+use dom::bindings::codegen::Bindings::DocumentBinding::{DocumentMethods, DocumentReadyStateValues};
use dom::bindings::codegen::Bindings::HTMLIFrameElementBinding;
use dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElementMethods;
use dom::bindings::codegen::InheritTypes::{NodeCast, ElementCast};
@@ -15,7 +16,7 @@ use dom::element::{HTMLIFrameElementTypeId, Element};
use dom::element::AttributeHandlers;
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement;
-use dom::node::{Node, NodeHelpers, ElementNodeTypeId, window_from_node};
+use dom::node::{Node, NodeHelpers, ElementNodeTypeId, window_from_node, document_from_node};
use dom::virtualmethods::VirtualMethods;
use dom::window::Window;
use page::IterablePage;
@@ -119,8 +120,13 @@ impl<'a> HTMLIFrameElementHelpers for JSRef<'a, HTMLIFrameElement> {
subpage_id: subpage_id,
}));
- let ConstellationChan(ref chan) = page.constellation_chan;
- chan.send(LoadIframeUrlMsg(url, page.id, subpage_id, sandboxed));
+ let doc = document_from_node(self).root();
+ if doc.ReadyState() != DocumentReadyStateValues::Complete {
+ // https://github.com/servo/servo/issues/3738
+ // We can't handle dynamic frame tree changes in the compositor right now.
+ let ConstellationChan(ref chan) = page.constellation_chan;
+ chan.send(LoadIframeUrlMsg(url, page.id, subpage_id, sandboxed));
+ }
}
}
diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs
index e47e67419ba..e3e7ef15bad 100644
--- a/components/script/dom/node.rs
+++ b/components/script/dom/node.rs
@@ -30,7 +30,7 @@ use dom::bindings::utils;
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
use dom::characterdata::CharacterData;
use dom::comment::Comment;
-use dom::document::{Document, DocumentHelpers, HTMLDocument, NonHTMLDocument};
+use dom::document::{Document, DocumentHelpers, HTMLDocument, NonHTMLDocument, NotFromParser};
use dom::documentfragment::DocumentFragment;
use dom::documenttype::DocumentType;
use dom::element::{AttributeHandlers, Element, ElementTypeId};
@@ -1511,7 +1511,7 @@ impl Node {
};
let window = document.window().root();
let document = Document::new(*window, Some(document.url().clone()),
- is_html_doc, None);
+ is_html_doc, None, NotFromParser);
NodeCast::from_temporary(document)
},
ElementNodeTypeId(..) => {
diff --git a/components/script/dom/webidls/Document.webidl b/components/script/dom/webidls/Document.webidl
index ec1d2d0c293..9ca633a8ba4 100644
--- a/components/script/dom/webidls/Document.webidl
+++ b/components/script/dom/webidls/Document.webidl
@@ -17,7 +17,6 @@ interface Document : Node {
readonly attribute DOMString compatMode;
readonly attribute DOMString characterSet;
readonly attribute DOMString contentType;
- readonly attribute Location location;
readonly attribute DocumentType? doctype;
readonly attribute Element? documentElement;
@@ -52,17 +51,23 @@ interface Document : Node {
[NewObject]
TreeWalker createTreeWalker(Node root, optional unsigned long whatToShow = 0xFFFFFFFF, optional NodeFilter? filter = null);
};
+Document implements ParentNode;
+
+enum DocumentReadyState { "loading", "interactive", "complete" };
/* http://www.whatwg.org/specs/web-apps/current-work/#the-document-object */
partial interface Document {
+ // resource metadata management
+ readonly attribute DocumentReadyState readyState;
readonly attribute DOMString lastModified;
+ readonly attribute Location location;
+
+ // DOM tree accessors
[SetterThrows]
attribute DOMString title;
[SetterThrows]
attribute HTMLElement? body;
readonly attribute HTMLHeadElement? head;
- NodeList getElementsByName(DOMString elementName);
-
readonly attribute HTMLCollection images;
readonly attribute HTMLCollection embeds;
readonly attribute HTMLCollection plugins;
@@ -71,7 +76,9 @@ partial interface Document {
readonly attribute HTMLCollection scripts;
readonly attribute HTMLCollection anchors;
readonly attribute HTMLCollection applets;
-};
+ NodeList getElementsByName(DOMString elementName);
-Document implements ParentNode;
+ // special event handler IDL attributes that only apply to Document objects
+ /*[LenientThis]*/ attribute EventHandler onreadystatechange;
+};
Document implements GlobalEventHandlers;
diff --git a/components/script/script_task.rs b/components/script/script_task.rs
index 2ea31959e05..363b7ea75cf 100644
--- a/components/script/script_task.rs
+++ b/components/script/script_task.rs
@@ -6,9 +6,10 @@
//! and layout tasks.
use dom::bindings::cell::DOMRefCell;
-use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
+use dom::bindings::codegen::Bindings::DocumentBinding::{DocumentMethods, DocumentReadyStateValues};
use dom::bindings::codegen::Bindings::DOMRectBinding::DOMRectMethods;
use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
+use dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods;
use dom::bindings::codegen::InheritTypes::{EventTargetCast, NodeCast, EventCast, ElementCast};
use dom::bindings::conversions;
use dom::bindings::conversions::{FromJSValConvertible, Empty};
@@ -17,7 +18,7 @@ use dom::bindings::js::{JS, JSRef, RootCollection, Temporary, OptionalRootable};
use dom::bindings::trace::JSTraceable;
use dom::bindings::utils::Reflectable;
use dom::bindings::utils::{wrap_for_same_compartment, pre_wrap};
-use dom::document::{Document, HTMLDocument, DocumentHelpers};
+use dom::document::{Document, HTMLDocument, DocumentHelpers, FromParser};
use dom::element::{Element, HTMLButtonElementTypeId, HTMLInputElementTypeId};
use dom::element::{HTMLSelectElementTypeId, HTMLTextAreaElementTypeId, HTMLOptionElementTypeId};
use dom::event::{Event, Bubbles, DoesNotBubble, Cancelable, NotCancelable};
@@ -762,7 +763,7 @@ impl ScriptTask {
url.clone()
};
let document = Document::new(*window, Some(doc_url), HTMLDocument,
- None).root();
+ None, FromParser).root();
window.init_browser_context(*document);
@@ -793,6 +794,8 @@ impl ScriptTask {
});
}
+ document.set_ready_state(DocumentReadyStateValues::Interactive);
+
// Send style sheets over to layout.
//
// FIXME: These should be streamed to layout as they're parsed. We don't need to stop here
@@ -849,11 +852,20 @@ impl ScriptTask {
}
});
+ // https://html.spec.whatwg.org/multipage/#the-end step 4
+ let event = Event::new(&global::Window(*window), "DOMContentLoaded".to_string(),
+ DoesNotBubble, NotCancelable).root();
+ let doctarget: JSRef<EventTarget> = EventTargetCast::from_ref(*document);
+ let _ = doctarget.DispatchEvent(*event);
+
// We have no concept of a document loader right now, so just dispatch the
// "load" event as soon as we've finished executing all scripts parsed during
// the initial load.
+
+ // https://html.spec.whatwg.org/multipage/#the-end step 7
+ document.set_ready_state(DocumentReadyStateValues::Complete);
+
let event = Event::new(&global::Window(*window), "load".to_string(), DoesNotBubble, NotCancelable).root();
- let doctarget: JSRef<EventTarget> = EventTargetCast::from_ref(*document);
let wintarget: JSRef<EventTarget> = EventTargetCast::from_ref(*window);
let _ = wintarget.dispatch_event_with_target(Some(doctarget), *event);
diff --git a/tests/content/test_document_readystate.html b/tests/content/test_document_readystate.html
new file mode 100644
index 00000000000..ef845b69981
--- /dev/null
+++ b/tests/content/test_document_readystate.html
@@ -0,0 +1,15 @@
+<html>
+<head>
+ <script src="harness.js"></script>
+</head>
+<!-- gNumChanges should be 2 once synchronous script execution is supported -->
+<body onload="is(document.readyState, 'complete'); is(gNumChanges, 1); finish()">
+ <script>
+ gNumChanges = 0;
+ document.addEventListener('readystatechange', function() {
+ gNumChanges++;
+ }, true);
+ is(document.readyState, "interactive");
+ </script>
+</body>
+</html>
diff --git a/tests/wpt/metadata/html/dom/documents/resource-metadata-management/document-readyState.html.ini b/tests/wpt/metadata/html/dom/documents/resource-metadata-management/document-readyState.html.ini
index 14677321e6a..2daf39c9ffe 100644
--- a/tests/wpt/metadata/html/dom/documents/resource-metadata-management/document-readyState.html.ini
+++ b/tests/wpt/metadata/html/dom/documents/resource-metadata-management/document-readyState.html.ini
@@ -1,3 +1,4 @@
[document-readyState.html]
type: testharness
- expected: TIMEOUT
+ [readystatechange event is fired each time document.readyState changes]
+ expected: FAIL
diff --git a/tests/wpt/metadata/html/dom/interfaces.html.ini b/tests/wpt/metadata/html/dom/interfaces.html.ini
index ef2669f31f1..5c69240bf80 100644
--- a/tests/wpt/metadata/html/dom/interfaces.html.ini
+++ b/tests/wpt/metadata/html/dom/interfaces.html.ini
@@ -9,9 +9,6 @@
[Document interface: attribute cookie]
expected: FAIL
- [Document interface: attribute readyState]
- expected: FAIL
-
[Document interface: attribute dir]
expected: FAIL
@@ -72,9 +69,6 @@
[Document interface: attribute commands]
expected: FAIL
- [Document interface: attribute onreadystatechange]
- expected: FAIL
-
[Document interface: attribute fgColor]
expected: FAIL
@@ -1110,9 +1104,6 @@
[Document interface: document.implementation.createDocument(null, "", null) must inherit property "cookie" with the proper type (35)]
expected: FAIL
- [Document interface: document.implementation.createDocument(null, "", null) must inherit property "readyState" with the proper type (37)]
- expected: FAIL
-
[Document interface: document.implementation.createDocument(null, "", null) must inherit property "dir" with the proper type (40)]
expected: FAIL
@@ -1206,9 +1197,6 @@
[Document interface: document.implementation.createDocument(null, "", null) must inherit property "commands" with the proper type (68)]
expected: FAIL
- [Document interface: document.implementation.createDocument(null, "", null) must inherit property "onreadystatechange" with the proper type (69)]
- expected: FAIL
-
[Document interface: document.implementation.createDocument(null, "", null) must inherit property "fgColor" with the proper type (70)]
expected: FAIL