mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
Implement Document.readyState. Prevent iframes from notifying the compositor after the initial parse. Fixes #1720. Fixes #3738.
This commit is contained in:
parent
470d27a668
commit
539c21f380
10 changed files with 113 additions and 41 deletions
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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(..) => {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
15
tests/content/test_document_readystate.html
Normal file
15
tests/content/test_document_readystate.html
Normal file
|
@ -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>
|
|
@ -1,3 +1,4 @@
|
|||
[document-readyState.html]
|
||||
type: testharness
|
||||
expected: TIMEOUT
|
||||
[readystatechange event is fired each time document.readyState changes]
|
||||
expected: FAIL
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue