diff options
-rw-r--r-- | components/script/dom/document.rs | 56 | ||||
-rw-r--r-- | components/script/dom/domimplementation.rs | 6 | ||||
-rw-r--r-- | components/script/dom/domparser.rs | 9 | ||||
-rw-r--r-- | components/script/dom/htmliframeelement.rs | 12 | ||||
-rw-r--r-- | components/script/dom/node.rs | 4 | ||||
-rw-r--r-- | components/script/dom/webidls/Document.webidl | 17 | ||||
-rw-r--r-- | components/script/script_task.rs | 20 | ||||
-rw-r--r-- | tests/content/test_document_readystate.html | 15 | ||||
-rw-r--r-- | tests/wpt/metadata/html/dom/documents/resource-metadata-management/document-readyState.html.ini | 3 | ||||
-rw-r--r-- | tests/wpt/metadata/html/dom/interfaces.html.ini | 12 |
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 |