Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Element.innerHTML setter #4888

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -58,7 +58,7 @@ impl<'a> DOMParserMethods for JSRef<'a, DOMParser> {
Some(content_type),
None,
DocumentSource::FromParser).root();
parse_html(document.r(), HTMLInput::InputString(s), &url);
parse_html(document.r(), HTMLInput::InputString(s), &url, None);
document.r().set_ready_state(DocumentReadyState::Complete);
Ok(Temporary::from_rooted(document.r()))
}
@@ -10,18 +10,21 @@ use dom::attr::AttrValue;
use dom::namednodemap::NamedNodeMap;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use dom::bindings::codegen::Bindings::ElementBinding;
use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods;
use dom::bindings::codegen::Bindings::NamedNodeMapBinding::NamedNodeMapMethods;
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::codegen::InheritTypes::{ElementCast, ElementDerived, EventTargetCast};
use dom::bindings::codegen::InheritTypes::{HTMLBodyElementDerived, HTMLInputElementCast};
use dom::bindings::codegen::InheritTypes::{HTMLInputElementDerived, HTMLTableElementCast};
use dom::bindings::codegen::InheritTypes::{HTMLTableElementDerived, HTMLTableCellElementDerived};
use dom::bindings::codegen::InheritTypes::{HTMLTableRowElementDerived, HTMLTextAreaElementDerived};
use dom::bindings::codegen::InheritTypes::{HTMLTableSectionElementDerived, NodeCast};
use dom::bindings::codegen::InheritTypes::HTMLAnchorElementCast;
use dom::bindings::codegen::InheritTypes::HTMLFormElementDerived;
use dom::bindings::error::{ErrorResult, Fallible};
use dom::bindings::error::Error::{NamespaceError, InvalidCharacter, Syntax};
use dom::bindings::js::{MutNullableJS, JS, JSRef, LayoutJS, Temporary, TemporaryPushable};
@@ -32,6 +35,7 @@ use dom::create::create_element;
use dom::domrect::DOMRect;
use dom::domrectlist::DOMRectList;
use dom::document::{Document, DocumentHelpers, LayoutDocumentHelpers};
use dom::document::{DocumentSource, IsHTMLDocument};
use dom::domtokenlist::DOMTokenList;
use dom::event::{Event, EventHelpers};
use dom::eventtarget::{EventTarget, EventTargetTypeId};
@@ -50,8 +54,10 @@ use dom::node::{CLICK_IN_PROGRESS, LayoutNodeHelpers, Node, NodeHelpers, NodeTyp
use dom::node::{NodeIterator, document_from_node, NodeDamage};
use dom::node::{window_from_node};
use dom::nodelist::NodeList;
use dom::servohtmlparser::FragmentContext;
use dom::virtualmethods::{VirtualMethods, vtable_for};
use devtools_traits::AttrInfo;
use parse::html::{HTMLInput, parse_html};
use style::legacy::{SimpleColorAttribute, UnsignedIntegerAttribute, IntegerAttribute, LengthAttribute};
use selectors::matching::matches;
use style::properties::{PropertyDeclarationBlock, PropertyDeclaration, parse_style_attribute};
@@ -1117,6 +1123,58 @@ impl<'a> ElementMethods for JSRef<'a, Element> {
Ok(serialize(&mut NodeIterator::new(NodeCast::from_ref(self), false, false)))
}

fn SetInnerHTML(self, value: DOMString) -> Fallible<()> {
let window = window_from_node(self).root();
let context_document = document_from_node(self).root();
let context_node: JSRef<Node> = NodeCast::from_ref(self);
let url = context_document.r().url();

// Follows https://html.spec.whatwg.org/multipage/syntax.html#parsing-html-fragments

// 1. Create a new Document node, and mark it as being an HTML document.
let document = Document::new(window.r(), Some(url.clone()),
IsHTMLDocument::HTMLDocument,
None, None,
DocumentSource::FromParser).root();

// 2. If the node document of the context element is in quirks mode,
// then let the Document be in quirks mode. Otherwise,
// the node document of the context element is in limited-quirks mode,
// then let the Document be in limited-quirks mode. Otherwise,
// leave the Document in no-quirks mode.
document.r().set_quirks_mode(context_document.r().quirks_mode());

// 11. Set the parser's form element pointer to the nearest node to
// the context element that is a form element (going straight up
// the ancestor chain, and including the element itself, if it
// is a form element), if any. (If there is no such form element,
// the form element pointer keeps its initial value, null.)
let form = context_node.inclusive_ancestors()
.find(|element| element.is_htmlformelement());
let fragment_context = FragmentContext {
context_elem: context_node,
form_elem: form,
};
parse_html(document.r(), HTMLInput::InputString(value), &url, Some(fragment_context));

// "14. Return the child nodes of root, in tree order."
// We do this by deleting all nodes of the context node,
// and then moving all nodes parsed into the new root_node
// into the context node.
while let Some(child) = context_node.GetFirstChild() {
try!(context_node.RemoveChild(child.root().r()));
}
let root_element = document.r().GetDocumentElement().expect("no document element").root();
let root_node: JSRef<Node> = NodeCast::from_ref(root_element.r());
while let Some(child) = root_node.GetFirstChild() {
let child = child.root();
try!(root_node.RemoveChild(child.r()));
try!(context_node.AppendChild(child.r()));
}

Ok(())
}

fn GetOuterHTML(self) -> Fallible<DOMString> {
Ok(serialize(&mut NodeIterator::new(NodeCast::from_ref(self), true, false)))
}
@@ -31,6 +31,14 @@ pub struct Sink {
pub document: JS<Document>,
}

/// FragmentContext is used only to pass this group of related values
/// into functions.
#[derive(Copy)]
pub struct FragmentContext<'a> {
pub context_elem: JSRef<'a, Node>,
pub form_elem: Option<JSRef<'a, Node>>,
}

pub type Tokenizer = tokenizer::Tokenizer<TreeBuilder<JS<Node>, Sink>>;

// NB: JSTraceable is *not* auto-derived.
@@ -76,6 +84,39 @@ impl ServoHTMLParser {
ServoHTMLParserBinding::Wrap)
}

#[allow(unrooted_must_root)]
pub fn new_for_fragment(base_url: Option<Url>, document: JSRef<Document>,
fragment_context: FragmentContext) -> Temporary<ServoHTMLParser> {
let window = document.window().root();
let sink = Sink {
base_url: base_url,
document: JS::from_rooted(document),
};

let tb_opts = TreeBuilderOpts {
ignore_missing_rules: true,
.. Default::default()
};
let tb = TreeBuilder::new_for_fragment(sink,
JS::from_rooted(fragment_context.context_elem),
fragment_context.form_elem.map(|n| JS::from_rooted(n)),
tb_opts);

let tok_opts = tokenizer::TokenizerOpts {
initial_state: Some(tb.tokenizer_state_for_context_elem()),
.. Default::default()
};
let tok = tokenizer::Tokenizer::new(tb, tok_opts);

let parser = ServoHTMLParser {
reflector_: Reflector::new(),
tokenizer: DOMRefCell::new(tok),
};

reflect_dom_object(box parser, GlobalRef::Window(window.r()),
ServoHTMLParserBinding::Wrap)
}

#[inline]
pub fn tokenizer<'a>(&'a self) -> &'a DOMRefCell<Tokenizer> {
&self.tokenizer
@@ -64,7 +64,7 @@ partial interface Element {
// http://domparsing.spec.whatwg.org/#extensions-to-the-element-interface
partial interface Element {
[Throws,TreatNullAs=EmptyString]
readonly attribute DOMString innerHTML;
attribute DOMString innerHTML;
[Throws,TreatNullAs=EmptyString]
readonly attribute DOMString outerHTML;
};
@@ -16,7 +16,7 @@ use dom::htmlscriptelement::HTMLScriptElement;
use dom::htmlscriptelement::HTMLScriptElementHelpers;
use dom::node::{Node, NodeHelpers};
use dom::servohtmlparser;
use dom::servohtmlparser::ServoHTMLParser;
use dom::servohtmlparser::{ServoHTMLParser, FragmentContext};
use dom::text::Text;
use parse::Parser;

@@ -171,8 +171,12 @@ impl<'a> TreeSink for servohtmlparser::Sink {

pub fn parse_html(document: JSRef<Document>,
input: HTMLInput,
url: &Url) {
let parser = ServoHTMLParser::new(Some(url.clone()), document).root();
url: &Url,
fragment_context: Option<FragmentContext>) {
let parser = match fragment_context {
None => ServoHTMLParser::new(Some(url.clone()), document).root(),
Some(fc) => ServoHTMLParser::new_for_fragment(Some(url.clone()), document, fc).root(),
};
let parser: JSRef<ServoHTMLParser> = parser.r();

let nested_parse = task_state::get().contains(task_state::IN_HTML_PARSER);
@@ -1013,7 +1013,7 @@ impl ScriptTask {
HTMLInput::InputUrl(response)
};

parse_html(document.r(), parse_input, &final_url);
parse_html(document.r(), parse_input, &final_url, None);

document.r().set_ready_state(DocumentReadyState::Interactive);
self.compositor.borrow_mut().set_ready_state(incomplete.pipeline_id, PerformingLayout);
@@ -1,11 +1,5 @@
[Document-getElementById.html]
type: testharness
[add id attribute via innerHTML]
expected: FAIL

[remove id attribute via innerHTML]
expected: FAIL

[add id attribute via outerHTML]
expected: FAIL

This file was deleted.

This file was deleted.

ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.