servo: Merge #20054 - Implement element.innerText setter (from ferjm:innertext.setter); r=emilio
authorFernando Jiménez Moreno <ferjmoreno@gmail.com>
Fri, 16 Feb 2018 05:38:38 -0500
changeset 404209 f084649dce30aba28687d8259905c684de66c685
parent 404208 799ef07581c3c78e140e96e1084a76094420c25b
child 404210 841865bb1ee82a560dd4ca0163a37f87908d3df1
push id99968
push userrgurzau@mozilla.com
push dateFri, 16 Feb 2018 22:14:56 +0000
treeherdermozilla-inbound@2e16779c96cc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
milestone60.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
servo: Merge #20054 - Implement element.innerText setter (from ferjm:innertext.setter); r=emilio - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes fix #16107 - [X] There are tests for these changes Source-Repo: https://github.com/servo/servo Source-Revision: a6113af87335d69d11e53bc0ef2618dc7f6d16a0
servo/components/script/dom/htmlelement.rs
--- a/servo/components/script/dom/htmlelement.rs
+++ b/servo/components/script/dom/htmlelement.rs
@@ -12,27 +12,30 @@ use dom::bindings::codegen::Bindings::No
 use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
 use dom::bindings::error::{Error, ErrorResult};
 use dom::bindings::inheritance::{ElementTypeId, HTMLElementTypeId, NodeTypeId};
 use dom::bindings::inheritance::Castable;
 use dom::bindings::root::{Dom, DomRoot, MutNullableDom, RootedReference};
 use dom::bindings::str::DOMString;
 use dom::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration, CSSStyleOwner};
 use dom::document::{Document, FocusType};
+use dom::documentfragment::DocumentFragment;
 use dom::domstringmap::DOMStringMap;
 use dom::element::{AttributeMutation, Element};
 use dom::eventtarget::EventTarget;
 use dom::htmlbodyelement::HTMLBodyElement;
+use dom::htmlbrelement::HTMLBRElement;
 use dom::htmlframesetelement::HTMLFrameSetElement;
 use dom::htmlhtmlelement::HTMLHtmlElement;
 use dom::htmlinputelement::{HTMLInputElement, InputType};
 use dom::htmllabelelement::HTMLLabelElement;
 use dom::node::{Node, NodeFlags};
 use dom::node::{document_from_node, window_from_node};
 use dom::nodelist::NodeList;
+use dom::text::Text;
 use dom::virtualmethods::VirtualMethods;
 use dom::window::ReflowReason;
 use dom_struct::dom_struct;
 use html5ever::{LocalName, Prefix};
 use script_layout_interface::message::ReflowGoal;
 use std::collections::HashSet;
 use std::default::Default;
 use std::rc::Rc;
@@ -416,21 +419,74 @@ impl HTMLElementMethods for HTMLElement 
             return node.GetTextContent().unwrap();
         }
 
         window.reflow(ReflowGoal::ElementInnerTextQuery(node.to_trusted_node_address()), ReflowReason::Query);
         DOMString::from(window.layout().element_inner_text())
     }
 
     // https://html.spec.whatwg.org/multipage/#the-innertext-idl-attribute
-    fn SetInnerText(&self, _: DOMString) {
-        // XXX (ferjm) implement this.
+    fn SetInnerText(&self, input: DOMString) {
+        // Step 1.
+        let document = document_from_node(self);
+
+        // Step 2.
+        let fragment = DocumentFragment::new(&document);
+
+        // Step 3. The given value is already named 'input'.
+
+        // Step 4.
+        let mut position = input.chars().peekable();
+
+        // Step 5.
+        let mut text = String::new();
+
+        // Step 6.
+        while let Some(ch) = position.next() {
+            match ch {
+                '\u{000A}' | '\u{000D}' => {
+                    if ch == '\u{000D}' && position.peek() == Some(&'\u{000A}') {
+                        // a \r\n pair should only generate one <br>,
+                        // so just skip the \r.
+                        position.next();
+                    }
+
+                    if !text.is_empty() {
+                        append_text_node_to_fragment(&document, &fragment, text);
+                        text = String::new();
+                    }
+
+                    let br = HTMLBRElement::new(local_name!("br"), None, &document);
+                    fragment.upcast::<Node>().AppendChild(&br.upcast()).unwrap();
+                },
+                _ => {
+                    text.push(ch);
+                }
+            }
+        }
+
+        if !text.is_empty() {
+            append_text_node_to_fragment(&document, &fragment, text);
+        }
+
+        // Step 7.
+        Node::replace_all(Some(fragment.upcast()), self.upcast::<Node>());
     }
 }
 
+fn append_text_node_to_fragment(
+    document: &Document,
+    fragment: &DocumentFragment,
+    text: String
+) {
+    let text = Text::new(DOMString::from(text), document);
+    let node = DomRoot::upcast::<Node>(text);
+    fragment.upcast::<Node>().AppendChild(&node).unwrap();
+}
+
 // https://html.spec.whatwg.org/multipage/#attr-data-*
 
 static DATA_PREFIX: &str = "data-";
 static DATA_HYPHEN_SEPARATOR: char = '\x2d';
 
 fn is_ascii_uppercase(c: char) -> bool {
     'A' <= c && c <= 'Z'
 }