Преглед изворни кода

LibWeb: Implement "plaintext-only" state for contenteditable

Jelle Raaijmakers пре 8 месеци
родитељ
комит
217567981a

+ 1 - 2
Libraries/LibWeb/Editing/Internal/Algorithms.cpp

@@ -816,11 +816,10 @@ bool is_editing_host(GC::Ref<DOM::Node> node)
     // An editing host is either an HTML element with its contenteditable attribute in the true
     // state or plaintext-only state, or a child HTML element of a Document whose design mode
     // enabled is true.
-    // FIXME: check contenteditable "plaintext-only"
     if (!is<HTML::HTMLElement>(*node))
         return false;
     auto const& html_element = static_cast<HTML::HTMLElement&>(*node);
-    return html_element.content_editable() == "true"sv || node->document().design_mode_enabled_state();
+    return html_element.content_editable().is_one_of("true"sv, "plaintext-only"sv) || node->document().design_mode_enabled_state();
 }
 
 // https://w3c.github.io/editing/docs/execCommand/#element-with-inline-contents

+ 22 - 12
Libraries/LibWeb/HTML/HTMLElement.cpp

@@ -88,6 +88,7 @@ bool HTMLElement::is_editable() const
 {
     switch (m_content_editable_state) {
     case ContentEditableState::True:
+    case ContentEditableState::PlaintextOnly:
         return true;
     case ContentEditableState::False:
         return false;
@@ -100,7 +101,8 @@ bool HTMLElement::is_editable() const
 
 bool HTMLElement::is_focusable() const
 {
-    return m_content_editable_state == ContentEditableState::True;
+    return m_content_editable_state == ContentEditableState::True
+        || m_content_editable_state == ContentEditableState::PlaintextOnly;
 }
 
 // https://html.spec.whatwg.org/multipage/interaction.html#dom-iscontenteditable
@@ -118,6 +120,8 @@ StringView HTMLElement::content_editable() const
         return "true"sv;
     case ContentEditableState::False:
         return "false"sv;
+    case ContentEditableState::PlaintextOnly:
+        return "plaintext-only"sv;
     case ContentEditableState::Inherit:
         return "inherit"sv;
     }
@@ -135,11 +139,15 @@ WebIDL::ExceptionOr<void> HTMLElement::set_content_editable(StringView content_e
         MUST(set_attribute(HTML::AttributeNames::contenteditable, "true"_string));
         return {};
     }
+    if (content_editable.equals_ignoring_ascii_case("plaintext-only"sv)) {
+        MUST(set_attribute(HTML::AttributeNames::contenteditable, "plaintext-only"_string));
+        return {};
+    }
     if (content_editable.equals_ignoring_ascii_case("false"sv)) {
         MUST(set_attribute(HTML::AttributeNames::contenteditable, "false"_string));
         return {};
     }
-    return WebIDL::SyntaxError::create(realm(), "Invalid contentEditable value, must be 'true', 'false', or 'inherit'"_string);
+    return WebIDL::SyntaxError::create(realm(), "Invalid contentEditable value, must be 'true', 'false', 'plaintext-only' or 'inherit'"_string);
 }
 
 // https://html.spec.whatwg.org/multipage/dom.html#set-the-inner-text-steps
@@ -602,18 +610,20 @@ void HTMLElement::attribute_changed(FlyString const& name, Optional<String> cons
 
     if (name == HTML::AttributeNames::contenteditable) {
         if (!value.has_value()) {
+            // No value maps to the "inherit" state.
             m_content_editable_state = ContentEditableState::Inherit;
+        } else if (value->is_empty() || value->equals_ignoring_ascii_case("true"sv)) {
+            // "true", an empty string or a missing value map to the "true" state.
+            m_content_editable_state = ContentEditableState::True;
+        } else if (value->equals_ignoring_ascii_case("false"sv)) {
+            // "false" maps to the "false" state.
+            m_content_editable_state = ContentEditableState::False;
+        } else if (value->equals_ignoring_ascii_case("plaintext-only"sv)) {
+            // "plaintext-only" maps to the "plaintext-only" state.
+            m_content_editable_state = ContentEditableState::PlaintextOnly;
         } else {
-            if (value->is_empty() || value->equals_ignoring_ascii_case("true"sv)) {
-                // "true", an empty string or a missing value map to the "true" state.
-                m_content_editable_state = ContentEditableState::True;
-            } else if (value->equals_ignoring_ascii_case("false"sv)) {
-                // "false" maps to the "false" state.
-                m_content_editable_state = ContentEditableState::False;
-            } else {
-                // Having no such attribute or an invalid value maps to the "inherit" state.
-                m_content_editable_state = ContentEditableState::Inherit;
-            }
+            // Having an invalid value maps to the "inherit" state.
+            m_content_editable_state = ContentEditableState::Inherit;
         }
     }
 

+ 2 - 0
Libraries/LibWeb/HTML/HTMLElement.h

@@ -105,9 +105,11 @@ private:
     // https://html.spec.whatwg.org/multipage/custom-elements.html#attached-internals
     GC::Ptr<ElementInternals> m_attached_internals;
 
+    // https://html.spec.whatwg.org/#attr-contenteditable
     enum class ContentEditableState {
         True,
         False,
+        PlaintextOnly,
         Inherit,
     };
     ContentEditableState m_content_editable_state { ContentEditableState::Inherit };

+ 34 - 0
Tests/LibWeb/Text/expected/wpt-import/html/editing/editing-0/contenteditable/contenteditable-enumerated-ascii-case-insensitive.txt

@@ -0,0 +1,34 @@
+Summary
+
+Harness status: OK
+
+Rerun
+
+Found 24 tests
+
+24 Pass
+Details
+Result	Test Name	MessagePass	IDL attribute getter for attribute value "true"	
+Pass	IDL attribute setter for value "true"	
+Pass	IDL attribute getter for attribute value "TRUE"	
+Pass	IDL attribute setter for value "TRUE"	
+Pass	IDL attribute getter for attribute value "false"	
+Pass	IDL attribute setter for value "false"	
+Pass	IDL attribute getter for attribute value "FALSE"	
+Pass	IDL attribute setter for value "FALSE"	
+Pass	IDL attribute getter for attribute value "inherit"	
+Pass	IDL attribute setter for value "inherit"	
+Pass	IDL attribute getter for attribute value "INHERIT"	
+Pass	IDL attribute setter for value "INHERIT"	
+Pass	IDL attribute getter for attribute value "plaintext-only"	
+Pass	IDL attribute setter for value "plaintext-only"	
+Pass	IDL attribute getter for attribute value "PLAINTEXT-ONLY"	
+Pass	IDL attribute setter for value "PLAINTEXT-ONLY"	
+Pass	IDL attribute getter for attribute value "foobar"	
+Pass	IDL attribute setter for value "foobar"	
+Pass	IDL attribute getter for attribute value "falſe"	
+Pass	IDL attribute setter for value "falſe"	
+Pass	IDL attribute getter for attribute value "plaıntext-only"	
+Pass	IDL attribute setter for value "plaıntext-only"	
+Pass	IDL attribute getter for attribute value "plaİntext-only"	
+Pass	IDL attribute setter for value "plaİntext-only"	

+ 45 - 0
Tests/LibWeb/Text/input/wpt-import/html/editing/editing-0/contenteditable/contenteditable-enumerated-ascii-case-insensitive.html

@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="help" href="https://html.spec.whatwg.org/#attr-contenteditable">
+<link rel="help" href="https://html.spec.whatwg.org/#enumerated-attribute">
+<meta name="assert" content="@contenteditable values are ASCII case-insensitive">
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+
+<script>
+function testValue(value, isValid) {
+  const valueLower = value.toLowerCase();
+
+  test(() => {
+    const el = document.createElement('div');
+    if (valueLower !== "inherit") {
+      el.setAttribute('contenteditable', value);
+    }
+    assert_equals(el.contentEditable, isValid ? valueLower : "inherit");
+  }, `IDL attribute getter for attribute value "${value}"`);
+
+  test(() => {
+    const el = document.createElement('div');
+    if (isValid) {
+      el.contentEditable = value;
+      assert_equals(el.getAttribute('contenteditable'), valueLower === "inherit" ? null : valueLower);
+    } else {
+      assert_throws_dom("SyntaxError", () => {
+        el.contentEditable = value;
+      });
+    }
+  }, `IDL attribute setter for value "${value}"`);
+}
+
+const valid = ["true", "false", "inherit", "plaintext-only"]; // "inherit" is treated specially
+const invalid = ["foobar", "falſe", "plaıntext-only", "plaİntext-only"];
+
+for (const value of valid) {
+  testValue(value, true);
+  testValue(value.toUpperCase(), true);
+}
+
+for (const value of invalid) {
+  testValue(value, false);
+}
+</script>