Skip to content

Commit

Permalink
LibWeb: Honor appearance: none when creating input element layout node
Browse files Browse the repository at this point in the history
Per css-ui-4, setting `appearance: none` is supposed to suppress the
creation of a native-looking widget for stuff like checkboxes, radio
buttons, etc.

This patch implements this behavior by simply falling back to creating
a layout node based on the CSS `display` property in such cases.

This fixes an issue on the hey.com imbox page where we were rendering
checkboxes on top of sender profile photos.

(cherry picked from commit 58c523ae46af90ab17d6b98966d97fa776ae2bf4)
  • Loading branch information
awesomekling authored and nico committed Nov 4, 2024
1 parent 36a2826 commit b3e8e67
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 3 deletions.
77 changes: 77 additions & 0 deletions Tests/LibWeb/Layout/expected/css-appearance-none.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
BlockContainer <html> at (0,0) content-size 800x126 [BFC] children: not-inline
BlockContainer <body> at (8,8) content-size 784x110 children: inline
frag 0 from BlockContainer start: 0, length: 0, rect: [13,10 0x17] baseline: 15.296875
frag 1 from TextNode start: 0, length: 13, rect: [18,10 116.171875x17] baseline: 13.296875
"Normal button"
frag 2 from BlockContainer start: 0, length: 0, rect: [13,31 0x17] baseline: 15.296875
frag 3 from TextNode start: 0, length: 20, rect: [18,31 181.203125x17] baseline: 13.296875
"No appearance button"
frag 4 from CheckBox start: 0, length: 0, rect: [8,50 13x13] baseline: 13
frag 5 from TextNode start: 0, length: 15, rect: [21,50 136.296875x17] baseline: 13.296875
"Normal checkbox"
frag 6 from BlockContainer start: 0, length: 0, rect: [8,80 0x0] baseline: 0
frag 7 from TextNode start: 0, length: 22, rect: [8,67 201.328125x17] baseline: 13.296875
"No appearance checkbox"
frag 8 from RadioButton start: 0, length: 0, rect: [8,85 12x12] baseline: 12
frag 9 from TextNode start: 0, length: 12, rect: [20,84 104.203125x17] baseline: 13.296875
"Normal radio"
frag 10 from BlockContainer start: 0, length: 0, rect: [8,114 0x0] baseline: 0
frag 11 from TextNode start: 0, length: 19, rect: [8,101 169.234375x17] baseline: 13.296875
"No appearance radio"
BlockContainer <input> at (13,10) content-size 0x17 inline-block [BFC] children: not-inline
BlockContainer <(anonymous)> at (13,10) content-size 0x17 flex-container(column) [FFC] children: not-inline
BlockContainer <(anonymous)> at (13,10) content-size 0x17 flex-item [BFC] children: inline
frag 0 from BlockContainer start: 0, length: 0, rect: [13,23 0x0] baseline: 0
BlockContainer <span> at (13,23) content-size 0x0 inline-block [BFC] children: inline
TextNode <#text>
TextNode <#text>
BreakNode <br>
TextNode <#text>
BlockContainer <input> at (13,31) content-size 0x17 inline-block [BFC] children: not-inline
BlockContainer <(anonymous)> at (13,31) content-size 0x17 flex-container(column) [FFC] children: not-inline
BlockContainer <(anonymous)> at (13,31) content-size 0x17 flex-item [BFC] children: inline
frag 0 from BlockContainer start: 0, length: 0, rect: [13,44 0x0] baseline: 0
BlockContainer <span> at (13,44) content-size 0x0 inline-block [BFC] children: inline
TextNode <#text>
TextNode <#text>
BreakNode <br>
TextNode <#text>
CheckBox <input> at (8,50) content-size 13x13 inline-block children: not-inline
TextNode <#text>
BreakNode <br>
TextNode <#text>
BlockContainer <input> at (8,80) content-size 0x0 inline-block [BFC] children: not-inline
TextNode <#text>
BreakNode <br>
TextNode <#text>
RadioButton <input> at (8,85) content-size 12x12 inline-block children: not-inline
TextNode <#text>
BreakNode <br>
TextNode <#text>
BlockContainer <input> at (8,114) content-size 0x0 inline-block [BFC] children: not-inline
TextNode <#text>
BreakNode <br>
TextNode <#text>

ViewportPaintable (Viewport<#document>) [0,0 800x600]
PaintableWithLines (BlockContainer<HTML>) [0,0 800x126]
PaintableWithLines (BlockContainer<BODY>) [8,8 784x110]
PaintableWithLines (BlockContainer<INPUT>) [8,8 10x21]
PaintableWithLines (BlockContainer(anonymous)) [13,10 0x17]
PaintableWithLines (BlockContainer(anonymous)) [13,10 0x17]
PaintableWithLines (BlockContainer<SPAN>) [13,23 0x0]
TextPaintable (TextNode<#text>)
PaintableWithLines (BlockContainer<INPUT>) [8,29 10x21]
PaintableWithLines (BlockContainer(anonymous)) [13,31 0x17]
PaintableWithLines (BlockContainer(anonymous)) [13,31 0x17]
PaintableWithLines (BlockContainer<SPAN>) [13,44 0x0]
TextPaintable (TextNode<#text>)
CheckBoxPaintable (CheckBox<INPUT>) [8,50 13x13]
TextPaintable (TextNode<#text>)
PaintableWithLines (BlockContainer<INPUT>) [8,80 0x0]
TextPaintable (TextNode<#text>)
RadioButtonPaintable (RadioButton<INPUT>) [8,85 12x12]
TextPaintable (TextNode<#text>)
PaintableWithLines (BlockContainer<INPUT>) [8,114 0x0]
TextPaintable (TextNode<#text>)
7 changes: 7 additions & 0 deletions Tests/LibWeb/Layout/input/css-appearance-none.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<!doctype html>
<input type="button">Normal button<br>
<input type="button" style="appearance: none">No appearance button<br>
<input type="checkbox">Normal checkbox<br>
<input type="checkbox" style="appearance: none">No appearance checkbox<br>
<input type="radio">Normal radio<br>
<input type="radio" style="appearance: none">No appearance radio<br>
17 changes: 14 additions & 3 deletions Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,23 @@ JS::GCPtr<Layout::Node> HTMLInputElement::create_layout_node(NonnullRefPtr<CSS::
if (type_state() == TypeAttributeState::Hidden)
return nullptr;

// NOTE: Image inputs are `appearance: none` per the default UA style,
// but we still need to create an ImageBox for them, or no image will get loaded.
if (type_state() == TypeAttributeState::ImageButton) {
return heap().allocate_without_realm<Layout::ImageBox>(document(), *this, move(style), *this);
}

// https://drafts.csswg.org/css-ui/#appearance-switching
// This specification introduces the appearance property to provide some control over this behavior.
// In particular, using appearance: none allows authors to suppress the native appearance of widgets,
// giving them a primitive appearance where CSS can be used to restyle them.
if (style->appearance() == CSS::Appearance::None) {
return Element::create_layout_node_for_display_type(document(), style->display(), style, this);
}

if (type_state() == TypeAttributeState::SubmitButton || type_state() == TypeAttributeState::Button || type_state() == TypeAttributeState::ResetButton)
return heap().allocate_without_realm<Layout::BlockContainer>(document(), this, move(style));

if (type_state() == TypeAttributeState::ImageButton)
return heap().allocate_without_realm<Layout::ImageBox>(document(), *this, move(style), *this);

if (type_state() == TypeAttributeState::Checkbox)
return heap().allocate_without_realm<Layout::CheckBox>(document(), *this, move(style));

Expand Down

0 comments on commit b3e8e67

Please sign in to comment.