瀏覽代碼

LibWeb: Implement the `HTMLInputElement.width` attribute

This allows the width of an image button input to be set and queried.
Tim Ledbetter 8 月之前
父節點
當前提交
45a2823e08

+ 36 - 0
Libraries/LibWeb/HTML/HTMLInputElement.cpp

@@ -47,6 +47,7 @@
 #include <LibWeb/MimeSniff/Resource.h>
 #include <LibWeb/Namespace.h>
 #include <LibWeb/Page/Page.h>
+#include <LibWeb/Painting/PaintableBox.h>
 #include <LibWeb/Selection/Selection.h>
 #include <LibWeb/UIEvents/EventNames.h>
 #include <LibWeb/UIEvents/MouseEvent.h>
@@ -1881,6 +1882,41 @@ WebIDL::ExceptionOr<void> HTMLInputElement::set_size(WebIDL::UnsignedLong value)
     return set_attribute(HTML::AttributeNames::size, String::number(value));
 }
 
+// https://html.spec.whatwg.org/multipage/input.html#dom-input-width
+WebIDL::UnsignedLong HTMLInputElement::width() const
+{
+    const_cast<DOM::Document&>(document()).update_layout();
+
+    // When the input element's type attribute is not in the Image Button state, then no image is available.
+    if (type_state() != TypeAttributeState::ImageButton)
+        return 0;
+
+    // Return the rendered width of the image, in CSS pixels, if the image is being rendered.
+    if (auto* paintable_box = this->paintable_box())
+        return paintable_box->content_width().to_int();
+
+    // On setting [the width or height IDL attribute], they must act as if they reflected the respective content attributes of the same name.
+    if (auto width_string = get_attribute(HTML::AttributeNames::width); width_string.has_value()) {
+        if (auto width = parse_non_negative_integer(*width_string); width.has_value() && *width <= 2147483647)
+            return *width;
+    }
+
+    // ...or else the natural width and height of the image, in CSS pixels, if an image is available but not being rendered
+    if (auto bitmap = current_image_bitmap())
+        return bitmap->width();
+
+    // ...or else 0, if the image is not available or does not have intrinsic dimensions.
+    return 0;
+}
+
+WebIDL::ExceptionOr<void> HTMLInputElement::set_width(WebIDL::UnsignedLong value)
+{
+    if (value > 2147483647)
+        value = 0;
+
+    return set_attribute(HTML::AttributeNames::width, String::number(value));
+}
+
 // https://html.spec.whatwg.org/multipage/input.html#concept-input-value-string-number
 Optional<double> HTMLInputElement::convert_string_to_number(StringView input) const
 {

+ 3 - 0
Libraries/LibWeb/HTML/HTMLInputElement.h

@@ -128,6 +128,9 @@ public:
     WebIDL::UnsignedLong size() const;
     WebIDL::ExceptionOr<void> set_size(WebIDL::UnsignedLong value);
 
+    WebIDL::UnsignedLong width() const;
+    WebIDL::ExceptionOr<void> set_width(WebIDL::UnsignedLong value);
+
     struct SelectedCoordinate {
         int x { 0 };
         int y { 0 };

+ 1 - 1
Libraries/LibWeb/HTML/HTMLInputElement.idl

@@ -43,7 +43,7 @@ interface HTMLInputElement : HTMLElement {
     [CEReactions, LegacyNullToEmptyString] attribute DOMString value;
     attribute object? valueAsDate;
     attribute unrestricted double valueAsNumber;
-    [FIXME, CEReactions] attribute unsigned long width;
+    [CEReactions] attribute unsigned long width;
 
     undefined stepUp(optional long n = 1);
     undefined stepDown(optional long n = 1);

+ 20 - 0
Tests/LibWeb/Text/expected/HTML/unsigned-long-reflection.txt

@@ -37,6 +37,26 @@ input.getAttribute("size") after input.setAttribute("size", "4294967295"): 42949
 input.size after input.setAttribute("size", "4294967295"): 20
 input.getAttribute("size") after input.size = 4294967295: 20
 input.size after input.size = 4294967295: 20
+input.getAttribute("width") after input.setAttribute("width", "0"): 0
+input.width after input.setAttribute("width", "0"): 0
+input.getAttribute("width") after input.width = 0: 0
+input.width after input.width = 0: 0
+input.getAttribute("width") after input.setAttribute("width", "1"): 1
+input.width after input.setAttribute("width", "1"): 1
+input.getAttribute("width") after input.width = 1: 1
+input.width after input.width = 1: 0
+input.getAttribute("width") after input.setAttribute("width", "2147483647"): 2147483647
+input.width after input.setAttribute("width", "2147483647"): 2147483647
+input.getAttribute("width") after input.width = 2147483647: 2147483647
+input.width after input.width = 2147483647: 0
+input.getAttribute("width") after input.setAttribute("width", "2147483648"): 2147483648
+input.width after input.setAttribute("width", "2147483648"): 0
+input.getAttribute("width") after input.width = 2147483648: 0
+input.width after input.width = 2147483648: 0
+input.getAttribute("width") after input.setAttribute("width", "4294967295"): 4294967295
+input.width after input.setAttribute("width", "4294967295"): 0
+input.getAttribute("width") after input.width = 4294967295: 0
+input.width after input.width = 4294967295: 0
 marquee.getAttribute("scrollamount") after marquee.setAttribute("scrollAmount", "0"): 0
 marquee.scrollAmount after marquee.setAttribute("scrollamount", "0"): 0
 marquee.getAttribute("scrollamount") after marquee.scrollAmount = 0: 0

+ 19 - 3
Tests/LibWeb/Text/input/HTML/unsigned-long-reflection.html

@@ -2,10 +2,19 @@
 <script src="../include.js"></script>
 <script>
     test(() => {
-        function testProperty(elementName, propertyName, propertyGetter, propertySetter) {
+        function testProperty(elementNameOrFactory, propertyName, propertyGetter, propertySetter) {
             const attributeName = propertyName.toLowerCase();
             function setValue(value) {
-                let element = document.createElement(elementName);
+                let element;
+                let elementName;
+                if (typeof elementNameOrFactory === "string") {
+                    element = document.createElement(elementNameOrFactory);
+                    elementName = elementNameOrFactory;
+                } else {
+                    element = elementNameOrFactory();
+                    elementName = element.tagName.toLowerCase();
+                }
+
                 element.setAttribute(attributeName, value.toString());
                 println(`${elementName}.getAttribute("${attributeName}") after ${elementName}.setAttribute("${propertyName}", "${value}"): ${element.getAttribute(`${attributeName}`)}`);
                 println(`${elementName}.${propertyName} after ${elementName}.setAttribute("${attributeName}", "${value}"): ${propertyGetter(element)}`);
@@ -16,7 +25,7 @@
                     println(`${elementName}.getAttribute("${attributeName}") after ${elementName}.${propertyName} = ${value}: ${element.getAttribute(attributeName)}`);
                     println(`${elementName}.${propertyName} after ${elementName}.${propertyName} = ${value}: ${propertyGetter(element)}`);
                 } catch (e) {
-                    println(`${elementName}.${propertyName} = ${value} threw exception of type ${e.name}`);    
+                    println(`${elementName}.${propertyName} = ${value} threw exception of type ${e.name}`);
                 }
             }
 
@@ -27,8 +36,15 @@
             setValue(4294967295);
         }
 
+        const imageButtonInputFactory = () => {
+            const input = document.createElement("input");
+            input.type = "image";
+            return input;
+        }
+
         testProperty("img", "hspace", (img) => img.hspace, (img, value) => img.hspace = value);
         testProperty("input", "size", (input) => input.size, (input, value) => input.size = value);
+        testProperty(imageButtonInputFactory, "width", (input) => input.width, (input, value) => input.width = value);
         testProperty("marquee", "scrollAmount", (marquee) => marquee.scrollAmount, (marquee, value) => marquee.scrollAmount = value);
         testProperty("marquee", "scrollDelay", (marquee) => marquee.scrollDelay, (marquee, value) => marquee.scrollDelay = value);
         testProperty("select", "size", (select) => select.size, (select, value) => select.size = value);