mirror of
https://github.com/servo/servo.git
synced 2025-08-05 13:40:08 +01:00
Properly insert text in document when parsing
If the last child of a node is a Text child and we are inserting text in that node, we need to append it to that Text child. Doing that means that HTMLStyleElement::children_changed gets called less frequently, but that's not a problem during parsing thanks to the pop hook.
This commit is contained in:
parent
8caa564540
commit
0cb09468a0
17 changed files with 72 additions and 31 deletions
|
@ -36,6 +36,7 @@ pub struct HTMLStyleElement {
|
||||||
cssom_stylesheet: MutNullableJS<CSSStyleSheet>,
|
cssom_stylesheet: MutNullableJS<CSSStyleSheet>,
|
||||||
/// https://html.spec.whatwg.org/multipage/#a-style-sheet-that-is-blocking-scripts
|
/// https://html.spec.whatwg.org/multipage/#a-style-sheet-that-is-blocking-scripts
|
||||||
parser_inserted: Cell<bool>,
|
parser_inserted: Cell<bool>,
|
||||||
|
in_stack_of_open_elements: Cell<bool>,
|
||||||
pending_loads: Cell<u32>,
|
pending_loads: Cell<u32>,
|
||||||
any_failed_load: Cell<bool>,
|
any_failed_load: Cell<bool>,
|
||||||
}
|
}
|
||||||
|
@ -50,6 +51,7 @@ impl HTMLStyleElement {
|
||||||
stylesheet: DOMRefCell::new(None),
|
stylesheet: DOMRefCell::new(None),
|
||||||
cssom_stylesheet: MutNullableJS::new(None),
|
cssom_stylesheet: MutNullableJS::new(None),
|
||||||
parser_inserted: Cell::new(creator == ElementCreator::ParserCreated),
|
parser_inserted: Cell::new(creator == ElementCreator::ParserCreated),
|
||||||
|
in_stack_of_open_elements: Cell::new(creator == ElementCreator::ParserCreated),
|
||||||
pending_loads: Cell::new(0),
|
pending_loads: Cell::new(0),
|
||||||
any_failed_load: Cell::new(false),
|
any_failed_load: Cell::new(false),
|
||||||
}
|
}
|
||||||
|
@ -124,20 +126,38 @@ impl VirtualMethods for HTMLStyleElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn children_changed(&self, mutation: &ChildrenMutation) {
|
fn children_changed(&self, mutation: &ChildrenMutation) {
|
||||||
if let Some(ref s) = self.super_type() {
|
self.super_type().unwrap().children_changed(mutation);
|
||||||
s.children_changed(mutation);
|
|
||||||
}
|
// https://html.spec.whatwg.org/multipage/#update-a-style-block
|
||||||
if self.upcast::<Node>().is_in_doc() {
|
// Handles the case when:
|
||||||
|
// "The element is not on the stack of open elements of an HTML parser or XML parser,
|
||||||
|
// and one of its child nodes is modified by a script."
|
||||||
|
// TODO: Handle Text child contents being mutated.
|
||||||
|
if self.upcast::<Node>().is_in_doc() && !self.in_stack_of_open_elements.get() {
|
||||||
self.parse_own_css();
|
self.parse_own_css();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bind_to_tree(&self, tree_in_doc: bool) {
|
fn bind_to_tree(&self, tree_in_doc: bool) {
|
||||||
if let Some(ref s) = self.super_type() {
|
self.super_type().unwrap().bind_to_tree(tree_in_doc);
|
||||||
s.bind_to_tree(tree_in_doc);
|
|
||||||
}
|
|
||||||
|
|
||||||
if tree_in_doc {
|
// https://html.spec.whatwg.org/multipage/#update-a-style-block
|
||||||
|
// Handles the case when:
|
||||||
|
// "The element is not on the stack of open elements of an HTML parser or XML parser,
|
||||||
|
// and it becomes connected or disconnected."
|
||||||
|
if tree_in_doc && !self.in_stack_of_open_elements.get() {
|
||||||
|
self.parse_own_css();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pop(&self) {
|
||||||
|
self.super_type().unwrap().pop();
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/#update-a-style-block
|
||||||
|
// Handles the case when:
|
||||||
|
// "The element is popped off the stack of open elements of an HTML parser or XML parser."
|
||||||
|
self.in_stack_of_open_elements.set(false);
|
||||||
|
if self.upcast::<Node>().is_in_doc() {
|
||||||
self.parse_own_css();
|
self.parse_own_css();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -425,6 +425,13 @@ impl VirtualMethods for HTMLTextAreaElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pop(&self) {
|
||||||
|
self.super_type().unwrap().pop();
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/#the-textarea-element:stack-of-open-elements
|
||||||
|
self.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FormControl for HTMLTextAreaElement {}
|
impl FormControl for HTMLTextAreaElement {}
|
||||||
|
|
|
@ -20,6 +20,7 @@ use dom::htmltemplateelement::HTMLTemplateElement;
|
||||||
use dom::node::Node;
|
use dom::node::Node;
|
||||||
use dom::processinginstruction::ProcessingInstruction;
|
use dom::processinginstruction::ProcessingInstruction;
|
||||||
use dom::text::Text;
|
use dom::text::Text;
|
||||||
|
use dom::virtualmethods::vtable_for;
|
||||||
use html5ever::Attribute;
|
use html5ever::Attribute;
|
||||||
use html5ever::serialize::{AttrRef, Serializable, Serializer};
|
use html5ever::serialize::{AttrRef, Serializable, Serializer};
|
||||||
use html5ever::serialize::TraversalScope;
|
use html5ever::serialize::TraversalScope;
|
||||||
|
@ -124,7 +125,7 @@ struct Sink {
|
||||||
document: JS<Document>,
|
document: JS<Document>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TreeSink for Sink {
|
impl TreeSink for Sink {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
fn finish(self) -> Self { self }
|
fn finish(self) -> Self { self }
|
||||||
|
|
||||||
|
@ -233,7 +234,11 @@ impl<'a> TreeSink for Sink {
|
||||||
while let Some(ref child) = node.GetFirstChild() {
|
while let Some(ref child) = node.GetFirstChild() {
|
||||||
new_parent.AppendChild(&child).unwrap();
|
new_parent.AppendChild(&child).unwrap();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pop(&mut self, node: JS<Node>) {
|
||||||
|
let node = Root::from_ref(&*node);
|
||||||
|
vtable_for(&node).pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,10 +248,13 @@ fn insert(parent: &Node, reference_child: Option<&Node>, child: NodeOrText<JS<No
|
||||||
assert!(parent.InsertBefore(&n, reference_child).is_ok());
|
assert!(parent.InsertBefore(&n, reference_child).is_ok());
|
||||||
},
|
},
|
||||||
NodeOrText::AppendText(t) => {
|
NodeOrText::AppendText(t) => {
|
||||||
// FIXME(ajeffrey): convert directly from tendrils to DOMStrings
|
if let Some(text) = parent.GetLastChild().and_then(Root::downcast::<Text>) {
|
||||||
let s: String = t.into();
|
text.upcast::<CharacterData>().append_data(&t);
|
||||||
let text = Text::new(DOMString::from(s), &parent.owner_doc());
|
} else {
|
||||||
assert!(parent.InsertBefore(text.upcast(), reference_child).is_ok());
|
let s: String = t.into();
|
||||||
|
let text = Text::new(DOMString::from(s), &parent.owner_doc());
|
||||||
|
parent.InsertBefore(text.upcast(), reference_child).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ use dom::htmlscriptelement::HTMLScriptElement;
|
||||||
use dom::node::Node;
|
use dom::node::Node;
|
||||||
use dom::processinginstruction::ProcessingInstruction;
|
use dom::processinginstruction::ProcessingInstruction;
|
||||||
use dom::text::Text;
|
use dom::text::Text;
|
||||||
|
use dom::virtualmethods::vtable_for;
|
||||||
use html5ever::tokenizer::buffer_queue::BufferQueue;
|
use html5ever::tokenizer::buffer_queue::BufferQueue;
|
||||||
use html5ever_atoms::{Prefix, QualName};
|
use html5ever_atoms::{Prefix, QualName};
|
||||||
use js::jsapi::JSTracer;
|
use js::jsapi::JSTracer;
|
||||||
|
@ -196,4 +197,9 @@ impl TreeSink for Sink {
|
||||||
NextParserState::Continue
|
NextParserState::Continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pop(&mut self, node: Self::Handle) {
|
||||||
|
let node = Root::from_ref(&*node);
|
||||||
|
vtable_for(&node).pop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,6 +124,14 @@ pub trait VirtualMethods {
|
||||||
s.cloning_steps(copy, maybe_doc, clone_children);
|
s.cloning_steps(copy, maybe_doc, clone_children);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Called on an element when it is popped off the stack of open elements
|
||||||
|
/// of a parser.
|
||||||
|
fn pop(&self) {
|
||||||
|
if let Some(ref s) = self.super_type() {
|
||||||
|
s.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Obtain a VirtualMethods instance for a given Node-derived object. Any
|
/// Obtain a VirtualMethods instance for a given Node-derived object. Any
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[open-url-javascript-window-2.htm]
|
[open-url-javascript-window-2.htm]
|
||||||
type: testharness
|
type: testharness
|
||||||
expected: TIMEOUT
|
expected: TIMEOUT
|
||||||
[XMLHttpRequest: open() - resolving URLs (javascript: ]
|
[XMLHttpRequest: open() - resolving URLs (javascript: <iframe>; 2)]
|
||||||
expected: TIMEOUT
|
expected: TIMEOUT
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[open-url-javascript-window.htm]
|
[open-url-javascript-window.htm]
|
||||||
type: testharness
|
type: testharness
|
||||||
expected: TIMEOUT
|
expected: TIMEOUT
|
||||||
[XMLHttpRequest: open() - resolving URLs (javascript: ]
|
[XMLHttpRequest: open() - resolving URLs (javascript: <iframe>; 1)]
|
||||||
expected: TIMEOUT
|
expected: TIMEOUT
|
||||||
|
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
[051.html]
|
|
||||||
type: testharness
|
|
||||||
[document.write \\r\\n]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[resource-selection-invoke-insert-source-networkState.html]
|
[resource-selection-invoke-insert-source-networkState.html]
|
||||||
type: testharness
|
type: testharness
|
||||||
[NOT invoking resource selection by inserting ]
|
[NOT invoking resource selection by inserting <source> when networkState is not NETWORK_EMPTY]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[resource-selection-invoke-insert-source-not-in-document.html]
|
[resource-selection-invoke-insert-source-not-in-document.html]
|
||||||
type: testharness
|
type: testharness
|
||||||
[invoking resource selection by inserting ]
|
[invoking resource selection by inserting <source> in video not in a document]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[resource-selection-invoke-insert-source.html]
|
[resource-selection-invoke-insert-source.html]
|
||||||
type: testharness
|
type: testharness
|
||||||
[invoking resource selection by inserting ]
|
[invoking resource selection by inserting <source>]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[resource-selection-remove-source.html]
|
[resource-selection-remove-source.html]
|
||||||
type: testharness
|
type: testharness
|
||||||
[Changes to networkState when inserting and removing a ]
|
[Changes to networkState when inserting and removing a <source>]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[resource-selection-source-media.html]
|
[resource-selection-source-media.html]
|
||||||
type: testharness
|
type: testharness
|
||||||
[the ]
|
[the <source> media attribute has no effect]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
[math-parse01.html]
|
[math-parse01.html]
|
||||||
type: testharness
|
type: testharness
|
||||||
[Testing rang and lang entity code points]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[html tags allowed in annotation-xml/@encoding='text/html'.]
|
[html tags allowed in annotation-xml/@encoding='text/html'.]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[compile-error-cross-origin.html]
|
[compile-error-cross-origin.html]
|
||||||
type: testharness
|
type: testharness
|
||||||
[window.onerror - compile error in ]
|
[window.onerror - compile error in <script src=//www1...>]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[window.onerror - compile error in <script src=//www1...> (column)]
|
[window.onerror - compile error in <script src=//www1...> (column)]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[runtime-error-cross-origin.html]
|
[runtime-error-cross-origin.html]
|
||||||
type: testharness
|
type: testharness
|
||||||
[window.onerror - runtime error in ]
|
[window.onerror - runtime error in <script src=//www1...>]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[window.onerror - runtime error in <script src=//www1...> (column)]
|
[window.onerror - runtime error in <script src=//www1...> (column)]
|
||||||
|
|
|
@ -10,7 +10,7 @@ function do_test() {
|
||||||
t.step(function() {
|
t.step(function() {
|
||||||
var doc = document.getElementsByTagName("iframe")[0].contentDocument;
|
var doc = document.getElementsByTagName("iframe")[0].contentDocument;
|
||||||
doc = doc.getElementsByTagName("iframe")[0].contentDocument;
|
doc = doc.getElementsByTagName("iframe")[0].contentDocument;
|
||||||
assert_equals(doc.body.firstChild.textContent, "foo");
|
assert_equals(doc.body.firstChild.textContent, "foo\n");
|
||||||
t.done();
|
t.done();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue