servo: Merge #14539 - Show the placeholder text for textarea elements (from frewsxcv:textarea-placeholder); r=mbrubeck
authorCorey Farwell <coreyf@rwell.org>
Wed, 14 Dec 2016 12:13:06 -0800
changeset 340338 9f5a4f88bf77c4e89885341edf2d869ff74a5540
parent 340337 c51f5a888776060e01179484d39239e2c0bf70cc
child 340339 36db531c4b8145b0f30eaa4230245725b907c459
push id31307
push usergszorc@mozilla.com
push dateSat, 04 Feb 2017 00:59:06 +0000
treeherdermozilla-central@94079d43835f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmbrubeck
servo: Merge #14539 - Show the placeholder text for textarea elements (from frewsxcv:textarea-placeholder); r=mbrubeck Fixes https://github.com/servo/servo/issues/10552. All this logic was taken from htmlinputelement.rs. Source-Repo: https://github.com/servo/servo Source-Revision: 816b2969c34a43c51365ea2b49d90c08d434c9ea
servo/components/script/dom/htmltextareaelement.rs
servo/servo-tidy.toml
--- a/servo/components/script/dom/htmltextareaelement.rs
+++ b/servo/components/script/dom/htmltextareaelement.rs
@@ -34,16 +34,17 @@ use style::attr::AttrValue;
 use style::element_state::*;
 use textinput::{KeyReaction, Lines, SelectionDirection, TextInput};
 
 #[dom_struct]
 pub struct HTMLTextAreaElement {
     htmlelement: HTMLElement,
     #[ignore_heap_size_of = "#7193"]
     textinput: DOMRefCell<TextInput<IpcSender<ConstellationMsg>>>,
+    placeholder: DOMRefCell<DOMString>,
     // https://html.spec.whatwg.org/multipage/#concept-textarea-dirty
     value_changed: Cell<bool>,
 }
 
 pub trait LayoutHTMLTextAreaElementHelpers {
     #[allow(unsafe_code)]
     unsafe fn get_value_for_layout(self) -> String;
     #[allow(unsafe_code)]
@@ -53,17 +54,22 @@ pub trait LayoutHTMLTextAreaElementHelpe
     #[allow(unsafe_code)]
     fn get_rows(self) -> u32;
 }
 
 impl LayoutHTMLTextAreaElementHelpers for LayoutJS<HTMLTextAreaElement> {
     #[allow(unrooted_must_root)]
     #[allow(unsafe_code)]
     unsafe fn get_value_for_layout(self) -> String {
-        String::from((*self.unsafe_get()).textinput.borrow_for_layout().get_content())
+        let text = (*self.unsafe_get()).textinput.borrow_for_layout().get_content();
+        String::from(if text.is_empty() {
+            (*self.unsafe_get()).placeholder.borrow_for_layout().clone()
+        } else {
+            text
+        })
     }
 
     #[allow(unrooted_must_root)]
     #[allow(unsafe_code)]
     unsafe fn selection_for_layout(self) -> Option<Range<usize>> {
         if !(*self.unsafe_get()).upcast::<Element>().focus_state() {
             return None;
         }
@@ -100,30 +106,39 @@ impl HTMLTextAreaElement {
     fn new_inherited(local_name: LocalName,
                      prefix: Option<DOMString>,
                      document: &Document) -> HTMLTextAreaElement {
         let chan = document.window().upcast::<GlobalScope>().constellation_chan().clone();
         HTMLTextAreaElement {
             htmlelement:
                 HTMLElement::new_inherited_with_state(IN_ENABLED_STATE | IN_READ_WRITE_STATE,
                                                       local_name, prefix, document),
+            placeholder: DOMRefCell::new(DOMString::new()),
             textinput: DOMRefCell::new(TextInput::new(
                     Lines::Multiple, DOMString::new(), chan, None, None, SelectionDirection::None)),
             value_changed: Cell::new(false),
         }
     }
 
     #[allow(unrooted_must_root)]
     pub fn new(local_name: LocalName,
                prefix: Option<DOMString>,
                document: &Document) -> Root<HTMLTextAreaElement> {
         Node::reflect_node(box HTMLTextAreaElement::new_inherited(local_name, prefix, document),
                            document,
                            HTMLTextAreaElementBinding::Wrap)
     }
+
+    fn update_placeholder_shown_state(&self) {
+        let has_placeholder = !self.placeholder.borrow().is_empty();
+        let has_value = !self.textinput.borrow().is_empty();
+        let el = self.upcast::<Element>();
+        el.set_placeholder_shown_state(has_placeholder && !has_value);
+        el.set_placeholder_shown_state(has_placeholder);
+    }
 }
 
 impl HTMLTextAreaElementMethods for HTMLTextAreaElement {
     // TODO A few of these attributes have default values and additional
     // constraints
 
     // https://html.spec.whatwg.org/multipage/#dom-textarea-cols
     make_uint_getter!(Cols, "cols", DEFAULT_COLS);
@@ -306,16 +321,26 @@ impl VirtualMethods for HTMLTextAreaElem
                         el.check_ancestors_disabled_state_for_form_control();
 
                         if !el.disabled_state() && !el.read_write_state() {
                             el.set_read_write_state(true);
                         }
                     }
                 }
             },
+            local_name!("placeholder") => {
+                {
+                    let mut placeholder = self.placeholder.borrow_mut();
+                    placeholder.clear();
+                    if let AttributeMutation::Set(_) = mutation {
+                        placeholder.push_str(&attr.value());
+                    }
+                }
+                self.update_placeholder_shown_state();
+            },
             local_name!("readonly") => {
                 let el = self.upcast::<Element>();
                 match mutation {
                     AttributeMutation::Set(_) => {
                         el.set_read_write_state(false);
                     },
                     AttributeMutation::Removed => {
                         el.set_read_write_state(!el.disabled_state());
@@ -370,20 +395,24 @@ impl VirtualMethods for HTMLTextAreaElem
         }
 
         if event.type_() == atom!("click") && !event.DefaultPrevented() {
             //TODO: set the editing position for text inputs
 
             document_from_node(self).request_focus(self.upcast());
         } else if event.type_() == atom!("keydown") && !event.DefaultPrevented() {
             if let Some(kevent) = event.downcast::<KeyboardEvent>() {
-                match self.textinput.borrow_mut().handle_keydown(kevent) {
+                // This can't be inlined, as holding on to textinput.borrow_mut()
+                // during self.implicit_submission will cause a panic.
+                let action = self.textinput.borrow_mut().handle_keydown(kevent);
+                match action {
                     KeyReaction::TriggerDefaultAction => (),
                     KeyReaction::DispatchInput => {
                         self.value_changed.set(true);
+                        self.update_placeholder_shown_state();
 
                         if event.IsTrusted() {
                             let window = window_from_node(self);
                             let _ = window.user_interaction_task_source().queue_event(
                                 &self.upcast(),
                                 atom!("input"),
                                 EventBubbles::Bubbles,
                                 EventCancelable::NotCancelable,
--- a/servo/servo-tidy.toml
+++ b/servo/servo-tidy.toml
@@ -24,16 +24,17 @@ files = [
   "./tests/wpt/metadata-css/MANIFEST.json",
   "./components/script/dom/webidls/ForceTouchEvent.webidl",
   "./support/android/openssl.sh",
   # Ignore those files since the issues reported are on purpose
   "./tests/html/bad-line-ends.html",
   "./tests/unit/net/parsable_mime/text",
   "./tests/wpt/mozilla/tests/css/fonts",
   "./tests/wpt/mozilla/tests/css/pre_with_tab.html",
+  "./tests/wpt/mozilla/tests/mozilla/textarea_placeholder.html",
   # FIXME(pcwalton, #11679): This is a workaround for a tidy error on the quoted string
   # `"__TEXT,_info_plist"` inside an attribute.
   "./components/servo/platform/macos/mod.rs",
 ]
 # Directories that are ignored for the non-WPT tidy check.
 directories = [
   # Upstream
   "./support/android/apk",