summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibWeb
diff options
context:
space:
mode:
Diffstat (limited to 'Userland/Libraries/LibWeb')
-rw-r--r--Userland/Libraries/LibWeb/Bindings/EventListenerWrapper.cpp46
-rw-r--r--Userland/Libraries/LibWeb/Bindings/EventListenerWrapper.h49
-rw-r--r--Userland/Libraries/LibWeb/Bindings/EventTargetWrapperFactory.cpp37
-rw-r--r--Userland/Libraries/LibWeb/Bindings/EventTargetWrapperFactory.h36
-rw-r--r--Userland/Libraries/LibWeb/Bindings/EventWrapperFactory.cpp42
-rw-r--r--Userland/Libraries/LibWeb/Bindings/EventWrapperFactory.h36
-rw-r--r--Userland/Libraries/LibWeb/Bindings/LocationObject.cpp146
-rw-r--r--Userland/Libraries/LibWeb/Bindings/LocationObject.h58
-rw-r--r--Userland/Libraries/LibWeb/Bindings/NavigatorObject.cpp69
-rw-r--r--Userland/Libraries/LibWeb/Bindings/NavigatorObject.h48
-rw-r--r--Userland/Libraries/LibWeb/Bindings/NodeWrapperFactory.cpp347
-rw-r--r--Userland/Libraries/LibWeb/Bindings/NodeWrapperFactory.h38
-rw-r--r--Userland/Libraries/LibWeb/Bindings/RangeConstructor.cpp62
-rw-r--r--Userland/Libraries/LibWeb/Bindings/RangeConstructor.h47
-rw-r--r--Userland/Libraries/LibWeb/Bindings/RangePrototype.cpp161
-rw-r--r--Userland/Libraries/LibWeb/Bindings/RangePrototype.h53
-rw-r--r--Userland/Libraries/LibWeb/Bindings/RangeWrapper.cpp47
-rw-r--r--Userland/Libraries/LibWeb/Bindings/RangeWrapper.h48
-rw-r--r--Userland/Libraries/LibWeb/Bindings/ScriptExecutionContext.cpp35
-rw-r--r--Userland/Libraries/LibWeb/Bindings/ScriptExecutionContext.h43
-rw-r--r--Userland/Libraries/LibWeb/Bindings/WindowObject.cpp361
-rw-r--r--Userland/Libraries/LibWeb/Bindings/WindowObject.h88
-rw-r--r--Userland/Libraries/LibWeb/Bindings/Wrappable.cpp44
-rw-r--r--Userland/Libraries/LibWeb/Bindings/Wrappable.h57
-rw-r--r--Userland/Libraries/LibWeb/Bindings/Wrapper.h49
-rw-r--r--Userland/Libraries/LibWeb/Bindings/XMLHttpRequestConstructor.cpp73
-rw-r--r--Userland/Libraries/LibWeb/Bindings/XMLHttpRequestConstructor.h47
-rw-r--r--Userland/Libraries/LibWeb/Bindings/XMLHttpRequestPrototype.cpp112
-rw-r--r--Userland/Libraries/LibWeb/Bindings/XMLHttpRequestPrototype.h49
-rw-r--r--Userland/Libraries/LibWeb/Bindings/XMLHttpRequestWrapper.cpp62
-rw-r--r--Userland/Libraries/LibWeb/Bindings/XMLHttpRequestWrapper.h47
-rw-r--r--Userland/Libraries/LibWeb/CMakeLists.txt399
-rw-r--r--Userland/Libraries/LibWeb/CSS/.gitignore3
-rw-r--r--Userland/Libraries/LibWeb/CSS/ComputedValues.h161
-rw-r--r--Userland/Libraries/LibWeb/CSS/Default.css196
-rw-r--r--Userland/Libraries/LibWeb/CSS/Identifiers.json122
-rw-r--r--Userland/Libraries/LibWeb/CSS/Length.cpp103
-rw-r--r--Userland/Libraries/LibWeb/CSS/Length.h181
-rw-r--r--Userland/Libraries/LibWeb/CSS/LengthBox.h40
-rw-r--r--Userland/Libraries/LibWeb/CSS/Parser/CSSParser.cpp902
-rw-r--r--Userland/Libraries/LibWeb/CSS/Parser/CSSParser.h59
-rw-r--r--Userland/Libraries/LibWeb/CSS/Properties.json368
-rw-r--r--Userland/Libraries/LibWeb/CSS/QuirksMode.css3
-rw-r--r--Userland/Libraries/LibWeb/CSS/Selector.cpp67
-rw-r--r--Userland/Libraries/LibWeb/CSS/Selector.h106
-rw-r--r--Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp172
-rw-r--r--Userland/Libraries/LibWeb/CSS/SelectorEngine.h36
-rw-r--r--Userland/Libraries/LibWeb/CSS/StyleDeclaration.cpp40
-rw-r--r--Userland/Libraries/LibWeb/CSS/StyleDeclaration.h58
-rw-r--r--Userland/Libraries/LibWeb/CSS/StyleInvalidator.cpp74
-rw-r--r--Userland/Libraries/LibWeb/CSS/StyleInvalidator.h46
-rw-r--r--Userland/Libraries/LibWeb/CSS/StyleProperties.cpp484
-rw-r--r--Userland/Libraries/LibWeb/CSS/StyleProperties.h96
-rw-r--r--Userland/Libraries/LibWeb/CSS/StyleResolver.cpp598
-rw-r--r--Userland/Libraries/LibWeb/CSS/StyleResolver.h65
-rw-r--r--Userland/Libraries/LibWeb/CSS/StyleRule.cpp41
-rw-r--r--Userland/Libraries/LibWeb/CSS/StyleRule.h57
-rw-r--r--Userland/Libraries/LibWeb/CSS/StyleSheet.cpp40
-rw-r--r--Userland/Libraries/LibWeb/CSS/StyleSheet.h52
-rw-r--r--Userland/Libraries/LibWeb/CSS/StyleSheetList.cpp41
-rw-r--r--Userland/Libraries/LibWeb/CSS/StyleSheetList.h52
-rw-r--r--Userland/Libraries/LibWeb/CSS/StyleValue.cpp194
-rw-r--r--Userland/Libraries/LibWeb/CSS/StyleValue.h357
-rw-r--r--Userland/Libraries/LibWeb/CodeGenerators/CMakeLists.txt11
-rw-r--r--Userland/Libraries/LibWeb/CodeGenerators/Generate_CSS_PropertyID_cpp.cpp119
-rw-r--r--Userland/Libraries/LibWeb/CodeGenerators/Generate_CSS_PropertyID_h.cpp106
-rw-r--r--Userland/Libraries/LibWeb/CodeGenerators/Generate_CSS_ValueID_cpp.cpp115
-rw-r--r--Userland/Libraries/LibWeb/CodeGenerators/Generate_CSS_ValueID_h.cpp98
-rw-r--r--Userland/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp951
-rw-r--r--Userland/Libraries/LibWeb/DOM/Attribute.h51
-rw-r--r--Userland/Libraries/LibWeb/DOM/CharacterData.cpp41
-rw-r--r--Userland/Libraries/LibWeb/DOM/CharacterData.h57
-rw-r--r--Userland/Libraries/LibWeb/DOM/CharacterData.idl9
-rw-r--r--Userland/Libraries/LibWeb/DOM/Comment.cpp41
-rw-r--r--Userland/Libraries/LibWeb/DOM/Comment.h44
-rw-r--r--Userland/Libraries/LibWeb/DOM/Comment.idl3
-rw-r--r--Userland/Libraries/LibWeb/DOM/DOMImplementation.cpp75
-rw-r--r--Userland/Libraries/LibWeb/DOM/DOMImplementation.h60
-rw-r--r--Userland/Libraries/LibWeb/DOM/DOMImplementation.idl7
-rw-r--r--Userland/Libraries/LibWeb/DOM/Document.cpp690
-rw-r--r--Userland/Libraries/LibWeb/DOM/Document.h295
-rw-r--r--Userland/Libraries/LibWeb/DOM/Document.idl37
-rw-r--r--Userland/Libraries/LibWeb/DOM/DocumentFragment.cpp40
-rw-r--r--Userland/Libraries/LibWeb/DOM/DocumentFragment.h56
-rw-r--r--Userland/Libraries/LibWeb/DOM/DocumentFragment.idl11
-rw-r--r--Userland/Libraries/LibWeb/DOM/DocumentType.cpp40
-rw-r--r--Userland/Libraries/LibWeb/DOM/DocumentType.h58
-rw-r--r--Userland/Libraries/LibWeb/DOM/DocumentType.idl7
-rw-r--r--Userland/Libraries/LibWeb/DOM/Element.cpp335
-rw-r--r--Userland/Libraries/LibWeb/DOM/Element.h127
-rw-r--r--Userland/Libraries/LibWeb/DOM/Element.idl23
-rw-r--r--Userland/Libraries/LibWeb/DOM/ElementFactory.cpp264
-rw-r--r--Userland/Libraries/LibWeb/DOM/ElementFactory.h35
-rw-r--r--Userland/Libraries/LibWeb/DOM/Event.cpp59
-rw-r--r--Userland/Libraries/LibWeb/DOM/Event.h186
-rw-r--r--Userland/Libraries/LibWeb/DOM/Event.idl23
-rw-r--r--Userland/Libraries/LibWeb/DOM/EventDispatcher.cpp332
-rw-r--r--Userland/Libraries/LibWeb/DOM/EventDispatcher.h43
-rw-r--r--Userland/Libraries/LibWeb/DOM/EventListener.cpp38
-rw-r--r--Userland/Libraries/LibWeb/DOM/EventListener.h72
-rw-r--r--Userland/Libraries/LibWeb/DOM/EventTarget.cpp70
-rw-r--r--Userland/Libraries/LibWeb/DOM/EventTarget.h85
-rw-r--r--Userland/Libraries/LibWeb/DOM/EventTarget.idl6
-rw-r--r--Userland/Libraries/LibWeb/DOM/Node.cpp263
-rw-r--r--Userland/Libraries/LibWeb/DOM/Node.h156
-rw-r--r--Userland/Libraries/LibWeb/DOM/Node.idl16
-rw-r--r--Userland/Libraries/LibWeb/DOM/NonDocumentTypeChildNode.h73
-rw-r--r--Userland/Libraries/LibWeb/DOM/NonElementParentNode.h60
-rw-r--r--Userland/Libraries/LibWeb/DOM/ParentNode.cpp83
-rw-r--r--Userland/Libraries/LibWeb/DOM/ParentNode.h68
-rw-r--r--Userland/Libraries/LibWeb/DOM/Position.cpp49
-rw-r--r--Userland/Libraries/LibWeb/DOM/Position.h78
-rw-r--r--Userland/Libraries/LibWeb/DOM/Range.cpp75
-rw-r--r--Userland/Libraries/LibWeb/DOM/Range.h90
-rw-r--r--Userland/Libraries/LibWeb/DOM/ShadowRoot.cpp49
-rw-r--r--Userland/Libraries/LibWeb/DOM/ShadowRoot.h58
-rw-r--r--Userland/Libraries/LibWeb/DOM/ShadowRoot.idl6
-rw-r--r--Userland/Libraries/LibWeb/DOM/Text.cpp46
-rw-r--r--Userland/Libraries/LibWeb/DOM/Text.h48
-rw-r--r--Userland/Libraries/LibWeb/DOM/Text.idl3
-rw-r--r--Userland/Libraries/LibWeb/DOM/Timer.cpp60
-rw-r--r--Userland/Libraries/LibWeb/DOM/Timer.h63
-rw-r--r--Userland/Libraries/LibWeb/DOM/Window.cpp177
-rw-r--r--Userland/Libraries/LibWeb/DOM/Window.h98
-rw-r--r--Userland/Libraries/LibWeb/DOM/XMLHttpRequest.cpp121
-rw-r--r--Userland/Libraries/LibWeb/DOM/XMLHttpRequest.h85
-rw-r--r--Userland/Libraries/LibWeb/DOMTreeModel.cpp159
-rw-r--r--Userland/Libraries/LibWeb/DOMTreeModel.h60
-rw-r--r--Userland/Libraries/LibWeb/Dump.cpp403
-rw-r--r--Userland/Libraries/LibWeb/Dump.h41
-rw-r--r--Userland/Libraries/LibWeb/DumpLayoutTree/CMakeLists.txt6
-rw-r--r--Userland/Libraries/LibWeb/DumpLayoutTree/main.cpp60
-rw-r--r--Userland/Libraries/LibWeb/FontCache.cpp47
-rw-r--r--Userland/Libraries/LibWeb/FontCache.h61
-rw-r--r--Userland/Libraries/LibWeb/Forward.h297
-rw-r--r--Userland/Libraries/LibWeb/HTML/AttributeNames.cpp63
-rw-r--r--Userland/Libraries/LibWeb/HTML/AttributeNames.h163
-rw-r--r--Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp227
-rw-r--r--Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h103
-rw-r--r--Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.idl29
-rw-r--r--Userland/Libraries/LibWeb/HTML/EventNames.cpp49
-rw-r--r--Userland/Libraries/LibWeb/HTML/EventNames.h85
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLAnchorElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLAnchorElement.h46
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLAnchorElement.idl16
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLAreaElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLAreaElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLAreaElement.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLAudioElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLAudioElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLAudioElement.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLBRElement.cpp46
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLBRElement.h43
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLBRElement.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLBaseElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLBaseElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLBaseElement.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLBlinkElement.cpp57
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLBlinkElement.h45
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLBodyElement.cpp81
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLBodyElement.h47
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLBodyElement.idl10
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLButtonElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLButtonElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLButtonElement.idl8
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp105
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.h58
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.idl7
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLDListElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLDListElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLDListElement.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLDataElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLDataElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLDataElement.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLDataListElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLDataListElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLDataListElement.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLDetailsElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLDetailsElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLDetailsElement.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLDialogElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLDialogElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLDialogElement.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLDirectoryElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLDirectoryElement.h42
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLDirectoryElement.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLDivElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLDivElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLDivElement.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLElement.cpp141
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLElement.h60
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLElement.idl11
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLEmbedElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLEmbedElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLEmbedElement.idl11
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLFieldSetElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLFieldSetElement.h47
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLFieldSetElement.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLFontElement.cpp53
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLFontElement.h43
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLFontElement.idl7
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLFormElement.cpp151
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLFormElement.h53
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLFormElement.idl10
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLFrameElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLFrameElement.h42
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLFrameElement.idl9
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLFrameSetElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLFrameSetElement.h42
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLFrameSetElement.idl6
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLHRElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLHRElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLHRElement.idl9
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLHeadElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLHeadElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLHeadElement.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLHeadingElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLHeadingElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLHeadingElement.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLHtmlElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLHtmlElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLHtmlElement.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLIFrameElement.cpp108
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLIFrameElement.h60
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLIFrameElement.idl19
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLImageElement.cpp100
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLImageElement.h61
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLImageElement.idl14
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp117
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLInputElement.h57
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLInputElement.idl27
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLLIElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLLIElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLLIElement.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLLabelElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLLabelElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLLabelElement.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLLegendElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLLegendElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLLegendElement.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLLinkElement.cpp104
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLLinkElement.h69
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLLinkElement.idl17
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLMapElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLMapElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLMapElement.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLMarqueeElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLMarqueeElement.h42
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLMarqueeElement.idl9
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLMediaElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLMediaElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLMediaElement.idl10
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLMenuElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLMenuElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLMenuElement.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLMetaElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLMetaElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLMetaElement.idl9
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLMeterElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLMeterElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLMeterElement.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLModElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLModElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLModElement.idl6
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLOListElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLOListElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLOListElement.idl8
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLObjectElement.cpp77
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLObjectElement.h55
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLObjectElement.idl19
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLOptGroupElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLOptGroupElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLOptGroupElement.idl6
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLOptionElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLOptionElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLOptionElement.idl6
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLOutputElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLOutputElement.h47
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLOutputElement.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLParagraphElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLParagraphElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLParagraphElement.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLParamElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLParamElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLParamElement.idl9
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLPictureElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLPictureElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLPictureElement.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLPreElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLPreElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLPreElement.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLProgressElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLProgressElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLProgressElement.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLQuoteElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLQuoteElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLQuoteElement.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLScriptElement.cpp193
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLScriptElement.h70
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLScriptElement.idl13
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLSelectElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLSelectElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLSelectElement.idl7
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLSlotElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLSlotElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLSlotElement.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLSourceElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLSourceElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLSourceElement.idl9
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLSpanElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLSpanElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLSpanElement.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLStyleElement.cpp67
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLStyleElement.h47
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLStyleElement.idl7
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLTableCaptionElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLTableCaptionElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLTableCaptionElement.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLTableCellElement.cpp65
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLTableCellElement.h44
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLTableCellElement.idl18
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLTableColElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLTableColElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLTableColElement.idl9
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLTableElement.cpp63
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLTableElement.h44
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLTableElement.idl14
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLTableRowElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLTableRowElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLTableRowElement.idl10
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLTableSectionElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLTableSectionElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLTableSectionElement.idl8
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLTemplateElement.cpp61
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLTemplateElement.h50
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLTemplateElement.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.h47
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.idl12
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLTimeElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLTimeElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLTimeElement.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLTitleElement.cpp49
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLTitleElement.h44
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLTitleElement.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLTrackElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLTrackElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLTrackElement.idl8
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLUListElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLUListElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLUListElement.idl6
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLUnknownElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLUnknownElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLUnknownElement.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLVideoElement.cpp40
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLVideoElement.h41
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLVideoElement.idl6
-rw-r--r--Userland/Libraries/LibWeb/HTML/ImageData.cpp85
-rw-r--r--Userland/Libraries/LibWeb/HTML/ImageData.h61
-rw-r--r--Userland/Libraries/LibWeb/HTML/ImageData.idl7
-rw-r--r--Userland/Libraries/LibWeb/HTML/Parser/Entities.cpp2302
-rw-r--r--Userland/Libraries/LibWeb/HTML/Parser/Entities.h43
-rw-r--r--Userland/Libraries/LibWeb/HTML/Parser/HTMLDocumentParser.cpp3027
-rw-r--r--Userland/Libraries/LibWeb/HTML/Parser/HTMLDocumentParser.h193
-rw-r--r--Userland/Libraries/LibWeb/HTML/Parser/HTMLToken.cpp83
-rw-r--r--Userland/Libraries/LibWeb/HTML/Parser/HTMLToken.h226
-rw-r--r--Userland/Libraries/LibWeb/HTML/Parser/HTMLTokenizer.cpp2663
-rw-r--r--Userland/Libraries/LibWeb/HTML/Parser/HTMLTokenizer.h190
-rw-r--r--Userland/Libraries/LibWeb/HTML/Parser/ListOfActiveFormattingElements.cpp84
-rw-r--r--Userland/Libraries/LibWeb/HTML/Parser/ListOfActiveFormattingElements.h65
-rw-r--r--Userland/Libraries/LibWeb/HTML/Parser/StackOfOpenElements.cpp159
-rw-r--r--Userland/Libraries/LibWeb/HTML/Parser/StackOfOpenElements.h82
-rw-r--r--Userland/Libraries/LibWeb/HTML/SubmitEvent.h54
-rw-r--r--Userland/Libraries/LibWeb/HTML/SubmitEvent.idl5
-rw-r--r--Userland/Libraries/LibWeb/HTML/TagNames.cpp51
-rw-r--r--Userland/Libraries/LibWeb/HTML/TagNames.h178
-rw-r--r--Userland/Libraries/LibWeb/HighResolutionTime/Performance.cpp73
-rw-r--r--Userland/Libraries/LibWeb/HighResolutionTime/Performance.h60
-rw-r--r--Userland/Libraries/LibWeb/HighResolutionTime/Performance.idl4
-rw-r--r--Userland/Libraries/LibWeb/InProcessWebView.cpp435
-rw-r--r--Userland/Libraries/LibWeb/InProcessWebView.h119
-rw-r--r--Userland/Libraries/LibWeb/Layout/BlockBox.cpp133
-rw-r--r--Userland/Libraries/LibWeb/Layout/BlockBox.h79
-rw-r--r--Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp566
-rw-r--r--Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h73
-rw-r--r--Userland/Libraries/LibWeb/Layout/Box.cpp212
-rw-r--r--Userland/Libraries/LibWeb/Layout/Box.h146
-rw-r--r--Userland/Libraries/LibWeb/Layout/BoxModelMetrics.cpp61
-rw-r--r--Userland/Libraries/LibWeb/Layout/BoxModelMetrics.h52
-rw-r--r--Userland/Libraries/LibWeb/Layout/BreakNode.cpp48
-rw-r--r--Userland/Libraries/LibWeb/Layout/BreakNode.h45
-rw-r--r--Userland/Libraries/LibWeb/Layout/ButtonBox.cpp117
-rw-r--r--Userland/Libraries/LibWeb/Layout/ButtonBox.h55
-rw-r--r--Userland/Libraries/LibWeb/Layout/CanvasBox.cpp68
-rw-r--r--Userland/Libraries/LibWeb/Layout/CanvasBox.h45
-rw-r--r--Userland/Libraries/LibWeb/Layout/CheckBox.cpp103
-rw-r--r--Userland/Libraries/LibWeb/Layout/CheckBox.h54
-rw-r--r--Userland/Libraries/LibWeb/Layout/FormattingContext.cpp548
-rw-r--r--Userland/Libraries/LibWeb/Layout/FormattingContext.h78
-rw-r--r--Userland/Libraries/LibWeb/Layout/FrameBox.cpp102
-rw-r--r--Userland/Libraries/LibWeb/Layout/FrameBox.h49
-rw-r--r--Userland/Libraries/LibWeb/Layout/ImageBox.cpp135
-rw-r--r--Userland/Libraries/LibWeb/Layout/ImageBox.h55
-rw-r--r--Userland/Libraries/LibWeb/Layout/InitialContainingBlockBox.cpp133
-rw-r--r--Userland/Libraries/LibWeb/Layout/InitialContainingBlockBox.h60
-rw-r--r--Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp254
-rw-r--r--Userland/Libraries/LibWeb/Layout/InlineFormattingContext.h50
-rw-r--r--Userland/Libraries/LibWeb/Layout/InlineNode.cpp71
-rw-r--r--Userland/Libraries/LibWeb/Layout/InlineNode.h43
-rw-r--r--Userland/Libraries/LibWeb/Layout/LayoutPosition.cpp67
-rw-r--r--Userland/Libraries/LibWeb/Layout/LayoutPosition.h78
-rw-r--r--Userland/Libraries/LibWeb/Layout/LineBox.cpp83
-rw-r--r--Userland/Libraries/LibWeb/Layout/LineBox.h57
-rw-r--r--Userland/Libraries/LibWeb/Layout/LineBoxFragment.cpp174
-rw-r--r--Userland/Libraries/LibWeb/Layout/LineBoxFragment.h92
-rw-r--r--Userland/Libraries/LibWeb/Layout/ListItemBox.cpp62
-rw-r--r--Userland/Libraries/LibWeb/Layout/ListItemBox.h47
-rw-r--r--Userland/Libraries/LibWeb/Layout/ListItemMarkerBox.cpp52
-rw-r--r--Userland/Libraries/LibWeb/Layout/ListItemMarkerBox.h41
-rw-r--r--Userland/Libraries/LibWeb/Layout/Node.cpp339
-rw-r--r--Userland/Libraries/LibWeb/Layout/Node.h278
-rw-r--r--Userland/Libraries/LibWeb/Layout/ReplacedBox.cpp58
-rw-r--r--Userland/Libraries/LibWeb/Layout/ReplacedBox.h74
-rw-r--r--Userland/Libraries/LibWeb/Layout/SVGBox.cpp55
-rw-r--r--Userland/Libraries/LibWeb/Layout/SVGBox.h44
-rw-r--r--Userland/Libraries/LibWeb/Layout/SVGGraphicsBox.cpp52
-rw-r--r--Userland/Libraries/LibWeb/Layout/SVGGraphicsBox.h43
-rw-r--r--Userland/Libraries/LibWeb/Layout/SVGPathBox.cpp89
-rw-r--r--Userland/Libraries/LibWeb/Layout/SVGPathBox.h44
-rw-r--r--Userland/Libraries/LibWeb/Layout/SVGSVGBox.cpp62
-rw-r--r--Userland/Libraries/LibWeb/Layout/SVGSVGBox.h49
-rw-r--r--Userland/Libraries/LibWeb/Layout/TableBox.cpp46
-rw-r--r--Userland/Libraries/LibWeb/Layout/TableBox.h42
-rw-r--r--Userland/Libraries/LibWeb/Layout/TableCellBox.cpp61
-rw-r--r--Userland/Libraries/LibWeb/Layout/TableCellBox.h50
-rw-r--r--Userland/Libraries/LibWeb/Layout/TableFormattingContext.cpp133
-rw-r--r--Userland/Libraries/LibWeb/Layout/TableFormattingContext.h46
-rw-r--r--Userland/Libraries/LibWeb/Layout/TableRowBox.cpp46
-rw-r--r--Userland/Libraries/LibWeb/Layout/TableRowBox.h42
-rw-r--r--Userland/Libraries/LibWeb/Layout/TableRowGroupBox.cpp56
-rw-r--r--Userland/Libraries/LibWeb/Layout/TableRowGroupBox.h41
-rw-r--r--Userland/Libraries/LibWeb/Layout/TextNode.cpp311
-rw-r--r--Userland/Libraries/LibWeb/Layout/TextNode.h59
-rw-r--r--Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp302
-rw-r--r--Userland/Libraries/LibWeb/Layout/TreeBuilder.h59
-rw-r--r--Userland/Libraries/LibWeb/Layout/WidgetBox.cpp68
-rw-r--r--Userland/Libraries/LibWeb/Layout/WidgetBox.h49
-rw-r--r--Userland/Libraries/LibWeb/LayoutTreeModel.cpp163
-rw-r--r--Userland/Libraries/LibWeb/LayoutTreeModel.h60
-rw-r--r--Userland/Libraries/LibWeb/Loader/ContentFilter.cpp68
-rw-r--r--Userland/Libraries/LibWeb/Loader/ContentFilter.h51
-rw-r--r--Userland/Libraries/LibWeb/Loader/FrameLoader.cpp296
-rw-r--r--Userland/Libraries/LibWeb/Loader/FrameLoader.h66
-rw-r--r--Userland/Libraries/LibWeb/Loader/ImageLoader.cpp164
-rw-r--r--Userland/Libraries/LibWeb/Loader/ImageLoader.h79
-rw-r--r--Userland/Libraries/LibWeb/Loader/ImageResource.cpp103
-rw-r--r--Userland/Libraries/LibWeb/Loader/ImageResource.h66
-rw-r--r--Userland/Libraries/LibWeb/Loader/LoadRequest.h94
-rw-r--r--Userland/Libraries/LibWeb/Loader/Resource.cpp168
-rw-r--r--Userland/Libraries/LibWeb/Loader/Resource.h117
-rw-r--r--Userland/Libraries/LibWeb/Loader/ResourceLoader.cpp235
-rw-r--r--Userland/Libraries/LibWeb/Loader/ResourceLoader.h69
-rw-r--r--Userland/Libraries/LibWeb/Namespace.cpp49
-rw-r--r--Userland/Libraries/LibWeb/Namespace.h45
-rw-r--r--Userland/Libraries/LibWeb/Origin.h62
-rw-r--r--Userland/Libraries/LibWeb/OutOfProcessWebView.cpp257
-rw-r--r--Userland/Libraries/LibWeb/OutOfProcessWebView.h96
-rw-r--r--Userland/Libraries/LibWeb/Page/EditEventHandler.cpp124
-rw-r--r--Userland/Libraries/LibWeb/Page/EditEventHandler.h49
-rw-r--r--Userland/Libraries/LibWeb/Page/EventHandler.cpp414
-rw-r--r--Userland/Libraries/LibWeb/Page/EventHandler.h72
-rw-r--r--Userland/Libraries/LibWeb/Page/Frame.cpp274
-rw-r--r--Userland/Libraries/LibWeb/Page/Frame.h121
-rw-r--r--Userland/Libraries/LibWeb/Page/Page.cpp95
-rw-r--r--Userland/Libraries/LibWeb/Page/Page.h107
-rw-r--r--Userland/Libraries/LibWeb/Painting/BorderPainting.cpp162
-rw-r--r--Userland/Libraries/LibWeb/Painting/BorderPainting.h42
-rw-r--r--Userland/Libraries/LibWeb/Painting/PaintContext.h73
-rw-r--r--Userland/Libraries/LibWeb/Painting/StackingContext.cpp95
-rw-r--r--Userland/Libraries/LibWeb/Painting/StackingContext.h52
-rw-r--r--Userland/Libraries/LibWeb/QualifiedName.h52
-rw-r--r--Userland/Libraries/LibWeb/SVG/SVGContext.h62
-rw-r--r--Userland/Libraries/LibWeb/SVG/SVGElement.cpp36
-rw-r--r--Userland/Libraries/LibWeb/SVG/SVGElement.h41
-rw-r--r--Userland/Libraries/LibWeb/SVG/SVGElement.idl3
-rw-r--r--Userland/Libraries/LibWeb/SVG/SVGGeometryElement.cpp36
-rw-r--r--Userland/Libraries/LibWeb/SVG/SVGGeometryElement.h41
-rw-r--r--Userland/Libraries/LibWeb/SVG/SVGGeometryElement.idl3
-rw-r--r--Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.cpp51
-rw-r--r--Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.h54
-rw-r--r--Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.idl3
-rw-r--r--Userland/Libraries/LibWeb/SVG/SVGPathElement.cpp659
-rw-r--r--Userland/Libraries/LibWeb/SVG/SVGPathElement.h124
-rw-r--r--Userland/Libraries/LibWeb/SVG/SVGPathElement.idl3
-rw-r--r--Userland/Libraries/LibWeb/SVG/SVGSVGElement.cpp61
-rw-r--r--Userland/Libraries/LibWeb/SVG/SVGSVGElement.h49
-rw-r--r--Userland/Libraries/LibWeb/SVG/SVGSVGElement.idl3
-rw-r--r--Userland/Libraries/LibWeb/SVG/TagNames.cpp48
-rw-r--r--Userland/Libraries/LibWeb/SVG/TagNames.h48
-rwxr-xr-xUserland/Libraries/LibWeb/Scripts/GenerateStyleSheetSource.sh10
-rw-r--r--Userland/Libraries/LibWeb/StylePropertiesModel.cpp81
-rw-r--r--Userland/Libraries/LibWeb/StylePropertiesModel.h65
-rw-r--r--Userland/Libraries/LibWeb/Tests/DOM/Comment.js15
-rw-r--r--Userland/Libraries/LibWeb/Tests/DOM/Element.js39
-rw-r--r--Userland/Libraries/LibWeb/Tests/DOM/Node.js28
-rw-r--r--Userland/Libraries/LibWeb/Tests/DOM/Text.js15
-rw-r--r--Userland/Libraries/LibWeb/Tests/DOM/document.createComment.js13
-rw-r--r--Userland/Libraries/LibWeb/Tests/DOM/document.createDocumentFragment.js11
-rw-r--r--Userland/Libraries/LibWeb/Tests/DOM/document.createTextNode.js13
-rw-r--r--Userland/Libraries/LibWeb/Tests/DOM/document.doctype.js18
-rw-r--r--Userland/Libraries/LibWeb/Tests/DOM/document.documentElement.js16
-rw-r--r--Userland/Libraries/LibWeb/Tests/DOM/mixins/NonElementParentNode.js19
-rw-r--r--Userland/Libraries/LibWeb/Tests/DOM/mixins/ParentNode.js25
-rw-r--r--Userland/Libraries/LibWeb/Tests/HTML/HTMLElement.js9
-rw-r--r--Userland/Libraries/LibWeb/Tests/HTML/HTMLTemplateElement.js27
-rw-r--r--Userland/Libraries/LibWeb/Tests/HTML/document.body.js55
-rw-r--r--Userland/Libraries/LibWeb/Tests/HTML/document.head.js16
-rw-r--r--Userland/Libraries/LibWeb/Tests/HTML/document.readyState.js31
-rw-r--r--Userland/Libraries/LibWeb/Tests/Pages/Comment.html8
-rw-r--r--Userland/Libraries/LibWeb/Tests/Pages/ParentNode.html12
-rw-r--r--Userland/Libraries/LibWeb/Tests/Pages/Template.html14
-rw-r--r--Userland/Libraries/LibWeb/Tests/Window/Base64.js19
-rw-r--r--Userland/Libraries/LibWeb/Tests/Window/window.window_frames_self.js8
-rw-r--r--Userland/Libraries/LibWeb/Tests/libweb_tester.d.ts16
-rw-r--r--Userland/Libraries/LibWeb/Tests/test-common.js31
-rw-r--r--Userland/Libraries/LibWeb/TreeNode.h443
-rw-r--r--Userland/Libraries/LibWeb/UIEvents/EventNames.cpp49
-rw-r--r--Userland/Libraries/LibWeb/UIEvents/EventNames.h49
-rw-r--r--Userland/Libraries/LibWeb/UIEvents/MouseEvent.cpp54
-rw-r--r--Userland/Libraries/LibWeb/UIEvents/MouseEvent.h58
-rw-r--r--Userland/Libraries/LibWeb/UIEvents/MouseEvent.idl6
-rw-r--r--Userland/Libraries/LibWeb/UIEvents/UIEvent.h46
-rw-r--r--Userland/Libraries/LibWeb/UIEvents/UIEvent.idl3
-rw-r--r--Userland/Libraries/LibWeb/URLEncoder.cpp46
-rw-r--r--Userland/Libraries/LibWeb/URLEncoder.h41
-rw-r--r--Userland/Libraries/LibWeb/WebContentClient.cpp149
-rw-r--r--Userland/Libraries/LibWeb/WebContentClient.h68
-rw-r--r--Userland/Libraries/LibWeb/WebViewHooks.h51
538 files changed, 47674 insertions, 0 deletions
diff --git a/Userland/Libraries/LibWeb/Bindings/EventListenerWrapper.cpp b/Userland/Libraries/LibWeb/Bindings/EventListenerWrapper.cpp
new file mode 100644
index 0000000000..2f9cbac2c2
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Bindings/EventListenerWrapper.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibJS/Runtime/Function.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibWeb/Bindings/EventListenerWrapper.h>
+#include <LibWeb/DOM/EventListener.h>
+
+namespace Web {
+namespace Bindings {
+
+EventListenerWrapper::EventListenerWrapper(JS::GlobalObject& global_object, DOM::EventListener& impl)
+ : Wrapper(*global_object.object_prototype())
+ , m_impl(impl)
+{
+}
+
+EventListenerWrapper::~EventListenerWrapper()
+{
+}
+
+}
+}
diff --git a/Userland/Libraries/LibWeb/Bindings/EventListenerWrapper.h b/Userland/Libraries/LibWeb/Bindings/EventListenerWrapper.h
new file mode 100644
index 0000000000..fdd74108ea
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Bindings/EventListenerWrapper.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/Bindings/Wrapper.h>
+
+namespace Web {
+namespace Bindings {
+
+class EventListenerWrapper final : public Wrapper {
+ JS_OBJECT(EventListenerWrapper, Wrapper);
+
+public:
+ EventListenerWrapper(JS::GlobalObject&, DOM::EventListener&);
+ virtual ~EventListenerWrapper() override;
+
+ DOM::EventListener& impl() { return *m_impl; }
+ const DOM::EventListener& impl() const { return *m_impl; }
+
+private:
+ NonnullRefPtr<DOM::EventListener> m_impl;
+};
+
+}
+}
diff --git a/Userland/Libraries/LibWeb/Bindings/EventTargetWrapperFactory.cpp b/Userland/Libraries/LibWeb/Bindings/EventTargetWrapperFactory.cpp
new file mode 100644
index 0000000000..4a6e6344e9
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Bindings/EventTargetWrapperFactory.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/Bindings/EventTargetWrapperFactory.h>
+#include <LibWeb/DOM/EventTarget.h>
+
+namespace Web::Bindings {
+
+EventTargetWrapper* wrap(JS::GlobalObject& global_object, DOM::EventTarget& target)
+{
+ return target.create_wrapper(global_object);
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Bindings/EventTargetWrapperFactory.h b/Userland/Libraries/LibWeb/Bindings/EventTargetWrapperFactory.h
new file mode 100644
index 0000000000..c28c7724ca
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Bindings/EventTargetWrapperFactory.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibJS/Forward.h>
+#include <LibWeb/Forward.h>
+
+namespace Web::Bindings {
+
+EventTargetWrapper* wrap(JS::GlobalObject&, DOM::EventTarget&);
+
+}
diff --git a/Userland/Libraries/LibWeb/Bindings/EventWrapperFactory.cpp b/Userland/Libraries/LibWeb/Bindings/EventWrapperFactory.cpp
new file mode 100644
index 0000000000..3064799cf0
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Bindings/EventWrapperFactory.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/Bindings/EventWrapper.h>
+#include <LibWeb/Bindings/EventWrapperFactory.h>
+#include <LibWeb/Bindings/MouseEventWrapper.h>
+
+namespace Web {
+namespace Bindings {
+
+EventWrapper* wrap(JS::GlobalObject& global_object, DOM::Event& event)
+{
+ if (is<UIEvents::MouseEvent>(event))
+ return static_cast<MouseEventWrapper*>(wrap_impl(global_object, static_cast<UIEvents::MouseEvent&>(event)));
+ return static_cast<EventWrapper*>(wrap_impl(global_object, event));
+}
+
+}
+}
diff --git a/Userland/Libraries/LibWeb/Bindings/EventWrapperFactory.h b/Userland/Libraries/LibWeb/Bindings/EventWrapperFactory.h
new file mode 100644
index 0000000000..ebe82c5e88
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Bindings/EventWrapperFactory.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibJS/Forward.h>
+#include <LibWeb/Forward.h>
+
+namespace Web::Bindings {
+
+EventWrapper* wrap(JS::GlobalObject&, DOM::Event&);
+
+}
diff --git a/Userland/Libraries/LibWeb/Bindings/LocationObject.cpp b/Userland/Libraries/LibWeb/Bindings/LocationObject.cpp
new file mode 100644
index 0000000000..93d00d8af7
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Bindings/LocationObject.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/FlyString.h>
+#include <AK/StringBuilder.h>
+#include <LibWeb/Bindings/LocationObject.h>
+#include <LibWeb/Bindings/WindowObject.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/DOM/Window.h>
+
+namespace Web {
+namespace Bindings {
+
+LocationObject::LocationObject(JS::GlobalObject& global_object)
+ : Object(*global_object.object_prototype())
+{
+}
+
+void LocationObject::initialize(JS::GlobalObject& global_object)
+{
+ Object::initialize(global_object);
+ u8 attr = JS::Attribute::Writable | JS::Attribute::Enumerable;
+ define_native_property("href", href_getter, href_setter, attr);
+ define_native_property("host", host_getter, nullptr, attr);
+ define_native_property("hostname", hostname_getter, nullptr, attr);
+ define_native_property("pathname", pathname_getter, nullptr, attr);
+ define_native_property("hash", hash_getter, nullptr, attr);
+ define_native_property("search", search_getter, nullptr, attr);
+ define_native_property("protocol", protocol_getter, nullptr, attr);
+
+ define_native_function("reload", reload, 0, JS::Attribute::Enumerable);
+}
+
+LocationObject::~LocationObject()
+{
+}
+
+JS_DEFINE_NATIVE_GETTER(LocationObject::href_getter)
+{
+ auto& window = static_cast<WindowObject&>(global_object);
+ return JS::js_string(vm, window.impl().document().url().to_string());
+}
+
+JS_DEFINE_NATIVE_SETTER(LocationObject::href_setter)
+{
+ auto& window = static_cast<WindowObject&>(global_object);
+ auto new_href = value.to_string(global_object);
+ if (vm.exception())
+ return;
+ auto href_url = window.impl().document().complete_url(new_href);
+ if (!href_url.is_valid()) {
+ vm.throw_exception<JS::URIError>(global_object, String::formatted("Invalid URL '{}'", new_href));
+ return;
+ }
+ window.impl().did_set_location_href({}, href_url);
+}
+
+JS_DEFINE_NATIVE_GETTER(LocationObject::pathname_getter)
+{
+ auto& window = static_cast<WindowObject&>(global_object);
+ return JS::js_string(vm, window.impl().document().url().path());
+}
+
+JS_DEFINE_NATIVE_GETTER(LocationObject::hostname_getter)
+{
+ auto& window = static_cast<WindowObject&>(global_object);
+ return JS::js_string(vm, window.impl().document().url().host());
+}
+
+JS_DEFINE_NATIVE_GETTER(LocationObject::host_getter)
+{
+ auto& window = static_cast<WindowObject&>(global_object);
+ auto url = window.impl().document().url();
+ StringBuilder builder;
+ builder.append(url.host());
+ builder.append(':');
+ builder.appendf("%u", url.port());
+ return JS::js_string(vm, builder.to_string());
+}
+
+JS_DEFINE_NATIVE_GETTER(LocationObject::hash_getter)
+{
+ auto& window = static_cast<WindowObject&>(global_object);
+ auto fragment = window.impl().document().url().fragment();
+ if (!fragment.length())
+ return JS::js_string(vm, "");
+ StringBuilder builder;
+ builder.append('#');
+ builder.append(fragment);
+ return JS::js_string(vm, builder.to_string());
+}
+
+JS_DEFINE_NATIVE_GETTER(LocationObject::search_getter)
+{
+ auto& window = static_cast<WindowObject&>(global_object);
+ auto query = window.impl().document().url().query();
+ if (!query.length())
+ return JS::js_string(vm, "");
+ StringBuilder builder;
+ builder.append('?');
+ builder.append(query);
+ return JS::js_string(vm, builder.to_string());
+}
+
+JS_DEFINE_NATIVE_GETTER(LocationObject::protocol_getter)
+{
+ auto& window = static_cast<WindowObject&>(global_object);
+ StringBuilder builder;
+ builder.append(window.impl().document().url().protocol());
+ builder.append(':');
+ return JS::js_string(vm, builder.to_string());
+}
+
+JS_DEFINE_NATIVE_FUNCTION(LocationObject::reload)
+{
+ auto& window = static_cast<WindowObject&>(global_object);
+ window.impl().did_call_location_reload({});
+ return JS::js_undefined();
+}
+
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Bindings/LocationObject.h b/Userland/Libraries/LibWeb/Bindings/LocationObject.h
new file mode 100644
index 0000000000..76a38946c1
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Bindings/LocationObject.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibJS/Runtime/Object.h>
+#include <LibWeb/Forward.h>
+
+namespace Web {
+namespace Bindings {
+
+class LocationObject final : public JS::Object {
+ JS_OBJECT(LocationObject, JS::Object);
+
+public:
+ explicit LocationObject(JS::GlobalObject&);
+ virtual void initialize(JS::GlobalObject&) override;
+ virtual ~LocationObject() override;
+
+private:
+ JS_DECLARE_NATIVE_FUNCTION(reload);
+
+ JS_DECLARE_NATIVE_GETTER(href_getter);
+ JS_DECLARE_NATIVE_SETTER(href_setter);
+
+ JS_DECLARE_NATIVE_GETTER(host_getter);
+ JS_DECLARE_NATIVE_GETTER(hostname_getter);
+ JS_DECLARE_NATIVE_GETTER(pathname_getter);
+ JS_DECLARE_NATIVE_GETTER(hash_getter);
+ JS_DECLARE_NATIVE_GETTER(search_getter);
+ JS_DECLARE_NATIVE_GETTER(protocol_getter);
+};
+
+}
+}
diff --git a/Userland/Libraries/LibWeb/Bindings/NavigatorObject.cpp b/Userland/Libraries/LibWeb/Bindings/NavigatorObject.cpp
new file mode 100644
index 0000000000..248097d8e3
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Bindings/NavigatorObject.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/FlyString.h>
+#include <LibJS/Runtime/Array.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibWeb/Bindings/NavigatorObject.h>
+#include <LibWeb/Loader/ResourceLoader.h>
+
+namespace Web {
+namespace Bindings {
+
+NavigatorObject::NavigatorObject(JS::GlobalObject& global_object)
+ : Object(*global_object.object_prototype())
+{
+}
+
+void NavigatorObject::initialize(JS::GlobalObject& global_object)
+{
+ auto& heap = this->heap();
+ auto* languages = JS::Array::create(global_object);
+ languages->indexed_properties().append(js_string(heap, "en-US"));
+
+ define_property("appCodeName", js_string(heap, "Mozilla"));
+ define_property("appName", js_string(heap, "Netscape"));
+ define_property("appVersion", js_string(heap, "4.0"));
+ define_property("language", languages->get(0));
+ define_property("languages", languages);
+ define_property("platform", js_string(heap, "SerenityOS"));
+ define_property("product", js_string(heap, "Gecko"));
+
+ define_native_property("userAgent", user_agent_getter, nullptr);
+}
+
+NavigatorObject::~NavigatorObject()
+{
+}
+
+JS_DEFINE_NATIVE_GETTER(NavigatorObject::user_agent_getter)
+{
+ return JS::js_string(vm, ResourceLoader::the().user_agent());
+}
+
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Bindings/NavigatorObject.h b/Userland/Libraries/LibWeb/Bindings/NavigatorObject.h
new file mode 100644
index 0000000000..fe10271e96
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Bindings/NavigatorObject.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibJS/Runtime/Object.h>
+#include <LibWeb/Forward.h>
+
+namespace Web {
+namespace Bindings {
+
+class NavigatorObject final : public JS::Object {
+ JS_OBJECT(NavigatorObject, JS::Object);
+
+public:
+ NavigatorObject(JS::GlobalObject&);
+ virtual void initialize(JS::GlobalObject&) override;
+ virtual ~NavigatorObject() override;
+
+private:
+ JS_DECLARE_NATIVE_GETTER(user_agent_getter);
+};
+
+}
+}
diff --git a/Userland/Libraries/LibWeb/Bindings/NodeWrapperFactory.cpp b/Userland/Libraries/LibWeb/Bindings/NodeWrapperFactory.cpp
new file mode 100644
index 0000000000..f78491d33a
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Bindings/NodeWrapperFactory.cpp
@@ -0,0 +1,347 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2020, Luke Wilde <luke.wilde@live.co.uk>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/Bindings/CharacterDataWrapper.h>
+#include <LibWeb/Bindings/CommentWrapper.h>
+#include <LibWeb/Bindings/DocumentFragmentWrapper.h>
+#include <LibWeb/Bindings/DocumentTypeWrapper.h>
+#include <LibWeb/Bindings/DocumentWrapper.h>
+#include <LibWeb/Bindings/HTMLAnchorElementWrapper.h>
+#include <LibWeb/Bindings/HTMLAreaElementWrapper.h>
+#include <LibWeb/Bindings/HTMLAudioElementWrapper.h>
+#include <LibWeb/Bindings/HTMLBRElementWrapper.h>
+#include <LibWeb/Bindings/HTMLBaseElementWrapper.h>
+#include <LibWeb/Bindings/HTMLBodyElementWrapper.h>
+#include <LibWeb/Bindings/HTMLButtonElementWrapper.h>
+#include <LibWeb/Bindings/HTMLCanvasElementWrapper.h>
+#include <LibWeb/Bindings/HTMLDListElementWrapper.h>
+#include <LibWeb/Bindings/HTMLDataElementWrapper.h>
+#include <LibWeb/Bindings/HTMLDataListElementWrapper.h>
+#include <LibWeb/Bindings/HTMLDetailsElementWrapper.h>
+#include <LibWeb/Bindings/HTMLDialogElementWrapper.h>
+#include <LibWeb/Bindings/HTMLDirectoryElementWrapper.h>
+#include <LibWeb/Bindings/HTMLDivElementWrapper.h>
+#include <LibWeb/Bindings/HTMLElementWrapper.h>
+#include <LibWeb/Bindings/HTMLEmbedElementWrapper.h>
+#include <LibWeb/Bindings/HTMLFieldSetElementWrapper.h>
+#include <LibWeb/Bindings/HTMLFontElementWrapper.h>
+#include <LibWeb/Bindings/HTMLFormElementWrapper.h>
+#include <LibWeb/Bindings/HTMLFrameElementWrapper.h>
+#include <LibWeb/Bindings/HTMLFrameSetElementWrapper.h>
+#include <LibWeb/Bindings/HTMLHRElementWrapper.h>
+#include <LibWeb/Bindings/HTMLHeadElementWrapper.h>
+#include <LibWeb/Bindings/HTMLHeadingElementWrapper.h>
+#include <LibWeb/Bindings/HTMLHtmlElementWrapper.h>
+#include <LibWeb/Bindings/HTMLIFrameElementWrapper.h>
+#include <LibWeb/Bindings/HTMLImageElementWrapper.h>
+#include <LibWeb/Bindings/HTMLInputElementWrapper.h>
+#include <LibWeb/Bindings/HTMLLIElementWrapper.h>
+#include <LibWeb/Bindings/HTMLLabelElementWrapper.h>
+#include <LibWeb/Bindings/HTMLLegendElementWrapper.h>
+#include <LibWeb/Bindings/HTMLLinkElementWrapper.h>
+#include <LibWeb/Bindings/HTMLMapElementWrapper.h>
+#include <LibWeb/Bindings/HTMLMarqueeElementWrapper.h>
+#include <LibWeb/Bindings/HTMLMenuElementWrapper.h>
+#include <LibWeb/Bindings/HTMLMetaElementWrapper.h>
+#include <LibWeb/Bindings/HTMLMeterElementWrapper.h>
+#include <LibWeb/Bindings/HTMLModElementWrapper.h>
+#include <LibWeb/Bindings/HTMLOListElementWrapper.h>
+#include <LibWeb/Bindings/HTMLObjectElementWrapper.h>
+#include <LibWeb/Bindings/HTMLOptGroupElementWrapper.h>
+#include <LibWeb/Bindings/HTMLOptionElementWrapper.h>
+#include <LibWeb/Bindings/HTMLOutputElementWrapper.h>
+#include <LibWeb/Bindings/HTMLParagraphElementWrapper.h>
+#include <LibWeb/Bindings/HTMLParamElementWrapper.h>
+#include <LibWeb/Bindings/HTMLPictureElementWrapper.h>
+#include <LibWeb/Bindings/HTMLPreElementWrapper.h>
+#include <LibWeb/Bindings/HTMLProgressElementWrapper.h>
+#include <LibWeb/Bindings/HTMLQuoteElementWrapper.h>
+#include <LibWeb/Bindings/HTMLScriptElementWrapper.h>
+#include <LibWeb/Bindings/HTMLSelectElementWrapper.h>
+#include <LibWeb/Bindings/HTMLSlotElementWrapper.h>
+#include <LibWeb/Bindings/HTMLSourceElementWrapper.h>
+#include <LibWeb/Bindings/HTMLSpanElementWrapper.h>
+#include <LibWeb/Bindings/HTMLStyleElementWrapper.h>
+#include <LibWeb/Bindings/HTMLTableCaptionElementWrapper.h>
+#include <LibWeb/Bindings/HTMLTableCellElementWrapper.h>
+#include <LibWeb/Bindings/HTMLTableColElementWrapper.h>
+#include <LibWeb/Bindings/HTMLTableElementWrapper.h>
+#include <LibWeb/Bindings/HTMLTableRowElementWrapper.h>
+#include <LibWeb/Bindings/HTMLTableSectionElementWrapper.h>
+#include <LibWeb/Bindings/HTMLTemplateElementWrapper.h>
+#include <LibWeb/Bindings/HTMLTextAreaElementWrapper.h>
+#include <LibWeb/Bindings/HTMLTimeElementWrapper.h>
+#include <LibWeb/Bindings/HTMLTitleElementWrapper.h>
+#include <LibWeb/Bindings/HTMLTrackElementWrapper.h>
+#include <LibWeb/Bindings/HTMLUListElementWrapper.h>
+#include <LibWeb/Bindings/HTMLUnknownElementWrapper.h>
+#include <LibWeb/Bindings/HTMLVideoElementWrapper.h>
+#include <LibWeb/Bindings/NodeWrapper.h>
+#include <LibWeb/Bindings/NodeWrapperFactory.h>
+#include <LibWeb/Bindings/SVGPathElementWrapper.h>
+#include <LibWeb/Bindings/SVGSVGElementWrapper.h>
+#include <LibWeb/Bindings/TextWrapper.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/DOM/Node.h>
+#include <LibWeb/HTML/HTMLAnchorElement.h>
+#include <LibWeb/HTML/HTMLAreaElement.h>
+#include <LibWeb/HTML/HTMLAudioElement.h>
+#include <LibWeb/HTML/HTMLBRElement.h>
+#include <LibWeb/HTML/HTMLBaseElement.h>
+#include <LibWeb/HTML/HTMLBodyElement.h>
+#include <LibWeb/HTML/HTMLButtonElement.h>
+#include <LibWeb/HTML/HTMLCanvasElement.h>
+#include <LibWeb/HTML/HTMLDListElement.h>
+#include <LibWeb/HTML/HTMLDataElement.h>
+#include <LibWeb/HTML/HTMLDataListElement.h>
+#include <LibWeb/HTML/HTMLDetailsElement.h>
+#include <LibWeb/HTML/HTMLDialogElement.h>
+#include <LibWeb/HTML/HTMLDirectoryElement.h>
+#include <LibWeb/HTML/HTMLDivElement.h>
+#include <LibWeb/HTML/HTMLEmbedElement.h>
+#include <LibWeb/HTML/HTMLFieldSetElement.h>
+#include <LibWeb/HTML/HTMLFontElement.h>
+#include <LibWeb/HTML/HTMLFormElement.h>
+#include <LibWeb/HTML/HTMLFrameElement.h>
+#include <LibWeb/HTML/HTMLFrameSetElement.h>
+#include <LibWeb/HTML/HTMLHRElement.h>
+#include <LibWeb/HTML/HTMLHeadElement.h>
+#include <LibWeb/HTML/HTMLHeadingElement.h>
+#include <LibWeb/HTML/HTMLHtmlElement.h>
+#include <LibWeb/HTML/HTMLIFrameElement.h>
+#include <LibWeb/HTML/HTMLImageElement.h>
+#include <LibWeb/HTML/HTMLInputElement.h>
+#include <LibWeb/HTML/HTMLLIElement.h>
+#include <LibWeb/HTML/HTMLLabelElement.h>
+#include <LibWeb/HTML/HTMLLegendElement.h>
+#include <LibWeb/HTML/HTMLLinkElement.h>
+#include <LibWeb/HTML/HTMLMapElement.h>
+#include <LibWeb/HTML/HTMLMarqueeElement.h>
+#include <LibWeb/HTML/HTMLMenuElement.h>
+#include <LibWeb/HTML/HTMLMetaElement.h>
+#include <LibWeb/HTML/HTMLMeterElement.h>
+#include <LibWeb/HTML/HTMLModElement.h>
+#include <LibWeb/HTML/HTMLOListElement.h>
+#include <LibWeb/HTML/HTMLObjectElement.h>
+#include <LibWeb/HTML/HTMLOptGroupElement.h>
+#include <LibWeb/HTML/HTMLOptionElement.h>
+#include <LibWeb/HTML/HTMLOutputElement.h>
+#include <LibWeb/HTML/HTMLParagraphElement.h>
+#include <LibWeb/HTML/HTMLParamElement.h>
+#include <LibWeb/HTML/HTMLPictureElement.h>
+#include <LibWeb/HTML/HTMLPreElement.h>
+#include <LibWeb/HTML/HTMLProgressElement.h>
+#include <LibWeb/HTML/HTMLQuoteElement.h>
+#include <LibWeb/HTML/HTMLScriptElement.h>
+#include <LibWeb/HTML/HTMLSelectElement.h>
+#include <LibWeb/HTML/HTMLSlotElement.h>
+#include <LibWeb/HTML/HTMLSourceElement.h>
+#include <LibWeb/HTML/HTMLSpanElement.h>
+#include <LibWeb/HTML/HTMLStyleElement.h>
+#include <LibWeb/HTML/HTMLTableCaptionElement.h>
+#include <LibWeb/HTML/HTMLTableCellElement.h>
+#include <LibWeb/HTML/HTMLTableColElement.h>
+#include <LibWeb/HTML/HTMLTableElement.h>
+#include <LibWeb/HTML/HTMLTableRowElement.h>
+#include <LibWeb/HTML/HTMLTableSectionElement.h>
+#include <LibWeb/HTML/HTMLTemplateElement.h>
+#include <LibWeb/HTML/HTMLTextAreaElement.h>
+#include <LibWeb/HTML/HTMLTimeElement.h>
+#include <LibWeb/HTML/HTMLTitleElement.h>
+#include <LibWeb/HTML/HTMLTrackElement.h>
+#include <LibWeb/HTML/HTMLUListElement.h>
+#include <LibWeb/HTML/HTMLUnknownElement.h>
+#include <LibWeb/HTML/HTMLVideoElement.h>
+#include <LibWeb/SVG/SVGPathElement.h>
+#include <LibWeb/SVG/SVGSVGElement.h>
+
+namespace Web::Bindings {
+
+NodeWrapper* wrap(JS::GlobalObject& global_object, DOM::Node& node)
+{
+ if (is<DOM::Document>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<DOM::Document>(node)));
+ if (is<DOM::DocumentType>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<DOM::DocumentType>(node)));
+ if (is<HTML::HTMLAnchorElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLAnchorElement>(node)));
+ if (is<HTML::HTMLAreaElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLAreaElement>(node)));
+ if (is<HTML::HTMLAudioElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLAudioElement>(node)));
+ if (is<HTML::HTMLBaseElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLBaseElement>(node)));
+ if (is<HTML::HTMLBodyElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLBodyElement>(node)));
+ if (is<HTML::HTMLBRElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLBRElement>(node)));
+ if (is<HTML::HTMLButtonElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLButtonElement>(node)));
+ if (is<HTML::HTMLCanvasElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLCanvasElement>(node)));
+ if (is<HTML::HTMLDataElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLDataElement>(node)));
+ if (is<HTML::HTMLDataListElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLDataListElement>(node)));
+ if (is<HTML::HTMLDetailsElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLDetailsElement>(node)));
+ if (is<HTML::HTMLDialogElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLDialogElement>(node)));
+ if (is<HTML::HTMLDirectoryElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLDirectoryElement>(node)));
+ if (is<HTML::HTMLDivElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLDivElement>(node)));
+ if (is<HTML::HTMLDListElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLDListElement>(node)));
+ if (is<HTML::HTMLEmbedElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLEmbedElement>(node)));
+ if (is<HTML::HTMLFieldSetElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLFieldSetElement>(node)));
+ if (is<HTML::HTMLFontElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLFontElement>(node)));
+ if (is<HTML::HTMLFormElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLFormElement>(node)));
+ if (is<HTML::HTMLFrameElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLFrameElement>(node)));
+ if (is<HTML::HTMLFrameSetElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLFrameSetElement>(node)));
+ if (is<HTML::HTMLHeadElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLHeadElement>(node)));
+ if (is<HTML::HTMLHeadingElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLHeadingElement>(node)));
+ if (is<HTML::HTMLHRElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLHRElement>(node)));
+ if (is<HTML::HTMLHtmlElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLHtmlElement>(node)));
+ if (is<HTML::HTMLIFrameElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLIFrameElement>(node)));
+ if (is<HTML::HTMLImageElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLImageElement>(node)));
+ if (is<HTML::HTMLInputElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLInputElement>(node)));
+ if (is<HTML::HTMLLabelElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLLabelElement>(node)));
+ if (is<HTML::HTMLLegendElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLLegendElement>(node)));
+ if (is<HTML::HTMLLIElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLLIElement>(node)));
+ if (is<HTML::HTMLLinkElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLLinkElement>(node)));
+ if (is<HTML::HTMLMapElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLMapElement>(node)));
+ if (is<HTML::HTMLMarqueeElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLMarqueeElement>(node)));
+ if (is<HTML::HTMLMenuElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLMenuElement>(node)));
+ if (is<HTML::HTMLMetaElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLMetaElement>(node)));
+ if (is<HTML::HTMLMeterElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLMeterElement>(node)));
+ if (is<HTML::HTMLModElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLModElement>(node)));
+ if (is<HTML::HTMLObjectElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLObjectElement>(node)));
+ if (is<HTML::HTMLOListElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLOListElement>(node)));
+ if (is<HTML::HTMLOptGroupElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLOptGroupElement>(node)));
+ if (is<HTML::HTMLOptionElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLOptionElement>(node)));
+ if (is<HTML::HTMLOutputElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLOutputElement>(node)));
+ if (is<HTML::HTMLParagraphElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLParagraphElement>(node)));
+ if (is<HTML::HTMLParamElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLParamElement>(node)));
+ if (is<HTML::HTMLPictureElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLPictureElement>(node)));
+ if (is<HTML::HTMLPreElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLPreElement>(node)));
+ if (is<HTML::HTMLProgressElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLProgressElement>(node)));
+ if (is<HTML::HTMLQuoteElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLQuoteElement>(node)));
+ if (is<HTML::HTMLScriptElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLScriptElement>(node)));
+ if (is<HTML::HTMLSelectElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLSelectElement>(node)));
+ if (is<HTML::HTMLSlotElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLSlotElement>(node)));
+ if (is<HTML::HTMLSourceElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLSourceElement>(node)));
+ if (is<HTML::HTMLSpanElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLSpanElement>(node)));
+ if (is<HTML::HTMLStyleElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLStyleElement>(node)));
+ if (is<HTML::HTMLTableCaptionElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLTableCaptionElement>(node)));
+ if (is<HTML::HTMLTableCellElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLTableCellElement>(node)));
+ if (is<HTML::HTMLTableColElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLTableColElement>(node)));
+ if (is<HTML::HTMLTableElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLTableElement>(node)));
+ if (is<HTML::HTMLTableRowElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLTableRowElement>(node)));
+ if (is<HTML::HTMLTableSectionElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLTableSectionElement>(node)));
+ if (is<HTML::HTMLTemplateElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLTemplateElement>(node)));
+ if (is<HTML::HTMLTextAreaElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLTextAreaElement>(node)));
+ if (is<HTML::HTMLTimeElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLTimeElement>(node)));
+ if (is<HTML::HTMLTitleElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLTitleElement>(node)));
+ if (is<HTML::HTMLTrackElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLTrackElement>(node)));
+ if (is<HTML::HTMLUListElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLUListElement>(node)));
+ if (is<HTML::HTMLUnknownElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLUnknownElement>(node)));
+ if (is<HTML::HTMLVideoElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLVideoElement>(node)));
+ if (is<HTML::HTMLElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<HTML::HTMLElement>(node)));
+ if (is<SVG::SVGSVGElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<SVG::SVGSVGElement>(node)));
+ if (is<SVG::SVGPathElement>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<SVG::SVGPathElement>(node)));
+ if (is<DOM::Element>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<DOM::Element>(node)));
+ if (is<DOM::DocumentFragment>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<DOM::DocumentFragment>(node)));
+ if (is<DOM::Comment>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<DOM::Comment>(node)));
+ if (is<DOM::Text>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<DOM::Text>(node)));
+ if (is<DOM::CharacterData>(node))
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, downcast<DOM::CharacterData>(node)));
+ return static_cast<NodeWrapper*>(wrap_impl(global_object, node));
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Bindings/NodeWrapperFactory.h b/Userland/Libraries/LibWeb/Bindings/NodeWrapperFactory.h
new file mode 100644
index 0000000000..153e78cb32
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Bindings/NodeWrapperFactory.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibJS/Forward.h>
+#include <LibWeb/Forward.h>
+
+namespace Web {
+namespace Bindings {
+
+NodeWrapper* wrap(JS::GlobalObject&, DOM::Node&);
+
+}
+}
diff --git a/Userland/Libraries/LibWeb/Bindings/RangeConstructor.cpp b/Userland/Libraries/LibWeb/Bindings/RangeConstructor.cpp
new file mode 100644
index 0000000000..27311c4420
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Bindings/RangeConstructor.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2020, the SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibJS/Heap/Heap.h>
+#include <LibWeb/Bindings/RangeConstructor.h>
+#include <LibWeb/Bindings/RangePrototype.h>
+#include <LibWeb/Bindings/RangeWrapper.h>
+#include <LibWeb/Bindings/WindowObject.h>
+#include <LibWeb/DOM/Range.h>
+
+namespace Web::Bindings {
+
+RangeConstructor::RangeConstructor(JS::GlobalObject& global_object)
+ : NativeFunction(*global_object.function_prototype())
+{
+}
+
+void RangeConstructor::initialize(JS::GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ NativeFunction::initialize(global_object);
+ auto& window = static_cast<WindowObject&>(global_object);
+ define_property(vm.names.prototype, window.range_prototype(), 0);
+ define_property(vm.names.length, JS::Value(0), JS::Attribute::Configurable);
+}
+
+JS::Value RangeConstructor::call()
+{
+ vm().throw_exception<JS::TypeError>(global_object(), JS::ErrorType::ConstructorWithoutNew, "Range");
+ return {};
+}
+
+JS::Value RangeConstructor::construct(Function&)
+{
+ auto& window = static_cast<WindowObject&>(global_object());
+ return heap().allocate<RangeWrapper>(window, window, DOM::Range::create(window.impl()));
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Bindings/RangeConstructor.h b/Userland/Libraries/LibWeb/Bindings/RangeConstructor.h
new file mode 100644
index 0000000000..3189c67bc6
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Bindings/RangeConstructor.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020, the SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibJS/Runtime/NativeFunction.h>
+
+namespace Web::Bindings {
+
+class RangeConstructor final : public JS::NativeFunction {
+public:
+ explicit RangeConstructor(JS::GlobalObject&);
+
+ void initialize(JS::GlobalObject&) override;
+
+ JS::Value call() override;
+ JS::Value construct(JS::Function& new_target) override;
+
+private:
+ bool has_constructor() const override { return true; }
+ const char* class_name() const override { return "RangeConstructor"; }
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Bindings/RangePrototype.cpp b/Userland/Libraries/LibWeb/Bindings/RangePrototype.cpp
new file mode 100644
index 0000000000..ed317cb1c6
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Bindings/RangePrototype.cpp
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2020, the SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/Function.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibWeb/Bindings/NodeWrapper.h>
+#include <LibWeb/Bindings/NodeWrapperFactory.h>
+#include <LibWeb/Bindings/RangePrototype.h>
+#include <LibWeb/Bindings/RangeWrapper.h>
+#include <LibWeb/DOM/Range.h>
+
+namespace Web::Bindings {
+
+RangePrototype::RangePrototype(JS::GlobalObject& global_object)
+ : Object(*global_object.object_prototype())
+{
+}
+
+void RangePrototype::initialize(JS::GlobalObject& global_object)
+{
+ auto default_attributes = JS::Attribute::Enumerable | JS::Attribute::Configurable;
+
+ Object::initialize(global_object);
+
+ define_native_function("setStart", set_start, 2);
+ define_native_function("setEnd", set_end, 2);
+ define_native_function("cloneRange", clone_range, 0);
+
+ define_native_property("startContainer", start_container_getter, nullptr, default_attributes);
+ define_native_property("endContainer", end_container_getter, nullptr, default_attributes);
+ define_native_property("startOffset", start_offset_getter, nullptr, default_attributes);
+ define_native_property("endOffset", end_offset_getter, nullptr, default_attributes);
+}
+
+static DOM::Range* impl_from(JS::VM& vm, JS::GlobalObject& global_object)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return nullptr;
+ if (StringView("RangeWrapper") != this_object->class_name()) {
+ vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "Range");
+ return nullptr;
+ }
+ return &static_cast<RangeWrapper*>(this_object)->impl();
+}
+
+JS_DEFINE_NATIVE_FUNCTION(RangePrototype::set_start)
+{
+ auto* impl = impl_from(vm, global_object);
+ if (!impl)
+ return {};
+
+ auto arg0 = vm.argument(0).to_object(global_object);
+ if (vm.exception())
+ return {};
+ auto arg1 = vm.argument(1).to_number(global_object);
+ if (vm.exception())
+ return {};
+
+ if (!is<NodeWrapper>(arg0)) {
+ vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "Node");
+ return {};
+ }
+
+ impl->set_start(static_cast<NodeWrapper*>(arg0)->impl(), arg1.as_i32());
+
+ return JS::js_undefined();
+}
+
+JS_DEFINE_NATIVE_FUNCTION(RangePrototype::set_end)
+{
+ auto* impl = impl_from(vm, global_object);
+ if (!impl)
+ return {};
+
+ auto arg0 = vm.argument(0).to_object(global_object);
+ if (vm.exception())
+ return {};
+ auto arg1 = vm.argument(1).to_number(global_object);
+ if (vm.exception())
+ return {};
+
+ if (!is<NodeWrapper>(arg0)) {
+ vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "Node");
+ return {};
+ }
+
+ impl->set_end(static_cast<NodeWrapper*>(arg0)->impl(), arg1.as_i32());
+
+ return JS::js_undefined();
+}
+
+JS_DEFINE_NATIVE_FUNCTION(RangePrototype::clone_range)
+{
+ auto* impl = impl_from(vm, global_object);
+ if (!impl)
+ return {};
+
+ return wrap(global_object, *impl->clone_range());
+}
+
+JS_DEFINE_NATIVE_GETTER(RangePrototype::start_container_getter)
+{
+ auto* impl = impl_from(vm, global_object);
+ if (!impl)
+ return {};
+
+ return wrap(global_object, *impl->start_container());
+}
+
+JS_DEFINE_NATIVE_GETTER(RangePrototype::end_container_getter)
+{
+ auto* impl = impl_from(vm, global_object);
+ if (!impl)
+ return {};
+
+ return wrap(global_object, *impl->end_container());
+}
+
+JS_DEFINE_NATIVE_GETTER(RangePrototype::start_offset_getter)
+{
+ auto* impl = impl_from(vm, global_object);
+ if (!impl)
+ return {};
+
+ return JS::Value(impl->start_offset());
+}
+
+JS_DEFINE_NATIVE_GETTER(RangePrototype::end_offset_getter)
+{
+ auto* impl = impl_from(vm, global_object);
+ if (!impl)
+ return {};
+
+ return JS::Value(impl->end_offset());
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Bindings/RangePrototype.h b/Userland/Libraries/LibWeb/Bindings/RangePrototype.h
new file mode 100644
index 0000000000..560eaa4a7c
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Bindings/RangePrototype.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020, the SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibJS/Runtime/Object.h>
+
+namespace Web::Bindings {
+
+class RangePrototype final : public JS::Object {
+ JS_OBJECT(RangePrototype, JS::Object);
+
+public:
+ explicit RangePrototype(JS::GlobalObject&);
+
+ void initialize(JS::GlobalObject&) override;
+
+private:
+ JS_DECLARE_NATIVE_FUNCTION(set_start);
+ JS_DECLARE_NATIVE_FUNCTION(set_end);
+ JS_DECLARE_NATIVE_FUNCTION(clone_range);
+
+ JS_DECLARE_NATIVE_GETTER(start_container_getter);
+ JS_DECLARE_NATIVE_GETTER(end_container_getter);
+ JS_DECLARE_NATIVE_GETTER(start_offset_getter);
+ JS_DECLARE_NATIVE_GETTER(end_offset_getter);
+ JS_DECLARE_NATIVE_GETTER(collapsed_getter);
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Bindings/RangeWrapper.cpp b/Userland/Libraries/LibWeb/Bindings/RangeWrapper.cpp
new file mode 100644
index 0000000000..dc58a4358d
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Bindings/RangeWrapper.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020, the SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/Bindings/RangePrototype.h>
+#include <LibWeb/Bindings/RangeWrapper.h>
+#include <LibWeb/Bindings/WindowObject.h>
+#include <LibWeb/Bindings/Wrappable.h>
+#include <LibWeb/DOM/Range.h>
+
+namespace Web::Bindings {
+
+RangeWrapper::RangeWrapper(JS::GlobalObject& global_object, DOM::Range& impl)
+ : Wrapper(global_object)
+ , m_impl(impl)
+{
+ set_prototype(static_cast<WindowObject&>(global_object).range_prototype());
+}
+
+RangeWrapper* wrap(JS::GlobalObject& global_object, DOM::Range& impl)
+{
+ return static_cast<RangeWrapper*>(wrap_impl(global_object, impl));
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Bindings/RangeWrapper.h b/Userland/Libraries/LibWeb/Bindings/RangeWrapper.h
new file mode 100644
index 0000000000..5528d81911
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Bindings/RangeWrapper.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020, the SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/Bindings/Wrapper.h>
+
+namespace Web::Bindings {
+
+class RangeWrapper final : public Wrapper {
+public:
+ RangeWrapper(JS::GlobalObject&, DOM::Range&);
+
+ DOM::Range& impl() { return m_impl; }
+ const DOM::Range& impl() const { return m_impl; }
+
+private:
+ virtual const char* class_name() const override { return "RangeWrapper"; }
+
+ NonnullRefPtr<DOM::Range> m_impl;
+};
+
+RangeWrapper* wrap(JS::GlobalObject&, DOM::Range&);
+
+}
diff --git a/Userland/Libraries/LibWeb/Bindings/ScriptExecutionContext.cpp b/Userland/Libraries/LibWeb/Bindings/ScriptExecutionContext.cpp
new file mode 100644
index 0000000000..ea7e58864e
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Bindings/ScriptExecutionContext.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/Bindings/ScriptExecutionContext.h>
+
+namespace Web::Bindings {
+
+ScriptExecutionContext::~ScriptExecutionContext()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Bindings/ScriptExecutionContext.h b/Userland/Libraries/LibWeb/Bindings/ScriptExecutionContext.h
new file mode 100644
index 0000000000..1c21910741
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Bindings/ScriptExecutionContext.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/Weakable.h>
+#include <LibJS/Forward.h>
+#include <LibWeb/Forward.h>
+
+namespace Web::Bindings {
+
+class ScriptExecutionContext {
+public:
+ virtual ~ScriptExecutionContext();
+
+ // FIXME: This should not work this way long-term, interpreters should be on the stack.
+ virtual JS::Interpreter& interpreter() = 0;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Bindings/WindowObject.cpp b/Userland/Libraries/LibWeb/Bindings/WindowObject.cpp
new file mode 100644
index 0000000000..f147a86bf0
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Bindings/WindowObject.cpp
@@ -0,0 +1,361 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/Base64.h>
+#include <AK/ByteBuffer.h>
+#include <AK/FlyString.h>
+#include <AK/Function.h>
+#include <AK/String.h>
+#include <AK/Utf8View.h>
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/Function.h>
+#include <LibJS/Runtime/Shape.h>
+#include <LibTextCodec/Decoder.h>
+#include <LibWeb/Bindings/DocumentWrapper.h>
+#include <LibWeb/Bindings/EventWrapper.h>
+#include <LibWeb/Bindings/EventWrapperFactory.h>
+#include <LibWeb/Bindings/LocationObject.h>
+#include <LibWeb/Bindings/NavigatorObject.h>
+#include <LibWeb/Bindings/NodeWrapperFactory.h>
+#include <LibWeb/Bindings/PerformanceWrapper.h>
+#include <LibWeb/Bindings/RangeConstructor.h>
+#include <LibWeb/Bindings/RangePrototype.h>
+#include <LibWeb/Bindings/WindowObject.h>
+#include <LibWeb/Bindings/XMLHttpRequestConstructor.h>
+#include <LibWeb/Bindings/XMLHttpRequestPrototype.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/DOM/Event.h>
+#include <LibWeb/DOM/Window.h>
+#include <LibWeb/Origin.h>
+
+namespace Web {
+namespace Bindings {
+
+WindowObject::WindowObject(DOM::Window& impl)
+ : m_impl(impl)
+{
+ impl.set_wrapper({}, *this);
+}
+
+void WindowObject::initialize()
+{
+ GlobalObject::initialize();
+
+ define_property("window", this, JS::Attribute::Enumerable);
+ define_property("frames", this, JS::Attribute::Enumerable);
+ define_property("self", this, JS::Attribute::Enumerable);
+ define_native_property("document", document_getter, document_setter, JS::Attribute::Enumerable);
+ define_native_property("performance", performance_getter, nullptr, JS::Attribute::Enumerable);
+ define_native_function("alert", alert);
+ define_native_function("confirm", confirm);
+ define_native_function("setInterval", set_interval, 1);
+ define_native_function("setTimeout", set_timeout, 1);
+ define_native_function("clearInterval", clear_interval, 1);
+ define_native_function("clearTimeout", clear_timeout, 1);
+ define_native_function("requestAnimationFrame", request_animation_frame, 1);
+ define_native_function("cancelAnimationFrame", cancel_animation_frame, 1);
+ define_native_function("atob", atob, 1);
+ define_native_function("btoa", btoa, 1);
+
+ // Legacy
+ define_native_property("event", event_getter, nullptr, JS::Attribute::Enumerable);
+
+ define_property("navigator", heap().allocate<NavigatorObject>(*this, *this), JS::Attribute::Enumerable | JS::Attribute::Configurable);
+ define_property("location", heap().allocate<LocationObject>(*this, *this), JS::Attribute::Enumerable | JS::Attribute::Configurable);
+
+ m_xhr_prototype = heap().allocate<XMLHttpRequestPrototype>(*this, *this);
+ add_constructor("XMLHttpRequest", m_xhr_constructor, m_xhr_prototype);
+
+ m_range_prototype = heap().allocate<RangePrototype>(*this, *this);
+ add_constructor("Range", m_range_constructor, m_range_prototype);
+}
+
+WindowObject::~WindowObject()
+{
+}
+
+void WindowObject::visit_edges(Visitor& visitor)
+{
+ GlobalObject::visit_edges(visitor);
+ visitor.visit(m_xhr_constructor);
+ visitor.visit(m_xhr_prototype);
+ visitor.visit(m_range_constructor);
+ visitor.visit(m_range_prototype);
+}
+
+Origin WindowObject::origin() const
+{
+ return impl().document().origin();
+}
+
+static DOM::Window* impl_from(JS::VM& vm, JS::GlobalObject& global_object)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object) {
+ ASSERT_NOT_REACHED();
+ return nullptr;
+ }
+ if (StringView("WindowObject") != this_object->class_name()) {
+ vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "WindowObject");
+ return nullptr;
+ }
+ return &static_cast<WindowObject*>(this_object)->impl();
+}
+
+JS_DEFINE_NATIVE_FUNCTION(WindowObject::alert)
+{
+ auto* impl = impl_from(vm, global_object);
+ if (!impl)
+ return {};
+ String message = "";
+ if (vm.argument_count()) {
+ message = vm.argument(0).to_string(global_object);
+ if (vm.exception())
+ return {};
+ }
+ impl->alert(message);
+ return JS::js_undefined();
+}
+
+JS_DEFINE_NATIVE_FUNCTION(WindowObject::confirm)
+{
+ auto* impl = impl_from(vm, global_object);
+ if (!impl)
+ return {};
+ String message = "";
+ if (vm.argument_count()) {
+ message = vm.argument(0).to_string(global_object);
+ if (vm.exception())
+ return {};
+ }
+ return JS::Value(impl->confirm(message));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(WindowObject::set_interval)
+{
+ auto* impl = impl_from(vm, global_object);
+ if (!impl)
+ return {};
+ if (!vm.argument_count()) {
+ vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::BadArgCountAtLeastOne, "setInterval");
+ return {};
+ }
+ auto* callback_object = vm.argument(0).to_object(global_object);
+ if (!callback_object)
+ return {};
+ if (!callback_object->is_function()) {
+ vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotAFunctionNoParam);
+ return {};
+ }
+ i32 interval = 0;
+ if (vm.argument_count() >= 2) {
+ interval = vm.argument(1).to_i32(global_object);
+ if (vm.exception())
+ return {};
+ if (interval < 0)
+ interval = 0;
+ }
+
+ auto timer_id = impl->set_interval(*static_cast<JS::Function*>(callback_object), interval);
+ return JS::Value(timer_id);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(WindowObject::set_timeout)
+{
+ auto* impl = impl_from(vm, global_object);
+ if (!impl)
+ return {};
+ if (!vm.argument_count()) {
+ vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::BadArgCountAtLeastOne, "setTimeout");
+ return {};
+ }
+ auto* callback_object = vm.argument(0).to_object(global_object);
+ if (!callback_object)
+ return {};
+ if (!callback_object->is_function()) {
+ vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotAFunctionNoParam);
+ return {};
+ }
+ i32 interval = 0;
+ if (vm.argument_count() >= 2) {
+ interval = vm.argument(1).to_i32(global_object);
+ if (vm.exception())
+ return {};
+ if (interval < 0)
+ interval = 0;
+ }
+
+ auto timer_id = impl->set_timeout(*static_cast<JS::Function*>(callback_object), interval);
+ return JS::Value(timer_id);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(WindowObject::clear_timeout)
+{
+ auto* impl = impl_from(vm, global_object);
+ if (!impl)
+ return {};
+ if (!vm.argument_count()) {
+ vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::BadArgCountAtLeastOne, "clearTimeout");
+ return {};
+ }
+ i32 timer_id = vm.argument(0).to_i32(global_object);
+ if (vm.exception())
+ return {};
+ impl->clear_timeout(timer_id);
+ return JS::js_undefined();
+}
+
+JS_DEFINE_NATIVE_FUNCTION(WindowObject::clear_interval)
+{
+ auto* impl = impl_from(vm, global_object);
+ if (!impl)
+ return {};
+ if (!vm.argument_count()) {
+ vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::BadArgCountAtLeastOne, "clearInterval");
+ return {};
+ }
+ i32 timer_id = vm.argument(0).to_i32(global_object);
+ if (vm.exception())
+ return {};
+ impl->clear_timeout(timer_id);
+ return JS::js_undefined();
+}
+
+JS_DEFINE_NATIVE_FUNCTION(WindowObject::request_animation_frame)
+{
+ auto* impl = impl_from(vm, global_object);
+ if (!impl)
+ return {};
+ if (!vm.argument_count()) {
+ vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::BadArgCountOne, "requestAnimationFrame");
+ return {};
+ }
+ auto* callback_object = vm.argument(0).to_object(global_object);
+ if (!callback_object)
+ return {};
+ if (!callback_object->is_function()) {
+ vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotAFunctionNoParam);
+ return {};
+ }
+ return JS::Value(impl->request_animation_frame(*static_cast<JS::Function*>(callback_object)));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(WindowObject::cancel_animation_frame)
+{
+ auto* impl = impl_from(vm, global_object);
+ if (!impl)
+ return {};
+ if (!vm.argument_count()) {
+ vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::BadArgCountOne, "cancelAnimationFrame");
+ return {};
+ }
+ auto id = vm.argument(0).to_i32(global_object);
+ if (vm.exception())
+ return {};
+ impl->cancel_animation_frame(id);
+ return JS::js_undefined();
+}
+
+JS_DEFINE_NATIVE_FUNCTION(WindowObject::atob)
+{
+ auto* impl = impl_from(vm, global_object);
+ if (!impl)
+ return {};
+ if (!vm.argument_count()) {
+ vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::BadArgCountOne, "atob");
+ return {};
+ }
+ auto string = vm.argument(0).to_string(global_object);
+ if (vm.exception())
+ return {};
+ auto decoded = decode_base64(StringView(string));
+
+ // decode_base64() returns a byte string. LibJS uses UTF-8 for strings. Use Latin1Decoder to convert bytes 128-255 to UTF-8.
+ auto decoder = TextCodec::decoder_for("windows-1252");
+ ASSERT(decoder);
+ return JS::js_string(vm, decoder->to_utf8(decoded));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(WindowObject::btoa)
+{
+ auto* impl = impl_from(vm, global_object);
+ if (!impl)
+ return {};
+ if (!vm.argument_count()) {
+ vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::BadArgCountOne, "btoa");
+ return {};
+ }
+ auto string = vm.argument(0).to_string(global_object);
+ if (vm.exception())
+ return {};
+
+ Vector<u8> byte_string;
+ byte_string.ensure_capacity(string.length());
+ for (u32 code_point : Utf8View(string)) {
+ if (code_point > 0xff) {
+ vm.throw_exception<JS::InvalidCharacterError>(global_object, JS::ErrorType::NotAByteString, "btoa");
+ return {};
+ }
+ byte_string.append(code_point);
+ }
+
+ auto encoded = encode_base64(byte_string.span());
+ return JS::js_string(vm, move(encoded));
+}
+
+JS_DEFINE_NATIVE_GETTER(WindowObject::document_getter)
+{
+ auto* impl = impl_from(vm, global_object);
+ if (!impl)
+ return {};
+ return wrap(global_object, impl->document());
+}
+
+JS_DEFINE_NATIVE_SETTER(WindowObject::document_setter)
+{
+ // FIXME: Figure out what we should do here. Just ignore attempts to set window.document for now.
+}
+
+JS_DEFINE_NATIVE_GETTER(WindowObject::performance_getter)
+{
+ auto* impl = impl_from(vm, global_object);
+ if (!impl)
+ return {};
+ return wrap(global_object, impl->performance());
+}
+
+JS_DEFINE_NATIVE_GETTER(WindowObject::event_getter)
+{
+ auto* impl = impl_from(vm, global_object);
+ if (!impl)
+ return {};
+ if (!impl->current_event())
+ return JS::js_undefined();
+ return wrap(global_object, const_cast<DOM::Event&>(*impl->current_event()));
+}
+
+}
+}
diff --git a/Userland/Libraries/LibWeb/Bindings/WindowObject.h b/Userland/Libraries/LibWeb/Bindings/WindowObject.h
new file mode 100644
index 0000000000..d68a3364c9
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Bindings/WindowObject.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/TypeCasts.h>
+#include <AK/Weakable.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibWeb/Forward.h>
+
+namespace Web {
+namespace Bindings {
+
+class WindowObject final
+ : public JS::GlobalObject
+ , public Weakable<WindowObject> {
+public:
+ explicit WindowObject(DOM::Window&);
+ virtual void initialize() override;
+ virtual ~WindowObject() override;
+
+ DOM::Window& impl() { return *m_impl; }
+ const DOM::Window& impl() const { return *m_impl; }
+
+ Origin origin() const;
+
+ XMLHttpRequestPrototype* xhr_prototype() { return m_xhr_prototype; }
+ XMLHttpRequestConstructor* xhr_constructor() { return m_xhr_constructor; }
+
+ RangePrototype* range_prototype() { return m_range_prototype; }
+ RangeConstructor* range_constructor() { return m_range_constructor; }
+
+private:
+ virtual const char* class_name() const override { return "WindowObject"; }
+ virtual void visit_edges(Visitor&) override;
+
+ JS_DECLARE_NATIVE_GETTER(document_getter);
+ JS_DECLARE_NATIVE_SETTER(document_setter);
+
+ JS_DECLARE_NATIVE_GETTER(performance_getter);
+
+ JS_DECLARE_NATIVE_GETTER(event_getter);
+
+ JS_DECLARE_NATIVE_FUNCTION(alert);
+ JS_DECLARE_NATIVE_FUNCTION(confirm);
+ JS_DECLARE_NATIVE_FUNCTION(set_interval);
+ JS_DECLARE_NATIVE_FUNCTION(set_timeout);
+ JS_DECLARE_NATIVE_FUNCTION(clear_interval);
+ JS_DECLARE_NATIVE_FUNCTION(clear_timeout);
+ JS_DECLARE_NATIVE_FUNCTION(request_animation_frame);
+ JS_DECLARE_NATIVE_FUNCTION(cancel_animation_frame);
+ JS_DECLARE_NATIVE_FUNCTION(atob);
+ JS_DECLARE_NATIVE_FUNCTION(btoa);
+
+ NonnullRefPtr<DOM::Window> m_impl;
+
+ XMLHttpRequestConstructor* m_xhr_constructor { nullptr };
+ XMLHttpRequestPrototype* m_xhr_prototype { nullptr };
+
+ RangePrototype* m_range_prototype { nullptr };
+ RangeConstructor* m_range_constructor { nullptr };
+};
+
+}
+}
diff --git a/Userland/Libraries/LibWeb/Bindings/Wrappable.cpp b/Userland/Libraries/LibWeb/Bindings/Wrappable.cpp
new file mode 100644
index 0000000000..b257a378a6
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Bindings/Wrappable.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/Bindings/Wrappable.h>
+#include <LibWeb/Bindings/Wrapper.h>
+
+namespace Web {
+namespace Bindings {
+
+Wrappable::~Wrappable()
+{
+}
+
+void Wrappable::set_wrapper(Wrapper& wrapper)
+{
+ ASSERT(!m_wrapper);
+ m_wrapper = wrapper.make_weak_ptr();
+}
+
+}
+}
diff --git a/Userland/Libraries/LibWeb/Bindings/Wrappable.h b/Userland/Libraries/LibWeb/Bindings/Wrappable.h
new file mode 100644
index 0000000000..211fc4755a
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Bindings/Wrappable.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/WeakPtr.h>
+#include <LibJS/Heap/Heap.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibWeb/Forward.h>
+
+namespace Web::Bindings {
+
+class Wrappable {
+public:
+ virtual ~Wrappable();
+
+ void set_wrapper(Wrapper&);
+ Wrapper* wrapper() { return m_wrapper; }
+ const Wrapper* wrapper() const { return m_wrapper; }
+
+private:
+ WeakPtr<Wrapper> m_wrapper;
+};
+
+template<class NativeObject>
+inline Wrapper* wrap_impl(JS::GlobalObject& global_object, NativeObject& native_object)
+{
+ if (!native_object.wrapper()) {
+ native_object.set_wrapper(*global_object.heap().allocate<typename NativeObject::WrapperType>(global_object, global_object, native_object));
+ }
+ return native_object.wrapper();
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Bindings/Wrapper.h b/Userland/Libraries/LibWeb/Bindings/Wrapper.h
new file mode 100644
index 0000000000..c83556605a
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Bindings/Wrapper.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/NonnullRefPtr.h>
+#include <AK/Weakable.h>
+#include <LibJS/Runtime/Object.h>
+#include <LibWeb/Forward.h>
+
+namespace Web::Bindings {
+
+class Wrapper
+ : public JS::Object
+ , public Weakable<Wrapper> {
+ JS_OBJECT(Wrapper, JS::Object);
+
+public:
+protected:
+ explicit Wrapper(Object& prototype)
+ : Object(prototype)
+ {
+ }
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestConstructor.cpp b/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestConstructor.cpp
new file mode 100644
index 0000000000..16bb2a4b8b
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestConstructor.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibJS/Heap/Heap.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibWeb/Bindings/WindowObject.h>
+#include <LibWeb/Bindings/XMLHttpRequestConstructor.h>
+#include <LibWeb/Bindings/XMLHttpRequestPrototype.h>
+#include <LibWeb/Bindings/XMLHttpRequestWrapper.h>
+#include <LibWeb/DOM/XMLHttpRequest.h>
+
+namespace Web::Bindings {
+
+XMLHttpRequestConstructor::XMLHttpRequestConstructor(JS::GlobalObject& global_object)
+ : NativeFunction(*global_object.function_prototype())
+{
+}
+
+void XMLHttpRequestConstructor::initialize(JS::GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ NativeFunction::initialize(global_object);
+ auto& window = static_cast<WindowObject&>(global_object);
+ define_property(vm.names.prototype, window.xhr_prototype(), 0);
+ define_property(vm.names.length, JS::Value(1), JS::Attribute::Configurable);
+
+ define_property("UNSENT", JS::Value((i32)XMLHttpRequest::ReadyState::Unsent), JS::Attribute::Enumerable);
+ define_property("OPENED", JS::Value((i32)XMLHttpRequest::ReadyState::Opened), JS::Attribute::Enumerable);
+ define_property("HEADERS_RECEIVED", JS::Value((i32)XMLHttpRequest::ReadyState::HeadersReceived), JS::Attribute::Enumerable);
+ define_property("LOADING", JS::Value((i32)XMLHttpRequest::ReadyState::Loading), JS::Attribute::Enumerable);
+ define_property("DONE", JS::Value((i32)XMLHttpRequest::ReadyState::Done), JS::Attribute::Enumerable);
+}
+
+XMLHttpRequestConstructor::~XMLHttpRequestConstructor()
+{
+}
+
+JS::Value XMLHttpRequestConstructor::call()
+{
+ vm().throw_exception<JS::TypeError>(global_object(), JS::ErrorType::ConstructorWithoutNew, "XMLHttpRequest");
+ return {};
+}
+
+JS::Value XMLHttpRequestConstructor::construct(Function&)
+{
+ auto& window = static_cast<WindowObject&>(global_object());
+ return heap().allocate<XMLHttpRequestWrapper>(window, window, XMLHttpRequest::create(window.impl()));
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestConstructor.h b/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestConstructor.h
new file mode 100644
index 0000000000..0ca6f5063f
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestConstructor.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibJS/Runtime/NativeFunction.h>
+
+namespace Web::Bindings {
+
+class XMLHttpRequestConstructor final : public JS::NativeFunction {
+public:
+ explicit XMLHttpRequestConstructor(JS::GlobalObject&);
+ virtual void initialize(JS::GlobalObject&) override;
+ virtual ~XMLHttpRequestConstructor() override;
+
+ virtual JS::Value call() override;
+ virtual JS::Value construct(JS::Function& new_target) override;
+
+private:
+ virtual bool has_constructor() const override { return true; }
+ virtual const char* class_name() const override { return "XMLHttpRequestConstructor"; }
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestPrototype.cpp b/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestPrototype.cpp
new file mode 100644
index 0000000000..2db588bc2c
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestPrototype.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/Function.h>
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibWeb/Bindings/XMLHttpRequestPrototype.h>
+#include <LibWeb/Bindings/XMLHttpRequestWrapper.h>
+#include <LibWeb/DOM/XMLHttpRequest.h>
+
+namespace Web::Bindings {
+
+XMLHttpRequestPrototype::XMLHttpRequestPrototype(JS::GlobalObject& global_object)
+ : Object(*global_object.object_prototype())
+{
+}
+
+void XMLHttpRequestPrototype::initialize(JS::GlobalObject& global_object)
+{
+ Object::initialize(global_object);
+ define_native_function("open", open, 2);
+ define_native_function("send", send, 0);
+ define_native_property("readyState", ready_state_getter, nullptr, JS::Attribute::Enumerable | JS::Attribute::Configurable);
+ define_native_property("responseText", response_text_getter, nullptr, JS::Attribute::Enumerable | JS::Attribute::Configurable);
+
+ define_property("UNSENT", JS::Value((i32)XMLHttpRequest::ReadyState::Unsent), JS::Attribute::Enumerable);
+ define_property("OPENED", JS::Value((i32)XMLHttpRequest::ReadyState::Opened), JS::Attribute::Enumerable);
+ define_property("HEADERS_RECEIVED", JS::Value((i32)XMLHttpRequest::ReadyState::HeadersReceived), JS::Attribute::Enumerable);
+ define_property("LOADING", JS::Value((i32)XMLHttpRequest::ReadyState::Loading), JS::Attribute::Enumerable);
+ define_property("DONE", JS::Value((i32)XMLHttpRequest::ReadyState::Done), JS::Attribute::Enumerable);
+}
+
+XMLHttpRequestPrototype::~XMLHttpRequestPrototype()
+{
+}
+
+static XMLHttpRequest* impl_from(JS::VM& vm, JS::GlobalObject& global_object)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return nullptr;
+ if (!is<XMLHttpRequestWrapper>(this_object)) {
+ vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "XMLHttpRequest");
+ return nullptr;
+ }
+ return &static_cast<XMLHttpRequestWrapper*>(this_object)->impl();
+}
+
+JS_DEFINE_NATIVE_FUNCTION(XMLHttpRequestPrototype::open)
+{
+ auto* impl = impl_from(vm, global_object);
+ if (!impl)
+ return {};
+ auto arg0 = vm.argument(0).to_string(global_object);
+ if (vm.exception())
+ return {};
+ auto arg1 = vm.argument(1).to_string(global_object);
+ if (vm.exception())
+ return {};
+ impl->open(arg0, arg1);
+ return JS::js_undefined();
+}
+
+JS_DEFINE_NATIVE_FUNCTION(XMLHttpRequestPrototype::send)
+{
+ auto* impl = impl_from(vm, global_object);
+ if (!impl)
+ return {};
+ impl->send();
+ return JS::js_undefined();
+}
+
+JS_DEFINE_NATIVE_GETTER(XMLHttpRequestPrototype::ready_state_getter)
+{
+ auto* impl = impl_from(vm, global_object);
+ if (!impl)
+ return {};
+ return JS::Value((i32)impl->ready_state());
+}
+
+JS_DEFINE_NATIVE_GETTER(XMLHttpRequestPrototype::response_text_getter)
+{
+ auto* impl = impl_from(vm, global_object);
+ if (!impl)
+ return {};
+ return JS::js_string(vm, impl->response_text());
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestPrototype.h b/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestPrototype.h
new file mode 100644
index 0000000000..7b534b3b00
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestPrototype.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibJS/Runtime/Object.h>
+
+namespace Web::Bindings {
+
+class XMLHttpRequestPrototype final : public JS::Object {
+ JS_OBJECT(XMLHttpRequestPrototype, JS::Object);
+
+public:
+ explicit XMLHttpRequestPrototype(JS::GlobalObject&);
+ virtual void initialize(JS::GlobalObject&) override;
+ virtual ~XMLHttpRequestPrototype() override;
+
+private:
+ JS_DECLARE_NATIVE_FUNCTION(open);
+ JS_DECLARE_NATIVE_FUNCTION(send);
+
+ JS_DECLARE_NATIVE_GETTER(ready_state_getter);
+ JS_DECLARE_NATIVE_GETTER(response_text_getter);
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestWrapper.cpp b/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestWrapper.cpp
new file mode 100644
index 0000000000..dedd97313a
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestWrapper.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/FlyString.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/Value.h>
+#include <LibWeb/Bindings/WindowObject.h>
+#include <LibWeb/Bindings/XMLHttpRequestPrototype.h>
+#include <LibWeb/Bindings/XMLHttpRequestWrapper.h>
+#include <LibWeb/DOM/XMLHttpRequest.h>
+
+namespace Web::Bindings {
+
+XMLHttpRequestWrapper* wrap(JS::GlobalObject& global_object, XMLHttpRequest& impl)
+{
+ return static_cast<XMLHttpRequestWrapper*>(wrap_impl(global_object, impl));
+}
+
+XMLHttpRequestWrapper::XMLHttpRequestWrapper(JS::GlobalObject& global_object, XMLHttpRequest& impl)
+ : EventTargetWrapper(global_object, impl)
+{
+ set_prototype(static_cast<WindowObject&>(global_object).xhr_prototype());
+}
+
+XMLHttpRequestWrapper::~XMLHttpRequestWrapper()
+{
+}
+
+XMLHttpRequest& XMLHttpRequestWrapper::impl()
+{
+ return static_cast<XMLHttpRequest&>(EventTargetWrapper::impl());
+}
+
+const XMLHttpRequest& XMLHttpRequestWrapper::impl() const
+{
+ return static_cast<const XMLHttpRequest&>(EventTargetWrapper::impl());
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestWrapper.h b/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestWrapper.h
new file mode 100644
index 0000000000..a961564b60
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Bindings/XMLHttpRequestWrapper.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/Bindings/EventTargetWrapper.h>
+
+namespace Web::Bindings {
+
+class XMLHttpRequestWrapper final : public EventTargetWrapper {
+public:
+ XMLHttpRequestWrapper(JS::GlobalObject&, XMLHttpRequest&);
+ virtual ~XMLHttpRequestWrapper() override;
+
+ XMLHttpRequest& impl();
+ const XMLHttpRequest& impl() const;
+
+private:
+ virtual const char* class_name() const override { return "XMLHttpRequestWrapper"; }
+};
+
+XMLHttpRequestWrapper* wrap(JS::GlobalObject&, XMLHttpRequest&);
+
+}
diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt
new file mode 100644
index 0000000000..4e4abc1551
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CMakeLists.txt
@@ -0,0 +1,399 @@
+set(SOURCES
+ Bindings/EventListenerWrapper.cpp
+ Bindings/EventWrapperFactory.cpp
+ Bindings/EventTargetWrapperFactory.cpp
+ Bindings/LocationObject.cpp
+ Bindings/NavigatorObject.cpp
+ Bindings/NodeWrapperFactory.cpp
+ Bindings/ScriptExecutionContext.cpp
+ Bindings/WindowObject.cpp
+ Bindings/Wrappable.cpp
+ Bindings/XMLHttpRequestConstructor.cpp
+ Bindings/XMLHttpRequestPrototype.cpp
+ Bindings/XMLHttpRequestWrapper.cpp
+ Bindings/RangeConstructor.cpp
+ Bindings/RangePrototype.cpp
+ Bindings/RangeWrapper.cpp
+ CSS/DefaultStyleSheetSource.cpp
+ CSS/Length.cpp
+ CSS/Parser/CSSParser.cpp
+ CSS/PropertyID.cpp
+ CSS/PropertyID.h
+ CSS/QuirksModeStyleSheetSource.cpp
+ CSS/Selector.cpp
+ CSS/SelectorEngine.cpp
+ CSS/StyleDeclaration.cpp
+ CSS/StyleInvalidator.cpp
+ CSS/StyleProperties.cpp
+ CSS/StyleResolver.cpp
+ CSS/StyleRule.cpp
+ CSS/StyleSheet.cpp
+ CSS/StyleSheetList.cpp
+ CSS/StyleValue.cpp
+ CSS/ValueID.cpp
+ CSS/ValueID.h
+ DOM/CharacterData.cpp
+ DOM/CharacterData.idl
+ DOM/Comment.cpp
+ DOM/Document.cpp
+ DOM/DocumentFragment.cpp
+ DOM/DocumentType.cpp
+ DOM/DOMImplementation.cpp
+ DOM/Element.cpp
+ DOM/ElementFactory.cpp
+ DOM/Event.cpp
+ DOM/Range.cpp
+ DOM/EventDispatcher.cpp
+ DOM/EventListener.cpp
+ DOM/EventTarget.cpp
+ DOM/Node.cpp
+ DOM/ParentNode.cpp
+ DOM/Position.cpp
+ DOM/ShadowRoot.cpp
+ DOM/Text.cpp
+ DOM/Text.idl
+ DOM/Timer.cpp
+ DOM/Window.cpp
+ DOM/XMLHttpRequest.cpp
+ DOMTreeModel.cpp
+ Dump.cpp
+ FontCache.cpp
+ HTML/AttributeNames.cpp
+ HTML/CanvasRenderingContext2D.cpp
+ HTML/EventNames.cpp
+ HTML/HTMLAnchorElement.cpp
+ HTML/HTMLAreaElement.cpp
+ HTML/HTMLAudioElement.cpp
+ HTML/HTMLBRElement.cpp
+ HTML/HTMLBaseElement.cpp
+ HTML/HTMLBlinkElement.cpp
+ HTML/HTMLBodyElement.cpp
+ HTML/HTMLButtonElement.cpp
+ HTML/HTMLCanvasElement.cpp
+ HTML/HTMLDListElement.cpp
+ HTML/HTMLDataElement.cpp
+ HTML/HTMLDataListElement.cpp
+ HTML/HTMLDetailsElement.cpp
+ HTML/HTMLDialogElement.cpp
+ HTML/HTMLDirectoryElement.cpp
+ HTML/HTMLDivElement.cpp
+ HTML/HTMLElement.cpp
+ HTML/HTMLEmbedElement.cpp
+ HTML/HTMLFieldSetElement.cpp
+ HTML/HTMLFontElement.cpp
+ HTML/HTMLFormElement.cpp
+ HTML/HTMLFrameElement.cpp
+ HTML/HTMLFrameSetElement.cpp
+ HTML/HTMLHRElement.cpp
+ HTML/HTMLHeadElement.cpp
+ HTML/HTMLHeadingElement.cpp
+ HTML/HTMLHtmlElement.cpp
+ HTML/HTMLIFrameElement.cpp
+ HTML/HTMLImageElement.cpp
+ HTML/HTMLInputElement.cpp
+ HTML/HTMLLIElement.cpp
+ HTML/HTMLLabelElement.cpp
+ HTML/HTMLLegendElement.cpp
+ HTML/HTMLLinkElement.cpp
+ HTML/HTMLMapElement.cpp
+ HTML/HTMLMarqueeElement.cpp
+ HTML/HTMLMediaElement.cpp
+ HTML/HTMLMenuElement.cpp
+ HTML/HTMLMetaElement.cpp
+ HTML/HTMLMeterElement.cpp
+ HTML/HTMLModElement.cpp
+ HTML/HTMLOListElement.cpp
+ HTML/HTMLObjectElement.cpp
+ HTML/HTMLOptGroupElement.cpp
+ HTML/HTMLOptionElement.cpp
+ HTML/HTMLOutputElement.cpp
+ HTML/HTMLParagraphElement.cpp
+ HTML/HTMLParamElement.cpp
+ HTML/HTMLPictureElement.cpp
+ HTML/HTMLPreElement.cpp
+ HTML/HTMLProgressElement.cpp
+ HTML/HTMLQuoteElement.cpp
+ HTML/HTMLScriptElement.cpp
+ HTML/HTMLSelectElement.cpp
+ HTML/HTMLSlotElement.cpp
+ HTML/HTMLSourceElement.cpp
+ HTML/HTMLSpanElement.cpp
+ HTML/HTMLStyleElement.cpp
+ HTML/HTMLTableCaptionElement.cpp
+ HTML/HTMLTableCellElement.cpp
+ HTML/HTMLTableColElement.cpp
+ HTML/HTMLTableElement.cpp
+ HTML/HTMLTableRowElement.cpp
+ HTML/HTMLTableSectionElement.cpp
+ HTML/HTMLTemplateElement.cpp
+ HTML/HTMLTextAreaElement.cpp
+ HTML/HTMLTimeElement.cpp
+ HTML/HTMLTitleElement.cpp
+ HTML/HTMLTrackElement.cpp
+ HTML/HTMLUListElement.cpp
+ HTML/HTMLUnknownElement.cpp
+ HTML/HTMLVideoElement.cpp
+ HTML/ImageData.cpp
+ HTML/Parser/Entities.cpp
+ HTML/Parser/HTMLDocumentParser.cpp
+ HTML/Parser/HTMLToken.cpp
+ HTML/Parser/HTMLTokenizer.cpp
+ HTML/Parser/ListOfActiveFormattingElements.cpp
+ HTML/Parser/StackOfOpenElements.cpp
+ HTML/TagNames.cpp
+ HighResolutionTime/Performance.cpp
+ InProcessWebView.cpp
+ Layout/BlockBox.cpp
+ Layout/BlockFormattingContext.cpp
+ Layout/Box.cpp
+ Layout/BoxModelMetrics.cpp
+ Layout/BreakNode.cpp
+ Layout/ButtonBox.cpp
+ Layout/CanvasBox.cpp
+ Layout/CheckBox.cpp
+ Layout/FormattingContext.cpp
+ Layout/FrameBox.cpp
+ Layout/ImageBox.cpp
+ Layout/InitialContainingBlockBox.cpp
+ Layout/InlineFormattingContext.cpp
+ Layout/InlineNode.cpp
+ Layout/LayoutPosition.cpp
+ Layout/LineBox.cpp
+ Layout/LineBoxFragment.cpp
+ Layout/ListItemBox.cpp
+ Layout/ListItemMarkerBox.cpp
+ Layout/Node.cpp
+ Layout/ReplacedBox.cpp
+ Layout/SVGBox.cpp
+ Layout/SVGGraphicsBox.cpp
+ Layout/SVGPathBox.cpp
+ Layout/SVGSVGBox.cpp
+ Layout/TableBox.cpp
+ Layout/TableCellBox.cpp
+ Layout/TableFormattingContext.cpp
+ Layout/TableRowBox.cpp
+ Layout/TableRowGroupBox.cpp
+ Layout/TextNode.cpp
+ Layout/TreeBuilder.cpp
+ Layout/WidgetBox.cpp
+ LayoutTreeModel.cpp
+ Loader/ContentFilter.cpp
+ Loader/FrameLoader.cpp
+ Loader/ImageLoader.cpp
+ Loader/ImageResource.cpp
+ Loader/Resource.cpp
+ Loader/ResourceLoader.cpp
+ Namespace.cpp
+ OutOfProcessWebView.cpp
+ Page/EventHandler.cpp
+ Page/EditEventHandler.cpp
+ Page/Frame.cpp
+ Page/Page.cpp
+ Painting/BorderPainting.cpp
+ Painting/StackingContext.cpp
+ SVG/SVGElement.cpp
+ SVG/SVGGeometryElement.cpp
+ SVG/SVGGraphicsElement.cpp
+ SVG/SVGPathElement.cpp
+ SVG/SVGSVGElement.cpp
+ SVG/TagNames.cpp
+ StylePropertiesModel.cpp
+ UIEvents/EventNames.cpp
+ UIEvents/MouseEvent.cpp
+ URLEncoder.cpp
+ WebContentClient.cpp
+)
+
+set(GENERATED_SOURCES
+ ../../Services/ProtocolServer/ProtocolClientEndpoint.h
+ ../../Services/ProtocolServer/ProtocolServerEndpoint.h
+ ../../Services/WebContent/WebContentClientEndpoint.h
+ ../../Services/WebContent/WebContentServerEndpoint.h
+)
+
+set_property(GLOBAL PROPERTY wrapper_sources)
+function(add_wrapper_sources)
+ get_property(tmp GLOBAL PROPERTY wrapper_sources)
+ foreach(arg ${ARGV})
+ set(tmp ${tmp}
+ ${arg}
+ )
+ endforeach()
+ set_property(GLOBAL PROPERTY wrapper_sources "${tmp}")
+endfunction(add_wrapper_sources)
+
+function(libweb_js_wrapper class)
+ get_filename_component(basename ${class} NAME)
+ add_wrapper_sources(Bindings/${basename}Wrapper.cpp Bindings/${basename}Wrapper.h)
+ add_custom_command(
+ OUTPUT Bindings/${basename}Wrapper.h
+ COMMAND ${write_if_different} Bindings/${basename}Wrapper.h CodeGenerators/WrapperGenerator --header ${CMAKE_CURRENT_SOURCE_DIR}/${class}.idl
+ VERBATIM
+ DEPENDS WrapperGenerator
+ MAIN_DEPENDENCY ${class}.idl
+ )
+ add_custom_command(
+ OUTPUT Bindings/${basename}Wrapper.cpp
+ COMMAND ${write_if_different} Bindings/${basename}Wrapper.cpp CodeGenerators/WrapperGenerator --implementation ${CMAKE_CURRENT_SOURCE_DIR}/${class}.idl
+ VERBATIM
+ DEPENDS WrapperGenerator
+ MAIN_DEPENDENCY ${class}.idl
+ )
+ add_custom_target(generate_${basename}Wrapper.h DEPENDS Bindings/${class}Wrapper.h)
+ add_custom_target(generate_${basename}Wrapper.cpp DEPENDS Bindings/${class}Wrapper.cpp)
+endfunction()
+
+libweb_js_wrapper(DOM/CharacterData)
+libweb_js_wrapper(DOM/Comment)
+libweb_js_wrapper(DOM/Document)
+libweb_js_wrapper(DOM/DocumentFragment)
+libweb_js_wrapper(DOM/DocumentType)
+libweb_js_wrapper(DOM/DOMImplementation)
+libweb_js_wrapper(DOM/Element)
+libweb_js_wrapper(DOM/Event)
+libweb_js_wrapper(DOM/EventTarget)
+libweb_js_wrapper(DOM/ShadowRoot)
+libweb_js_wrapper(DOM/Node)
+libweb_js_wrapper(DOM/Text)
+libweb_js_wrapper(HTML/CanvasRenderingContext2D)
+libweb_js_wrapper(HTML/HTMLAnchorElement)
+libweb_js_wrapper(HTML/HTMLAreaElement)
+libweb_js_wrapper(HTML/HTMLAudioElement)
+libweb_js_wrapper(HTML/HTMLBaseElement)
+libweb_js_wrapper(HTML/HTMLBodyElement)
+libweb_js_wrapper(HTML/HTMLBRElement)
+libweb_js_wrapper(HTML/HTMLButtonElement)
+libweb_js_wrapper(HTML/HTMLCanvasElement)
+libweb_js_wrapper(HTML/HTMLDataElement)
+libweb_js_wrapper(HTML/HTMLDataListElement)
+libweb_js_wrapper(HTML/HTMLDetailsElement)
+libweb_js_wrapper(HTML/HTMLDialogElement)
+libweb_js_wrapper(HTML/HTMLDirectoryElement)
+libweb_js_wrapper(HTML/HTMLDivElement)
+libweb_js_wrapper(HTML/HTMLDListElement)
+libweb_js_wrapper(HTML/HTMLElement)
+libweb_js_wrapper(HTML/HTMLEmbedElement)
+libweb_js_wrapper(HTML/HTMLFieldSetElement)
+libweb_js_wrapper(HTML/HTMLFontElement)
+libweb_js_wrapper(HTML/HTMLFormElement)
+libweb_js_wrapper(HTML/HTMLFrameElement)
+libweb_js_wrapper(HTML/HTMLFrameSetElement)
+libweb_js_wrapper(HTML/HTMLHeadElement)
+libweb_js_wrapper(HTML/HTMLHeadingElement)
+libweb_js_wrapper(HTML/HTMLHRElement)
+libweb_js_wrapper(HTML/HTMLHtmlElement)
+libweb_js_wrapper(HTML/HTMLIFrameElement)
+libweb_js_wrapper(HTML/HTMLImageElement)
+libweb_js_wrapper(HTML/HTMLInputElement)
+libweb_js_wrapper(HTML/HTMLLabelElement)
+libweb_js_wrapper(HTML/HTMLLegendElement)
+libweb_js_wrapper(HTML/HTMLLIElement)
+libweb_js_wrapper(HTML/HTMLLinkElement)
+libweb_js_wrapper(HTML/HTMLMapElement)
+libweb_js_wrapper(HTML/HTMLMarqueeElement)
+libweb_js_wrapper(HTML/HTMLMediaElement)
+libweb_js_wrapper(HTML/HTMLMenuElement)
+libweb_js_wrapper(HTML/HTMLMetaElement)
+libweb_js_wrapper(HTML/HTMLMeterElement)
+libweb_js_wrapper(HTML/HTMLModElement)
+libweb_js_wrapper(HTML/HTMLObjectElement)
+libweb_js_wrapper(HTML/HTMLOListElement)
+libweb_js_wrapper(HTML/HTMLOptGroupElement)
+libweb_js_wrapper(HTML/HTMLOptionElement)
+libweb_js_wrapper(HTML/HTMLOutputElement)
+libweb_js_wrapper(HTML/HTMLParagraphElement)
+libweb_js_wrapper(HTML/HTMLParamElement)
+libweb_js_wrapper(HTML/HTMLPictureElement)
+libweb_js_wrapper(HTML/HTMLPreElement)
+libweb_js_wrapper(HTML/HTMLProgressElement)
+libweb_js_wrapper(HTML/HTMLQuoteElement)
+libweb_js_wrapper(HTML/HTMLScriptElement)
+libweb_js_wrapper(HTML/HTMLSelectElement)
+libweb_js_wrapper(HTML/HTMLSlotElement)
+libweb_js_wrapper(HTML/HTMLSourceElement)
+libweb_js_wrapper(HTML/HTMLSpanElement)
+libweb_js_wrapper(HTML/HTMLStyleElement)
+libweb_js_wrapper(HTML/HTMLTableCaptionElement)
+libweb_js_wrapper(HTML/HTMLTableCellElement)
+libweb_js_wrapper(HTML/HTMLTableColElement)
+libweb_js_wrapper(HTML/HTMLTableElement)
+libweb_js_wrapper(HTML/HTMLTableRowElement)
+libweb_js_wrapper(HTML/HTMLTableSectionElement)
+libweb_js_wrapper(HTML/HTMLTemplateElement)
+libweb_js_wrapper(HTML/HTMLTextAreaElement)
+libweb_js_wrapper(HTML/HTMLTimeElement)
+libweb_js_wrapper(HTML/HTMLTitleElement)
+libweb_js_wrapper(HTML/HTMLTrackElement)
+libweb_js_wrapper(HTML/HTMLUListElement)
+libweb_js_wrapper(HTML/HTMLUnknownElement)
+libweb_js_wrapper(HTML/HTMLVideoElement)
+libweb_js_wrapper(HTML/ImageData)
+libweb_js_wrapper(HTML/SubmitEvent)
+libweb_js_wrapper(HighResolutionTime/Performance)
+libweb_js_wrapper(SVG/SVGElement)
+libweb_js_wrapper(SVG/SVGGeometryElement)
+libweb_js_wrapper(SVG/SVGGraphicsElement)
+libweb_js_wrapper(SVG/SVGPathElement)
+libweb_js_wrapper(SVG/SVGSVGElement)
+libweb_js_wrapper(UIEvents/MouseEvent)
+libweb_js_wrapper(UIEvents/UIEvent)
+
+get_property(WRAPPER_SOURCES GLOBAL PROPERTY wrapper_sources)
+set(SOURCES ${SOURCES} ${WRAPPER_SOURCES})
+
+add_custom_command(
+ OUTPUT CSS/PropertyID.h
+ COMMAND ${write_if_different} CSS/PropertyID.h CodeGenerators/Generate_CSS_PropertyID_h ${CMAKE_CURRENT_SOURCE_DIR}/CSS/Properties.json
+ VERBATIM
+ DEPENDS Generate_CSS_PropertyID_h
+ MAIN_DEPENDENCY CSS/Properties.json
+)
+add_custom_target(generate_PropertyID.h DEPENDS CSS/PropertyID.h)
+
+add_custom_command(
+ OUTPUT CSS/PropertyID.cpp
+ COMMAND /bin/mkdir -p CSS
+ COMMAND ${write_if_different} CSS/PropertyID.cpp CodeGenerators/Generate_CSS_PropertyID_cpp ${CMAKE_CURRENT_SOURCE_DIR}/CSS/Properties.json
+ VERBATIM
+ DEPENDS Generate_CSS_PropertyID_cpp
+ MAIN_DEPENDENCY CSS/Properties.json
+)
+
+add_custom_command(
+ OUTPUT CSS/ValueID.h
+ COMMAND ${write_if_different} CSS/ValueID.h CodeGenerators/Generate_CSS_ValueID_h ${CMAKE_CURRENT_SOURCE_DIR}/CSS/Identifiers.json
+ VERBATIM
+ DEPENDS Generate_CSS_ValueID_h
+ MAIN_DEPENDENCY CSS/Identifiers.json
+)
+add_custom_target(generate_ValueID.h DEPENDS CSS/ValueID.h)
+
+add_custom_command(
+ OUTPUT CSS/ValueID.cpp
+ COMMAND /bin/mkdir -p CSS
+ COMMAND ${write_if_different} CSS/ValueID.cpp CodeGenerators/Generate_CSS_ValueID_cpp ${CMAKE_CURRENT_SOURCE_DIR}/CSS/Identifiers.json
+ VERBATIM
+ DEPENDS Generate_CSS_ValueID_cpp
+ MAIN_DEPENDENCY CSS/Identifiers.json
+)
+
+add_custom_command(
+ OUTPUT CSS/DefaultStyleSheetSource.cpp
+ COMMAND ${write_if_different} CSS/DefaultStyleSheetSource.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Scripts/GenerateStyleSheetSource.sh default_stylesheet_source ${CMAKE_CURRENT_SOURCE_DIR}/CSS/Default.css
+ VERBATIM
+ DEPENDS Scripts/GenerateStyleSheetSource.sh
+ MAIN_DEPENDENCY CSS/Default.css
+)
+
+add_custom_command(
+ OUTPUT CSS/QuirksModeStyleSheetSource.cpp
+ COMMAND ${write_if_different} CSS/QuirksModeStyleSheetSource.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Scripts/GenerateStyleSheetSource.sh quirks_mode_stylesheet_source ${CMAKE_CURRENT_SOURCE_DIR}/CSS/QuirksMode.css
+ VERBATIM
+ DEPENDS Scripts/GenerateStyleSheetSource.sh
+ MAIN_DEPENDENCY CSS/Default.css
+)
+
+serenity_lib(LibWeb web)
+target_link_libraries(LibWeb LibCore LibJS LibMarkdown LibGemini LibGUI LibGfx LibTextCodec LibProtocol LibImageDecoderClient)
+
+add_subdirectory(DumpLayoutTree)
diff --git a/Userland/Libraries/LibWeb/CSS/.gitignore b/Userland/Libraries/LibWeb/CSS/.gitignore
new file mode 100644
index 0000000000..ae00c71e95
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CSS/.gitignore
@@ -0,0 +1,3 @@
+DefaultStyleSheetSource.cpp
+PropertyID.cpp
+PropertyID.h
diff --git a/Userland/Libraries/LibWeb/CSS/ComputedValues.h b/Userland/Libraries/LibWeb/CSS/ComputedValues.h
new file mode 100644
index 0000000000..57866b07f4
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CSS/ComputedValues.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/Optional.h>
+#include <LibWeb/CSS/LengthBox.h>
+#include <LibWeb/CSS/StyleValue.h>
+
+namespace Web::CSS {
+
+class InitialValues {
+public:
+ static CSS::Float float_() { return CSS::Float::None; }
+ static CSS::Clear clear() { return CSS::Clear::None; }
+ static CSS::WhiteSpace white_space() { return CSS::WhiteSpace::Normal; }
+ static CSS::TextAlign text_align() { return CSS::TextAlign::Left; }
+ static CSS::Position position() { return CSS::Position::Static; }
+ static CSS::TextDecorationLine text_decoration_line() { return CSS::TextDecorationLine::None; }
+ static CSS::TextTransform text_transform() { return CSS::TextTransform::None; }
+ static CSS::Display display() { return CSS::Display::Inline; }
+ static Color color() { return Color::Black; }
+ static Color background_color() { return Color::Transparent; }
+ static CSS::ListStyleType list_style_type() { return CSS::ListStyleType::Disc; }
+};
+
+struct BorderData {
+public:
+ Color color { Color::Transparent };
+ CSS::LineStyle line_style { CSS::LineStyle::None };
+ float width { 0 };
+};
+
+class ComputedValues {
+public:
+ CSS::Float float_() const { return m_noninherited.float_; }
+ CSS::Clear clear() const { return m_noninherited.clear; }
+ CSS::Display display() const { return m_noninherited.display; }
+ Optional<int> z_index() const { return m_noninherited.z_index; }
+ CSS::TextAlign text_align() const { return m_inherited.text_align; }
+ CSS::TextDecorationLine text_decoration_line() const { return m_noninherited.text_decoration_line; }
+ CSS::TextTransform text_transform() const { return m_inherited.text_transform; }
+ CSS::Position position() const { return m_noninherited.position; }
+ CSS::WhiteSpace white_space() const { return m_inherited.white_space; }
+ const CSS::Length& width() const { return m_noninherited.width; }
+ const CSS::Length& min_width() const { return m_noninherited.min_width; }
+ const CSS::Length& max_width() const { return m_noninherited.max_width; }
+ const CSS::Length& height() const { return m_noninherited.height; }
+ const CSS::Length& min_height() const { return m_noninherited.min_height; }
+ const CSS::Length& max_height() const { return m_noninherited.max_height; }
+
+ const CSS::LengthBox& offset() const { return m_noninherited.offset; }
+ const CSS::LengthBox& margin() const { return m_noninherited.margin; }
+ const CSS::LengthBox& padding() const { return m_noninherited.padding; }
+
+ const BorderData& border_left() const { return m_noninherited.border_left; }
+ const BorderData& border_top() const { return m_noninherited.border_top; }
+ const BorderData& border_right() const { return m_noninherited.border_right; }
+ const BorderData& border_bottom() const { return m_noninherited.border_bottom; }
+
+ Color color() const { return m_inherited.color; }
+ Color background_color() const { return m_noninherited.background_color; }
+
+ CSS::ListStyleType list_style_type() const { return m_inherited.list_style_type; }
+
+ ComputedValues clone_inherited_values() const
+ {
+ ComputedValues clone;
+ clone.m_inherited = m_inherited;
+ return clone;
+ }
+
+protected:
+ struct {
+ Color color { InitialValues::color() };
+ CSS::TextAlign text_align { InitialValues::text_align() };
+ CSS::TextTransform text_transform { InitialValues::text_transform() };
+ CSS::WhiteSpace white_space { InitialValues::white_space() };
+ CSS::ListStyleType list_style_type { InitialValues::list_style_type() };
+ } m_inherited;
+
+ struct {
+ CSS::Float float_ { InitialValues::float_() };
+ CSS::Clear clear { InitialValues::clear() };
+ CSS::Display display { InitialValues::display() };
+ Optional<int> z_index;
+ CSS::TextDecorationLine text_decoration_line { InitialValues::text_decoration_line() };
+ CSS::Position position { InitialValues::position() };
+ CSS::Length width;
+ CSS::Length min_width;
+ CSS::Length max_width;
+ CSS::Length height;
+ CSS::Length min_height;
+ CSS::Length max_height;
+ CSS::LengthBox offset;
+ CSS::LengthBox margin;
+ CSS::LengthBox padding;
+ BorderData border_left;
+ BorderData border_top;
+ BorderData border_right;
+ BorderData border_bottom;
+ Color background_color { InitialValues::background_color() };
+ } m_noninherited;
+};
+
+class ImmutableComputedValues final : public ComputedValues {
+};
+
+class MutableComputedValues final : public ComputedValues {
+public:
+ void set_color(const Color& color) { m_inherited.color = color; }
+ void set_background_color(const Color& color) { m_noninherited.background_color = color; }
+ void set_float(CSS::Float value) { m_noninherited.float_ = value; }
+ void set_clear(CSS::Clear value) { m_noninherited.clear = value; }
+ void set_z_index(Optional<int> value) { m_noninherited.z_index = value; }
+ void set_text_align(CSS::TextAlign text_align) { m_inherited.text_align = text_align; }
+ void set_text_decoration_line(CSS::TextDecorationLine value) { m_noninherited.text_decoration_line = value; }
+ void set_text_transform(CSS::TextTransform value) { m_inherited.text_transform = value; }
+ void set_position(CSS::Position position) { m_noninherited.position = position; }
+ void set_white_space(CSS::WhiteSpace value) { m_inherited.white_space = value; }
+ void set_width(const CSS::Length& width) { m_noninherited.width = width; }
+ void set_min_width(const CSS::Length& width) { m_noninherited.min_width = width; }
+ void set_max_width(const CSS::Length& width) { m_noninherited.max_width = width; }
+ void set_height(const CSS::Length& height) { m_noninherited.height = height; }
+ void set_min_height(const CSS::Length& height) { m_noninherited.min_height = height; }
+ void set_max_height(const CSS::Length& height) { m_noninherited.max_height = height; }
+ void set_offset(const CSS::LengthBox& offset) { m_noninherited.offset = offset; }
+ void set_margin(const CSS::LengthBox& margin) { m_noninherited.margin = margin; }
+ void set_padding(const CSS::LengthBox& padding) { m_noninherited.padding = padding; }
+ void set_list_style_type(CSS::ListStyleType value) { m_inherited.list_style_type = value; }
+ void set_display(CSS::Display value) { m_noninherited.display = value; }
+ BorderData& border_left() { return m_noninherited.border_left; }
+ BorderData& border_top() { return m_noninherited.border_top; }
+ BorderData& border_right() { return m_noninherited.border_right; }
+ BorderData& border_bottom() { return m_noninherited.border_bottom; }
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/CSS/Default.css b/Userland/Libraries/LibWeb/CSS/Default.css
new file mode 100644
index 0000000000..a63c7873ef
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CSS/Default.css
@@ -0,0 +1,196 @@
+html {
+ font-family: sans-serif;
+}
+
+head,
+link,
+meta,
+script,
+style,
+title {
+ display: none;
+}
+
+body {
+ margin: 8px;
+}
+
+h1,
+h2 {
+ font-family: Pebbleton;
+ font-size: 14px;
+ font-weight: bold;
+}
+
+h3,
+h4,
+h5,
+h6 {
+ font-weight: bold;
+}
+
+pre {
+ font-family: monospace;
+ margin-bottom: 8px;
+ margin-top: 8px;
+ white-space: pre;
+}
+
+code {
+ font-family: monospace;
+}
+
+u,
+ins {
+ text-decoration: underline;
+}
+
+strong,
+b {
+ font-weight: bold;
+}
+
+html,
+address,
+blockquote,
+body,
+dd,
+div,
+dl,
+dt,
+fieldset,
+form,
+frame,
+frameset,
+hgroup,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+noframes,
+ol,
+p,
+ul,
+center,
+dir,
+hr,
+menu,
+pre,
+header,
+footer,
+nav,
+main,
+article,
+aside,
+section {
+ display: block;
+}
+
+center {
+ text-align: -libweb-center;
+}
+
+h1,
+h2,
+h3 {
+ margin: 8px 0 8px 0;
+}
+
+h4,
+p,
+blockquote,
+ul,
+fieldset,
+form,
+ol,
+dl,
+dir,
+menu {
+ margin: 4px 0 4px 0;
+}
+
+h5,
+h6 {
+ margin: 2px 0 2px 0;
+}
+
+li {
+ display: list-item;
+ margin-left: 8px;
+ margin-top: 2px;
+ margin-bottom: 2px;
+}
+
+a:link {
+ color: -libweb-link;
+ text-decoration: underline;
+}
+
+a:hover {
+ color: red;
+}
+
+hr {
+ margin-top: 0.5em;
+ margin-bottom: 0.5em;
+ border: 1px inset #888888;
+}
+
+blink {
+ display: inline;
+}
+
+table {
+ display: table;
+}
+
+thead {
+ display: table-header-group;
+ vertical-align: middle;
+ border-color: inherit;
+}
+
+tbody {
+ display: table-row-group;
+ vertical-align: middle;
+ border-color: inherit;
+}
+
+tfoot {
+ display: table-footer-group;
+ vertical-align: middle;
+ border-color: inherit;
+}
+
+tr {
+ display: table-row;
+}
+
+td,
+th {
+ display: table-cell;
+}
+
+col {
+ display: table-column;
+}
+
+colgroup {
+ display: table-column-group;
+}
+
+basefont {
+ display: block;
+}
+
+blockquote {
+ margin-left: 25px;
+ margin-right: 25px;
+}
+
+ul,
+ol {
+ padding-left: 20px;
+}
diff --git a/Userland/Libraries/LibWeb/CSS/Identifiers.json b/Userland/Libraries/LibWeb/CSS/Identifiers.json
new file mode 100644
index 0000000000..5e412147fe
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CSS/Identifiers.json
@@ -0,0 +1,122 @@
+[
+ "-libweb-center",
+ "-libweb-link",
+ "-libweb-palette-active-link",
+ "-libweb-palette-active-window-border1",
+ "-libweb-palette-active-window-border2",
+ "-libweb-palette-active-window-title",
+ "-libweb-palette-base",
+ "-libweb-palette-base-text",
+ "-libweb-palette-button",
+ "-libweb-palette-button-text",
+ "-libweb-palette-desktop-background",
+ "-libweb-palette-focus-outline",
+ "-libweb-palette-highlight-window-border1",
+ "-libweb-palette-highlight-window-border2",
+ "-libweb-palette-highlight-window-title",
+ "-libweb-palette-hover-highlight",
+ "-libweb-palette-inactive-selection",
+ "-libweb-palette-inactive-selection-text",
+ "-libweb-palette-inactive-window-border1",
+ "-libweb-palette-inactive-window-border2",
+ "-libweb-palette-inactive-window-title",
+ "-libweb-palette-link",
+ "-libweb-palette-menu-base",
+ "-libweb-palette-menu-base-text",
+ "-libweb-palette-menu-selection",
+ "-libweb-palette-menu-selection-text",
+ "-libweb-palette-menu-stripe",
+ "-libweb-palette-moving-window-border1",
+ "-libweb-palette-moving-window-border2",
+ "-libweb-palette-moving-window-title",
+ "-libweb-palette-rubber-band-border",
+ "-libweb-palette-rubber-band-fill",
+ "-libweb-palette-ruler",
+ "-libweb-palette-ruler-active-text",
+ "-libweb-palette-ruler-border",
+ "-libweb-palette-ruler-inactive-text",
+ "-libweb-palette-selection",
+ "-libweb-palette-selection-text",
+ "-libweb-palette-syntax-comment",
+ "-libweb-palette-syntax-control-keyword",
+ "-libweb-palette-syntax-identifier",
+ "-libweb-palette-syntax-keyword",
+ "-libweb-palette-syntax-number",
+ "-libweb-palette-syntax-operator",
+ "-libweb-palette-syntax-preprocessor-statement",
+ "-libweb-palette-syntax-preprocessor-value",
+ "-libweb-palette-syntax-punctuation",
+ "-libweb-palette-syntax-string",
+ "-libweb-palette-syntax-type",
+ "-libweb-palette-text-cursor",
+ "-libweb-palette-threed-highlight",
+ "-libweb-palette-threed-shadow1",
+ "-libweb-palette-threed-shadow2",
+ "-libweb-palette-visited-link",
+ "-libweb-palette-window",
+ "-libweb-palette-window-text",
+ "absolute",
+ "blink",
+ "block",
+ "bold",
+ "bolder",
+ "both",
+ "capitalize",
+ "center",
+ "circle",
+ "dashed",
+ "decimal",
+ "disc",
+ "dotted",
+ "double",
+ "fixed",
+ "full-size-kana",
+ "full-width",
+ "groove",
+ "hidden",
+ "inline",
+ "inline-block",
+ "inset",
+ "justify",
+ "large",
+ "larger",
+ "left",
+ "lighter",
+ "line-through",
+ "list-item",
+ "lowercase",
+ "medium",
+ "none",
+ "normal",
+ "nowrap",
+ "outset",
+ "overline",
+ "pre",
+ "pre-line",
+ "pre-wrap",
+ "relative",
+ "ridge",
+ "right",
+ "small",
+ "smaller",
+ "solid",
+ "square",
+ "static",
+ "sticky",
+ "table",
+ "table-caption",
+ "table-cell",
+ "table-column",
+ "table-column-group",
+ "table-footer-group",
+ "table-header-group",
+ "table-row",
+ "table-row-group",
+ "underline",
+ "uppercase",
+ "x-large",
+ "x-small",
+ "xx-large",
+ "xx-small",
+ "xxx-large"
+]
diff --git a/Userland/Libraries/LibWeb/CSS/Length.cpp b/Userland/Libraries/LibWeb/CSS/Length.cpp
new file mode 100644
index 0000000000..08b2ca500f
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CSS/Length.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/CSS/Length.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/HTML/HTMLHtmlElement.h>
+#include <LibWeb/Page/Frame.h>
+
+namespace Web::CSS {
+
+float Length::relative_length_to_px(const Layout::Node& layout_node) const
+{
+ switch (m_type) {
+ case Type::Ex:
+ return m_value * layout_node.font().x_height();
+ case Type::Em:
+ return m_value * layout_node.font_size();
+ case Type::Rem:
+ return m_value * layout_node.document().document_element()->layout_node()->font_size();
+ case Type::Vw:
+ return layout_node.document().frame()->viewport_rect().width() * (m_value / 100);
+ case Type::Vh:
+ return layout_node.document().frame()->viewport_rect().height() * (m_value / 100);
+ case Type::Vmin: {
+ auto viewport = layout_node.document().frame()->viewport_rect();
+
+ return min(viewport.width(), viewport.height()) * (m_value / 100);
+ }
+ case Type::Vmax: {
+ auto viewport = layout_node.document().frame()->viewport_rect();
+
+ return max(viewport.width(), viewport.height()) * (m_value / 100);
+ }
+ default:
+ ASSERT_NOT_REACHED();
+ }
+}
+
+const char* Length::unit_name() const
+{
+ switch (m_type) {
+ case Type::Cm:
+ return "cm";
+ case Type::In:
+ return "in";
+ case Type::Px:
+ return "px";
+ case Type::Pt:
+ return "pt";
+ case Type::Mm:
+ return "mm";
+ case Type::Q:
+ return "Q";
+ case Type::Pc:
+ return "pc";
+ case Type::Ex:
+ return "ex";
+ case Type::Em:
+ return "em";
+ case Type::Rem:
+ return "rem";
+ case Type::Auto:
+ return "auto";
+ case Type::Percentage:
+ return "%";
+ case Type::Undefined:
+ return "undefined";
+ case Type::Vh:
+ return "vh";
+ case Type::Vw:
+ return "vw";
+ case Type::Vmax:
+ return "vmax";
+ case Type::Vmin:
+ return "vmin";
+ }
+ ASSERT_NOT_REACHED();
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/CSS/Length.h b/Userland/Libraries/LibWeb/CSS/Length.h
new file mode 100644
index 0000000000..bce76a059c
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CSS/Length.h
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/String.h>
+#include <LibWeb/Forward.h>
+
+namespace Web::CSS {
+
+class Length {
+public:
+ enum class Type {
+ Undefined,
+ Percentage,
+ Auto,
+ Cm,
+ In,
+ Mm,
+ Q,
+ Px,
+ Pt,
+ Pc,
+ Ex,
+ Em,
+ Rem,
+ Vh,
+ Vw,
+ Vmax,
+ Vmin,
+ };
+
+ Length() { }
+ Length(int value, Type type)
+ : m_type(type)
+ , m_value(value)
+ {
+ }
+ Length(float value, Type type)
+ : m_type(type)
+ , m_value(value)
+ {
+ }
+
+ static Length make_auto() { return Length(0, Type::Auto); }
+ static Length make_px(float value) { return Length(value, Type::Px); }
+
+ Length resolved(const Length& fallback_for_undefined, const Layout::Node& layout_node, float reference_for_percent) const
+ {
+ if (is_undefined())
+ return fallback_for_undefined;
+ if (is_percentage())
+ return make_px(raw_value() / 100.0 * reference_for_percent);
+ if (is_relative())
+ return make_px(to_px(layout_node));
+ return *this;
+ }
+
+ Length resolved_or_auto(const Layout::Node& layout_node, float reference_for_percent) const
+ {
+ return resolved(make_auto(), layout_node, reference_for_percent);
+ }
+
+ Length resolved_or_zero(const Layout::Node& layout_node, float reference_for_percent) const
+ {
+ return resolved(make_px(0), layout_node, reference_for_percent);
+ }
+
+ bool is_undefined_or_auto() const { return m_type == Type::Undefined || m_type == Type::Auto; }
+ bool is_undefined() const { return m_type == Type::Undefined; }
+ bool is_percentage() const { return m_type == Type::Percentage; }
+ bool is_auto() const { return m_type == Type::Auto; }
+
+ bool is_absolute() const
+ {
+ return m_type == Type::Cm
+ || m_type == Type::In
+ || m_type == Type::Mm
+ || m_type == Type::Px
+ || m_type == Type::Pt
+ || m_type == Type::Pc
+ || m_type == Type::Q;
+ }
+
+ bool is_relative() const
+ {
+ return m_type == Type::Ex
+ || m_type == Type::Em
+ || m_type == Type::Rem
+ || m_type == Type::Vh
+ || m_type == Type::Vw
+ || m_type == Type::Vmax
+ || m_type == Type::Vmin;
+ }
+
+ float raw_value() const { return m_value; }
+ ALWAYS_INLINE float to_px(const Layout::Node& layout_node) const
+ {
+ if (is_relative())
+ return relative_length_to_px(layout_node);
+ constexpr float inch_pixels = 96.0f;
+ constexpr float centimeter_pixels = (inch_pixels / 2.54f);
+ switch (m_type) {
+ case Type::Auto:
+ return 0;
+ case Type::Cm:
+ return m_value * centimeter_pixels; // 1cm = 96px/2.54
+ case Type::In:
+ return m_value * inch_pixels; // 1in = 2.54 cm = 96px
+ case Type::Px:
+ return m_value; // 1px = 1/96th of 1in
+ case Type::Pt:
+ return m_value * ((1.0f / 72.0f) * inch_pixels); // 1pt = 1/72th of 1in
+ case Type::Pc:
+ return m_value * ((1.0f / 6.0f) * inch_pixels); // 1pc = 1/6th of 1in
+ case Type::Mm:
+ return m_value * ((1.0f / 10.0f) * centimeter_pixels); // 1mm = 1/10th of 1cm
+ case Type::Q:
+ return m_value * ((1.0f / 40.0f) * centimeter_pixels); // 1Q = 1/40th of 1cm
+ case Type::Undefined:
+ case Type::Percentage:
+ default:
+ ASSERT_NOT_REACHED();
+ }
+ }
+
+ String to_string() const
+ {
+ if (is_auto())
+ return "[auto]";
+ return String::formatted("[{} {}]", m_value, unit_name());
+ }
+
+ bool operator==(const Length& other) const
+ {
+ return m_type == other.m_type && m_value == other.m_value;
+ }
+
+ bool operator!=(const Length& other) const
+ {
+ return !(*this == other);
+ }
+
+private:
+ float relative_length_to_px(const Layout::Node&) const;
+
+ const char* unit_name() const;
+
+ Type m_type { Type::Undefined };
+ float m_value { 0 };
+};
+
+inline const LogStream& operator<<(const LogStream& stream, const Length& value)
+{
+ return stream << value.to_string();
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/CSS/LengthBox.h b/Userland/Libraries/LibWeb/CSS/LengthBox.h
new file mode 100644
index 0000000000..931d6c6da2
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CSS/LengthBox.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/CSS/Length.h>
+
+namespace Web::CSS {
+
+struct LengthBox {
+ Length top { Length::make_auto() };
+ Length right { Length::make_auto() };
+ Length bottom { Length::make_auto() };
+ Length left { Length::make_auto() };
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/CSS/Parser/CSSParser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/CSSParser.cpp
new file mode 100644
index 0000000000..62bee3b733
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CSS/Parser/CSSParser.cpp
@@ -0,0 +1,902 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/HashMap.h>
+#include <LibWeb/CSS/Parser/CSSParser.h>
+#include <LibWeb/CSS/PropertyID.h>
+#include <LibWeb/CSS/StyleSheet.h>
+#include <LibWeb/DOM/Document.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define PARSE_ASSERT(x) \
+ if (!(x)) { \
+ dbg() << "CSS PARSER ASSERTION FAILED: " << #x; \
+ dbg() << "At character# " << index << " in CSS: _" << css << "_"; \
+ ASSERT_NOT_REACHED(); \
+ }
+
+#define PARSE_ERROR() \
+ do { \
+ dbgln("CSS parse error"); \
+ } while (0)
+
+namespace Web {
+
+namespace CSS {
+
+ParsingContext::ParsingContext()
+{
+}
+
+ParsingContext::ParsingContext(const DOM::Document& document)
+ : m_document(&document)
+{
+}
+
+ParsingContext::ParsingContext(const DOM::ParentNode& parent_node)
+ : m_document(&parent_node.document())
+{
+}
+
+bool ParsingContext::in_quirks_mode() const
+{
+ return m_document ? m_document->in_quirks_mode() : false;
+}
+
+}
+
+static Optional<Color> parse_css_color(const CSS::ParsingContext&, const StringView& view)
+{
+ if (view.equals_ignoring_case("transparent"))
+ return Color::from_rgba(0x00000000);
+
+ auto color = Color::from_string(view.to_string().to_lowercase());
+ if (color.has_value())
+ return color;
+
+ return {};
+}
+
+static Optional<float> try_parse_float(const StringView& string)
+{
+ const char* str = string.characters_without_null_termination();
+ size_t len = string.length();
+ size_t weight = 1;
+ int exp_val = 0;
+ float value = 0.0f;
+ float fraction = 0.0f;
+ bool has_sign = false;
+ bool is_negative = false;
+ bool is_fractional = false;
+ bool is_scientific = false;
+
+ if (str[0] == '-') {
+ is_negative = true;
+ has_sign = true;
+ }
+ if (str[0] == '+') {
+ has_sign = true;
+ }
+
+ for (size_t i = has_sign; i < len; i++) {
+
+ // Looks like we're about to start working on the fractional part
+ if (str[i] == '.') {
+ is_fractional = true;
+ continue;
+ }
+
+ if (str[i] == 'e' || str[i] == 'E') {
+ if (str[i + 1] == '-' || str[i + 1] == '+')
+ exp_val = atoi(str + i + 2);
+ else
+ exp_val = atoi(str + i + 1);
+
+ is_scientific = true;
+ continue;
+ }
+
+ if (str[i] < '0' || str[i] > '9' || exp_val != 0) {
+ return {};
+ continue;
+ }
+
+ if (is_fractional) {
+ fraction *= 10;
+ fraction += str[i] - '0';
+ weight *= 10;
+ } else {
+ value = value * 10;
+ value += str[i] - '0';
+ }
+ }
+
+ fraction /= weight;
+ value += fraction;
+
+ if (is_scientific) {
+ bool divide = exp_val < 0;
+ if (divide)
+ exp_val *= -1;
+
+ for (int i = 0; i < exp_val; i++) {
+ if (divide)
+ value /= 10;
+ else
+ value *= 10;
+ }
+ }
+
+ return is_negative ? -value : value;
+}
+
+static CSS::Length parse_length(const CSS::ParsingContext& context, const StringView& view, bool& is_bad_length)
+{
+ CSS::Length::Type type = CSS::Length::Type::Undefined;
+ Optional<float> value;
+
+ if (view.ends_with('%')) {
+ type = CSS::Length::Type::Percentage;
+ value = try_parse_float(view.substring_view(0, view.length() - 1));
+ } else if (view.ends_with("px", CaseSensitivity::CaseInsensitive)) {
+ type = CSS::Length::Type::Px;
+ value = try_parse_float(view.substring_view(0, view.length() - 2));
+ } else if (view.ends_with("pt", CaseSensitivity::CaseInsensitive)) {
+ type = CSS::Length::Type::Pt;
+ value = try_parse_float(view.substring_view(0, view.length() - 2));
+ } else if (view.ends_with("pc", CaseSensitivity::CaseInsensitive)) {
+ type = CSS::Length::Type::Pc;
+ value = try_parse_float(view.substring_view(0, view.length() - 2));
+ } else if (view.ends_with("mm", CaseSensitivity::CaseInsensitive)) {
+ type = CSS::Length::Type::Mm;
+ value = try_parse_float(view.substring_view(0, view.length() - 2));
+ } else if (view.ends_with("rem", CaseSensitivity::CaseInsensitive)) {
+ type = CSS::Length::Type::Rem;
+ value = try_parse_float(view.substring_view(0, view.length() - 3));
+ } else if (view.ends_with("em", CaseSensitivity::CaseInsensitive)) {
+ type = CSS::Length::Type::Em;
+ value = try_parse_float(view.substring_view(0, view.length() - 2));
+ } else if (view.ends_with("ex", CaseSensitivity::CaseInsensitive)) {
+ type = CSS::Length::Type::Ex;
+ value = try_parse_float(view.substring_view(0, view.length() - 2));
+ } else if (view.ends_with("vw", CaseSensitivity::CaseInsensitive)) {
+ type = CSS::Length::Type::Vw;
+ value = try_parse_float(view.substring_view(0, view.length() - 2));
+ } else if (view.ends_with("vh", CaseSensitivity::CaseInsensitive)) {
+ type = CSS::Length::Type::Vh;
+ value = try_parse_float(view.substring_view(0, view.length() - 2));
+ } else if (view.ends_with("vmax", CaseSensitivity::CaseInsensitive)) {
+ type = CSS::Length::Type::Vmax;
+ value = try_parse_float(view.substring_view(0, view.length() - 4));
+ } else if (view.ends_with("vmin", CaseSensitivity::CaseInsensitive)) {
+ type = CSS::Length::Type::Vmin;
+ value = try_parse_float(view.substring_view(0, view.length() - 4));
+ } else if (view.ends_with("cm", CaseSensitivity::CaseInsensitive)) {
+ type = CSS::Length::Type::Cm;
+ value = try_parse_float(view.substring_view(0, view.length() - 2));
+ } else if (view.ends_with("in", CaseSensitivity::CaseInsensitive)) {
+ type = CSS::Length::Type::In;
+ value = try_parse_float(view.substring_view(0, view.length() - 2));
+ } else if (view.ends_with("Q", CaseSensitivity::CaseInsensitive)) {
+ type = CSS::Length::Type::Q;
+ value = try_parse_float(view.substring_view(0, view.length() - 1));
+ } else if (view == "0") {
+ type = CSS::Length::Type::Px;
+ value = 0;
+ } else if (context.in_quirks_mode()) {
+ type = CSS::Length::Type::Px;
+ value = try_parse_float(view);
+ } else {
+ value = try_parse_float(view);
+ if (value.has_value())
+ is_bad_length = true;
+ }
+
+ if (!value.has_value())
+ return {};
+
+ return CSS::Length(value.value(), type);
+}
+
+static bool takes_integer_value(CSS::PropertyID property_id)
+{
+ return property_id == CSS::PropertyID::ZIndex || property_id == CSS::PropertyID::FontWeight;
+}
+
+RefPtr<CSS::StyleValue> parse_css_value(const CSS::ParsingContext& context, const StringView& string, CSS::PropertyID property_id)
+{
+ bool is_bad_length = false;
+
+ if (takes_integer_value(property_id)) {
+ auto integer = string.to_int();
+ if (integer.has_value())
+ return CSS::LengthStyleValue::create(CSS::Length::make_px(integer.value()));
+ }
+
+ auto length = parse_length(context, string, is_bad_length);
+ if (is_bad_length)
+ return nullptr;
+ if (!length.is_undefined())
+ return CSS::LengthStyleValue::create(length);
+
+ if (string.equals_ignoring_case("inherit"))
+ return CSS::InheritStyleValue::create();
+ if (string.equals_ignoring_case("initial"))
+ return CSS::InitialStyleValue::create();
+ if (string.equals_ignoring_case("auto"))
+ return CSS::LengthStyleValue::create(CSS::Length::make_auto());
+
+ auto value_id = CSS::value_id_from_string(string);
+ if (value_id != CSS::ValueID::Invalid)
+ return CSS::IdentifierStyleValue::create(value_id);
+
+ auto color = parse_css_color(context, string);
+ if (color.has_value())
+ return CSS::ColorStyleValue::create(color.value());
+
+ return CSS::StringStyleValue::create(string);
+}
+
+RefPtr<CSS::LengthStyleValue> parse_line_width(const CSS::ParsingContext& context, const StringView& part)
+{
+ auto value = parse_css_value(context, part);
+ if (value && value->is_length())
+ return static_ptr_cast<CSS::LengthStyleValue>(value);
+ return nullptr;
+}
+
+RefPtr<CSS::ColorStyleValue> parse_color(const CSS::ParsingContext& context, const StringView& part)
+{
+ auto value = parse_css_value(context, part);
+ if (value && value->is_color())
+ return static_ptr_cast<CSS::ColorStyleValue>(value);
+ return nullptr;
+}
+
+RefPtr<CSS::StringStyleValue> parse_line_style(const CSS::ParsingContext& context, const StringView& part)
+{
+ auto parsed_value = parse_css_value(context, part);
+ if (!parsed_value || !parsed_value->is_string())
+ return nullptr;
+ auto value = static_ptr_cast<CSS::StringStyleValue>(parsed_value);
+ if (value->to_string() == "dotted")
+ return value;
+ if (value->to_string() == "dashed")
+ return value;
+ if (value->to_string() == "solid")
+ return value;
+ if (value->to_string() == "double")
+ return value;
+ if (value->to_string() == "groove")
+ return value;
+ if (value->to_string() == "ridge")
+ return value;
+ return nullptr;
+}
+
+class CSSParser {
+public:
+ CSSParser(const CSS::ParsingContext& context, const StringView& input)
+ : m_context(context)
+ , css(input)
+ {
+ }
+
+ bool next_is(const char* str) const
+ {
+ size_t len = strlen(str);
+ for (size_t i = 0; i < len; ++i) {
+ if (peek(i) != str[i])
+ return false;
+ }
+ return true;
+ }
+
+ char peek(size_t offset = 0) const
+ {
+ if ((index + offset) < css.length())
+ return css[index + offset];
+ return 0;
+ }
+
+ bool consume_specific(char ch)
+ {
+ if (peek() != ch) {
+ dbgln("CSSParser: Peeked '{:c}' wanted specific '{:c}'", peek(), ch);
+ }
+ if (!peek()) {
+ PARSE_ERROR();
+ return false;
+ }
+ if (peek() != ch) {
+ PARSE_ERROR();
+ ++index;
+ return false;
+ }
+ ++index;
+ return true;
+ }
+
+ char consume_one()
+ {
+ PARSE_ASSERT(index < css.length());
+ return css[index++];
+ };
+
+ bool consume_whitespace_or_comments()
+ {
+ size_t original_index = index;
+ bool in_comment = false;
+ for (; index < css.length(); ++index) {
+ char ch = peek();
+ if (isspace(ch))
+ continue;
+ if (!in_comment && ch == '/' && peek(1) == '*') {
+ in_comment = true;
+ ++index;
+ continue;
+ }
+ if (in_comment && ch == '*' && peek(1) == '/') {
+ in_comment = false;
+ ++index;
+ continue;
+ }
+ if (in_comment)
+ continue;
+ break;
+ }
+ return original_index != index;
+ }
+
+ bool is_valid_selector_char(char ch) const
+ {
+ return isalnum(ch) || ch == '-' || ch == '_' || ch == '(' || ch == ')' || ch == '@';
+ }
+
+ bool is_combinator(char ch) const
+ {
+ return ch == '~' || ch == '>' || ch == '+';
+ }
+
+ Optional<CSS::Selector::SimpleSelector> parse_simple_selector()
+ {
+ auto index_at_start = index;
+
+ if (consume_whitespace_or_comments())
+ return {};
+
+ if (!peek() || peek() == '{' || peek() == ',' || is_combinator(peek()))
+ return {};
+
+ CSS::Selector::SimpleSelector::Type type;
+
+ if (peek() == '*') {
+ type = CSS::Selector::SimpleSelector::Type::Universal;
+ consume_one();
+ return CSS::Selector::SimpleSelector {
+ type,
+ CSS::Selector::SimpleSelector::PseudoClass::None,
+ CSS::Selector::SimpleSelector::PseudoElement::None,
+ String(),
+ CSS::Selector::SimpleSelector::AttributeMatchType::None,
+ String(),
+ String()
+ };
+ }
+
+ if (peek() == '.') {
+ type = CSS::Selector::SimpleSelector::Type::Class;
+ consume_one();
+ } else if (peek() == '#') {
+ type = CSS::Selector::SimpleSelector::Type::Id;
+ consume_one();
+ } else if (isalpha(peek())) {
+ type = CSS::Selector::SimpleSelector::Type::TagName;
+ } else {
+ type = CSS::Selector::SimpleSelector::Type::Universal;
+ }
+
+ if (type != CSS::Selector::SimpleSelector::Type::Universal) {
+ while (is_valid_selector_char(peek()))
+ buffer.append(consume_one());
+ PARSE_ASSERT(!buffer.is_null());
+ }
+
+ auto value = String::copy(buffer);
+
+ if (type == CSS::Selector::SimpleSelector::Type::TagName) {
+ // Some stylesheets use uppercase tag names, so here's a hack to just lowercase them internally.
+ value = value.to_lowercase();
+ }
+
+ CSS::Selector::SimpleSelector simple_selector {
+ type,
+ CSS::Selector::SimpleSelector::PseudoClass::None,
+ CSS::Selector::SimpleSelector::PseudoElement::None,
+ value,
+ CSS::Selector::SimpleSelector::AttributeMatchType::None,
+ String(),
+ String()
+ };
+ buffer.clear();
+
+ if (peek() == '[') {
+ CSS::Selector::SimpleSelector::AttributeMatchType attribute_match_type = CSS::Selector::SimpleSelector::AttributeMatchType::HasAttribute;
+ String attribute_name;
+ String attribute_value;
+ bool in_value = false;
+ consume_specific('[');
+ char expected_end_of_attribute_selector = ']';
+ while (peek() != expected_end_of_attribute_selector) {
+ char ch = consume_one();
+ if (ch == '=' || (ch == '~' && peek() == '=')) {
+ if (ch == '=') {
+ attribute_match_type = CSS::Selector::SimpleSelector::AttributeMatchType::ExactValueMatch;
+ } else if (ch == '~') {
+ consume_one();
+ attribute_match_type = CSS::Selector::SimpleSelector::AttributeMatchType::Contains;
+ }
+ attribute_name = String::copy(buffer);
+ buffer.clear();
+ in_value = true;
+ consume_whitespace_or_comments();
+ if (peek() == '\'') {
+ expected_end_of_attribute_selector = '\'';
+ consume_one();
+ } else if (peek() == '"') {
+ expected_end_of_attribute_selector = '"';
+ consume_one();
+ }
+ continue;
+ }
+ // FIXME: This is a hack that will go away when we replace this with a big boy CSS parser.
+ if (ch == '\\')
+ ch = consume_one();
+ buffer.append(ch);
+ }
+ if (in_value)
+ attribute_value = String::copy(buffer);
+ else
+ attribute_name = String::copy(buffer);
+ buffer.clear();
+ simple_selector.attribute_match_type = attribute_match_type;
+ simple_selector.attribute_name = attribute_name;
+ simple_selector.attribute_value = attribute_value;
+ if (expected_end_of_attribute_selector != ']') {
+ if (!consume_specific(expected_end_of_attribute_selector))
+ return {};
+ }
+ consume_whitespace_or_comments();
+ if (!consume_specific(']'))
+ return {};
+ }
+
+ if (peek() == ':') {
+ // FIXME: Implement pseudo elements.
+ [[maybe_unused]] bool is_pseudo_element = false;
+ consume_one();
+ if (peek() == ':') {
+ is_pseudo_element = true;
+ consume_one();
+ }
+ if (next_is("not")) {
+ buffer.append(consume_one());
+ buffer.append(consume_one());
+ buffer.append(consume_one());
+ if (!consume_specific('('))
+ return {};
+ buffer.append('(');
+ while (peek() != ')')
+ buffer.append(consume_one());
+ if (!consume_specific(')'))
+ return {};
+ buffer.append(')');
+ } else {
+ while (is_valid_selector_char(peek()))
+ buffer.append(consume_one());
+ }
+
+ auto pseudo_name = String::copy(buffer);
+ buffer.clear();
+
+ // Ignore for now, otherwise we produce a "false positive" selector
+ // and apply styles to the element itself, not its pseudo element
+ if (is_pseudo_element)
+ return {};
+
+ if (pseudo_name.equals_ignoring_case("link"))
+ simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Link;
+ else if (pseudo_name.equals_ignoring_case("visited"))
+ simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Visited;
+ else if (pseudo_name.equals_ignoring_case("hover"))
+ simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Hover;
+ else if (pseudo_name.equals_ignoring_case("focus"))
+ simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Focus;
+ else if (pseudo_name.equals_ignoring_case("first-child"))
+ simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::FirstChild;
+ else if (pseudo_name.equals_ignoring_case("last-child"))
+ simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::LastChild;
+ else if (pseudo_name.equals_ignoring_case("only-child"))
+ simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::OnlyChild;
+ else if (pseudo_name.equals_ignoring_case("empty"))
+ simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Empty;
+ else if (pseudo_name.equals_ignoring_case("root"))
+ simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Root;
+ else if (pseudo_name.equals_ignoring_case("before"))
+ simple_selector.pseudo_element = CSS::Selector::SimpleSelector::PseudoElement::Before;
+ else if (pseudo_name.equals_ignoring_case("after"))
+ simple_selector.pseudo_element = CSS::Selector::SimpleSelector::PseudoElement::After;
+ }
+
+ if (index == index_at_start) {
+ // We consumed nothing.
+ return {};
+ }
+
+ return simple_selector;
+ }
+
+ Optional<CSS::Selector::ComplexSelector> parse_complex_selector()
+ {
+ auto relation = CSS::Selector::ComplexSelector::Relation::Descendant;
+
+ if (peek() == '{' || peek() == ',')
+ return {};
+
+ if (is_combinator(peek())) {
+ switch (peek()) {
+ case '>':
+ relation = CSS::Selector::ComplexSelector::Relation::ImmediateChild;
+ break;
+ case '+':
+ relation = CSS::Selector::ComplexSelector::Relation::AdjacentSibling;
+ break;
+ case '~':
+ relation = CSS::Selector::ComplexSelector::Relation::GeneralSibling;
+ break;
+ }
+ consume_one();
+ consume_whitespace_or_comments();
+ }
+
+ consume_whitespace_or_comments();
+
+ Vector<CSS::Selector::SimpleSelector> simple_selectors;
+ for (;;) {
+ auto component = parse_simple_selector();
+ if (!component.has_value())
+ break;
+ simple_selectors.append(component.value());
+ // If this assert triggers, we're most likely up to no good.
+ PARSE_ASSERT(simple_selectors.size() < 100);
+ }
+
+ if (simple_selectors.is_empty())
+ return {};
+
+ return CSS::Selector::ComplexSelector { relation, move(simple_selectors) };
+ }
+
+ void parse_selector()
+ {
+ Vector<CSS::Selector::ComplexSelector> complex_selectors;
+
+ for (;;) {
+ auto index_before = index;
+ auto complex_selector = parse_complex_selector();
+ if (complex_selector.has_value())
+ complex_selectors.append(complex_selector.value());
+ consume_whitespace_or_comments();
+ if (!peek() || peek() == ',' || peek() == '{')
+ break;
+ // HACK: If we didn't move forward, just let go.
+ if (index == index_before)
+ break;
+ }
+
+ if (complex_selectors.is_empty())
+ return;
+ complex_selectors.first().relation = CSS::Selector::ComplexSelector::Relation::None;
+
+ current_rule.selectors.append(CSS::Selector(move(complex_selectors)));
+ }
+
+ Optional<CSS::Selector> parse_individual_selector()
+ {
+ parse_selector();
+ if (current_rule.selectors.is_empty())
+ return {};
+ return current_rule.selectors.last();
+ }
+
+ void parse_selector_list()
+ {
+ for (;;) {
+ auto index_before = index;
+ parse_selector();
+ consume_whitespace_or_comments();
+ if (peek() == ',') {
+ consume_one();
+ continue;
+ }
+ if (peek() == '{')
+ break;
+ // HACK: If we didn't move forward, just let go.
+ if (index_before == index)
+ break;
+ }
+ }
+
+ bool is_valid_property_name_char(char ch) const
+ {
+ return ch && !isspace(ch) && ch != ':';
+ }
+
+ bool is_valid_property_value_char(char ch) const
+ {
+ return ch && ch != '!' && ch != ';' && ch != '}';
+ }
+
+ struct ValueAndImportant {
+ String value;
+ bool important { false };
+ };
+
+ ValueAndImportant consume_css_value()
+ {
+ buffer.clear();
+
+ int paren_nesting_level = 0;
+ bool important = false;
+
+ for (;;) {
+ char ch = peek();
+ if (ch == '(') {
+ ++paren_nesting_level;
+ buffer.append(consume_one());
+ continue;
+ }
+ if (ch == ')') {
+ PARSE_ASSERT(paren_nesting_level > 0);
+ --paren_nesting_level;
+ buffer.append(consume_one());
+ continue;
+ }
+ if (paren_nesting_level > 0) {
+ buffer.append(consume_one());
+ continue;
+ }
+ if (next_is("!important")) {
+ consume_specific('!');
+ consume_specific('i');
+ consume_specific('m');
+ consume_specific('p');
+ consume_specific('o');
+ consume_specific('r');
+ consume_specific('t');
+ consume_specific('a');
+ consume_specific('n');
+ consume_specific('t');
+ important = true;
+ continue;
+ }
+ if (next_is("/*")) {
+ consume_whitespace_or_comments();
+ continue;
+ }
+ if (!ch)
+ break;
+ if (ch == '\\') {
+ consume_one();
+ buffer.append(consume_one());
+ continue;
+ }
+ if (ch == '}')
+ break;
+ if (ch == ';')
+ break;
+ buffer.append(consume_one());
+ }
+
+ // Remove trailing whitespace.
+ while (!buffer.is_empty() && isspace(buffer.last()))
+ buffer.take_last();
+
+ auto string = String::copy(buffer);
+ buffer.clear();
+
+ return { string, important };
+ }
+
+ Optional<CSS::StyleProperty> parse_property()
+ {
+ consume_whitespace_or_comments();
+ if (peek() == ';') {
+ consume_one();
+ return {};
+ }
+ if (peek() == '}')
+ return {};
+ buffer.clear();
+ while (is_valid_property_name_char(peek()))
+ buffer.append(consume_one());
+ auto property_name = String::copy(buffer);
+ buffer.clear();
+ consume_whitespace_or_comments();
+ if (!consume_specific(':'))
+ return {};
+ consume_whitespace_or_comments();
+
+ auto [property_value, important] = consume_css_value();
+
+ consume_whitespace_or_comments();
+
+ if (peek() && peek() != '}') {
+ if (!consume_specific(';'))
+ return {};
+ }
+
+ auto property_id = CSS::property_id_from_string(property_name);
+ if (property_id == CSS::PropertyID::Invalid) {
+ dbg() << "CSSParser: Unrecognized property '" << property_name << "'";
+ }
+ auto value = parse_css_value(m_context, property_value, property_id);
+ if (!value)
+ return {};
+ return CSS::StyleProperty { property_id, value.release_nonnull(), important };
+ }
+
+ void parse_declaration()
+ {
+ for (;;) {
+ auto property = parse_property();
+ if (property.has_value())
+ current_rule.properties.append(property.value());
+ consume_whitespace_or_comments();
+ if (!peek() || peek() == '}')
+ break;
+ }
+ }
+
+ void parse_rule()
+ {
+ consume_whitespace_or_comments();
+ if (!peek())
+ return;
+
+ // FIXME: We ignore @-rules for now.
+ if (peek() == '@') {
+ while (peek() != '{')
+ consume_one();
+ int level = 0;
+ for (;;) {
+ auto ch = consume_one();
+ if (ch == '{') {
+ ++level;
+ } else if (ch == '}') {
+ --level;
+ if (level == 0)
+ break;
+ }
+ }
+ consume_whitespace_or_comments();
+ return;
+ }
+
+ parse_selector_list();
+ if (!consume_specific('{')) {
+ PARSE_ERROR();
+ return;
+ }
+ parse_declaration();
+ if (!consume_specific('}')) {
+ PARSE_ERROR();
+ return;
+ }
+ rules.append(CSS::StyleRule::create(move(current_rule.selectors), CSS::StyleDeclaration::create(move(current_rule.properties))));
+ consume_whitespace_or_comments();
+ }
+
+ RefPtr<CSS::StyleSheet> parse_sheet()
+ {
+ if (peek(0) == (char)0xef && peek(1) == (char)0xbb && peek(2) == (char)0xbf) {
+ // HACK: Skip UTF-8 BOM.
+ index += 3;
+ }
+
+ while (peek()) {
+ parse_rule();
+ }
+
+ return CSS::StyleSheet::create(move(rules));
+ }
+
+ RefPtr<CSS::StyleDeclaration> parse_standalone_declaration()
+ {
+ consume_whitespace_or_comments();
+ for (;;) {
+ auto property = parse_property();
+ if (property.has_value())
+ current_rule.properties.append(property.value());
+ consume_whitespace_or_comments();
+ if (!peek())
+ break;
+ }
+ return CSS::StyleDeclaration::create(move(current_rule.properties));
+ }
+
+private:
+ CSS::ParsingContext m_context;
+
+ NonnullRefPtrVector<CSS::StyleRule> rules;
+
+ struct CurrentRule {
+ Vector<CSS::Selector> selectors;
+ Vector<CSS::StyleProperty> properties;
+ };
+
+ CurrentRule current_rule;
+ Vector<char> buffer;
+
+ size_t index = 0;
+
+ StringView css;
+};
+
+Optional<CSS::Selector> parse_selector(const CSS::ParsingContext& context, const StringView& selector_text)
+{
+ CSSParser parser(context, selector_text);
+ return parser.parse_individual_selector();
+}
+
+RefPtr<CSS::StyleSheet> parse_css(const CSS::ParsingContext& context, const StringView& css)
+{
+ if (css.is_empty())
+ return CSS::StyleSheet::create({});
+ CSSParser parser(context, css);
+ return parser.parse_sheet();
+}
+
+RefPtr<CSS::StyleDeclaration> parse_css_declaration(const CSS::ParsingContext& context, const StringView& css)
+{
+ if (css.is_empty())
+ return CSS::StyleDeclaration::create({});
+ CSSParser parser(context, css);
+ return parser.parse_standalone_declaration();
+}
+
+RefPtr<CSS::StyleValue> parse_html_length(const DOM::Document& document, const StringView& string)
+{
+ auto integer = string.to_int();
+ if (integer.has_value())
+ return CSS::LengthStyleValue::create(CSS::Length::make_px(integer.value()));
+ return parse_css_value(CSS::ParsingContext(document), string);
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/CSS/Parser/CSSParser.h b/Userland/Libraries/LibWeb/CSS/Parser/CSSParser.h
new file mode 100644
index 0000000000..ca9a4688e2
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CSS/Parser/CSSParser.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/NonnullRefPtr.h>
+#include <LibWeb/CSS/StyleSheet.h>
+
+namespace Web::CSS {
+class ParsingContext {
+public:
+ ParsingContext();
+ explicit ParsingContext(const DOM::Document&);
+ explicit ParsingContext(const DOM::ParentNode&);
+
+ bool in_quirks_mode() const;
+
+private:
+ const DOM::Document* m_document { nullptr };
+};
+}
+
+namespace Web {
+
+RefPtr<CSS::StyleSheet> parse_css(const CSS::ParsingContext&, const StringView&);
+RefPtr<CSS::StyleDeclaration> parse_css_declaration(const CSS::ParsingContext&, const StringView&);
+RefPtr<CSS::StyleValue> parse_css_value(const CSS::ParsingContext&, const StringView&, CSS::PropertyID property_id = CSS::PropertyID::Invalid);
+Optional<CSS::Selector> parse_selector(const CSS::ParsingContext&, const StringView&);
+
+RefPtr<CSS::LengthStyleValue> parse_line_width(const CSS::ParsingContext&, const StringView&);
+RefPtr<CSS::ColorStyleValue> parse_color(const CSS::ParsingContext&, const StringView&);
+RefPtr<CSS::StringStyleValue> parse_line_style(const CSS::ParsingContext&, const StringView&);
+
+RefPtr<CSS::StyleValue> parse_html_length(const DOM::Document&, const StringView&);
+
+}
diff --git a/Userland/Libraries/LibWeb/CSS/Properties.json b/Userland/Libraries/LibWeb/CSS/Properties.json
new file mode 100644
index 0000000000..86c4009834
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CSS/Properties.json
@@ -0,0 +1,368 @@
+{
+ "background": {
+ },
+ "background-attachment": {
+ "inherited": false,
+ "initial": "scroll"
+ },
+ "background-color": {
+ "inherited": false,
+ "initial": "transparent"
+ },
+ "background-image": {
+ "inherited": false,
+ "initial": "none"
+ },
+ "background-position": {
+ "inherited": false,
+ "initial": "0% 0%"
+ },
+ "background-repeat": {
+ "inherited": false,
+ "initial": "repeat"
+ },
+ "border": {
+ "longhands": [
+ "border-width",
+ "border-style",
+ "border-color"
+ ]
+ },
+ "border-top": {
+ "longhands": [
+ "border-top-width",
+ "border-top-style",
+ "border-top-color"
+ ]
+ },
+ "border-right": {
+ "longhands": [
+ "border-right-width",
+ "border-right-style",
+ "border-right-color"
+ ]
+ },
+ "border-bottom": {
+ "longhands": [
+ "border-bottom-width",
+ "border-bottom-style",
+ "border-bottom-color"
+ ]
+ },
+ "border-left": {
+ "longhands": [
+ "border-left-width",
+ "border-left-style",
+ "border-left-color"
+ ]
+ },
+ "border-bottom-color": {
+ "initial": "currentColor",
+ "inherited": false
+ },
+ "border-bottom-style": {
+ "initial": "none",
+ "inherited": false
+ },
+ "border-bottom-width": {
+ "initial": "medium",
+ "inherited": false
+ },
+ "border-color": {
+ "longhands": [
+ "border-top-color",
+ "border-right-color",
+ "border-bottom-color",
+ "border-left-color"
+ ]
+ },
+ "border-collapse": {
+ "inherited": true,
+ "initial": "separate"
+ },
+ "border-left-color": {
+ "initial": "currentColor",
+ "inherited": false
+ },
+ "border-left-style": {
+ "initial": "none",
+ "inherited": false
+ },
+ "border-left-width": {
+ "initial": "medium",
+ "inherited": false
+ },
+ "border-right-color": {
+ "initial": "currentColor",
+ "inherited": false
+ },
+ "border-right-style": {
+ "initial": "none",
+ "inherited": false
+ },
+ "border-right-width": {
+ "initial": "medium",
+ "inherited": false
+ },
+ "border-spacing": {
+ "inherited": true,
+ "initial": "0"
+ },
+ "border-style": {
+ "longhands": [
+ "border-top-style",
+ "border-right-style",
+ "border-bottom-style",
+ "border-left-style"
+ ]
+ },
+ "border-top-color": {
+ "initial": "currentColor",
+ "inherited": false
+ },
+ "border-top-style": {
+ "initial": "none",
+ "inherited": false
+ },
+ "border-top-width": {
+ "initial": "medium",
+ "inherited": false
+ },
+ "border-width": {
+ "longhands": [
+ "border-top-width",
+ "border-right-width",
+ "border-bottom-width",
+ "border-left-width"
+ ]
+ },
+ "bottom": {
+ "inherited": false,
+ "initial": "auto"
+ },
+ "caption-side": {
+ "inherited": true,
+ "initial": "top"
+ },
+ "clear": {
+ "inherited": false,
+ "initial": "none"
+ },
+ "clip": {
+ "inherited": true,
+ "initial": "auto"
+ },
+ "color": {
+ "inherited": true,
+ "initial": ""
+ },
+ "cursor": {
+ "inherited": true,
+ "initial": "auto"
+ },
+ "direction": {
+ "inherited": true,
+ "initial": "ltr"
+ },
+ "display": {
+ "inherited": false,
+ "initial": "inline"
+ },
+ "float": {
+ "inherited": false,
+ "initial": "none"
+ },
+ "font-family": {
+ "inherited": true,
+ "initial": "sans-serif"
+ },
+ "font-size": {
+ "inherited": true,
+ "initial": "medium"
+ },
+ "font-style": {
+ "inherited": true,
+ "initial": "normal"
+ },
+ "font-variant": {
+ "inherited": true,
+ "initial": "normal"
+ },
+ "font-weight": {
+ "inherited": true,
+ "initial": "normal"
+ },
+ "height": {
+ "inherited": false,
+ "initial": "auto"
+ },
+ "left": {
+ "inherited": false,
+ "initial": "auto"
+ },
+ "letter-spacing": {
+ "inherited": true,
+ "initial": "normal"
+ },
+ "line-height": {
+ "inherited": true,
+ "initial": "normal"
+ },
+ "list-style": {
+ "longhands": [
+ "list-style-type",
+ "list-style-position",
+ "list-style-image"
+ ]
+ },
+ "list-style-image": {
+ "inherited": true,
+ "initial": "none"
+ },
+ "list-style-position": {
+ "inherited": true,
+ "initial": "outside"
+ },
+ "list-style-type": {
+ "inherited": true,
+ "initial": "disc"
+ },
+ "margin": {
+ "longhands": [
+ "margin-top",
+ "margin-right",
+ "margin-bottom",
+ "margin-left"
+ ]
+ },
+ "margin-bottom": {
+ "inherited": false,
+ "initial": "0"
+ },
+ "margin-left": {
+ "inherited": false,
+ "initial": "0"
+ },
+ "margin-right": {
+ "inherited": false,
+ "initial": "0"
+ },
+ "margin-top": {
+ "inherited": false,
+ "initial": "0"
+ },
+ "max-height": {
+ "inherited": false,
+ "initial": "none"
+ },
+ "max-width": {
+ "inherited": false,
+ "initial": "none"
+ },
+ "min-height": {
+ "inherited": false,
+ "initial": "0"
+ },
+ "min-width": {
+ "inherited": false,
+ "initial": "0"
+ },
+ "padding": {
+ "longhands": [
+ "padding-top",
+ "padding-right",
+ "padding-bottom",
+ "padding-left"
+ ]
+ },
+ "padding-bottom": {
+ "inherited": false,
+ "initial": "0"
+ },
+ "padding-left": {
+ "inherited": false,
+ "initial": "0"
+ },
+ "padding-right": {
+ "inherited": false,
+ "initial": "0"
+ },
+ "padding-top": {
+ "inherited": false,
+ "initial": "0"
+ },
+ "position": {
+ "inherited": false,
+ "initial": "static"
+ },
+ "right": {
+ "inherited": false,
+ "initial": "auto"
+ },
+ "text-align": {
+ "inherited": true,
+ "initial": "left"
+ },
+ "text-decoration": {
+ "inherited": false,
+ "initial": "none",
+ "longhands": [
+ "text-decoration-color",
+ "text-decoration-line",
+ "text-decoration-style",
+ "text-decoration-thickness"
+ ]
+ },
+ "text-decoration-color": {
+ "inherited": false,
+ "initial": "none"
+ },
+ "text-decoration-line": {
+ "inherited": false,
+ "initial": "none"
+ },
+ "text-decoration-style": {
+ "inherited": false,
+ "initial": "none"
+ },
+ "text-decoration-thickness": {
+ "inherited": false,
+ "initial": "none"
+ },
+ "text-indent": {
+ "inherited": true,
+ "initial": "0"
+ },
+ "text-transform": {
+ "inherited": true,
+ "initial": "none"
+ },
+ "top": {
+ "inherited": false,
+ "initial": "auto"
+ },
+ "vertical-align": {
+ "inherited": false,
+ "initial": "baseline"
+ },
+ "visibility": {
+ "inherited": true,
+ "initial": "visible"
+ },
+ "width": {
+ "inherited": false,
+ "initial": "auto"
+ },
+ "white-space": {
+ "inherited": true,
+ "initial": "normal"
+ },
+ "word-spacing": {
+ "inherited": true,
+ "initial": "normal"
+ },
+ "z-index": {
+ "inherited": false,
+ "initial": "auto"
+ }
+}
diff --git a/Userland/Libraries/LibWeb/CSS/QuirksMode.css b/Userland/Libraries/LibWeb/CSS/QuirksMode.css
new file mode 100644
index 0000000000..8e1aeb392b
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CSS/QuirksMode.css
@@ -0,0 +1,3 @@
+table {
+ text-align: left;
+}
diff --git a/Userland/Libraries/LibWeb/CSS/Selector.cpp b/Userland/Libraries/LibWeb/CSS/Selector.cpp
new file mode 100644
index 0000000000..bfd44f204a
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CSS/Selector.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/CSS/Selector.h>
+
+namespace Web::CSS {
+
+Selector::Selector(Vector<ComplexSelector>&& component_lists)
+ : m_complex_selectors(move(component_lists))
+{
+}
+
+Selector::~Selector()
+{
+}
+
+u32 Selector::specificity() const
+{
+ unsigned ids = 0;
+ unsigned tag_names = 0;
+ unsigned classes = 0;
+
+ for (auto& list : m_complex_selectors) {
+ for (auto& simple_selector : list.compound_selector) {
+ switch (simple_selector.type) {
+ case SimpleSelector::Type::Id:
+ ++ids;
+ break;
+ case SimpleSelector::Type::Class:
+ ++classes;
+ break;
+ case SimpleSelector::Type::TagName:
+ ++tag_names;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ return ids * 0x10000 + classes * 0x100 + tag_names;
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/CSS/Selector.h b/Userland/Libraries/LibWeb/CSS/Selector.h
new file mode 100644
index 0000000000..d5dae94f98
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CSS/Selector.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/FlyString.h>
+#include <AK/Vector.h>
+
+namespace Web::CSS {
+
+class Selector {
+public:
+ struct SimpleSelector {
+ enum class Type {
+ Invalid,
+ Universal,
+ TagName,
+ Id,
+ Class,
+ };
+ Type type { Type::Invalid };
+
+ enum class PseudoClass {
+ None,
+ Link,
+ Visited,
+ Hover,
+ Focus,
+ FirstChild,
+ LastChild,
+ OnlyChild,
+ Empty,
+ Root,
+ };
+ PseudoClass pseudo_class { PseudoClass::None };
+
+ enum class PseudoElement {
+ None,
+ Before,
+ After,
+ };
+ PseudoElement pseudo_element { PseudoElement::None };
+
+ FlyString value;
+
+ enum class AttributeMatchType {
+ None,
+ HasAttribute,
+ ExactValueMatch,
+ Contains,
+ };
+
+ AttributeMatchType attribute_match_type { AttributeMatchType::None };
+ FlyString attribute_name;
+ String attribute_value;
+ };
+
+ struct ComplexSelector {
+ enum class Relation {
+ None,
+ ImmediateChild,
+ Descendant,
+ AdjacentSibling,
+ GeneralSibling,
+ };
+ Relation relation { Relation::None };
+
+ using CompoundSelector = Vector<SimpleSelector>;
+ CompoundSelector compound_selector;
+ };
+
+ explicit Selector(Vector<ComplexSelector>&&);
+ ~Selector();
+
+ const Vector<ComplexSelector>& complex_selectors() const { return m_complex_selectors; }
+
+ u32 specificity() const;
+
+private:
+ Vector<ComplexSelector> m_complex_selectors;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp b/Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp
new file mode 100644
index 0000000000..5f553e2be0
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/CSS/SelectorEngine.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/DOM/Element.h>
+#include <LibWeb/DOM/Text.h>
+#include <LibWeb/HTML/AttributeNames.h>
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::SelectorEngine {
+
+static bool matches_hover_pseudo_class(const DOM::Element& element)
+{
+ auto* hovered_node = element.document().hovered_node();
+ if (!hovered_node)
+ return false;
+ if (&element == hovered_node)
+ return true;
+ return element.is_ancestor_of(*hovered_node);
+}
+
+static bool matches(const CSS::Selector::SimpleSelector& component, const DOM::Element& element)
+{
+ switch (component.pseudo_element) {
+ case CSS::Selector::SimpleSelector::PseudoElement::None:
+ break;
+ default:
+ // FIXME: Implement pseudo-elements.
+ return false;
+ }
+
+ switch (component.pseudo_class) {
+ case CSS::Selector::SimpleSelector::PseudoClass::None:
+ break;
+ case CSS::Selector::SimpleSelector::PseudoClass::Link:
+ if (!element.is_link())
+ return false;
+ break;
+ case CSS::Selector::SimpleSelector::PseudoClass::Visited:
+ // FIXME: Maybe match this selector sometimes?
+ return false;
+ case CSS::Selector::SimpleSelector::PseudoClass::Hover:
+ if (!matches_hover_pseudo_class(element))
+ return false;
+ break;
+ case CSS::Selector::SimpleSelector::PseudoClass::Focus:
+ // FIXME: Implement matches_focus_pseudo_class(element)
+ return false;
+ case CSS::Selector::SimpleSelector::PseudoClass::FirstChild:
+ if (element.previous_element_sibling())
+ return false;
+ break;
+ case CSS::Selector::SimpleSelector::PseudoClass::LastChild:
+ if (element.next_element_sibling())
+ return false;
+ break;
+ case CSS::Selector::SimpleSelector::PseudoClass::OnlyChild:
+ if (element.previous_element_sibling() || element.next_element_sibling())
+ return false;
+ break;
+ case CSS::Selector::SimpleSelector::PseudoClass::Empty:
+ if (element.first_child_of_type<DOM::Element>() || element.first_child_of_type<DOM::Text>())
+ return false;
+ break;
+ case CSS::Selector::SimpleSelector::PseudoClass::Root:
+ if (!is<HTML::HTMLElement>(element))
+ return false;
+ break;
+ }
+
+ switch (component.attribute_match_type) {
+ case CSS::Selector::SimpleSelector::AttributeMatchType::HasAttribute:
+ if (!element.has_attribute(component.attribute_name))
+ return false;
+ break;
+ case CSS::Selector::SimpleSelector::AttributeMatchType::ExactValueMatch:
+ if (element.attribute(component.attribute_name) != component.attribute_value)
+ return false;
+ break;
+ case CSS::Selector::SimpleSelector::AttributeMatchType::Contains:
+ if (!element.attribute(component.attribute_name).split(' ').contains_slow(component.attribute_value))
+ return false;
+ break;
+ default:
+ break;
+ }
+
+ switch (component.type) {
+ case CSS::Selector::SimpleSelector::Type::Universal:
+ return true;
+ case CSS::Selector::SimpleSelector::Type::Id:
+ return component.value == element.attribute(HTML::AttributeNames::id);
+ case CSS::Selector::SimpleSelector::Type::Class:
+ return element.has_class(component.value);
+ case CSS::Selector::SimpleSelector::Type::TagName:
+ return component.value == element.local_name();
+ default:
+ ASSERT_NOT_REACHED();
+ }
+}
+
+static bool matches(const CSS::Selector& selector, int component_list_index, const DOM::Element& element)
+{
+ auto& component_list = selector.complex_selectors()[component_list_index];
+ for (auto& component : component_list.compound_selector) {
+ if (!matches(component, element))
+ return false;
+ }
+ switch (component_list.relation) {
+ case CSS::Selector::ComplexSelector::Relation::None:
+ return true;
+ case CSS::Selector::ComplexSelector::Relation::Descendant:
+ ASSERT(component_list_index != 0);
+ for (auto* ancestor = element.parent(); ancestor; ancestor = ancestor->parent()) {
+ if (!is<DOM::Element>(*ancestor))
+ continue;
+ if (matches(selector, component_list_index - 1, downcast<DOM::Element>(*ancestor)))
+ return true;
+ }
+ return false;
+ case CSS::Selector::ComplexSelector::Relation::ImmediateChild:
+ ASSERT(component_list_index != 0);
+ if (!element.parent() || !is<DOM::Element>(*element.parent()))
+ return false;
+ return matches(selector, component_list_index - 1, downcast<DOM::Element>(*element.parent()));
+ case CSS::Selector::ComplexSelector::Relation::AdjacentSibling:
+ ASSERT(component_list_index != 0);
+ if (auto* sibling = element.previous_element_sibling())
+ return matches(selector, component_list_index - 1, *sibling);
+ return false;
+ case CSS::Selector::ComplexSelector::Relation::GeneralSibling:
+ ASSERT(component_list_index != 0);
+ for (auto* sibling = element.previous_element_sibling(); sibling; sibling = sibling->previous_element_sibling()) {
+ if (matches(selector, component_list_index - 1, *sibling))
+ return true;
+ }
+ return false;
+ }
+ ASSERT_NOT_REACHED();
+}
+
+bool matches(const CSS::Selector& selector, const DOM::Element& element)
+{
+ ASSERT(!selector.complex_selectors().is_empty());
+ return matches(selector, selector.complex_selectors().size() - 1, element);
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/CSS/SelectorEngine.h b/Userland/Libraries/LibWeb/CSS/SelectorEngine.h
new file mode 100644
index 0000000000..96bd76de36
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CSS/SelectorEngine.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/CSS/Selector.h>
+#include <LibWeb/DOM/Element.h>
+
+namespace Web::SelectorEngine {
+
+bool matches(const CSS::Selector&, const DOM::Element&);
+
+}
diff --git a/Userland/Libraries/LibWeb/CSS/StyleDeclaration.cpp b/Userland/Libraries/LibWeb/CSS/StyleDeclaration.cpp
new file mode 100644
index 0000000000..eb56b94625
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CSS/StyleDeclaration.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/CSS/StyleDeclaration.h>
+
+namespace Web::CSS {
+
+StyleDeclaration::StyleDeclaration(Vector<StyleProperty>&& properties)
+ : m_properties(move(properties))
+{
+}
+
+StyleDeclaration::~StyleDeclaration()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/CSS/StyleDeclaration.h b/Userland/Libraries/LibWeb/CSS/StyleDeclaration.h
new file mode 100644
index 0000000000..787429eb15
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CSS/StyleDeclaration.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/String.h>
+#include <AK/Vector.h>
+#include <LibWeb/CSS/StyleValue.h>
+
+namespace Web::CSS {
+
+struct StyleProperty {
+ CSS::PropertyID property_id;
+ NonnullRefPtr<StyleValue> value;
+ bool important { false };
+};
+
+class StyleDeclaration : public RefCounted<StyleDeclaration> {
+public:
+ static NonnullRefPtr<StyleDeclaration> create(Vector<StyleProperty>&& properties)
+ {
+ return adopt(*new StyleDeclaration(move(properties)));
+ }
+
+ ~StyleDeclaration();
+
+ const Vector<StyleProperty>& properties() const { return m_properties; }
+
+private:
+ explicit StyleDeclaration(Vector<StyleProperty>&&);
+
+ Vector<StyleProperty> m_properties;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/CSS/StyleInvalidator.cpp b/Userland/Libraries/LibWeb/CSS/StyleInvalidator.cpp
new file mode 100644
index 0000000000..bf5f15c332
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CSS/StyleInvalidator.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/CSS/StyleInvalidator.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/DOM/Element.h>
+
+namespace Web::CSS {
+
+StyleInvalidator::StyleInvalidator(DOM::Document& document)
+ : m_document(document)
+{
+ if (!m_document.should_invalidate_styles_on_attribute_changes())
+ return;
+ auto& style_resolver = m_document.style_resolver();
+ m_document.for_each_in_subtree_of_type<DOM::Element>([&](auto& element) {
+ m_elements_and_matching_rules_before.set(&element, style_resolver.collect_matching_rules(element));
+ return IterationDecision::Continue;
+ });
+}
+
+StyleInvalidator::~StyleInvalidator()
+{
+ if (!m_document.should_invalidate_styles_on_attribute_changes())
+ return;
+ auto& style_resolver = m_document.style_resolver();
+ m_document.for_each_in_subtree_of_type<DOM::Element>([&](auto& element) {
+ auto maybe_matching_rules_before = m_elements_and_matching_rules_before.get(&element);
+ if (!maybe_matching_rules_before.has_value()) {
+ element.set_needs_style_update(true);
+ return IterationDecision::Continue;
+ }
+ auto& matching_rules_before = maybe_matching_rules_before.value();
+ auto matching_rules_after = style_resolver.collect_matching_rules(element);
+ if (matching_rules_before.size() != matching_rules_after.size()) {
+ element.set_needs_style_update(true);
+ return IterationDecision::Continue;
+ }
+ style_resolver.sort_matching_rules(matching_rules_before);
+ style_resolver.sort_matching_rules(matching_rules_after);
+ for (size_t i = 0; i < matching_rules_before.size(); ++i) {
+ if (matching_rules_before[i].rule != matching_rules_after[i].rule) {
+ element.set_needs_style_update(true);
+ break;
+ }
+ }
+ return IterationDecision::Continue;
+ });
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/CSS/StyleInvalidator.h b/Userland/Libraries/LibWeb/CSS/StyleInvalidator.h
new file mode 100644
index 0000000000..aea59c8e65
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CSS/StyleInvalidator.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/HashMap.h>
+#include <LibWeb/CSS/StyleResolver.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/DOM/Element.h>
+
+namespace Web::CSS {
+
+class StyleInvalidator {
+public:
+ explicit StyleInvalidator(DOM::Document&);
+ ~StyleInvalidator();
+
+private:
+ DOM::Document& m_document;
+ HashMap<DOM::Element*, Vector<MatchingRule>> m_elements_and_matching_rules_before;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp
new file mode 100644
index 0000000000..76b2834771
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp
@@ -0,0 +1,484 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibCore/DirIterator.h>
+#include <LibGfx/FontDatabase.h>
+#include <LibWeb/CSS/StyleProperties.h>
+#include <LibWeb/FontCache.h>
+#include <ctype.h>
+
+namespace Web::CSS {
+
+StyleProperties::StyleProperties()
+{
+}
+
+StyleProperties::StyleProperties(const StyleProperties& other)
+ : m_property_values(other.m_property_values)
+{
+ if (other.m_font) {
+ m_font = other.m_font->clone();
+ } else {
+ m_font = nullptr;
+ }
+}
+
+NonnullRefPtr<StyleProperties> StyleProperties::clone() const
+{
+ return adopt(*new StyleProperties(*this));
+}
+
+void StyleProperties::set_property(CSS::PropertyID id, NonnullRefPtr<StyleValue> value)
+{
+ m_property_values.set((unsigned)id, move(value));
+}
+
+void StyleProperties::set_property(CSS::PropertyID id, const StringView& value)
+{
+ m_property_values.set((unsigned)id, StringStyleValue::create(value));
+}
+
+Optional<NonnullRefPtr<StyleValue>> StyleProperties::property(CSS::PropertyID id) const
+{
+ auto it = m_property_values.find((unsigned)id);
+ if (it == m_property_values.end())
+ return {};
+ return it->value;
+}
+
+Length StyleProperties::length_or_fallback(CSS::PropertyID id, const Length& fallback) const
+{
+ auto value = property(id);
+ if (!value.has_value())
+ return fallback;
+ return value.value()->to_length();
+}
+
+LengthBox StyleProperties::length_box(CSS::PropertyID left_id, CSS::PropertyID top_id, CSS::PropertyID right_id, CSS::PropertyID bottom_id, const CSS::Length& default_value) const
+{
+ LengthBox box;
+ box.left = length_or_fallback(left_id, default_value);
+ box.top = length_or_fallback(top_id, default_value);
+ box.right = length_or_fallback(right_id, default_value);
+ box.bottom = length_or_fallback(bottom_id, default_value);
+ return box;
+}
+
+String StyleProperties::string_or_fallback(CSS::PropertyID id, const StringView& fallback) const
+{
+ auto value = property(id);
+ if (!value.has_value())
+ return fallback;
+ return value.value()->to_string();
+}
+
+Color StyleProperties::color_or_fallback(CSS::PropertyID id, const DOM::Document& document, Color fallback) const
+{
+ auto value = property(id);
+ if (!value.has_value())
+ return fallback;
+ return value.value()->to_color(document);
+}
+
+void StyleProperties::load_font() const
+{
+ auto family_value = string_or_fallback(CSS::PropertyID::FontFamily, "Katica");
+ auto font_size = property(CSS::PropertyID::FontSize).value_or(IdentifierStyleValue::create(CSS::ValueID::Medium));
+ auto font_weight = property(CSS::PropertyID::FontWeight).value_or(IdentifierStyleValue::create(CSS::ValueID::Normal));
+
+ auto family_parts = family_value.split(',');
+ auto family = family_parts[0];
+
+ if (family.is_one_of("monospace", "ui-monospace"))
+ family = "Csilla";
+ else if (family.is_one_of("serif", "sans-serif", "cursive", "fantasy", "ui-serif", "ui-sans-serif", "ui-rounded"))
+ family = "Katica";
+
+ int weight = 400;
+ if (font_weight->is_identifier()) {
+ switch (static_cast<const IdentifierStyleValue&>(*font_weight).id()) {
+ case CSS::ValueID::Normal:
+ weight = 400;
+ break;
+ case CSS::ValueID::Bold:
+ weight = 700;
+ break;
+ case CSS::ValueID::Lighter:
+ // FIXME: This should be relative to the parent.
+ weight = 400;
+ break;
+ case CSS::ValueID::Bolder:
+ // FIXME: This should be relative to the parent.
+ weight = 700;
+ break;
+ default:
+ break;
+ }
+ } else if (font_weight->is_length()) {
+ // FIXME: This isn't really a length, it's a numeric value..
+ int font_weight_integer = font_weight->to_length().raw_value();
+ if (font_weight_integer <= 400)
+ weight = 400;
+ if (font_weight_integer <= 700)
+ weight = 700;
+ weight = 900;
+ }
+
+ int size = 10;
+ if (font_size->is_identifier()) {
+ switch (static_cast<const IdentifierStyleValue&>(*font_size).id()) {
+ case CSS::ValueID::XxSmall:
+ case CSS::ValueID::XSmall:
+ case CSS::ValueID::Small:
+ case CSS::ValueID::Medium:
+ // FIXME: Should be based on "user's default font size"
+ size = 10;
+ break;
+ case CSS::ValueID::Large:
+ case CSS::ValueID::XLarge:
+ case CSS::ValueID::XxLarge:
+ case CSS::ValueID::XxxLarge:
+ // FIXME: Should be based on "user's default font size"
+ size = 12;
+ break;
+ case CSS::ValueID::Smaller:
+ // FIXME: This should be relative to the parent.
+ size = 10;
+ break;
+ case CSS::ValueID::Larger:
+ // FIXME: This should be relative to the parent.
+ size = 12;
+ break;
+
+ default:
+ break;
+ }
+ } else if (font_size->is_length()) {
+ // FIXME: This isn't really a length, it's a numeric value..
+ int font_size_integer = font_size->to_length().raw_value();
+ if (font_size_integer <= 10)
+ size = 10;
+ else if (font_size_integer <= 12)
+ size = 12;
+ else
+ size = 14;
+ }
+
+ FontSelector font_selector { family, size, weight };
+
+ auto found_font = FontCache::the().get(font_selector);
+ if (found_font) {
+ m_font = found_font;
+ return;
+ }
+
+ Gfx::FontDatabase::the().for_each_font([&](auto& font) {
+ if (font.family() == family && font.weight() == weight && font.presentation_size() == size)
+ found_font = font;
+ });
+
+ if (!found_font) {
+ dbgln("Font not found: '{}' {} {}", family, size, weight);
+ found_font = Gfx::FontDatabase::default_font();
+ }
+
+ m_font = found_font;
+ FontCache::the().set(font_selector, *m_font);
+}
+
+float StyleProperties::line_height(const Layout::Node& layout_node) const
+{
+ auto line_height_length = length_or_fallback(CSS::PropertyID::LineHeight, Length::make_auto());
+ if (line_height_length.is_absolute())
+ return (float)line_height_length.to_px(layout_node);
+ return (float)font().glyph_height() * 1.4f;
+}
+
+Optional<int> StyleProperties::z_index() const
+{
+ auto value = property(CSS::PropertyID::ZIndex);
+ if (!value.has_value())
+ return {};
+ return static_cast<int>(value.value()->to_length().raw_value());
+}
+
+Optional<CSS::Position> StyleProperties::position() const
+{
+ auto value = property(CSS::PropertyID::Position);
+ if (!value.has_value() || !value.value()->is_identifier())
+ return {};
+ switch (static_cast<const IdentifierStyleValue&>(*value.value()).id()) {
+ case CSS::ValueID::Static:
+ return CSS::Position::Static;
+ case CSS::ValueID::Relative:
+ return CSS::Position::Relative;
+ case CSS::ValueID::Absolute:
+ return CSS::Position::Absolute;
+ case CSS::ValueID::Fixed:
+ return CSS::Position::Fixed;
+ case CSS::ValueID::Sticky:
+ return CSS::Position::Sticky;
+ default:
+ return {};
+ }
+}
+
+bool StyleProperties::operator==(const StyleProperties& other) const
+{
+ if (m_property_values.size() != other.m_property_values.size())
+ return false;
+
+ for (auto& it : m_property_values) {
+ auto jt = other.m_property_values.find(it.key);
+ if (jt == other.m_property_values.end())
+ return false;
+ auto& my_value = *it.value;
+ auto& other_value = *jt->value;
+ if (my_value.type() != other_value.type())
+ return false;
+ if (my_value != other_value)
+ return false;
+ }
+
+ return true;
+}
+
+Optional<CSS::TextAlign> StyleProperties::text_align() const
+{
+ auto value = property(CSS::PropertyID::TextAlign);
+ if (!value.has_value() || !value.value()->is_identifier())
+ return {};
+
+ switch (static_cast<const IdentifierStyleValue&>(*value.value()).id()) {
+ case CSS::ValueID::Left:
+ return CSS::TextAlign::Left;
+ case CSS::ValueID::Center:
+ return CSS::TextAlign::Center;
+ case CSS::ValueID::Right:
+ return CSS::TextAlign::Right;
+ case CSS::ValueID::Justify:
+ return CSS::TextAlign::Justify;
+ case CSS::ValueID::LibwebCenter:
+ return CSS::TextAlign::LibwebCenter;
+ default:
+ return {};
+ }
+}
+
+Optional<CSS::WhiteSpace> StyleProperties::white_space() const
+{
+ auto value = property(CSS::PropertyID::WhiteSpace);
+ if (!value.has_value() || !value.value()->is_identifier())
+ return {};
+ switch (static_cast<const IdentifierStyleValue&>(*value.value()).id()) {
+ case CSS::ValueID::Normal:
+ return CSS::WhiteSpace::Normal;
+ case CSS::ValueID::Nowrap:
+ return CSS::WhiteSpace::Nowrap;
+ case CSS::ValueID::Pre:
+ return CSS::WhiteSpace::Pre;
+ case CSS::ValueID::PreLine:
+ return CSS::WhiteSpace::PreLine;
+ case CSS::ValueID::PreWrap:
+ return CSS::WhiteSpace::PreWrap;
+ default:
+ return {};
+ }
+}
+
+Optional<CSS::LineStyle> StyleProperties::line_style(CSS::PropertyID property_id) const
+{
+ auto value = property(property_id);
+ if (!value.has_value() || !value.value()->is_identifier())
+ return {};
+ switch (static_cast<const IdentifierStyleValue&>(*value.value()).id()) {
+ case CSS::ValueID::None:
+ return CSS::LineStyle::None;
+ case CSS::ValueID::Hidden:
+ return CSS::LineStyle::Hidden;
+ case CSS::ValueID::Dotted:
+ return CSS::LineStyle::Dotted;
+ case CSS::ValueID::Dashed:
+ return CSS::LineStyle::Dashed;
+ case CSS::ValueID::Solid:
+ return CSS::LineStyle::Solid;
+ case CSS::ValueID::Double:
+ return CSS::LineStyle::Double;
+ case CSS::ValueID::Groove:
+ return CSS::LineStyle::Groove;
+ case CSS::ValueID::Ridge:
+ return CSS::LineStyle::Ridge;
+ case CSS::ValueID::Inset:
+ return CSS::LineStyle::Inset;
+ case CSS::ValueID::Outset:
+ return CSS::LineStyle::Outset;
+ default:
+ return {};
+ }
+}
+
+Optional<CSS::Float> StyleProperties::float_() const
+{
+ auto value = property(CSS::PropertyID::Float);
+ if (!value.has_value() || !value.value()->is_identifier())
+ return {};
+ switch (static_cast<const IdentifierStyleValue&>(*value.value()).id()) {
+ case CSS::ValueID::None:
+ return CSS::Float::None;
+ case CSS::ValueID::Left:
+ return CSS::Float::Left;
+ case CSS::ValueID::Right:
+ return CSS::Float::Right;
+ default:
+ return {};
+ }
+}
+
+Optional<CSS::Clear> StyleProperties::clear() const
+{
+ auto value = property(CSS::PropertyID::Clear);
+ if (!value.has_value() || !value.value()->is_identifier())
+ return {};
+ switch (static_cast<const IdentifierStyleValue&>(*value.value()).id()) {
+ case CSS::ValueID::None:
+ return CSS::Clear::None;
+ case CSS::ValueID::Left:
+ return CSS::Clear::Left;
+ case CSS::ValueID::Right:
+ return CSS::Clear::Right;
+ case CSS::ValueID::Both:
+ return CSS::Clear::Both;
+ default:
+ return {};
+ }
+}
+
+CSS::Display StyleProperties::display() const
+{
+ auto value = property(CSS::PropertyID::Display);
+ if (!value.has_value() || !value.value()->is_identifier())
+ return CSS::Display::Inline;
+ switch (static_cast<const IdentifierStyleValue&>(*value.value()).id()) {
+ case CSS::ValueID::None:
+ return CSS::Display::None;
+ case CSS::ValueID::Block:
+ return CSS::Display::Block;
+ case CSS::ValueID::Inline:
+ return CSS::Display::Inline;
+ case CSS::ValueID::InlineBlock:
+ return CSS::Display::InlineBlock;
+ case CSS::ValueID::ListItem:
+ return CSS::Display::ListItem;
+ case CSS::ValueID::Table:
+ return CSS::Display::Table;
+ case CSS::ValueID::TableRow:
+ return CSS::Display::TableRow;
+ case CSS::ValueID::TableCell:
+ return CSS::Display::TableCell;
+ case CSS::ValueID::TableColumn:
+ return CSS::Display::TableColumn;
+ case CSS::ValueID::TableColumnGroup:
+ return CSS::Display::TableColumnGroup;
+ case CSS::ValueID::TableCaption:
+ return CSS::Display::TableCaption;
+ case CSS::ValueID::TableRowGroup:
+ return CSS::Display::TableRowGroup;
+ case CSS::ValueID::TableHeaderGroup:
+ return CSS::Display::TableHeaderGroup;
+ case CSS::ValueID::TableFooterGroup:
+ return CSS::Display::TableFooterGroup;
+ default:
+ return CSS::Display::Block;
+ }
+}
+
+Optional<CSS::TextDecorationLine> StyleProperties::text_decoration_line() const
+{
+ auto value = property(CSS::PropertyID::TextDecorationLine);
+ if (!value.has_value() || !value.value()->is_identifier())
+ return {};
+ switch (static_cast<const IdentifierStyleValue&>(*value.value()).id()) {
+ case CSS::ValueID::None:
+ return CSS::TextDecorationLine::None;
+ case CSS::ValueID::Underline:
+ return CSS::TextDecorationLine::Underline;
+ case CSS::ValueID::Overline:
+ return CSS::TextDecorationLine::Overline;
+ case CSS::ValueID::LineThrough:
+ return CSS::TextDecorationLine::LineThrough;
+ case CSS::ValueID::Blink:
+ return CSS::TextDecorationLine::Blink;
+ default:
+ return {};
+ }
+}
+
+Optional<CSS::TextTransform> StyleProperties::text_transform() const
+{
+ auto value = property(CSS::PropertyID::TextTransform);
+ if (!value.has_value())
+ return {};
+ switch (value.value()->to_identifier()) {
+ case CSS::ValueID::None:
+ return CSS::TextTransform::None;
+ case CSS::ValueID::Lowercase:
+ return CSS::TextTransform::Lowercase;
+ case CSS::ValueID::Uppercase:
+ return CSS::TextTransform::Uppercase;
+ case CSS::ValueID::Capitalize:
+ return CSS::TextTransform::Capitalize;
+ case CSS::ValueID::FullWidth:
+ return CSS::TextTransform::FullWidth;
+ case CSS::ValueID::FullSizeKana:
+ return CSS::TextTransform::FullSizeKana;
+ default:
+ return {};
+ }
+}
+
+Optional<CSS::ListStyleType> StyleProperties::list_style_type() const
+{
+ auto value = property(CSS::PropertyID::ListStyleType);
+ if (!value.has_value())
+ return {};
+
+ switch (value.value()->to_identifier()) {
+ case CSS::ValueID::None:
+ return CSS::ListStyleType::None;
+ case CSS::ValueID::Disc:
+ return CSS::ListStyleType::Disc;
+ case CSS::ValueID::Circle:
+ return CSS::ListStyleType::Circle;
+ case CSS::ValueID::Square:
+ return CSS::ListStyleType::Square;
+ case CSS::ValueID::Decimal:
+ return CSS::ListStyleType::Decimal;
+ default:
+ return {};
+ }
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.h b/Userland/Libraries/LibWeb/CSS/StyleProperties.h
new file mode 100644
index 0000000000..29da1ab073
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/HashMap.h>
+#include <AK/NonnullRefPtr.h>
+#include <LibGfx/Font.h>
+#include <LibGfx/Forward.h>
+#include <LibWeb/CSS/LengthBox.h>
+#include <LibWeb/CSS/StyleValue.h>
+
+namespace Web::CSS {
+
+class StyleProperties : public RefCounted<StyleProperties> {
+public:
+ StyleProperties();
+
+ explicit StyleProperties(const StyleProperties&);
+
+ static NonnullRefPtr<StyleProperties> create() { return adopt(*new StyleProperties); }
+
+ NonnullRefPtr<StyleProperties> clone() const;
+
+ template<typename Callback>
+ inline void for_each_property(Callback callback) const
+ {
+ for (auto& it : m_property_values)
+ callback((CSS::PropertyID)it.key, *it.value);
+ }
+
+ void set_property(CSS::PropertyID, NonnullRefPtr<StyleValue> value);
+ void set_property(CSS::PropertyID, const StringView&);
+ Optional<NonnullRefPtr<StyleValue>> property(CSS::PropertyID) const;
+
+ Length length_or_fallback(CSS::PropertyID, const Length& fallback) const;
+ LengthBox length_box(CSS::PropertyID left_id, CSS::PropertyID top_id, CSS::PropertyID right_id, CSS::PropertyID bottom_id, const CSS::Length& default_value) const;
+ String string_or_fallback(CSS::PropertyID, const StringView& fallback) const;
+ Color color_or_fallback(CSS::PropertyID, const DOM::Document&, Color fallback) const;
+ Optional<CSS::TextAlign> text_align() const;
+ CSS::Display display() const;
+ Optional<CSS::Float> float_() const;
+ Optional<CSS::Clear> clear() const;
+ Optional<CSS::WhiteSpace> white_space() const;
+ Optional<CSS::LineStyle> line_style(CSS::PropertyID) const;
+ Optional<CSS::TextDecorationLine> text_decoration_line() const;
+ Optional<CSS::TextTransform> text_transform() const;
+ Optional<CSS::ListStyleType> list_style_type() const;
+
+ const Gfx::Font& font() const
+ {
+ if (!m_font)
+ load_font();
+ return *m_font;
+ }
+
+ float line_height(const Layout::Node&) const;
+
+ bool operator==(const StyleProperties&) const;
+ bool operator!=(const StyleProperties& other) const { return !(*this == other); }
+
+ Optional<CSS::Position> position() const;
+ Optional<int> z_index() const;
+
+private:
+ HashMap<unsigned, NonnullRefPtr<StyleValue>> m_property_values;
+
+ void load_font() const;
+
+ mutable RefPtr<Gfx::Font> m_font;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/CSS/StyleResolver.cpp b/Userland/Libraries/LibWeb/CSS/StyleResolver.cpp
new file mode 100644
index 0000000000..21715ee92b
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CSS/StyleResolver.cpp
@@ -0,0 +1,598 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/QuickSort.h>
+#include <LibWeb/CSS/Parser/CSSParser.h>
+#include <LibWeb/CSS/SelectorEngine.h>
+#include <LibWeb/CSS/StyleResolver.h>
+#include <LibWeb/CSS/StyleSheet.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/DOM/Element.h>
+#include <LibWeb/Dump.h>
+#include <ctype.h>
+#include <stdio.h>
+
+namespace Web::CSS {
+
+StyleResolver::StyleResolver(DOM::Document& document)
+ : m_document(document)
+{
+}
+
+StyleResolver::~StyleResolver()
+{
+}
+
+static StyleSheet& default_stylesheet()
+{
+ static StyleSheet* sheet;
+ if (!sheet) {
+ extern const char default_stylesheet_source[];
+ String css = default_stylesheet_source;
+ sheet = parse_css(CSS::ParsingContext(), css).leak_ref();
+ }
+ return *sheet;
+}
+
+static StyleSheet& quirks_mode_stylesheet()
+{
+ static StyleSheet* sheet;
+ if (!sheet) {
+ extern const char quirks_mode_stylesheet_source[];
+ String css = quirks_mode_stylesheet_source;
+ sheet = parse_css(CSS::ParsingContext(), css).leak_ref();
+ }
+ return *sheet;
+}
+
+template<typename Callback>
+void StyleResolver::for_each_stylesheet(Callback callback) const
+{
+ callback(default_stylesheet());
+ if (document().in_quirks_mode())
+ callback(quirks_mode_stylesheet());
+ for (auto& sheet : document().style_sheets().sheets()) {
+ callback(sheet);
+ }
+}
+
+Vector<MatchingRule> StyleResolver::collect_matching_rules(const DOM::Element& element) const
+{
+ Vector<MatchingRule> matching_rules;
+
+ size_t style_sheet_index = 0;
+ for_each_stylesheet([&](auto& sheet) {
+ size_t rule_index = 0;
+ for (auto& rule : sheet.rules()) {
+ size_t selector_index = 0;
+ for (auto& selector : rule.selectors()) {
+ if (SelectorEngine::matches(selector, element)) {
+ matching_rules.append({ rule, style_sheet_index, rule_index, selector_index });
+ break;
+ }
+ ++selector_index;
+ }
+ ++rule_index;
+ }
+ ++style_sheet_index;
+ });
+
+ return matching_rules;
+}
+
+void StyleResolver::sort_matching_rules(Vector<MatchingRule>& matching_rules) const
+{
+ quick_sort(matching_rules, [&](MatchingRule& a, MatchingRule& b) {
+ auto& a_selector = a.rule->selectors()[a.selector_index];
+ auto& b_selector = b.rule->selectors()[b.selector_index];
+ auto a_specificity = a_selector.specificity();
+ auto b_specificity = b_selector.specificity();
+ if (a_selector.specificity() == b_selector.specificity()) {
+ if (a.style_sheet_index == b.style_sheet_index)
+ return a.rule_index < b.rule_index;
+ return a.style_sheet_index < b.style_sheet_index;
+ }
+ return a_specificity < b_specificity;
+ });
+}
+
+bool StyleResolver::is_inherited_property(CSS::PropertyID property_id)
+{
+ static HashTable<CSS::PropertyID> inherited_properties;
+ if (inherited_properties.is_empty()) {
+ inherited_properties.set(CSS::PropertyID::BorderCollapse);
+ inherited_properties.set(CSS::PropertyID::BorderSpacing);
+ inherited_properties.set(CSS::PropertyID::Color);
+ inherited_properties.set(CSS::PropertyID::FontFamily);
+ inherited_properties.set(CSS::PropertyID::FontSize);
+ inherited_properties.set(CSS::PropertyID::FontStyle);
+ inherited_properties.set(CSS::PropertyID::FontVariant);
+ inherited_properties.set(CSS::PropertyID::FontWeight);
+ inherited_properties.set(CSS::PropertyID::LetterSpacing);
+ inherited_properties.set(CSS::PropertyID::LineHeight);
+ inherited_properties.set(CSS::PropertyID::ListStyle);
+ inherited_properties.set(CSS::PropertyID::ListStyleImage);
+ inherited_properties.set(CSS::PropertyID::ListStylePosition);
+ inherited_properties.set(CSS::PropertyID::ListStyleType);
+ inherited_properties.set(CSS::PropertyID::TextAlign);
+ inherited_properties.set(CSS::PropertyID::TextIndent);
+ inherited_properties.set(CSS::PropertyID::TextTransform);
+ inherited_properties.set(CSS::PropertyID::Visibility);
+ inherited_properties.set(CSS::PropertyID::WhiteSpace);
+ inherited_properties.set(CSS::PropertyID::WordSpacing);
+
+ // FIXME: This property is not supposed to be inherited, but we currently
+ // rely on inheritance to propagate decorations into line boxes.
+ inherited_properties.set(CSS::PropertyID::TextDecorationLine);
+ }
+ return inherited_properties.contains(property_id);
+}
+
+static Vector<String> split_on_whitespace(const StringView& string)
+{
+ if (string.is_empty())
+ return {};
+
+ Vector<String> v;
+ size_t substart = 0;
+ for (size_t i = 0; i < string.length(); ++i) {
+ char ch = string.characters_without_null_termination()[i];
+ if (isspace(ch)) {
+ size_t sublen = i - substart;
+ if (sublen != 0)
+ v.append(string.substring_view(substart, sublen));
+ substart = i + 1;
+ }
+ }
+ size_t taillen = string.length() - substart;
+ if (taillen != 0)
+ v.append(string.substring_view(substart, taillen));
+ return v;
+}
+
+enum class Edge {
+ Top,
+ Right,
+ Bottom,
+ Left,
+ All,
+};
+
+static bool contains(Edge a, Edge b)
+{
+ return a == b || b == Edge::All;
+}
+
+static inline void set_property_border_width(StyleProperties& style, const StyleValue& value, Edge edge)
+{
+ ASSERT(value.is_length());
+ if (contains(Edge::Top, edge))
+ style.set_property(CSS::PropertyID::BorderTopWidth, value);
+ if (contains(Edge::Right, edge))
+ style.set_property(CSS::PropertyID::BorderRightWidth, value);
+ if (contains(Edge::Bottom, edge))
+ style.set_property(CSS::PropertyID::BorderBottomWidth, value);
+ if (contains(Edge::Left, edge))
+ style.set_property(CSS::PropertyID::BorderLeftWidth, value);
+}
+
+static inline void set_property_border_color(StyleProperties& style, const StyleValue& value, Edge edge)
+{
+ ASSERT(value.is_color());
+ if (contains(Edge::Top, edge))
+ style.set_property(CSS::PropertyID::BorderTopColor, value);
+ if (contains(Edge::Right, edge))
+ style.set_property(CSS::PropertyID::BorderRightColor, value);
+ if (contains(Edge::Bottom, edge))
+ style.set_property(CSS::PropertyID::BorderBottomColor, value);
+ if (contains(Edge::Left, edge))
+ style.set_property(CSS::PropertyID::BorderLeftColor, value);
+}
+
+static inline void set_property_border_style(StyleProperties& style, const StyleValue& value, Edge edge)
+{
+ ASSERT(value.is_string());
+ if (contains(Edge::Top, edge))
+ style.set_property(CSS::PropertyID::BorderTopStyle, value);
+ if (contains(Edge::Right, edge))
+ style.set_property(CSS::PropertyID::BorderRightStyle, value);
+ if (contains(Edge::Bottom, edge))
+ style.set_property(CSS::PropertyID::BorderBottomStyle, value);
+ if (contains(Edge::Left, edge))
+ style.set_property(CSS::PropertyID::BorderLeftStyle, value);
+}
+
+static void set_property_expanding_shorthands(StyleProperties& style, CSS::PropertyID property_id, const StyleValue& value, DOM::Document& document)
+{
+ CSS::ParsingContext context(document);
+
+ if (property_id == CSS::PropertyID::TextDecoration) {
+ switch (value.to_identifier()) {
+ case CSS::ValueID::None:
+ case CSS::ValueID::Underline:
+ case CSS::ValueID::Overline:
+ case CSS::ValueID::LineThrough:
+ case CSS::ValueID::Blink:
+ set_property_expanding_shorthands(style, CSS::PropertyID::TextDecorationLine, value, document);
+ default:
+ break;
+ }
+ return;
+ }
+
+ if (property_id == CSS::PropertyID::Border) {
+ set_property_expanding_shorthands(style, CSS::PropertyID::BorderTop, value, document);
+ set_property_expanding_shorthands(style, CSS::PropertyID::BorderRight, value, document);
+ set_property_expanding_shorthands(style, CSS::PropertyID::BorderBottom, value, document);
+ set_property_expanding_shorthands(style, CSS::PropertyID::BorderLeft, value, document);
+ return;
+ }
+
+ if (property_id == CSS::PropertyID::BorderTop
+ || property_id == CSS::PropertyID::BorderRight
+ || property_id == CSS::PropertyID::BorderBottom
+ || property_id == CSS::PropertyID::BorderLeft) {
+
+ Edge edge = Edge::All;
+ switch (property_id) {
+ case CSS::PropertyID::BorderTop:
+ edge = Edge::Top;
+ break;
+ case CSS::PropertyID::BorderRight:
+ edge = Edge::Right;
+ break;
+ case CSS::PropertyID::BorderBottom:
+ edge = Edge::Bottom;
+ break;
+ case CSS::PropertyID::BorderLeft:
+ edge = Edge::Left;
+ break;
+ default:
+ break;
+ }
+
+ auto parts = split_on_whitespace(value.to_string());
+ if (value.is_length()) {
+ set_property_border_width(style, value, edge);
+ return;
+ }
+ if (value.is_color()) {
+ set_property_border_color(style, value, edge);
+ return;
+ }
+ if (value.is_string()) {
+ auto parts = split_on_whitespace(value.to_string());
+
+ if (parts.size() == 1) {
+ if (auto value = parse_line_style(context, parts[0])) {
+ set_property_border_style(style, value.release_nonnull(), edge);
+ set_property_border_color(style, ColorStyleValue::create(Gfx::Color::Black), edge);
+ set_property_border_width(style, LengthStyleValue::create(Length(3, Length::Type::Px)), edge);
+ return;
+ }
+ }
+
+ RefPtr<LengthStyleValue> line_width_value;
+ RefPtr<ColorStyleValue> color_value;
+ RefPtr<StringStyleValue> line_style_value;
+
+ for (auto& part : parts) {
+ if (auto value = parse_line_width(context, part)) {
+ if (line_width_value)
+ return;
+ line_width_value = move(value);
+ continue;
+ }
+ if (auto value = parse_color(context, part)) {
+ if (color_value)
+ return;
+ color_value = move(value);
+ continue;
+ }
+ if (auto value = parse_line_style(context, part)) {
+ if (line_style_value)
+ return;
+ line_style_value = move(value);
+ continue;
+ }
+ }
+
+ if (line_width_value)
+ set_property_border_width(style, line_width_value.release_nonnull(), edge);
+ if (color_value)
+ set_property_border_color(style, color_value.release_nonnull(), edge);
+ if (line_style_value)
+ set_property_border_style(style, line_style_value.release_nonnull(), edge);
+
+ return;
+ }
+ return;
+ }
+
+ if (property_id == CSS::PropertyID::BorderStyle) {
+ auto parts = split_on_whitespace(value.to_string());
+ if (value.is_string() && parts.size() == 3) {
+ auto top = parse_css_value(context, parts[0]);
+ auto right = parse_css_value(context, parts[1]);
+ auto bottom = parse_css_value(context, parts[2]);
+ auto left = parse_css_value(context, parts[1]);
+ if (top && right && bottom && left) {
+ style.set_property(CSS::PropertyID::BorderTopStyle, *top);
+ style.set_property(CSS::PropertyID::BorderRightStyle, *right);
+ style.set_property(CSS::PropertyID::BorderBottomStyle, *bottom);
+ style.set_property(CSS::PropertyID::BorderLeftStyle, *left);
+ }
+ } else {
+ style.set_property(CSS::PropertyID::BorderTopStyle, value);
+ style.set_property(CSS::PropertyID::BorderRightStyle, value);
+ style.set_property(CSS::PropertyID::BorderBottomStyle, value);
+ style.set_property(CSS::PropertyID::BorderLeftStyle, value);
+ }
+ return;
+ }
+
+ if (property_id == CSS::PropertyID::BorderWidth) {
+ auto parts = split_on_whitespace(value.to_string());
+ if (value.is_string() && parts.size() == 2) {
+ auto vertical_border_width = parse_css_value(context, parts[0]);
+ auto horizontal_border_width = parse_css_value(context, parts[1]);
+ if (vertical_border_width && horizontal_border_width) {
+ style.set_property(CSS::PropertyID::BorderTopWidth, *vertical_border_width);
+ style.set_property(CSS::PropertyID::BorderRightWidth, *horizontal_border_width);
+ style.set_property(CSS::PropertyID::BorderBottomWidth, *vertical_border_width);
+ style.set_property(CSS::PropertyID::BorderLeftWidth, *horizontal_border_width);
+ }
+ } else {
+ style.set_property(CSS::PropertyID::BorderTopWidth, value);
+ style.set_property(CSS::PropertyID::BorderRightWidth, value);
+ style.set_property(CSS::PropertyID::BorderBottomWidth, value);
+ style.set_property(CSS::PropertyID::BorderLeftWidth, value);
+ }
+ return;
+ }
+
+ if (property_id == CSS::PropertyID::BorderColor) {
+ auto parts = split_on_whitespace(value.to_string());
+ if (value.is_string() && parts.size() == 4) {
+ auto top = parse_css_value(context, parts[0]);
+ auto right = parse_css_value(context, parts[1]);
+ auto bottom = parse_css_value(context, parts[2]);
+ auto left = parse_css_value(context, parts[3]);
+ if (top && right && bottom && left) {
+ style.set_property(CSS::PropertyID::BorderTopColor, *top);
+ style.set_property(CSS::PropertyID::BorderRightColor, *right);
+ style.set_property(CSS::PropertyID::BorderBottomColor, *bottom);
+ style.set_property(CSS::PropertyID::BorderLeftColor, *left);
+ }
+ } else {
+ style.set_property(CSS::PropertyID::BorderTopColor, value);
+ style.set_property(CSS::PropertyID::BorderRightColor, value);
+ style.set_property(CSS::PropertyID::BorderBottomColor, value);
+ style.set_property(CSS::PropertyID::BorderLeftColor, value);
+ }
+ return;
+ }
+
+ if (property_id == CSS::PropertyID::Background) {
+ if (value.is_identifier() && static_cast<const IdentifierStyleValue&>(value).id() == CSS::ValueID::None) {
+ style.set_property(CSS::PropertyID::BackgroundColor, ColorStyleValue::create(Color::Transparent));
+ return;
+ }
+ auto parts = split_on_whitespace(value.to_string());
+ NonnullRefPtrVector<StyleValue> values;
+ for (auto& part : parts) {
+ auto value = parse_css_value(context, part);
+ if (!value)
+ return;
+ values.append(value.release_nonnull());
+ }
+
+ // HACK: Disallow more than one color value in a 'background' shorthand
+ size_t color_value_count = 0;
+ for (auto& value : values)
+ color_value_count += value.is_color();
+
+ if (values[0].is_color() && color_value_count == 1)
+ style.set_property(CSS::PropertyID::BackgroundColor, values[0]);
+
+ for (auto& value : values) {
+ if (!value.is_string())
+ continue;
+ auto string = value.to_string();
+ set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundImage, value, document);
+ }
+ return;
+ }
+
+ if (property_id == CSS::PropertyID::BackgroundImage) {
+ if (!value.is_string())
+ return;
+ auto string = value.to_string();
+ if (!string.starts_with("url("))
+ return;
+ if (!string.ends_with(')'))
+ return;
+ auto url = string.substring_view(4, string.length() - 5);
+ if (url.length() >= 2 && url.starts_with('"') && url.ends_with('"'))
+ url = url.substring_view(1, url.length() - 2);
+ else if (url.length() >= 2 && url.starts_with('\'') && url.ends_with('\''))
+ url = url.substring_view(1, url.length() - 2);
+
+ auto background_image_value = ImageStyleValue::create(document.complete_url(url), document);
+ style.set_property(CSS::PropertyID::BackgroundImage, move(background_image_value));
+ return;
+ }
+
+ if (property_id == CSS::PropertyID::Margin) {
+ if (value.is_length()) {
+ style.set_property(CSS::PropertyID::MarginTop, value);
+ style.set_property(CSS::PropertyID::MarginRight, value);
+ style.set_property(CSS::PropertyID::MarginBottom, value);
+ style.set_property(CSS::PropertyID::MarginLeft, value);
+ return;
+ }
+ if (value.is_string()) {
+ auto parts = split_on_whitespace(value.to_string());
+ if (value.is_string() && parts.size() == 2) {
+ auto vertical = parse_css_value(context, parts[0]);
+ auto horizontal = parse_css_value(context, parts[1]);
+ if (vertical && horizontal) {
+ style.set_property(CSS::PropertyID::MarginTop, *vertical);
+ style.set_property(CSS::PropertyID::MarginBottom, *vertical);
+ style.set_property(CSS::PropertyID::MarginLeft, *horizontal);
+ style.set_property(CSS::PropertyID::MarginRight, *horizontal);
+ }
+ return;
+ }
+ if (value.is_string() && parts.size() == 3) {
+ auto top = parse_css_value(context, parts[0]);
+ auto horizontal = parse_css_value(context, parts[1]);
+ auto bottom = parse_css_value(context, parts[2]);
+ if (top && horizontal && bottom) {
+ style.set_property(CSS::PropertyID::MarginTop, *top);
+ style.set_property(CSS::PropertyID::MarginBottom, *bottom);
+ style.set_property(CSS::PropertyID::MarginLeft, *horizontal);
+ style.set_property(CSS::PropertyID::MarginRight, *horizontal);
+ }
+ return;
+ }
+ if (value.is_string() && parts.size() == 4) {
+ auto top = parse_css_value(context, parts[0]);
+ auto right = parse_css_value(context, parts[1]);
+ auto bottom = parse_css_value(context, parts[2]);
+ auto left = parse_css_value(context, parts[3]);
+ if (top && right && bottom && left) {
+ style.set_property(CSS::PropertyID::MarginTop, *top);
+ style.set_property(CSS::PropertyID::MarginBottom, *bottom);
+ style.set_property(CSS::PropertyID::MarginLeft, *left);
+ style.set_property(CSS::PropertyID::MarginRight, *right);
+ }
+ return;
+ }
+ dbg() << "Unsure what to do with CSS margin value '" << value.to_string() << "'";
+ return;
+ }
+ return;
+ }
+
+ if (property_id == CSS::PropertyID::Padding) {
+ if (value.is_length()) {
+ style.set_property(CSS::PropertyID::PaddingTop, value);
+ style.set_property(CSS::PropertyID::PaddingRight, value);
+ style.set_property(CSS::PropertyID::PaddingBottom, value);
+ style.set_property(CSS::PropertyID::PaddingLeft, value);
+ return;
+ }
+ if (value.is_string()) {
+ auto parts = split_on_whitespace(value.to_string());
+ if (value.is_string() && parts.size() == 2) {
+ auto vertical = parse_css_value(context, parts[0]);
+ auto horizontal = parse_css_value(context, parts[1]);
+ if (vertical && horizontal) {
+ style.set_property(CSS::PropertyID::PaddingTop, *vertical);
+ style.set_property(CSS::PropertyID::PaddingBottom, *vertical);
+ style.set_property(CSS::PropertyID::PaddingLeft, *horizontal);
+ style.set_property(CSS::PropertyID::PaddingRight, *horizontal);
+ }
+ return;
+ }
+ if (value.is_string() && parts.size() == 3) {
+ auto top = parse_css_value(context, parts[0]);
+ auto horizontal = parse_css_value(context, parts[1]);
+ auto bottom = parse_css_value(context, parts[2]);
+ if (top && bottom && horizontal) {
+ style.set_property(CSS::PropertyID::PaddingTop, *top);
+ style.set_property(CSS::PropertyID::PaddingBottom, *bottom);
+ style.set_property(CSS::PropertyID::PaddingLeft, *horizontal);
+ style.set_property(CSS::PropertyID::PaddingRight, *horizontal);
+ }
+ return;
+ }
+ if (value.is_string() && parts.size() == 4) {
+ auto top = parse_css_value(context, parts[0]);
+ auto right = parse_css_value(context, parts[1]);
+ auto bottom = parse_css_value(context, parts[2]);
+ auto left = parse_css_value(context, parts[3]);
+ if (top && bottom && left && right) {
+ style.set_property(CSS::PropertyID::PaddingTop, *top);
+ style.set_property(CSS::PropertyID::PaddingBottom, *bottom);
+ style.set_property(CSS::PropertyID::PaddingLeft, *left);
+ style.set_property(CSS::PropertyID::PaddingRight, *right);
+ }
+ return;
+ }
+ dbg() << "Unsure what to do with CSS padding value '" << value.to_string() << "'";
+ return;
+ }
+ return;
+ }
+
+ if (property_id == CSS::PropertyID::ListStyle) {
+ auto parts = split_on_whitespace(value.to_string());
+ if (!parts.is_empty()) {
+ auto value = parse_css_value(context, parts[0]);
+ if (!value)
+ return;
+ style.set_property(CSS::PropertyID::ListStyleType, value.release_nonnull());
+ }
+ return;
+ }
+
+ style.set_property(property_id, value);
+}
+
+NonnullRefPtr<StyleProperties> StyleResolver::resolve_style(const DOM::Element& element) const
+{
+ auto style = StyleProperties::create();
+
+ if (auto* parent_style = element.parent_element() ? element.parent_element()->specified_css_values() : nullptr) {
+ parent_style->for_each_property([&](auto property_id, auto& value) {
+ if (is_inherited_property(property_id))
+ set_property_expanding_shorthands(style, property_id, value, m_document);
+ });
+ }
+
+ element.apply_presentational_hints(*style);
+
+ auto matching_rules = collect_matching_rules(element);
+ sort_matching_rules(matching_rules);
+
+ for (auto& match : matching_rules) {
+ for (auto& property : match.rule->declaration().properties()) {
+ set_property_expanding_shorthands(style, property.property_id, property.value, m_document);
+ }
+ }
+
+ if (auto* inline_style = element.inline_style()) {
+ for (auto& property : inline_style->properties()) {
+ set_property_expanding_shorthands(style, property.property_id, property.value, m_document);
+ }
+ }
+
+ return style;
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/CSS/StyleResolver.h b/Userland/Libraries/LibWeb/CSS/StyleResolver.h
new file mode 100644
index 0000000000..0dde35a261
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CSS/StyleResolver.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/NonnullRefPtrVector.h>
+#include <AK/OwnPtr.h>
+#include <LibWeb/CSS/StyleProperties.h>
+#include <LibWeb/Forward.h>
+
+namespace Web::CSS {
+
+struct MatchingRule {
+ RefPtr<StyleRule> rule;
+ size_t style_sheet_index { 0 };
+ size_t rule_index { 0 };
+ size_t selector_index { 0 };
+};
+
+class StyleResolver {
+public:
+ explicit StyleResolver(DOM::Document&);
+ ~StyleResolver();
+
+ DOM::Document& document() { return m_document; }
+ const DOM::Document& document() const { return m_document; }
+
+ NonnullRefPtr<StyleProperties> resolve_style(const DOM::Element&) const;
+
+ Vector<MatchingRule> collect_matching_rules(const DOM::Element&) const;
+ void sort_matching_rules(Vector<MatchingRule>&) const;
+
+ static bool is_inherited_property(CSS::PropertyID);
+
+private:
+ template<typename Callback>
+ void for_each_stylesheet(Callback) const;
+
+ DOM::Document& m_document;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/CSS/StyleRule.cpp b/Userland/Libraries/LibWeb/CSS/StyleRule.cpp
new file mode 100644
index 0000000000..d92f362500
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CSS/StyleRule.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/CSS/StyleRule.h>
+
+namespace Web::CSS {
+
+StyleRule::StyleRule(Vector<Selector>&& selectors, NonnullRefPtr<StyleDeclaration>&& declaration)
+ : m_selectors(move(selectors))
+ , m_declaration(move(declaration))
+{
+}
+
+StyleRule::~StyleRule()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/CSS/StyleRule.h b/Userland/Libraries/LibWeb/CSS/StyleRule.h
new file mode 100644
index 0000000000..d668c5b42b
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CSS/StyleRule.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/NonnullRefPtrVector.h>
+#include <LibWeb/CSS/Selector.h>
+#include <LibWeb/CSS/StyleDeclaration.h>
+
+namespace Web::CSS {
+
+class StyleRule : public RefCounted<StyleRule> {
+ AK_MAKE_NONCOPYABLE(StyleRule);
+ AK_MAKE_NONMOVABLE(StyleRule);
+
+public:
+ static NonnullRefPtr<StyleRule> create(Vector<Selector>&& selectors, NonnullRefPtr<StyleDeclaration>&& declaration)
+ {
+ return adopt(*new StyleRule(move(selectors), move(declaration)));
+ }
+
+ ~StyleRule();
+
+ const Vector<Selector>& selectors() const { return m_selectors; }
+ const StyleDeclaration& declaration() const { return m_declaration; }
+
+private:
+ StyleRule(Vector<Selector>&&, NonnullRefPtr<StyleDeclaration>&&);
+
+ Vector<Selector> m_selectors;
+ NonnullRefPtr<StyleDeclaration> m_declaration;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/CSS/StyleSheet.cpp b/Userland/Libraries/LibWeb/CSS/StyleSheet.cpp
new file mode 100644
index 0000000000..d4c37ba53e
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CSS/StyleSheet.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/CSS/StyleSheet.h>
+
+namespace Web::CSS {
+
+StyleSheet::StyleSheet(NonnullRefPtrVector<StyleRule>&& rules)
+ : m_rules(move(rules))
+{
+}
+
+StyleSheet::~StyleSheet()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/CSS/StyleSheet.h b/Userland/Libraries/LibWeb/CSS/StyleSheet.h
new file mode 100644
index 0000000000..9e3695e03d
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CSS/StyleSheet.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/NonnullRefPtrVector.h>
+#include <LibWeb/CSS/StyleRule.h>
+
+namespace Web::CSS {
+
+class StyleSheet : public RefCounted<StyleSheet> {
+public:
+ static NonnullRefPtr<StyleSheet> create(NonnullRefPtrVector<StyleRule>&& rules)
+ {
+ return adopt(*new StyleSheet(move(rules)));
+ }
+
+ ~StyleSheet();
+
+ const NonnullRefPtrVector<StyleRule>& rules() const { return m_rules; }
+ NonnullRefPtrVector<StyleRule>& rules() { return m_rules; }
+
+private:
+ explicit StyleSheet(NonnullRefPtrVector<StyleRule>&&);
+
+ NonnullRefPtrVector<StyleRule> m_rules;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/CSS/StyleSheetList.cpp b/Userland/Libraries/LibWeb/CSS/StyleSheetList.cpp
new file mode 100644
index 0000000000..addca5598d
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CSS/StyleSheetList.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/CSS/StyleSheetList.h>
+
+namespace Web::CSS {
+
+void StyleSheetList::add_sheet(NonnullRefPtr<StyleSheet> sheet)
+{
+ m_sheets.append(move(sheet));
+}
+
+StyleSheetList::StyleSheetList(DOM::Document& document)
+ : m_document(document)
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/CSS/StyleSheetList.h b/Userland/Libraries/LibWeb/CSS/StyleSheetList.h
new file mode 100644
index 0000000000..636e030f39
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CSS/StyleSheetList.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/RefCounted.h>
+#include <LibWeb/CSS/StyleSheet.h>
+
+namespace Web::CSS {
+
+class StyleSheetList : public RefCounted<StyleSheetList> {
+public:
+ static NonnullRefPtr<StyleSheetList> create(DOM::Document& document)
+ {
+ return adopt(*new StyleSheetList(document));
+ }
+
+ void add_sheet(NonnullRefPtr<StyleSheet>);
+
+ const NonnullRefPtrVector<StyleSheet>& sheets() const { return m_sheets; }
+
+private:
+ explicit StyleSheetList(DOM::Document&);
+
+ DOM::Document& m_document;
+ NonnullRefPtrVector<StyleSheet> m_sheets;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.cpp b/Userland/Libraries/LibWeb/CSS/StyleValue.cpp
new file mode 100644
index 0000000000..f961649bdd
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CSS/StyleValue.cpp
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/ByteBuffer.h>
+#include <LibGfx/PNGLoader.h>
+#include <LibGfx/Palette.h>
+#include <LibWeb/CSS/StyleValue.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/InProcessWebView.h>
+#include <LibWeb/Loader/LoadRequest.h>
+#include <LibWeb/Loader/ResourceLoader.h>
+#include <LibWeb/Page/Frame.h>
+
+namespace Web::CSS {
+
+StyleValue::StyleValue(Type type)
+ : m_type(type)
+{
+}
+
+StyleValue::~StyleValue()
+{
+}
+
+String IdentifierStyleValue::to_string() const
+{
+ return CSS::string_from_value_id(m_id);
+}
+
+Color IdentifierStyleValue::to_color(const DOM::Document& document) const
+{
+ if (id() == CSS::ValueID::LibwebLink)
+ return document.link_color();
+
+ ASSERT(document.page());
+ auto palette = document.page()->palette();
+ switch (id()) {
+ case CSS::ValueID::LibwebPaletteDesktopBackground:
+ return palette.color(ColorRole::DesktopBackground);
+ case CSS::ValueID::LibwebPaletteActiveWindowBorder1:
+ return palette.color(ColorRole::ActiveWindowBorder1);
+ case CSS::ValueID::LibwebPaletteActiveWindowBorder2:
+ return palette.color(ColorRole::ActiveWindowBorder2);
+ case CSS::ValueID::LibwebPaletteActiveWindowTitle:
+ return palette.color(ColorRole::ActiveWindowTitle);
+ case CSS::ValueID::LibwebPaletteInactiveWindowBorder1:
+ return palette.color(ColorRole::InactiveWindowBorder1);
+ case CSS::ValueID::LibwebPaletteInactiveWindowBorder2:
+ return palette.color(ColorRole::InactiveWindowBorder2);
+ case CSS::ValueID::LibwebPaletteInactiveWindowTitle:
+ return palette.color(ColorRole::InactiveWindowTitle);
+ case CSS::ValueID::LibwebPaletteMovingWindowBorder1:
+ return palette.color(ColorRole::MovingWindowBorder1);
+ case CSS::ValueID::LibwebPaletteMovingWindowBorder2:
+ return palette.color(ColorRole::MovingWindowBorder2);
+ case CSS::ValueID::LibwebPaletteMovingWindowTitle:
+ return palette.color(ColorRole::MovingWindowTitle);
+ case CSS::ValueID::LibwebPaletteHighlightWindowBorder1:
+ return palette.color(ColorRole::HighlightWindowBorder1);
+ case CSS::ValueID::LibwebPaletteHighlightWindowBorder2:
+ return palette.color(ColorRole::HighlightWindowBorder2);
+ case CSS::ValueID::LibwebPaletteHighlightWindowTitle:
+ return palette.color(ColorRole::HighlightWindowTitle);
+ case CSS::ValueID::LibwebPaletteMenuStripe:
+ return palette.color(ColorRole::MenuStripe);
+ case CSS::ValueID::LibwebPaletteMenuBase:
+ return palette.color(ColorRole::MenuBase);
+ case CSS::ValueID::LibwebPaletteMenuBaseText:
+ return palette.color(ColorRole::MenuBaseText);
+ case CSS::ValueID::LibwebPaletteMenuSelection:
+ return palette.color(ColorRole::MenuSelection);
+ case CSS::ValueID::LibwebPaletteMenuSelectionText:
+ return palette.color(ColorRole::MenuSelectionText);
+ case CSS::ValueID::LibwebPaletteWindow:
+ return palette.color(ColorRole::Window);
+ case CSS::ValueID::LibwebPaletteWindowText:
+ return palette.color(ColorRole::WindowText);
+ case CSS::ValueID::LibwebPaletteButton:
+ return palette.color(ColorRole::Button);
+ case CSS::ValueID::LibwebPaletteButtonText:
+ return palette.color(ColorRole::ButtonText);
+ case CSS::ValueID::LibwebPaletteBase:
+ return palette.color(ColorRole::Base);
+ case CSS::ValueID::LibwebPaletteBaseText:
+ return palette.color(ColorRole::BaseText);
+ case CSS::ValueID::LibwebPaletteThreedHighlight:
+ return palette.color(ColorRole::ThreedHighlight);
+ case CSS::ValueID::LibwebPaletteThreedShadow1:
+ return palette.color(ColorRole::ThreedShadow1);
+ case CSS::ValueID::LibwebPaletteThreedShadow2:
+ return palette.color(ColorRole::ThreedShadow2);
+ case CSS::ValueID::LibwebPaletteHoverHighlight:
+ return palette.color(ColorRole::HoverHighlight);
+ case CSS::ValueID::LibwebPaletteSelection:
+ return palette.color(ColorRole::Selection);
+ case CSS::ValueID::LibwebPaletteSelectionText:
+ return palette.color(ColorRole::SelectionText);
+ case CSS::ValueID::LibwebPaletteInactiveSelection:
+ return palette.color(ColorRole::InactiveSelection);
+ case CSS::ValueID::LibwebPaletteInactiveSelectionText:
+ return palette.color(ColorRole::InactiveSelectionText);
+ case CSS::ValueID::LibwebPaletteRubberBandFill:
+ return palette.color(ColorRole::RubberBandFill);
+ case CSS::ValueID::LibwebPaletteRubberBandBorder:
+ return palette.color(ColorRole::RubberBandBorder);
+ case CSS::ValueID::LibwebPaletteLink:
+ return palette.color(ColorRole::Link);
+ case CSS::ValueID::LibwebPaletteActiveLink:
+ return palette.color(ColorRole::ActiveLink);
+ case CSS::ValueID::LibwebPaletteVisitedLink:
+ return palette.color(ColorRole::VisitedLink);
+ case CSS::ValueID::LibwebPaletteRuler:
+ return palette.color(ColorRole::Ruler);
+ case CSS::ValueID::LibwebPaletteRulerBorder:
+ return palette.color(ColorRole::RulerBorder);
+ case CSS::ValueID::LibwebPaletteRulerActiveText:
+ return palette.color(ColorRole::RulerActiveText);
+ case CSS::ValueID::LibwebPaletteRulerInactiveText:
+ return palette.color(ColorRole::RulerInactiveText);
+ case CSS::ValueID::LibwebPaletteTextCursor:
+ return palette.color(ColorRole::TextCursor);
+ case CSS::ValueID::LibwebPaletteFocusOutline:
+ return palette.color(ColorRole::FocusOutline);
+ case CSS::ValueID::LibwebPaletteSyntaxComment:
+ return palette.color(ColorRole::SyntaxComment);
+ case CSS::ValueID::LibwebPaletteSyntaxNumber:
+ return palette.color(ColorRole::SyntaxNumber);
+ case CSS::ValueID::LibwebPaletteSyntaxString:
+ return palette.color(ColorRole::SyntaxString);
+ case CSS::ValueID::LibwebPaletteSyntaxType:
+ return palette.color(ColorRole::SyntaxType);
+ case CSS::ValueID::LibwebPaletteSyntaxPunctuation:
+ return palette.color(ColorRole::SyntaxPunctuation);
+ case CSS::ValueID::LibwebPaletteSyntaxOperator:
+ return palette.color(ColorRole::SyntaxOperator);
+ case CSS::ValueID::LibwebPaletteSyntaxKeyword:
+ return palette.color(ColorRole::SyntaxKeyword);
+ case CSS::ValueID::LibwebPaletteSyntaxControlKeyword:
+ return palette.color(ColorRole::SyntaxControlKeyword);
+ case CSS::ValueID::LibwebPaletteSyntaxIdentifier:
+ return palette.color(ColorRole::SyntaxIdentifier);
+ case CSS::ValueID::LibwebPaletteSyntaxPreprocessorStatement:
+ return palette.color(ColorRole::SyntaxPreprocessorStatement);
+ case CSS::ValueID::LibwebPaletteSyntaxPreprocessorValue:
+ return palette.color(ColorRole::SyntaxPreprocessorValue);
+ default:
+ return {};
+ }
+}
+
+ImageStyleValue::ImageStyleValue(const URL& url, DOM::Document& document)
+ : StyleValue(Type::Image)
+ , m_url(url)
+ , m_document(document)
+{
+ LoadRequest request;
+ request.set_url(url);
+ set_resource(ResourceLoader::the().load_resource(Resource::Type::Image, request));
+}
+
+void ImageStyleValue::resource_did_load()
+{
+ if (!m_document)
+ return;
+ m_bitmap = resource()->bitmap();
+ // FIXME: Do less than a full repaint if possible?
+ if (m_document->frame())
+ m_document->frame()->set_needs_display({});
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValue.h
new file mode 100644
index 0000000000..4b3d1d3ecc
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CSS/StyleValue.h
@@ -0,0 +1,357 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/RefCounted.h>
+#include <AK/RefPtr.h>
+#include <AK/String.h>
+#include <AK/StringView.h>
+#include <AK/URL.h>
+#include <AK/WeakPtr.h>
+#include <LibGfx/Bitmap.h>
+#include <LibGfx/Color.h>
+#include <LibWeb/CSS/Length.h>
+#include <LibWeb/CSS/PropertyID.h>
+#include <LibWeb/CSS/ValueID.h>
+#include <LibWeb/Forward.h>
+#include <LibWeb/Loader/ImageResource.h>
+
+namespace Web::CSS {
+
+enum class Position {
+ Static,
+ Relative,
+ Absolute,
+ Fixed,
+ Sticky,
+};
+
+enum class TextAlign {
+ Left,
+ Center,
+ Right,
+ Justify,
+ LibwebCenter,
+};
+
+enum class TextDecorationLine {
+ None,
+ Underline,
+ Overline,
+ LineThrough,
+ Blink,
+};
+
+enum class TextTransform {
+ None,
+ Capitalize,
+ Uppercase,
+ Lowercase,
+ FullWidth,
+ FullSizeKana,
+};
+
+enum class Display {
+ None,
+ Block,
+ Inline,
+ InlineBlock,
+ ListItem,
+ Table,
+ TableRow,
+ TableCell,
+ TableHeaderGroup,
+ TableRowGroup,
+ TableFooterGroup,
+ TableColumn,
+ TableColumnGroup,
+ TableCaption,
+};
+
+enum class WhiteSpace {
+ Normal,
+ Pre,
+ Nowrap,
+ PreLine,
+ PreWrap,
+};
+
+enum class Float {
+ None,
+ Left,
+ Right,
+};
+
+enum class Clear {
+ None,
+ Left,
+ Right,
+ Both,
+};
+
+enum class LineStyle {
+ None,
+ Hidden,
+ Dotted,
+ Dashed,
+ Solid,
+ Double,
+ Groove,
+ Ridge,
+ Inset,
+ Outset,
+};
+
+enum class ListStyleType {
+ None,
+ Disc,
+ Circle,
+ Square,
+ Decimal,
+};
+
+class StyleValue : public RefCounted<StyleValue> {
+public:
+ virtual ~StyleValue();
+
+ enum class Type {
+ Invalid,
+ Inherit,
+ Initial,
+ String,
+ Length,
+ Color,
+ Identifier,
+ Image,
+ Position,
+ };
+
+ Type type() const { return m_type; }
+
+ bool is_inherit() const { return type() == Type::Inherit; }
+ bool is_initial() const { return type() == Type::Initial; }
+ bool is_color() const { return type() == Type::Color; }
+ bool is_identifier() const { return type() == Type::Identifier; }
+ bool is_image() const { return type() == Type::Image; }
+ bool is_string() const { return type() == Type::String; }
+ bool is_length() const { return type() == Type::Length; }
+ bool is_position() const { return type() == Type::Position; }
+
+ virtual String to_string() const = 0;
+ virtual Length to_length() const { return Length::make_auto(); }
+ virtual Color to_color(const DOM::Document&) const { return {}; }
+
+ CSS::ValueID to_identifier() const;
+
+ virtual bool is_auto() const { return false; }
+
+ bool operator==(const StyleValue& other) const { return equals(other); }
+ bool operator!=(const StyleValue& other) const { return !(*this == other); }
+
+ virtual bool equals(const StyleValue& other) const
+ {
+ if (type() != other.type())
+ return false;
+ return to_string() == other.to_string();
+ }
+
+protected:
+ explicit StyleValue(Type);
+
+private:
+ Type m_type { Type::Invalid };
+};
+
+class StringStyleValue : public StyleValue {
+public:
+ static NonnullRefPtr<StringStyleValue> create(const String& string)
+ {
+ return adopt(*new StringStyleValue(string));
+ }
+ virtual ~StringStyleValue() override { }
+
+ String to_string() const override { return m_string; }
+
+private:
+ explicit StringStyleValue(const String& string)
+ : StyleValue(Type::String)
+ , m_string(string)
+ {
+ }
+
+ String m_string;
+};
+
+class LengthStyleValue : public StyleValue {
+public:
+ static NonnullRefPtr<LengthStyleValue> create(const Length& length)
+ {
+ return adopt(*new LengthStyleValue(length));
+ }
+ virtual ~LengthStyleValue() override { }
+
+ virtual String to_string() const override { return m_length.to_string(); }
+ virtual Length to_length() const override { return m_length; }
+
+ const Length& length() const { return m_length; }
+
+ virtual bool is_auto() const override { return m_length.is_auto(); }
+
+ virtual bool equals(const StyleValue& other) const override
+ {
+ if (type() != other.type())
+ return false;
+ return m_length == static_cast<const LengthStyleValue&>(other).m_length;
+ }
+
+private:
+ explicit LengthStyleValue(const Length& length)
+ : StyleValue(Type::Length)
+ , m_length(length)
+ {
+ }
+
+ Length m_length;
+};
+
+class InitialStyleValue final : public StyleValue {
+public:
+ static NonnullRefPtr<InitialStyleValue> create() { return adopt(*new InitialStyleValue); }
+ virtual ~InitialStyleValue() override { }
+
+ String to_string() const override { return "initial"; }
+
+private:
+ InitialStyleValue()
+ : StyleValue(Type::Initial)
+ {
+ }
+};
+
+class InheritStyleValue final : public StyleValue {
+public:
+ static NonnullRefPtr<InheritStyleValue> create() { return adopt(*new InheritStyleValue); }
+ virtual ~InheritStyleValue() override { }
+
+ String to_string() const override { return "inherit"; }
+
+private:
+ InheritStyleValue()
+ : StyleValue(Type::Inherit)
+ {
+ }
+};
+
+class ColorStyleValue : public StyleValue {
+public:
+ static NonnullRefPtr<ColorStyleValue> create(Color color)
+ {
+ return adopt(*new ColorStyleValue(color));
+ }
+ virtual ~ColorStyleValue() override { }
+
+ Color color() const { return m_color; }
+ String to_string() const override { return m_color.to_string(); }
+ Color to_color(const DOM::Document&) const override { return m_color; }
+
+ virtual bool equals(const StyleValue& other) const override
+ {
+ if (type() != other.type())
+ return false;
+ return m_color == static_cast<const ColorStyleValue&>(other).m_color;
+ }
+
+private:
+ explicit ColorStyleValue(Color color)
+ : StyleValue(Type::Color)
+ , m_color(color)
+ {
+ }
+
+ Color m_color;
+};
+
+class IdentifierStyleValue final : public StyleValue {
+public:
+ static NonnullRefPtr<IdentifierStyleValue> create(CSS::ValueID id)
+ {
+ return adopt(*new IdentifierStyleValue(id));
+ }
+ virtual ~IdentifierStyleValue() override { }
+
+ CSS::ValueID id() const { return m_id; }
+
+ virtual String to_string() const override;
+ virtual Color to_color(const DOM::Document&) const override;
+
+ virtual bool equals(const StyleValue& other) const override
+ {
+ if (type() != other.type())
+ return false;
+ return m_id == static_cast<const IdentifierStyleValue&>(other).m_id;
+ }
+
+private:
+ explicit IdentifierStyleValue(CSS::ValueID id)
+ : StyleValue(Type::Identifier)
+ , m_id(id)
+ {
+ }
+
+ CSS::ValueID m_id { CSS::ValueID::Invalid };
+};
+
+class ImageStyleValue final
+ : public StyleValue
+ , public ImageResourceClient {
+public:
+ static NonnullRefPtr<ImageStyleValue> create(const URL& url, DOM::Document& document) { return adopt(*new ImageStyleValue(url, document)); }
+ virtual ~ImageStyleValue() override { }
+
+ String to_string() const override { return String::formatted("Image({})", m_url.to_string()); }
+
+ const Gfx::Bitmap* bitmap() const { return m_bitmap; }
+
+private:
+ ImageStyleValue(const URL&, DOM::Document&);
+
+ // ^ResourceClient
+ virtual void resource_did_load() override;
+
+ URL m_url;
+ WeakPtr<DOM::Document> m_document;
+ RefPtr<Gfx::Bitmap> m_bitmap;
+};
+
+inline CSS::ValueID StyleValue::to_identifier() const
+{
+ if (is_identifier())
+ return static_cast<const IdentifierStyleValue&>(*this).id();
+ return CSS::ValueID::Invalid;
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/CodeGenerators/CMakeLists.txt b/Userland/Libraries/LibWeb/CodeGenerators/CMakeLists.txt
new file mode 100644
index 0000000000..b3c4073df9
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CodeGenerators/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_executable(Generate_CSS_PropertyID_h Generate_CSS_PropertyID_h.cpp)
+add_executable(Generate_CSS_PropertyID_cpp Generate_CSS_PropertyID_cpp.cpp)
+add_executable(Generate_CSS_ValueID_h Generate_CSS_ValueID_h.cpp)
+add_executable(Generate_CSS_ValueID_cpp Generate_CSS_ValueID_cpp.cpp)
+add_executable(WrapperGenerator WrapperGenerator.cpp)
+target_compile_options(WrapperGenerator PUBLIC -g)
+target_link_libraries(Generate_CSS_PropertyID_h LagomCore)
+target_link_libraries(Generate_CSS_PropertyID_cpp LagomCore)
+target_link_libraries(Generate_CSS_ValueID_h LagomCore)
+target_link_libraries(Generate_CSS_ValueID_cpp LagomCore)
+target_link_libraries(WrapperGenerator LagomCore)
diff --git a/Userland/Libraries/LibWeb/CodeGenerators/Generate_CSS_PropertyID_cpp.cpp b/Userland/Libraries/LibWeb/CodeGenerators/Generate_CSS_PropertyID_cpp.cpp
new file mode 100644
index 0000000000..2b7b2218c7
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CodeGenerators/Generate_CSS_PropertyID_cpp.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/ByteBuffer.h>
+#include <AK/JsonObject.h>
+#include <AK/SourceGenerator.h>
+#include <AK/StringBuilder.h>
+#include <LibCore/File.h>
+#include <ctype.h>
+#include <stdio.h>
+
+static String title_casify(const String& dashy_name)
+{
+ auto parts = dashy_name.split('-');
+ StringBuilder builder;
+ for (auto& part : parts) {
+ if (part.is_empty())
+ continue;
+ builder.append(toupper(part[0]));
+ if (part.length() == 1)
+ continue;
+ builder.append(part.substring_view(1, part.length() - 1));
+ }
+ return builder.to_string();
+}
+
+int main(int argc, char** argv)
+{
+ if (argc != 2) {
+ warnln("usage: {} <path/to/CSS/Properties.json>", argv[0]);
+ return 1;
+ }
+ auto file = Core::File::construct(argv[1]);
+ if (!file->open(Core::IODevice::ReadOnly))
+ return 1;
+
+ auto json = JsonValue::from_string(file->read_all());
+ ASSERT(json.has_value());
+ ASSERT(json.value().is_object());
+
+ StringBuilder builder;
+ SourceGenerator generator { builder };
+
+ generator.append(R"~~~(
+#include <AK/Assertions.h>
+#include <LibWeb/CSS/PropertyID.h>
+
+namespace Web::CSS {
+
+PropertyID property_id_from_string(const StringView& string)
+{
+)~~~");
+
+ json.value().as_object().for_each_member([&](auto& name, auto& value) {
+ ASSERT(value.is_object());
+
+ auto member_generator = generator.fork();
+ member_generator.set("name", name);
+ member_generator.set("name:titlecase", title_casify(name));
+ member_generator.append(R"~~~(
+ if (string.equals_ignoring_case("@name@"))
+ return PropertyID::@name:titlecase@;
+)~~~");
+ });
+
+ generator.append(R"~~~(
+ return PropertyID::Invalid;
+}
+
+const char* string_from_property_id(PropertyID property_id) {
+ switch (property_id) {
+)~~~");
+
+ json.value().as_object().for_each_member([&](auto& name, auto& value) {
+ ASSERT(value.is_object());
+
+ auto member_generator = generator.fork();
+ member_generator.set("name", name);
+ member_generator.set("name:titlecase", title_casify(name));
+ member_generator.append(R"~~~(
+ case PropertyID::@name:titlecase@:
+ return "@name@";
+ )~~~");
+ });
+
+ generator.append(R"~~~(
+ default:
+ return "(invalid CSS::PropertyID)";
+ }
+}
+
+} // namespace Web::CSS
+)~~~");
+
+ outln("{}", generator.as_string_view());
+}
diff --git a/Userland/Libraries/LibWeb/CodeGenerators/Generate_CSS_PropertyID_h.cpp b/Userland/Libraries/LibWeb/CodeGenerators/Generate_CSS_PropertyID_h.cpp
new file mode 100644
index 0000000000..e2f235db62
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CodeGenerators/Generate_CSS_PropertyID_h.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/ByteBuffer.h>
+#include <AK/JsonObject.h>
+#include <AK/SourceGenerator.h>
+#include <AK/StringBuilder.h>
+#include <LibCore/File.h>
+#include <ctype.h>
+#include <stdio.h>
+
+static String title_casify(const String& dashy_name)
+{
+ auto parts = dashy_name.split('-');
+ StringBuilder builder;
+ for (auto& part : parts) {
+ if (part.is_empty())
+ continue;
+ builder.append(toupper(part[0]));
+ if (part.length() == 1)
+ continue;
+ builder.append(part.substring_view(1, part.length() - 1));
+ }
+ return builder.to_string();
+}
+
+int main(int argc, char** argv)
+{
+ if (argc != 2) {
+ warnln("usage: {} <path/to/CSS/Properties.json>", argv[0]);
+ return 1;
+ }
+ auto file = Core::File::construct(argv[1]);
+ if (!file->open(Core::IODevice::ReadOnly))
+ return 1;
+
+ auto json = JsonValue::from_string(file->read_all());
+ ASSERT(json.has_value());
+ ASSERT(json.value().is_object());
+
+ StringBuilder builder;
+ SourceGenerator generator { builder };
+ generator.append(R"~~~(
+#pragma once
+
+#include <AK/StringView.h>
+#include <AK/Traits.h>
+
+namespace Web::CSS {
+
+enum class PropertyID {
+ Invalid,
+)~~~");
+
+ json.value().as_object().for_each_member([&](auto& name, auto& value) {
+ ASSERT(value.is_object());
+
+ auto member_generator = generator.fork();
+ member_generator.set("name:titlecase", title_casify(name));
+
+ member_generator.append(R"~~~(
+ @name:titlecase@,
+)~~~");
+ });
+
+ generator.append(R"~~~(
+};
+
+PropertyID property_id_from_string(const StringView&);
+const char* string_from_property_id(PropertyID);
+
+} // namespace Web::CSS
+
+namespace AK {
+template<>
+struct Traits<Web::CSS::PropertyID> : public GenericTraits<Web::CSS::PropertyID> {
+ static unsigned hash(Web::CSS::PropertyID property_id) { return int_hash((unsigned)property_id); }
+};
+} // namespace AK
+)~~~");
+
+ outln("{}", generator.as_string_view());
+}
diff --git a/Userland/Libraries/LibWeb/CodeGenerators/Generate_CSS_ValueID_cpp.cpp b/Userland/Libraries/LibWeb/CodeGenerators/Generate_CSS_ValueID_cpp.cpp
new file mode 100644
index 0000000000..4991a4040a
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CodeGenerators/Generate_CSS_ValueID_cpp.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/ByteBuffer.h>
+#include <AK/JsonObject.h>
+#include <AK/SourceGenerator.h>
+#include <AK/StringBuilder.h>
+#include <LibCore/File.h>
+#include <ctype.h>
+#include <stdio.h>
+
+static String title_casify(const String& dashy_name)
+{
+ auto parts = dashy_name.split('-');
+ StringBuilder builder;
+ for (auto& part : parts) {
+ if (part.is_empty())
+ continue;
+ builder.append(toupper(part[0]));
+ if (part.length() == 1)
+ continue;
+ builder.append(part.substring_view(1, part.length() - 1));
+ }
+ return builder.to_string();
+}
+
+int main(int argc, char** argv)
+{
+ if (argc != 2) {
+ warnln("usage: {} <path/to/CSS/Identifiers.json>", argv[0]);
+ return 1;
+ }
+ auto file = Core::File::construct(argv[1]);
+ if (!file->open(Core::IODevice::ReadOnly))
+ return 1;
+
+ auto json = JsonValue::from_string(file->read_all());
+ ASSERT(json.has_value());
+ ASSERT(json.value().is_array());
+
+ StringBuilder builder;
+ SourceGenerator generator { builder };
+
+ generator.append(R"~~~(
+#include <AK/Assertions.h>
+#include <LibWeb/CSS/ValueID.h>
+
+namespace Web::CSS {
+
+ValueID value_id_from_string(const StringView& string)
+{
+)~~~");
+
+ json.value().as_array().for_each([&](auto& name) {
+ auto member_generator = generator.fork();
+ member_generator.set("name", name.to_string());
+ member_generator.set("name:titlecase", title_casify(name.to_string()));
+ member_generator.append(R"~~~(
+ if (string.equals_ignoring_case("@name@"))
+ return ValueID::@name:titlecase@;
+)~~~");
+ });
+
+ generator.append(R"~~~(
+ return ValueID::Invalid;
+}
+
+const char* string_from_value_id(ValueID value_id) {
+ switch (value_id) {
+)~~~");
+
+ json.value().as_array().for_each([&](auto& name) {
+ auto member_generator = generator.fork();
+ member_generator.set("name", name.to_string());
+ member_generator.set("name:titlecase", title_casify(name.to_string()));
+ member_generator.append(R"~~~(
+ case ValueID::@name:titlecase@:
+ return "@name@";
+ )~~~");
+ });
+
+ generator.append(R"~~~(
+ default:
+ return "(invalid CSS::ValueID)";
+ }
+}
+
+} // namespace Web::CSS
+)~~~");
+
+ outln("{}", generator.as_string_view());
+}
diff --git a/Userland/Libraries/LibWeb/CodeGenerators/Generate_CSS_ValueID_h.cpp b/Userland/Libraries/LibWeb/CodeGenerators/Generate_CSS_ValueID_h.cpp
new file mode 100644
index 0000000000..7c85ade83f
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CodeGenerators/Generate_CSS_ValueID_h.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/ByteBuffer.h>
+#include <AK/JsonObject.h>
+#include <AK/SourceGenerator.h>
+#include <AK/StringBuilder.h>
+#include <LibCore/File.h>
+#include <ctype.h>
+#include <stdio.h>
+
+static String title_casify(const String& dashy_name)
+{
+ auto parts = dashy_name.split('-');
+ StringBuilder builder;
+ for (auto& part : parts) {
+ if (part.is_empty())
+ continue;
+ builder.append(toupper(part[0]));
+ if (part.length() == 1)
+ continue;
+ builder.append(part.substring_view(1, part.length() - 1));
+ }
+ return builder.to_string();
+}
+
+int main(int argc, char** argv)
+{
+ if (argc != 2) {
+ warnln("usage: {} <path/to/CSS/Identifiers.json>", argv[0]);
+ return 1;
+ }
+ auto file = Core::File::construct(argv[1]);
+ if (!file->open(Core::IODevice::ReadOnly))
+ return 1;
+
+ auto json = JsonValue::from_string(file->read_all());
+ ASSERT(json.has_value());
+ ASSERT(json.value().is_array());
+
+ StringBuilder builder;
+ SourceGenerator generator { builder };
+ generator.append(R"~~~(
+#pragma once
+
+#include <AK/StringView.h>
+#include <AK/Traits.h>
+
+namespace Web::CSS {
+
+enum class ValueID {
+ Invalid,
+)~~~");
+
+ json.value().as_array().for_each([&](auto& name) {
+ auto member_generator = generator.fork();
+ member_generator.set("name:titlecase", title_casify(name.to_string()));
+
+ member_generator.append(R"~~~(
+ @name:titlecase@,
+)~~~");
+ });
+
+ generator.append(R"~~~(
+};
+
+ValueID value_id_from_string(const StringView&);
+const char* string_from_value_id(ValueID);
+
+}
+
+)~~~");
+
+ outln("{}", generator.as_string_view());
+}
diff --git a/Userland/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp b/Userland/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp
new file mode 100644
index 0000000000..dbedd118b5
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp
@@ -0,0 +1,951 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/ByteBuffer.h>
+#include <AK/GenericLexer.h>
+#include <AK/HashMap.h>
+#include <AK/LexicalPath.h>
+#include <AK/SourceGenerator.h>
+#include <AK/StringBuilder.h>
+#include <LibCore/ArgsParser.h>
+#include <LibCore/File.h>
+#include <ctype.h>
+
+static String snake_name(const StringView& title_name)
+{
+ StringBuilder builder;
+ bool first = true;
+ bool last_was_uppercase = false;
+ for (auto ch : title_name) {
+ if (isupper(ch)) {
+ if (!first && !last_was_uppercase)
+ builder.append('_');
+ builder.append(tolower(ch));
+ } else {
+ builder.append(ch);
+ }
+ first = false;
+ last_was_uppercase = isupper(ch);
+ }
+ return builder.to_string();
+}
+
+static String make_input_acceptable_cpp(const String& input)
+{
+ if (input.is_one_of("class", "template", "for", "default", "char")) {
+ StringBuilder builder;
+ builder.append(input);
+ builder.append('_');
+ return builder.to_string();
+ }
+
+ String input_without_dashes = input;
+ input_without_dashes.replace("-", "_");
+
+ return input_without_dashes;
+}
+
+static void report_parsing_error(StringView message, StringView filename, StringView input, size_t offset)
+{
+ // FIXME: Spaghetti code ahead.
+
+ size_t lineno = 1;
+ size_t colno = 1;
+ size_t start_line = 0;
+ size_t line_length = 0;
+ for (size_t index = 0; index < input.length(); ++index) {
+ if (offset == index)
+ colno = index - start_line + 1;
+
+ if (input[index] == '\n') {
+ if (index >= offset)
+ break;
+
+ start_line = index + 1;
+ line_length = 0;
+ ++lineno;
+ } else {
+ ++line_length;
+ }
+ }
+
+ StringBuilder error_message;
+ error_message.appendff("{}\n", input.substring_view(start_line, line_length));
+ for (size_t i = 0; i < colno - 1; ++i)
+ error_message.append(' ');
+ error_message.append("\033[1;31m^\n");
+ error_message.appendff("{}:{}: error: {}\033[0m\n", filename, lineno, message);
+
+ warnln("{}", error_message.string_view());
+ exit(EXIT_FAILURE);
+}
+
+namespace IDL {
+
+struct Type {
+ String name;
+ bool nullable { false };
+};
+
+struct Parameter {
+ Type type;
+ String name;
+ bool optional { false };
+};
+
+struct Function {
+ Type return_type;
+ String name;
+ Vector<Parameter> parameters;
+ HashMap<String, String> extended_attributes;
+
+ size_t length() const
+ {
+ // FIXME: This seems to produce a length that is way over what it's supposed to be.
+ // For example, getElementsByTagName has its length set to 20 when it should be 1.
+ size_t length = 0;
+ for (auto& parameter : parameters) {
+ if (!parameter.optional)
+ length++;
+ }
+ return length;
+ }
+};
+
+struct Attribute {
+ bool readonly { false };
+ bool unsigned_ { false };
+ Type type;
+ String name;
+ HashMap<String, String> extended_attributes;
+
+ // Added for convenience after parsing
+ String getter_callback_name;
+ String setter_callback_name;
+};
+
+struct Interface {
+ String name;
+ String parent_name;
+
+ Vector<Attribute> attributes;
+ Vector<Function> functions;
+
+ // Added for convenience after parsing
+ String wrapper_class;
+ String wrapper_base_class;
+ String fully_qualified_name;
+};
+
+static OwnPtr<Interface> parse_interface(StringView filename, const StringView& input)
+{
+ auto interface = make<Interface>();
+
+ GenericLexer lexer(input);
+
+ auto assert_specific = [&](char ch) {
+ if (!lexer.consume_specific(ch))
+ report_parsing_error(String::formatted("expected '{}'", ch), filename, input, lexer.tell());
+ };
+
+ auto consume_whitespace = [&] {
+ bool consumed = true;
+ while (consumed) {
+ consumed = lexer.consume_while([](char ch) { return isspace(ch); }).length() > 0;
+
+ if (lexer.consume_specific("//")) {
+ lexer.consume_until('\n');
+ consumed = true;
+ }
+ }
+ };
+
+ auto assert_string = [&](const StringView& expected) {
+ if (!lexer.consume_specific(expected))
+ report_parsing_error(String::formatted("expected '{}'", expected), filename, input, lexer.tell());
+ };
+
+ assert_string("interface");
+ consume_whitespace();
+ interface->name = lexer.consume_until([](auto ch) { return isspace(ch); });
+ consume_whitespace();
+ if (lexer.consume_specific(':')) {
+ consume_whitespace();
+ interface->parent_name = lexer.consume_until([](auto ch) { return isspace(ch); });
+ consume_whitespace();
+ }
+ assert_specific('{');
+
+ auto parse_type = [&] {
+ auto name = lexer.consume_until([](auto ch) { return isspace(ch) || ch == '?'; });
+ auto nullable = lexer.consume_specific('?');
+ return Type { name, nullable };
+ };
+
+ auto parse_attribute = [&](HashMap<String, String>& extended_attributes) {
+ bool readonly = lexer.consume_specific("readonly");
+ if (readonly)
+ consume_whitespace();
+
+ if (lexer.consume_specific("attribute"))
+ consume_whitespace();
+
+ bool unsigned_ = lexer.consume_specific("unsigned");
+ if (unsigned_)
+ consume_whitespace();
+
+ auto type = parse_type();
+ consume_whitespace();
+ auto name = lexer.consume_until([](auto ch) { return isspace(ch) || ch == ';'; });
+ consume_whitespace();
+ assert_specific(';');
+ Attribute attribute;
+ attribute.readonly = readonly;
+ attribute.unsigned_ = unsigned_;
+ attribute.type = type;
+ attribute.name = name;
+ attribute.getter_callback_name = String::formatted("{}_getter", snake_name(attribute.name));
+ attribute.setter_callback_name = String::formatted("{}_setter", snake_name(attribute.name));
+ attribute.extended_attributes = move(extended_attributes);
+ interface->attributes.append(move(attribute));
+ };
+
+ auto parse_function = [&](HashMap<String, String>& extended_attributes) {
+ auto return_type = parse_type();
+ consume_whitespace();
+ auto name = lexer.consume_until([](auto ch) { return isspace(ch) || ch == '('; });
+ consume_whitespace();
+ assert_specific('(');
+
+ Vector<Parameter> parameters;
+
+ for (;;) {
+ if (lexer.consume_specific(')'))
+ break;
+ bool optional = lexer.consume_specific("optional");
+ if (optional)
+ consume_whitespace();
+ auto type = parse_type();
+ consume_whitespace();
+ auto name = lexer.consume_until([](auto ch) { return isspace(ch) || ch == ',' || ch == ')'; });
+ parameters.append({ move(type), move(name), optional });
+ if (lexer.consume_specific(')'))
+ break;
+ assert_specific(',');
+ consume_whitespace();
+ }
+
+ consume_whitespace();
+ assert_specific(';');
+
+ interface->functions.append(Function { return_type, name, move(parameters), move(extended_attributes) });
+ };
+
+ auto parse_extended_attributes = [&] {
+ HashMap<String, String> extended_attributes;
+ for (;;) {
+ consume_whitespace();
+ if (lexer.consume_specific(']'))
+ break;
+ auto name = lexer.consume_until([](auto ch) { return ch == ']' || ch == '=' || ch == ','; });
+ if (lexer.consume_specific('=')) {
+ auto value = lexer.consume_until([](auto ch) { return ch == ']' || ch == ','; });
+ extended_attributes.set(name, value);
+ } else {
+ extended_attributes.set(name, {});
+ }
+ lexer.consume_specific(',');
+ }
+ consume_whitespace();
+ return extended_attributes;
+ };
+
+ for (;;) {
+ HashMap<String, String> extended_attributes;
+
+ consume_whitespace();
+
+ if (lexer.consume_specific('}')) {
+ consume_whitespace();
+ assert_specific(';');
+ break;
+ }
+
+ if (lexer.consume_specific('[')) {
+ extended_attributes = parse_extended_attributes();
+ }
+
+ if (lexer.next_is("readonly") || lexer.next_is("attribute")) {
+ parse_attribute(extended_attributes);
+ continue;
+ }
+
+ parse_function(extended_attributes);
+ }
+
+ interface->wrapper_class = String::formatted("{}Wrapper", interface->name);
+ interface->wrapper_base_class = String::formatted("{}Wrapper", interface->parent_name.is_empty() ? String::empty() : interface->parent_name);
+
+ return interface;
+}
+
+}
+
+static void generate_header(const IDL::Interface&);
+static void generate_implementation(const IDL::Interface&);
+
+int main(int argc, char** argv)
+{
+ Core::ArgsParser args_parser;
+ const char* path = nullptr;
+ bool header_mode = false;
+ bool implementation_mode = false;
+ args_parser.add_option(header_mode, "Generate the wrapper .h file", "header", 'H');
+ args_parser.add_option(implementation_mode, "Generate the wrapper .cpp file", "implementation", 'I');
+ args_parser.add_positional_argument(path, "IDL file", "idl-file");
+ args_parser.parse(argc, argv);
+
+ auto file_or_error = Core::File::open(path, Core::IODevice::ReadOnly);
+ if (file_or_error.is_error()) {
+ fprintf(stderr, "Cannot open %s\n", path);
+ return 1;
+ }
+
+ LexicalPath lexical_path(path);
+ auto namespace_ = lexical_path.parts().at(lexical_path.parts().size() - 2);
+
+ auto data = file_or_error.value()->read_all();
+ auto interface = IDL::parse_interface(path, data);
+
+ if (!interface) {
+ warnln("Cannot parse {}", path);
+ return 1;
+ }
+
+ if (namespace_.is_one_of("DOM", "HTML", "UIEvents", "HighResolutionTime", "SVG")) {
+ StringBuilder builder;
+ builder.append(namespace_);
+ builder.append("::");
+ builder.append(interface->name);
+ interface->fully_qualified_name = builder.to_string();
+ } else {
+ interface->fully_qualified_name = interface->name;
+ }
+
+#if 0
+ dbgln("Attributes:");
+ for (auto& attribute : interface->attributes) {
+ dbg() << " " << (attribute.readonly ? "Readonly " : "")
+ << attribute.type.name << (attribute.type.nullable ? "?" : "")
+ << " " << attribute.name;
+ }
+
+ dbgln("Functions:");
+ for (auto& function : interface->functions) {
+ dbg() << " " << function.return_type.name << (function.return_type.nullable ? "?" : "")
+ << " " << function.name;
+ for (auto& parameter : function.parameters) {
+ dbg() << " " << parameter.type.name << (parameter.type.nullable ? "?" : "") << " " << parameter.name;
+ }
+ }
+#endif
+
+ if (header_mode)
+ generate_header(*interface);
+
+ if (implementation_mode)
+ generate_implementation(*interface);
+
+ return 0;
+}
+
+static bool should_emit_wrapper_factory(const IDL::Interface& interface)
+{
+ // FIXME: This is very hackish.
+ if (interface.name == "Event")
+ return false;
+ if (interface.name == "EventTarget")
+ return false;
+ if (interface.name == "Node")
+ return false;
+ if (interface.name == "Text")
+ return false;
+ if (interface.name == "Document")
+ return false;
+ if (interface.name == "DocumentType")
+ return false;
+ if (interface.name.ends_with("Element"))
+ return false;
+ return true;
+}
+
+static bool is_wrappable_type(const IDL::Type& type)
+{
+ if (type.name == "Node")
+ return true;
+ if (type.name == "Document")
+ return true;
+ if (type.name == "Text")
+ return true;
+ if (type.name == "DocumentType")
+ return true;
+ if (type.name.ends_with("Element"))
+ return true;
+ if (type.name == "ImageData")
+ return true;
+ return false;
+}
+
+static void generate_header(const IDL::Interface& interface)
+{
+ StringBuilder builder;
+ SourceGenerator generator { builder };
+
+ generator.set("name", interface.name);
+ generator.set("fully_qualified_name", interface.fully_qualified_name);
+ generator.set("wrapper_base_class", interface.wrapper_base_class);
+ generator.set("wrapper_class", interface.wrapper_class);
+ generator.set("wrapper_class:snakecase", snake_name(interface.wrapper_class));
+
+ generator.append(R"~~~(
+#pragma once
+
+#include <LibWeb/Bindings/Wrapper.h>
+
+// FIXME: This is very strange.
+#if __has_include(<LibWeb/DOM/@name@.h>)
+# include <LibWeb/DOM/@name@.h>
+#elif __has_include(<LibWeb/HTML/@name@.h>)
+# include <LibWeb/HTML/@name@.h>
+#elif __has_include(<LibWeb/UIEvents/@name@.h>)
+# include <LibWeb/UIEvents/@name@.h>
+#elif __has_include(<LibWeb/HighResolutionTime/@name@.h>)
+# include <LibWeb/HighResolutionTime/@name@.h>
+#elif __has_include(<LibWeb/SVG/@name@.h>)
+# include <LibWeb/SVG/@name@.h>
+#endif
+)~~~");
+
+ if (interface.wrapper_base_class != "Wrapper") {
+ generator.append(R"~~~(
+#include <LibWeb/Bindings/@wrapper_base_class@.h>
+)~~~");
+ }
+
+ generator.append(R"~~~(
+namespace Web::Bindings {
+
+class @wrapper_class@ : public @wrapper_base_class@ {
+ JS_OBJECT(@wrapper_class@, @wrapper_base_class@);
+public:
+ @wrapper_class@(JS::GlobalObject&, @fully_qualified_name@&);
+ virtual void initialize(JS::GlobalObject&) override;
+ virtual ~@wrapper_class@() override;
+)~~~");
+
+ if (interface.wrapper_base_class == "Wrapper") {
+ generator.append(R"~~~(
+ @fully_qualified_name@& impl() { return *m_impl; }
+ const @fully_qualified_name@& impl() const { return *m_impl; }
+)~~~");
+ } else {
+ generator.append(R"~~~(
+ @fully_qualified_name@& impl() { return static_cast<@fully_qualified_name@&>(@wrapper_base_class@::impl()); }
+ const @fully_qualified_name@& impl() const { return static_cast<const @fully_qualified_name@&>(@wrapper_base_class@::impl()); }
+)~~~");
+ }
+
+ generator.append(R"~~~(
+private:
+)~~~");
+
+ for (auto& function : interface.functions) {
+ auto function_generator = generator.fork();
+ function_generator.set("function.name:snakecase", snake_name(function.name));
+ function_generator.append(R"~~~(
+ JS_DECLARE_NATIVE_FUNCTION(@function.name:snakecase@);
+ )~~~");
+ }
+
+ for (auto& attribute : interface.attributes) {
+ auto attribute_generator = generator.fork();
+ attribute_generator.set("attribute.name:snakecase", snake_name(attribute.name));
+ attribute_generator.append(R"~~~(
+ JS_DECLARE_NATIVE_GETTER(@attribute.name:snakecase@_getter);
+)~~~");
+
+ if (!attribute.readonly) {
+ attribute_generator.append(R"~~~(
+ JS_DECLARE_NATIVE_SETTER(@attribute.name:snakecase@_setter);
+)~~~");
+ }
+ }
+
+ if (interface.wrapper_base_class == "Wrapper") {
+ generator.append(R"~~~(
+ NonnullRefPtr<@fully_qualified_name@> m_impl;
+ )~~~");
+ }
+
+ generator.append(R"~~~(
+};
+)~~~");
+
+ if (should_emit_wrapper_factory(interface)) {
+ generator.append(R"~~~(
+@wrapper_class@* wrap(JS::GlobalObject&, @fully_qualified_name@&);
+)~~~");
+ }
+
+ generator.append(R"~~~(
+} // namespace Web::Bindings
+)~~~");
+
+ outln("{}", generator.as_string_view());
+}
+
+void generate_implementation(const IDL::Interface& interface)
+{
+ StringBuilder builder;
+ SourceGenerator generator { builder };
+
+ generator.set("wrapper_class", interface.wrapper_class);
+ generator.set("wrapper_base_class", interface.wrapper_base_class);
+ generator.set("fully_qualified_name", interface.fully_qualified_name);
+
+ generator.append(R"~~~(
+#include <AK/FlyString.h>
+#include <LibJS/Runtime/Array.h>
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/Function.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/Uint8ClampedArray.h>
+#include <LibJS/Runtime/Value.h>
+#include <LibWeb/Bindings/@wrapper_class@.h>
+#include <LibWeb/Bindings/CanvasRenderingContext2DWrapper.h>
+#include <LibWeb/Bindings/CommentWrapper.h>
+#include <LibWeb/Bindings/DOMImplementationWrapper.h>
+#include <LibWeb/Bindings/DocumentFragmentWrapper.h>
+#include <LibWeb/Bindings/DocumentTypeWrapper.h>
+#include <LibWeb/Bindings/DocumentWrapper.h>
+#include <LibWeb/Bindings/EventTargetWrapperFactory.h>
+#include <LibWeb/Bindings/HTMLCanvasElementWrapper.h>
+#include <LibWeb/Bindings/HTMLHeadElementWrapper.h>
+#include <LibWeb/Bindings/HTMLImageElementWrapper.h>
+#include <LibWeb/Bindings/ImageDataWrapper.h>
+#include <LibWeb/Bindings/NodeWrapperFactory.h>
+#include <LibWeb/Bindings/TextWrapper.h>
+#include <LibWeb/Bindings/WindowObject.h>
+#include <LibWeb/DOM/Element.h>
+#include <LibWeb/DOM/EventListener.h>
+#include <LibWeb/HTML/HTMLElement.h>
+#include <LibWeb/Origin.h>
+
+// FIXME: This is a total hack until we can figure out the namespace for a given type somehow.
+using namespace Web::DOM;
+using namespace Web::HTML;
+
+namespace Web::Bindings {
+
+)~~~");
+
+ if (interface.wrapper_base_class == "Wrapper") {
+ generator.append(R"~~~(
+@wrapper_class@::@wrapper_class@(JS::GlobalObject& global_object, @fully_qualified_name@& impl)
+: Wrapper(*global_object.object_prototype())
+, m_impl(impl)
+{
+}
+)~~~");
+ } else {
+ generator.append(R"~~~(
+@wrapper_class@::@wrapper_class@(JS::GlobalObject& global_object, @fully_qualified_name@& impl)
+: @wrapper_base_class@(global_object, impl)
+{
+}
+)~~~");
+ }
+
+ generator.append(R"~~~(
+void @wrapper_class@::initialize(JS::GlobalObject& global_object)
+{
+ [[maybe_unused]] u8 default_attributes = JS::Attribute::Enumerable | JS::Attribute::Configurable;
+
+ @wrapper_base_class@::initialize(global_object);
+)~~~");
+
+ for (auto& attribute : interface.attributes) {
+ auto attribute_generator = generator.fork();
+ attribute_generator.set("attribute.name", attribute.name);
+ attribute_generator.set("attribute.getter_callback", attribute.getter_callback_name);
+
+ if (attribute.readonly)
+ attribute_generator.set("attribute.setter_callback", "nullptr");
+ else
+ attribute_generator.set("attribute.setter_callback", attribute.setter_callback_name);
+
+ attribute_generator.append(R"~~~(
+ define_native_property("@attribute.name@", @attribute.getter_callback@, @attribute.setter_callback@, default_attributes);
+)~~~");
+ }
+
+ for (auto& function : interface.functions) {
+ auto function_generator = generator.fork();
+ function_generator.set("function.name", function.name);
+ function_generator.set("function.name:snakecase", snake_name(function.name));
+ function_generator.set("function.name:length", String::number(function.name.length()));
+
+ function_generator.append(R"~~~(
+ define_native_function("@function.name@", @function.name:snakecase@, @function.name:length@, default_attributes);
+)~~~");
+ }
+
+ generator.append(R"~~~(
+}
+
+@wrapper_class@::~@wrapper_class@()
+{
+}
+)~~~");
+
+ if (!interface.attributes.is_empty() || !interface.functions.is_empty()) {
+ generator.append(R"~~~(
+static @fully_qualified_name@* impl_from(JS::VM& vm, JS::GlobalObject& global_object)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return {};
+ if (!is<@wrapper_class@>(this_object)) {
+ vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "@fully_qualified_name@");
+ return nullptr;
+ }
+
+ return &static_cast<@wrapper_class@*>(this_object)->impl();
+ }
+)~~~");
+ }
+
+ auto generate_to_cpp = [&](auto& parameter, auto& js_name, const auto& js_suffix, auto cpp_name, bool return_void = false, bool legacy_null_to_empty_string = false, bool optional = false) {
+ auto scoped_generator = generator.fork();
+ scoped_generator.set("cpp_name", cpp_name);
+ scoped_generator.set("js_name", js_name);
+ scoped_generator.set("js_suffix", js_suffix);
+ scoped_generator.set("legacy_null_to_empty_string", legacy_null_to_empty_string ? "true" : "false");
+ scoped_generator.set("parameter.type.name", parameter.type.name);
+
+ if (return_void)
+ scoped_generator.set("return_statement", "return;");
+ else
+ scoped_generator.set("return_statement", "return {};");
+
+ // FIXME: Add support for optional to all types
+ if (parameter.type.name == "DOMString") {
+ if (!optional) {
+ scoped_generator.append(R"~~~(
+ auto @cpp_name@ = @js_name@@js_suffix@.to_string(global_object, @legacy_null_to_empty_string@);
+ if (vm.exception())
+ @return_statement@
+)~~~");
+ } else {
+ scoped_generator.append(R"~~~(
+ String @cpp_name@;
+ if (!@js_name@@js_suffix@.is_undefined()) {
+ @cpp_name@ = @js_name@@js_suffix@.to_string(global_object, @legacy_null_to_empty_string@);
+ if (vm.exception())
+ @return_statement@
+ }
+)~~~");
+ }
+ } else if (parameter.type.name == "EventListener") {
+ scoped_generator.append(R"~~~(
+ if (!@js_name@@js_suffix@.is_function()) {
+ vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "Function");
+ @return_statement@
+ }
+ auto @cpp_name@ = adopt(*new EventListener(JS::make_handle(&@js_name@@js_suffix@.as_function())));
+)~~~");
+ } else if (is_wrappable_type(parameter.type)) {
+ scoped_generator.append(R"~~~(
+ auto @cpp_name@_object = @js_name@@js_suffix@.to_object(global_object);
+ if (vm.exception())
+ @return_statement@
+
+ if (!is<@parameter.type.name@Wrapper>(@cpp_name@_object)) {
+ vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "@parameter.type.name@");
+ @return_statement@
+ }
+
+ auto& @cpp_name@ = static_cast<@parameter.type.name@Wrapper*>(@cpp_name@_object)->impl();
+)~~~");
+ } else if (parameter.type.name == "double") {
+ scoped_generator.append(R"~~~(
+ auto @cpp_name@ = @js_name@@js_suffix@.to_double(global_object);
+ if (vm.exception())
+ @return_statement@
+)~~~");
+ } else if (parameter.type.name == "boolean") {
+ scoped_generator.append(R"~~~(
+ auto @cpp_name@ = @js_name@@js_suffix@.to_boolean();
+)~~~");
+ } else {
+ dbgln("Unimplemented JS-to-C++ conversion: {}", parameter.type.name);
+ ASSERT_NOT_REACHED();
+ }
+ };
+
+ auto generate_arguments = [&](auto& parameters, auto& arguments_builder, bool return_void = false) {
+ auto arguments_generator = generator.fork();
+
+ Vector<String> parameter_names;
+ size_t argument_index = 0;
+ for (auto& parameter : parameters) {
+ parameter_names.append(snake_name(parameter.name));
+ arguments_generator.set("argument.index", String::number(argument_index));
+
+ arguments_generator.append(R"~~~(
+ auto arg@argument.index@ = vm.argument(@argument.index@);
+)~~~");
+ // FIXME: Parameters can have [LegacyNullToEmptyString] attached.
+ generate_to_cpp(parameter, "arg", String::number(argument_index), snake_name(parameter.name), return_void, false, parameter.optional);
+ ++argument_index;
+ }
+
+ arguments_builder.join(", ", parameter_names);
+ };
+
+ auto generate_return_statement = [&](auto& return_type) {
+ auto scoped_generator = generator.fork();
+ scoped_generator.set("return_type", return_type.name);
+
+ if (return_type.name == "undefined") {
+ scoped_generator.append(R"~~~(
+ return JS::js_undefined();
+)~~~");
+ return;
+ }
+
+ if (return_type.nullable) {
+ if (return_type.name == "DOMString") {
+ scoped_generator.append(R"~~~(
+ if (retval.is_null())
+ return JS::js_null();
+)~~~");
+ } else {
+ scoped_generator.append(R"~~~(
+ if (!retval)
+ return JS::js_null();
+)~~~");
+ }
+ }
+
+ if (return_type.name == "DOMString") {
+ scoped_generator.append(R"~~~(
+ return JS::js_string(vm, retval);
+)~~~");
+ } else if (return_type.name == "ArrayFromVector") {
+ // FIXME: Remove this fake type hack once it's no longer needed.
+ // Basically once we have NodeList we can throw this out.
+ scoped_generator.append(R"~~~(
+ auto* new_array = JS::Array::create(global_object);
+ for (auto& element : retval)
+ new_array->indexed_properties().append(wrap(global_object, element));
+
+ return new_array;
+)~~~");
+ } else if (return_type.name == "long" || return_type.name == "double" || return_type.name == "boolean" || return_type.name == "short") {
+ scoped_generator.append(R"~~~(
+ return JS::Value(retval);
+)~~~");
+ } else if (return_type.name == "Uint8ClampedArray") {
+ scoped_generator.append(R"~~~(
+ return retval;
+)~~~");
+ } else {
+ scoped_generator.append(R"~~~(
+ return wrap(global_object, const_cast<@return_type@&>(*retval));
+)~~~");
+ }
+ };
+
+ for (auto& attribute : interface.attributes) {
+ auto attribute_generator = generator.fork();
+ attribute_generator.set("attribute.getter_callback", attribute.getter_callback_name);
+ attribute_generator.set("attribute.setter_callback", attribute.setter_callback_name);
+ attribute_generator.set("attribute.name:snakecase", snake_name(attribute.name));
+
+ if (attribute.extended_attributes.contains("Reflect")) {
+ auto attribute_name = attribute.extended_attributes.get("Reflect").value();
+ if (attribute_name.is_null())
+ attribute_name = attribute.name;
+ attribute_name = make_input_acceptable_cpp(attribute_name);
+
+ attribute_generator.set("attribute.reflect_name", attribute_name);
+ } else {
+ attribute_generator.set("attribute.reflect_name", snake_name(attribute.name));
+ }
+
+ attribute_generator.append(R"~~~(
+JS_DEFINE_NATIVE_GETTER(@wrapper_class@::@attribute.getter_callback@)
+{
+ auto* impl = impl_from(vm, global_object);
+ if (!impl)
+ return {};
+)~~~");
+
+ if (attribute.extended_attributes.contains("ReturnNullIfCrossOrigin")) {
+ attribute_generator.append(R"~~~(
+ if (!impl->may_access_from_origin(static_cast<WindowObject&>(global_object).origin()))
+ return JS::js_null();
+)~~~");
+ }
+
+ if (attribute.extended_attributes.contains("Reflect")) {
+ if (attribute.type.name != "boolean") {
+ attribute_generator.append(R"~~~(
+ auto retval = impl->attribute(HTML::AttributeNames::@attribute.reflect_name@);
+)~~~");
+ } else {
+ attribute_generator.append(R"~~~(
+ auto retval = impl->has_attribute(HTML::AttributeNames::@attribute.reflect_name@);
+)~~~");
+ }
+ } else {
+ attribute_generator.append(R"~~~(
+ auto retval = impl->@attribute.name:snakecase@();
+)~~~");
+ }
+
+ generate_return_statement(attribute.type);
+
+ attribute_generator.append(R"~~~(
+}
+)~~~");
+
+ if (!attribute.readonly) {
+ attribute_generator.append(R"~~~(
+JS_DEFINE_NATIVE_SETTER(@wrapper_class@::@attribute.setter_callback@)
+{
+ auto* impl = impl_from(vm, global_object);
+ if (!impl)
+ return;
+)~~~");
+
+ generate_to_cpp(attribute, "value", "", "cpp_value", true, attribute.extended_attributes.contains("LegacyNullToEmptyString"));
+
+ if (attribute.extended_attributes.contains("Reflect")) {
+ if (attribute.type.name != "boolean") {
+ attribute_generator.append(R"~~~(
+ impl->set_attribute(HTML::AttributeNames::@attribute.reflect_name@, cpp_value);
+)~~~");
+ } else {
+ attribute_generator.append(R"~~~(
+ if (!cpp_value)
+ impl->remove_attribute(HTML::AttributeNames::@attribute.reflect_name@);
+ else
+ impl->set_attribute(HTML::AttributeNames::@attribute.reflect_name@, String::empty());
+)~~~");
+ }
+ } else {
+ attribute_generator.append(R"~~~(
+ impl->set_@attribute.name:snakecase@(cpp_value);
+)~~~");
+ }
+
+ attribute_generator.append(R"~~~(
+}
+)~~~");
+ }
+ }
+
+ // Implementation: Functions
+ for (auto& function : interface.functions) {
+ auto function_generator = generator.fork();
+ function_generator.set("function.name", function.name);
+ function_generator.set("function.name:snakecase", snake_name(function.name));
+ function_generator.set("function.nargs", String::number(function.length()));
+
+ function_generator.append(R"~~~(\
+JS_DEFINE_NATIVE_FUNCTION(@wrapper_class@::@function.name:snakecase@)
+{
+ auto* impl = impl_from(vm, global_object);
+ if (!impl)
+ return {};
+)~~~");
+
+ if (function.length() > 0) {
+ if (function.length() == 1) {
+ function_generator.set(".bad_arg_count", "JS::ErrorType::BadArgCountOne");
+ function_generator.set(".arg_count_suffix", "");
+ } else {
+ function_generator.set(".bad_arg_count", "JS::ErrorType::BadArgCountMany");
+ function_generator.set(".arg_count_suffix", String::formatted(", \"{}\"", function.length()));
+ }
+
+ function_generator.append(R"~~~(
+ if (vm.argument_count() < @function.nargs@) {
+ vm.throw_exception<JS::TypeError>(global_object, @.bad_arg_count@, "@function.name@"@.arg_count_suffix@);
+ return {};
+ }
+)~~~");
+ }
+
+ StringBuilder arguments_builder;
+ generate_arguments(function.parameters, arguments_builder);
+
+ function_generator.set(".arguments", arguments_builder.string_view());
+
+ if (function.return_type.name != "undefined") {
+ function_generator.append(R"~~~(
+ auto retval = impl->@function.name:snakecase@(@.arguments@);
+)~~~");
+ } else {
+ function_generator.append(R"~~~(
+ impl->@function.name:snakecase@(@.arguments@);
+)~~~");
+ }
+
+ generate_return_statement(function.return_type);
+
+ function_generator.append(R"~~~(
+}
+)~~~");
+ }
+
+ if (should_emit_wrapper_factory(interface)) {
+ generator.append(R"~~~(
+@wrapper_class@* wrap(JS::GlobalObject& global_object, @fully_qualified_name@& impl)
+{
+ return static_cast<@wrapper_class@*>(wrap_impl(global_object, impl));
+}
+)~~~");
+ }
+
+ generator.append(R"~~~(
+} // namespace Web::Bindings
+)~~~");
+
+ outln("{}", generator.as_string_view());
+}
diff --git a/Userland/Libraries/LibWeb/DOM/Attribute.h b/Userland/Libraries/LibWeb/DOM/Attribute.h
new file mode 100644
index 0000000000..32ca3d11b4
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/Attribute.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/FlyString.h>
+
+namespace Web {
+
+class Attribute {
+public:
+ Attribute(const FlyString& name, const String& value)
+ : m_name(name)
+ , m_value(value)
+ {
+ }
+
+ const FlyString& name() const { return m_name; }
+ const String& value() const { return m_value; }
+
+ void set_value(const String& value) { m_value = value; }
+
+private:
+ FlyString m_name;
+ String m_value;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/CharacterData.cpp b/Userland/Libraries/LibWeb/DOM/CharacterData.cpp
new file mode 100644
index 0000000000..60fb60ef7f
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/CharacterData.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/DOM/CharacterData.h>
+
+namespace Web::DOM {
+
+CharacterData::CharacterData(Document& document, NodeType type, const String& data)
+ : Node(document, type)
+ , m_data(data)
+{
+}
+
+CharacterData::~CharacterData()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/CharacterData.h b/Userland/Libraries/LibWeb/DOM/CharacterData.h
new file mode 100644
index 0000000000..de09340e41
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/CharacterData.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/String.h>
+#include <LibWeb/DOM/Node.h>
+#include <LibWeb/DOM/NonDocumentTypeChildNode.h>
+
+namespace Web::DOM {
+
+class CharacterData
+ : public Node
+ , public NonDocumentTypeChildNode<CharacterData> {
+public:
+ using WrapperType = Bindings::CharacterDataWrapper;
+
+ virtual ~CharacterData() override;
+
+ const String& data() const { return m_data; }
+ void set_data(const String& data) { m_data = data; }
+
+ unsigned length() const { return m_data.length(); }
+
+ virtual String text_content() const override { return m_data; }
+
+protected:
+ explicit CharacterData(Document&, NodeType, const String&);
+
+private:
+ String m_data;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/CharacterData.idl b/Userland/Libraries/LibWeb/DOM/CharacterData.idl
new file mode 100644
index 0000000000..e024979c05
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/CharacterData.idl
@@ -0,0 +1,9 @@
+interface CharacterData : Node {
+
+ attribute DOMString data;
+ readonly attribute unsigned long length;
+
+ readonly attribute Element? nextElementSibling;
+ readonly attribute Element? previousElementSibling;
+
+};
diff --git a/Userland/Libraries/LibWeb/DOM/Comment.cpp b/Userland/Libraries/LibWeb/DOM/Comment.cpp
new file mode 100644
index 0000000000..7e3f0ab8d2
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/Comment.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/DOM/Comment.h>
+#include <LibWeb/Layout/TextNode.h>
+
+namespace Web::DOM {
+
+Comment::Comment(Document& document, const String& data)
+ : CharacterData(document, NodeType::COMMENT_NODE, data)
+{
+}
+
+Comment::~Comment()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/Comment.h b/Userland/Libraries/LibWeb/DOM/Comment.h
new file mode 100644
index 0000000000..4ab3bfbedb
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/Comment.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/FlyString.h>
+#include <LibWeb/DOM/CharacterData.h>
+
+namespace Web::DOM {
+
+class Comment final : public CharacterData {
+public:
+ using WrapperType = Bindings::CommentWrapper;
+
+ explicit Comment(Document&, const String&);
+ virtual ~Comment() override;
+
+ virtual FlyString node_name() const override { return "#comment"; }
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/Comment.idl b/Userland/Libraries/LibWeb/DOM/Comment.idl
new file mode 100644
index 0000000000..7fb01d1d70
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/Comment.idl
@@ -0,0 +1,3 @@
+interface Comment : CharacterData {
+
+};
diff --git a/Userland/Libraries/LibWeb/DOM/DOMImplementation.cpp b/Userland/Libraries/LibWeb/DOM/DOMImplementation.cpp
new file mode 100644
index 0000000000..0fdeb062b6
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/DOMImplementation.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2020, the SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/DOM/DOMImplementation.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/DOM/DocumentType.h>
+#include <LibWeb/DOM/ElementFactory.h>
+#include <LibWeb/DOM/Text.h>
+#include <LibWeb/Namespace.h>
+#include <LibWeb/Origin.h>
+
+namespace Web::DOM {
+
+DOMImplementation::DOMImplementation(Document& document)
+ : m_document(document)
+{
+}
+
+const NonnullRefPtr<Document> DOMImplementation::create_htmldocument(const String& title) const
+{
+ auto html_document = Document::create();
+
+ html_document->set_content_type("text/html");
+ html_document->set_ready_for_post_load_tasks(true);
+
+ auto doctype = adopt(*new DocumentType(html_document));
+ doctype->set_name("html");
+ html_document->append_child(doctype);
+
+ auto html_element = create_element(html_document, HTML::TagNames::html, Namespace::HTML);
+ html_document->append_child(html_element);
+
+ auto head_element = create_element(html_document, HTML::TagNames::head, Namespace::HTML);
+ html_element->append_child(head_element);
+
+ if (!title.is_null()) {
+ auto title_element = create_element(html_document, HTML::TagNames::title, Namespace::HTML);
+ head_element->append_child(title_element);
+
+ auto text_node = adopt(*new Text(html_document, title));
+ title_element->append_child(text_node);
+ }
+
+ auto body_element = create_element(html_document, HTML::TagNames::body, Namespace::HTML);
+ html_element->append_child(body_element);
+
+ html_document->set_origin(m_document.origin());
+
+ return html_document;
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/DOMImplementation.h b/Userland/Libraries/LibWeb/DOM/DOMImplementation.h
new file mode 100644
index 0000000000..ac59fab4d0
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/DOMImplementation.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2020, the SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/NonnullRefPtr.h>
+#include <AK/RefCounted.h>
+#include <AK/Weakable.h>
+#include <LibWeb/Bindings/Wrappable.h>
+
+namespace Web::DOM {
+
+class DOMImplementation final
+ : public RefCounted<DOMImplementation>
+ , public Weakable<DOMImplementation>
+ , public Bindings::Wrappable {
+public:
+ using WrapperType = Bindings::DOMImplementationWrapper;
+
+ static NonnullRefPtr<DOMImplementation> create(Document& document)
+ {
+ return adopt(*new DOMImplementation(document));
+ }
+
+ // FIXME: snake_case in WrapperGenerator turns "createHTMLDocument" into "create_htmldocument"
+ const NonnullRefPtr<Document> create_htmldocument(const String& title) const;
+
+ // https://dom.spec.whatwg.org/#dom-domimplementation-hasfeature
+ bool has_feature() const { return true; }
+
+private:
+ explicit DOMImplementation(Document&);
+
+ Document& m_document;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/DOMImplementation.idl b/Userland/Libraries/LibWeb/DOM/DOMImplementation.idl
new file mode 100644
index 0000000000..c979a587c3
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/DOMImplementation.idl
@@ -0,0 +1,7 @@
+interface DOMImplementation {
+
+ Document createHTMLDocument(optional DOMString title);
+
+ boolean hasFeature();
+
+};
diff --git a/Userland/Libraries/LibWeb/DOM/Document.cpp b/Userland/Libraries/LibWeb/DOM/Document.cpp
new file mode 100644
index 0000000000..f61e81fb70
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/Document.cpp
@@ -0,0 +1,690 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/StringBuilder.h>
+#include <AK/Utf8View.h>
+#include <LibCore/Timer.h>
+#include <LibGUI/Application.h>
+#include <LibGUI/DisplayLink.h>
+#include <LibGUI/MessageBox.h>
+#include <LibJS/Interpreter.h>
+#include <LibJS/Parser.h>
+#include <LibJS/Runtime/Function.h>
+#include <LibWeb/Bindings/DocumentWrapper.h>
+#include <LibWeb/Bindings/WindowObject.h>
+#include <LibWeb/CSS/StyleResolver.h>
+#include <LibWeb/DOM/Comment.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/DOM/DocumentFragment.h>
+#include <LibWeb/DOM/DocumentType.h>
+#include <LibWeb/DOM/Element.h>
+#include <LibWeb/DOM/ElementFactory.h>
+#include <LibWeb/DOM/Event.h>
+#include <LibWeb/DOM/Text.h>
+#include <LibWeb/DOM/Window.h>
+#include <LibWeb/Dump.h>
+#include <LibWeb/HTML/AttributeNames.h>
+#include <LibWeb/HTML/EventNames.h>
+#include <LibWeb/HTML/HTMLBodyElement.h>
+#include <LibWeb/HTML/HTMLFrameSetElement.h>
+#include <LibWeb/HTML/HTMLHeadElement.h>
+#include <LibWeb/HTML/HTMLHtmlElement.h>
+#include <LibWeb/HTML/HTMLScriptElement.h>
+#include <LibWeb/HTML/HTMLTitleElement.h>
+#include <LibWeb/InProcessWebView.h>
+#include <LibWeb/Layout/BlockFormattingContext.h>
+#include <LibWeb/Layout/InitialContainingBlockBox.h>
+#include <LibWeb/Layout/TreeBuilder.h>
+#include <LibWeb/Namespace.h>
+#include <LibWeb/Origin.h>
+#include <LibWeb/Page/Frame.h>
+#include <LibWeb/SVG/TagNames.h>
+#include <ctype.h>
+#include <stdio.h>
+
+namespace Web::DOM {
+
+Document::Document(const URL& url)
+ : ParentNode(*this, NodeType::DOCUMENT_NODE)
+ , m_style_resolver(make<CSS::StyleResolver>(*this))
+ , m_style_sheets(CSS::StyleSheetList::create(*this))
+ , m_url(url)
+ , m_window(Window::create_with_document(*this))
+ , m_implementation(DOMImplementation::create(*this))
+{
+ m_style_update_timer = Core::Timer::create_single_shot(0, [this] {
+ update_style();
+ });
+
+ m_forced_layout_timer = Core::Timer::create_single_shot(0, [this] {
+ force_layout();
+ });
+}
+
+Document::~Document()
+{
+}
+
+void Document::removed_last_ref()
+{
+ ASSERT(!ref_count());
+ ASSERT(!m_deletion_has_begun);
+
+ if (m_referencing_node_count) {
+ // The document has reached ref_count==0 but still has nodes keeping it alive.
+ // At this point, sever all the node links we control.
+ // If nodes remain elsewhere (e.g JS wrappers), they will keep the document alive.
+
+ // NOTE: This makes sure we stay alive across for the duration of the cleanup below.
+ increment_referencing_node_count();
+
+ m_focused_element = nullptr;
+ m_hovered_node = nullptr;
+ m_pending_parsing_blocking_script = nullptr;
+ m_inspected_node = nullptr;
+ m_scripts_to_execute_when_parsing_has_finished.clear();
+ m_scripts_to_execute_as_soon_as_possible.clear();
+ m_associated_inert_template_document = nullptr;
+
+ m_interpreter = nullptr;
+
+ {
+ // Gather up all the descendants of this document and prune them from the tree.
+ // FIXME: This could definitely be more elegant.
+ NonnullRefPtrVector<Node> descendants;
+ for_each_in_subtree([&](auto& node) {
+ if (&node != this)
+ descendants.append(node);
+ return IterationDecision::Continue;
+ });
+
+ for (auto& node : descendants) {
+ ASSERT(&node.document() == this);
+ ASSERT(!node.is_document());
+ if (node.parent())
+ node.parent()->remove_child(node);
+ }
+ }
+
+ m_in_removed_last_ref = false;
+ decrement_referencing_node_count();
+ return;
+ }
+
+ m_in_removed_last_ref = false;
+ m_deletion_has_begun = true;
+ delete this;
+}
+
+Origin Document::origin() const
+{
+ if (!m_url.is_valid())
+ return {};
+ return { m_url.protocol(), m_url.host(), m_url.port() };
+}
+
+void Document::set_origin(const Origin& origin)
+{
+ m_url.set_protocol(origin.protocol());
+ m_url.set_host(origin.host());
+ m_url.set_port(origin.port());
+}
+
+void Document::schedule_style_update()
+{
+ if (m_style_update_timer->is_active())
+ return;
+ m_style_update_timer->start();
+}
+
+void Document::schedule_forced_layout()
+{
+ if (m_forced_layout_timer->is_active())
+ return;
+ m_forced_layout_timer->start();
+}
+
+bool Document::is_child_allowed(const Node& node) const
+{
+ switch (node.type()) {
+ case NodeType::DOCUMENT_NODE:
+ case NodeType::TEXT_NODE:
+ return false;
+ case NodeType::COMMENT_NODE:
+ return true;
+ case NodeType::DOCUMENT_TYPE_NODE:
+ return !first_child_of_type<DocumentType>();
+ case NodeType::ELEMENT_NODE:
+ return !first_child_of_type<Element>();
+ default:
+ return false;
+ }
+}
+
+const Element* Document::document_element() const
+{
+ return first_child_of_type<Element>();
+}
+
+const HTML::HTMLHtmlElement* Document::html_element() const
+{
+ auto* html = document_element();
+ if (is<HTML::HTMLHtmlElement>(html))
+ return downcast<HTML::HTMLHtmlElement>(html);
+ return nullptr;
+}
+
+const HTML::HTMLHeadElement* Document::head() const
+{
+ auto* html = html_element();
+ if (!html)
+ return nullptr;
+ return html->first_child_of_type<HTML::HTMLHeadElement>();
+}
+
+const HTML::HTMLElement* Document::body() const
+{
+ auto* html = html_element();
+ if (!html)
+ return nullptr;
+ auto* first_body = html->first_child_of_type<HTML::HTMLBodyElement>();
+ if (first_body)
+ return first_body;
+ auto* first_frameset = html->first_child_of_type<HTML::HTMLFrameSetElement>();
+ if (first_frameset)
+ return first_frameset;
+ return nullptr;
+}
+
+void Document::set_body(HTML::HTMLElement& new_body)
+{
+ if (!is<HTML::HTMLBodyElement>(new_body) && !is<HTML::HTMLFrameSetElement>(new_body)) {
+ // FIXME: throw a "HierarchyRequestError" DOMException.
+ return;
+ }
+
+ auto* existing_body = body();
+ if (existing_body) {
+ TODO();
+ return;
+ }
+
+ auto* html = document_element();
+ if (!html) {
+ // FIXME: throw a "HierarchyRequestError" DOMException.
+ return;
+ }
+
+ // FIXME: Implement this once there's a non-const first_child_of_type:
+ // "Otherwise, the body element is null, but there's a document element. Append the new value to the document element."
+ TODO();
+}
+
+String Document::title() const
+{
+ auto* head_element = head();
+ if (!head_element)
+ return {};
+
+ auto* title_element = head_element->first_child_of_type<HTML::HTMLTitleElement>();
+ if (!title_element)
+ return {};
+
+ auto raw_title = title_element->text_content();
+
+ StringBuilder builder;
+ bool last_was_space = false;
+ for (auto code_point : Utf8View(raw_title)) {
+ if (isspace(code_point)) {
+ last_was_space = true;
+ } else {
+ if (last_was_space && !builder.is_empty())
+ builder.append(' ');
+ builder.append_code_point(code_point);
+ last_was_space = false;
+ }
+ }
+ return builder.to_string();
+}
+
+void Document::set_title(const String& title)
+{
+ auto* head_element = const_cast<HTML::HTMLHeadElement*>(head());
+ if (!head_element)
+ return;
+
+ RefPtr<HTML::HTMLTitleElement> title_element = head_element->first_child_of_type<HTML::HTMLTitleElement>();
+ if (!title_element) {
+ title_element = static_ptr_cast<HTML::HTMLTitleElement>(create_element(HTML::TagNames::title));
+ head_element->append_child(*title_element);
+ }
+
+ while (RefPtr<Node> child = title_element->first_child())
+ title_element->remove_child(child.release_nonnull());
+
+ title_element->append_child(adopt(*new Text(*this, title)));
+
+ if (auto* page = this->page())
+ page->client().page_did_change_title(title);
+}
+
+void Document::attach_to_frame(Badge<Frame>, Frame& frame)
+{
+ m_frame = frame;
+ update_layout();
+}
+
+void Document::detach_from_frame(Badge<Frame>, Frame& frame)
+{
+ ASSERT(&frame == m_frame);
+ tear_down_layout_tree();
+ m_frame = nullptr;
+}
+
+void Document::tear_down_layout_tree()
+{
+ if (!m_layout_root)
+ return;
+
+ // Gather up all the layout nodes in a vector and detach them from parents
+ // while the vector keeps them alive.
+
+ NonnullRefPtrVector<Layout::Node> layout_nodes;
+
+ m_layout_root->for_each_in_subtree([&](auto& layout_node) {
+ layout_nodes.append(layout_node);
+ return IterationDecision::Continue;
+ });
+
+ for (auto& layout_node : layout_nodes) {
+ if (layout_node.parent())
+ layout_node.parent()->remove_child(layout_node);
+ }
+
+ m_layout_root = nullptr;
+}
+
+Color Document::background_color(const Palette& palette) const
+{
+ auto default_color = palette.base();
+ auto* body_element = body();
+ if (!body_element)
+ return default_color;
+
+ auto* body_layout_node = body_element->layout_node();
+ if (!body_layout_node)
+ return default_color;
+
+ auto color = body_layout_node->computed_values().background_color();
+ if (!color.alpha())
+ return default_color;
+ return color;
+}
+
+RefPtr<Gfx::Bitmap> Document::background_image() const
+{
+ auto* body_element = body();
+ if (!body_element)
+ return {};
+
+ auto* body_layout_node = body_element->layout_node();
+ if (!body_layout_node)
+ return {};
+
+ auto background_image = body_layout_node->background_image();
+ if (!background_image)
+ return {};
+ return background_image->bitmap();
+}
+
+URL Document::complete_url(const String& string) const
+{
+ return m_url.complete_url(string);
+}
+
+void Document::invalidate_layout()
+{
+ tear_down_layout_tree();
+}
+
+void Document::force_layout()
+{
+ invalidate_layout();
+ update_layout();
+}
+
+void Document::update_layout()
+{
+ if (!frame())
+ return;
+
+ if (!m_layout_root) {
+ Layout::TreeBuilder tree_builder;
+ m_layout_root = static_ptr_cast<Layout::InitialContainingBlockBox>(tree_builder.build(*this));
+ }
+
+ Layout::BlockFormattingContext root_formatting_context(*m_layout_root, nullptr);
+ root_formatting_context.run(*m_layout_root, Layout::LayoutMode::Default);
+
+ m_layout_root->set_needs_display();
+
+ if (frame()->is_main_frame()) {
+ if (auto* page = this->page())
+ page->client().page_did_layout();
+ }
+}
+
+static void update_style_recursively(DOM::Node& node)
+{
+ node.for_each_child([&](auto& child) {
+ if (child.needs_style_update()) {
+ if (is<Element>(child))
+ downcast<Element>(child).recompute_style();
+ child.set_needs_style_update(false);
+ }
+ if (child.child_needs_style_update()) {
+ update_style_recursively(child);
+ child.set_child_needs_style_update(false);
+ }
+ return IterationDecision::Continue;
+ });
+}
+
+void Document::update_style()
+{
+ update_style_recursively(*this);
+ update_layout();
+}
+
+RefPtr<Layout::Node> Document::create_layout_node()
+{
+ return adopt(*new Layout::InitialContainingBlockBox(*this, CSS::StyleProperties::create()));
+}
+
+void Document::set_link_color(Color color)
+{
+ m_link_color = color;
+}
+
+void Document::set_active_link_color(Color color)
+{
+ m_active_link_color = color;
+}
+
+void Document::set_visited_link_color(Color color)
+{
+ m_visited_link_color = color;
+}
+
+const Layout::InitialContainingBlockBox* Document::layout_node() const
+{
+ return static_cast<const Layout::InitialContainingBlockBox*>(Node::layout_node());
+}
+
+Layout::InitialContainingBlockBox* Document::layout_node()
+{
+ return static_cast<Layout::InitialContainingBlockBox*>(Node::layout_node());
+}
+
+void Document::set_inspected_node(Node* node)
+{
+ if (m_inspected_node == node)
+ return;
+
+ if (m_inspected_node && m_inspected_node->layout_node())
+ m_inspected_node->layout_node()->set_needs_display();
+
+ m_inspected_node = node;
+
+ if (m_inspected_node && m_inspected_node->layout_node())
+ m_inspected_node->layout_node()->set_needs_display();
+}
+
+void Document::set_hovered_node(Node* node)
+{
+ if (m_hovered_node == node)
+ return;
+
+ RefPtr<Node> old_hovered_node = move(m_hovered_node);
+ m_hovered_node = node;
+
+ invalidate_style();
+}
+
+NonnullRefPtrVector<Element> Document::get_elements_by_name(const String& name) const
+{
+ NonnullRefPtrVector<Element> elements;
+ for_each_in_subtree_of_type<Element>([&](auto& element) {
+ if (element.attribute(HTML::AttributeNames::name) == name)
+ elements.append(element);
+ return IterationDecision::Continue;
+ });
+ return elements;
+}
+
+NonnullRefPtrVector<Element> Document::get_elements_by_tag_name(const FlyString& tag_name) const
+{
+ NonnullRefPtrVector<Element> elements;
+ for_each_in_subtree_of_type<Element>([&](auto& element) {
+ if (element.local_name() == tag_name)
+ elements.append(element);
+ return IterationDecision::Continue;
+ });
+ return elements;
+}
+
+NonnullRefPtrVector<Element> Document::get_elements_by_class_name(const FlyString& class_name) const
+{
+ NonnullRefPtrVector<Element> elements;
+ for_each_in_subtree_of_type<Element>([&](auto& element) {
+ if (element.has_class(class_name))
+ elements.append(element);
+ return IterationDecision::Continue;
+ });
+ return elements;
+}
+
+Color Document::link_color() const
+{
+ if (m_link_color.has_value())
+ return m_link_color.value();
+ if (!page())
+ return Color::Blue;
+ return page()->palette().link();
+}
+
+Color Document::active_link_color() const
+{
+ if (m_active_link_color.has_value())
+ return m_active_link_color.value();
+ if (!page())
+ return Color::Red;
+ return page()->palette().active_link();
+}
+
+Color Document::visited_link_color() const
+{
+ if (m_visited_link_color.has_value())
+ return m_visited_link_color.value();
+ if (!page())
+ return Color::Magenta;
+ return page()->palette().visited_link();
+}
+
+static JS::VM& main_thread_vm()
+{
+ static RefPtr<JS::VM> vm;
+ if (!vm) {
+ vm = JS::VM::create();
+ vm->set_should_log_exceptions(true);
+ }
+ return *vm;
+}
+
+JS::Interpreter& Document::interpreter()
+{
+ if (!m_interpreter)
+ m_interpreter = JS::Interpreter::create<Bindings::WindowObject>(main_thread_vm(), *m_window);
+ return *m_interpreter;
+}
+
+JS::Value Document::run_javascript(const StringView& source)
+{
+ auto parser = JS::Parser(JS::Lexer(source));
+ auto program = parser.parse_program();
+ if (parser.has_errors()) {
+ parser.print_errors();
+ return JS::js_undefined();
+ }
+ auto& interpreter = document().interpreter();
+ auto result = interpreter.run(interpreter.global_object(), *program);
+ if (interpreter.exception())
+ interpreter.vm().clear_exception();
+ return result;
+}
+
+NonnullRefPtr<Element> Document::create_element(const String& tag_name)
+{
+ // FIXME: Let namespace be the HTML namespace, if this is an HTML document or this’s content type is "application/xhtml+xml", and null otherwise.
+ return DOM::create_element(*this, tag_name, Namespace::HTML);
+}
+
+NonnullRefPtr<DocumentFragment> Document::create_document_fragment()
+{
+ return adopt(*new DocumentFragment(*this));
+}
+
+NonnullRefPtr<Text> Document::create_text_node(const String& data)
+{
+ return adopt(*new Text(*this, data));
+}
+
+NonnullRefPtr<Comment> Document::create_comment(const String& data)
+{
+ return adopt(*new Comment(*this, data));
+}
+
+void Document::set_pending_parsing_blocking_script(Badge<HTML::HTMLScriptElement>, HTML::HTMLScriptElement* script)
+{
+ m_pending_parsing_blocking_script = script;
+}
+
+NonnullRefPtr<HTML::HTMLScriptElement> Document::take_pending_parsing_blocking_script(Badge<HTML::HTMLDocumentParser>)
+{
+ return m_pending_parsing_blocking_script.release_nonnull();
+}
+
+void Document::add_script_to_execute_when_parsing_has_finished(Badge<HTML::HTMLScriptElement>, HTML::HTMLScriptElement& script)
+{
+ m_scripts_to_execute_when_parsing_has_finished.append(script);
+}
+
+NonnullRefPtrVector<HTML::HTMLScriptElement> Document::take_scripts_to_execute_when_parsing_has_finished(Badge<HTML::HTMLDocumentParser>)
+{
+ return move(m_scripts_to_execute_when_parsing_has_finished);
+}
+
+void Document::add_script_to_execute_as_soon_as_possible(Badge<HTML::HTMLScriptElement>, HTML::HTMLScriptElement& script)
+{
+ m_scripts_to_execute_as_soon_as_possible.append(script);
+}
+
+NonnullRefPtrVector<HTML::HTMLScriptElement> Document::take_scripts_to_execute_as_soon_as_possible(Badge<HTML::HTMLDocumentParser>)
+{
+ return move(m_scripts_to_execute_as_soon_as_possible);
+}
+
+void Document::adopt_node(Node& subtree_root)
+{
+ subtree_root.for_each_in_subtree([&](auto& node) {
+ node.set_document({}, *this);
+ return IterationDecision::Continue;
+ });
+}
+
+const DocumentType* Document::doctype() const
+{
+ return first_child_of_type<DocumentType>();
+}
+
+const String& Document::compat_mode() const
+{
+ static String back_compat = "BackCompat";
+ static String css1_compat = "CSS1Compat";
+
+ if (m_quirks_mode == QuirksMode::Yes)
+ return back_compat;
+
+ return css1_compat;
+}
+
+bool Document::is_editable() const
+{
+ return m_editable;
+}
+
+void Document::set_focused_element(Element* element)
+{
+ if (m_focused_element == element)
+ return;
+
+ m_focused_element = element;
+
+ if (m_layout_root)
+ m_layout_root->set_needs_display();
+}
+
+void Document::set_ready_state(const String& ready_state)
+{
+ m_ready_state = ready_state;
+ dispatch_event(Event::create(HTML::EventNames::readystatechange));
+}
+
+Page* Document::page()
+{
+ return m_frame ? m_frame->page() : nullptr;
+}
+
+const Page* Document::page() const
+{
+ return m_frame ? m_frame->page() : nullptr;
+}
+
+EventTarget* Document::get_parent(const Event& event)
+{
+ if (event.type() == HTML::EventNames::load)
+ return nullptr;
+
+ return &window();
+}
+
+void Document::completely_finish_loading()
+{
+ // FIXME: This needs to handle iframes.
+ dispatch_event(DOM::Event::create(HTML::EventNames::load));
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/Document.h b/Userland/Libraries/LibWeb/DOM/Document.h
new file mode 100644
index 0000000000..a46413758d
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/Document.h
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/FlyString.h>
+#include <AK/Function.h>
+#include <AK/NonnullRefPtrVector.h>
+#include <AK/OwnPtr.h>
+#include <AK/String.h>
+#include <AK/URL.h>
+#include <AK/WeakPtr.h>
+#include <LibCore/Forward.h>
+#include <LibJS/Forward.h>
+#include <LibWeb/Bindings/ScriptExecutionContext.h>
+#include <LibWeb/CSS/StyleResolver.h>
+#include <LibWeb/CSS/StyleSheet.h>
+#include <LibWeb/CSS/StyleSheetList.h>
+#include <LibWeb/DOM/DOMImplementation.h>
+#include <LibWeb/DOM/NonElementParentNode.h>
+#include <LibWeb/DOM/ParentNode.h>
+
+namespace Web::DOM {
+
+enum class QuirksMode {
+ No,
+ Limited,
+ Yes
+};
+
+class Document
+ : public ParentNode
+ , public NonElementParentNode<Document>
+ , public Bindings::ScriptExecutionContext {
+public:
+ using WrapperType = Bindings::DocumentWrapper;
+
+ static NonnullRefPtr<Document> create(const URL& url = "about:blank") { return adopt(*new Document(url)); }
+ virtual ~Document() override;
+
+ bool should_invalidate_styles_on_attribute_changes() const { return m_should_invalidate_styles_on_attribute_changes; }
+ void set_should_invalidate_styles_on_attribute_changes(bool b) { m_should_invalidate_styles_on_attribute_changes = b; }
+
+ void set_url(const URL& url) { m_url = url; }
+ URL url() const { return m_url; }
+
+ Origin origin() const;
+ void set_origin(const Origin& origin);
+
+ bool is_scripting_enabled() const { return true; }
+
+ URL complete_url(const String&) const;
+
+ CSS::StyleResolver& style_resolver() { return *m_style_resolver; }
+ const CSS::StyleResolver& style_resolver() const { return *m_style_resolver; }
+
+ CSS::StyleSheetList& style_sheets() { return *m_style_sheets; }
+ const CSS::StyleSheetList& style_sheets() const { return *m_style_sheets; }
+
+ virtual FlyString node_name() const override { return "#document"; }
+
+ void set_hovered_node(Node*);
+ Node* hovered_node() { return m_hovered_node; }
+ const Node* hovered_node() const { return m_hovered_node; }
+
+ void set_inspected_node(Node*);
+ Node* inspected_node() { return m_inspected_node; }
+ const Node* inspected_node() const { return m_inspected_node; }
+
+ const Element* document_element() const;
+ const HTML::HTMLHtmlElement* html_element() const;
+ const HTML::HTMLHeadElement* head() const;
+ const HTML::HTMLElement* body() const;
+ void set_body(HTML::HTMLElement& new_body);
+
+ String title() const;
+ void set_title(const String&);
+
+ void attach_to_frame(Badge<Frame>, Frame&);
+ void detach_from_frame(Badge<Frame>, Frame&);
+
+ Frame* frame() { return m_frame.ptr(); }
+ const Frame* frame() const { return m_frame.ptr(); }
+
+ Page* page();
+ const Page* page() const;
+
+ Color background_color(const Gfx::Palette&) const;
+ RefPtr<Gfx::Bitmap> background_image() const;
+
+ Color link_color() const;
+ void set_link_color(Color);
+
+ Color active_link_color() const;
+ void set_active_link_color(Color);
+
+ Color visited_link_color() const;
+ void set_visited_link_color(Color);
+
+ void force_layout();
+ void invalidate_layout();
+
+ void update_style();
+ void update_layout();
+
+ virtual bool is_child_allowed(const Node&) const override;
+
+ const Layout::InitialContainingBlockBox* layout_node() const;
+ Layout::InitialContainingBlockBox* layout_node();
+
+ void schedule_style_update();
+ void schedule_forced_layout();
+
+ NonnullRefPtrVector<Element> get_elements_by_name(const String&) const;
+ NonnullRefPtrVector<Element> get_elements_by_tag_name(const FlyString&) const;
+ NonnullRefPtrVector<Element> get_elements_by_class_name(const FlyString&) const;
+
+ const String& source() const { return m_source; }
+ void set_source(const String& source) { m_source = source; }
+
+ virtual JS::Interpreter& interpreter() override;
+
+ JS::Value run_javascript(const StringView&);
+
+ NonnullRefPtr<Element> create_element(const String& tag_name);
+ NonnullRefPtr<DocumentFragment> create_document_fragment();
+ NonnullRefPtr<Text> create_text_node(const String& data);
+ NonnullRefPtr<Comment> create_comment(const String& data);
+
+ void set_pending_parsing_blocking_script(Badge<HTML::HTMLScriptElement>, HTML::HTMLScriptElement*);
+ HTML::HTMLScriptElement* pending_parsing_blocking_script() { return m_pending_parsing_blocking_script; }
+ NonnullRefPtr<HTML::HTMLScriptElement> take_pending_parsing_blocking_script(Badge<HTML::HTMLDocumentParser>);
+
+ void add_script_to_execute_when_parsing_has_finished(Badge<HTML::HTMLScriptElement>, HTML::HTMLScriptElement&);
+ NonnullRefPtrVector<HTML::HTMLScriptElement> take_scripts_to_execute_when_parsing_has_finished(Badge<HTML::HTMLDocumentParser>);
+
+ void add_script_to_execute_as_soon_as_possible(Badge<HTML::HTMLScriptElement>, HTML::HTMLScriptElement&);
+ NonnullRefPtrVector<HTML::HTMLScriptElement> take_scripts_to_execute_as_soon_as_possible(Badge<HTML::HTMLDocumentParser>);
+
+ QuirksMode mode() const { return m_quirks_mode; }
+ bool in_quirks_mode() const { return m_quirks_mode == QuirksMode::Yes; }
+ void set_quirks_mode(QuirksMode mode) { m_quirks_mode = mode; }
+
+ void adopt_node(Node&);
+
+ const DocumentType* doctype() const;
+ const String& compat_mode() const;
+
+ void set_editable(bool editable) { m_editable = editable; }
+ virtual bool is_editable() const final;
+
+ Element* focused_element() { return m_focused_element; }
+ const Element* focused_element() const { return m_focused_element; }
+
+ void set_focused_element(Element*);
+
+ bool created_for_appropriate_template_contents() const { return m_created_for_appropriate_template_contents; }
+ void set_created_for_appropriate_template_contents(bool value) { m_created_for_appropriate_template_contents = value; }
+
+ Document* associated_inert_template_document() { return m_associated_inert_template_document; }
+ const Document* associated_inert_template_document() const { return m_associated_inert_template_document; }
+ void set_associated_inert_template_document(Document& document) { m_associated_inert_template_document = document; }
+
+ const String& ready_state() const { return m_ready_state; }
+ void set_ready_state(const String&);
+
+ void ref_from_node(Badge<Node>)
+ {
+ increment_referencing_node_count();
+ }
+
+ void unref_from_node(Badge<Node>)
+ {
+ decrement_referencing_node_count();
+ }
+
+ void removed_last_ref();
+
+ Window& window() { return *m_window; }
+
+ const String& content_type() const { return m_content_type; }
+ void set_content_type(const String& content_type) { m_content_type = content_type; }
+
+ const String& encoding() const { return m_encoding; }
+ void set_encoding(const String& encoding) { m_encoding = encoding; }
+
+ // NOTE: These are intended for the JS bindings
+ const String& character_set() const { return encoding(); }
+ const String& charset() const { return encoding(); }
+ const String& input_encoding() const { return encoding(); }
+
+ bool ready_for_post_load_tasks() const { return m_ready_for_post_load_tasks; }
+ void set_ready_for_post_load_tasks(bool ready) { m_ready_for_post_load_tasks = ready; }
+
+ void completely_finish_loading();
+
+ const NonnullRefPtr<DOMImplementation> implementation() const { return m_implementation; }
+
+ virtual EventTarget* get_parent(const Event&) override;
+
+private:
+ explicit Document(const URL&);
+
+ virtual RefPtr<Layout::Node> create_layout_node() override;
+
+ void tear_down_layout_tree();
+
+ void increment_referencing_node_count()
+ {
+ ASSERT(!m_deletion_has_begun);
+ ++m_referencing_node_count;
+ }
+
+ void decrement_referencing_node_count()
+ {
+ ASSERT(!m_deletion_has_begun);
+ ASSERT(m_referencing_node_count);
+ --m_referencing_node_count;
+ if (!m_referencing_node_count && !ref_count()) {
+ m_deletion_has_begun = true;
+ delete this;
+ }
+ }
+
+ unsigned m_referencing_node_count { 0 };
+
+ OwnPtr<CSS::StyleResolver> m_style_resolver;
+ RefPtr<CSS::StyleSheetList> m_style_sheets;
+ RefPtr<Node> m_hovered_node;
+ RefPtr<Node> m_inspected_node;
+ WeakPtr<Frame> m_frame;
+ URL m_url;
+
+ RefPtr<Window> m_window;
+
+ RefPtr<Layout::InitialContainingBlockBox> m_layout_root;
+
+ Optional<Color> m_link_color;
+ Optional<Color> m_active_link_color;
+ Optional<Color> m_visited_link_color;
+
+ RefPtr<Core::Timer> m_style_update_timer;
+ RefPtr<Core::Timer> m_forced_layout_timer;
+
+ String m_source;
+
+ OwnPtr<JS::Interpreter> m_interpreter;
+
+ RefPtr<HTML::HTMLScriptElement> m_pending_parsing_blocking_script;
+ NonnullRefPtrVector<HTML::HTMLScriptElement> m_scripts_to_execute_when_parsing_has_finished;
+ NonnullRefPtrVector<HTML::HTMLScriptElement> m_scripts_to_execute_as_soon_as_possible;
+
+ QuirksMode m_quirks_mode { QuirksMode::No };
+ bool m_editable { false };
+
+ WeakPtr<Element> m_focused_element;
+
+ bool m_created_for_appropriate_template_contents { false };
+ RefPtr<Document> m_associated_inert_template_document;
+
+ String m_ready_state { "loading" };
+ String m_content_type { "application/xml" };
+ String m_encoding { "UTF-8" };
+
+ bool m_ready_for_post_load_tasks { false };
+
+ NonnullRefPtr<DOMImplementation> m_implementation;
+
+ bool m_should_invalidate_styles_on_attribute_changes { true };
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/Document.idl b/Userland/Libraries/LibWeb/DOM/Document.idl
new file mode 100644
index 0000000000..3b34314e8d
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/Document.idl
@@ -0,0 +1,37 @@
+interface Document : Node {
+
+ readonly attribute DOMImplementation implementation;
+
+ readonly attribute DOMString characterSet;
+ readonly attribute DOMString charset;
+ readonly attribute DOMString inputEncoding;
+ readonly attribute DOMString contentType;
+
+ Element? getElementById(DOMString id);
+ ArrayFromVector getElementsByName(DOMString name);
+ ArrayFromVector getElementsByTagName(DOMString tagName);
+ ArrayFromVector getElementsByClassName(DOMString className);
+
+ readonly attribute Element? firstElementChild;
+ readonly attribute Element? lastElementChild;
+
+ Element? querySelector(DOMString selectors);
+ ArrayFromVector querySelectorAll(DOMString selectors);
+
+ Element createElement(DOMString tagName);
+ DocumentFragment createDocumentFragment();
+ Text createTextNode(DOMString data);
+ Comment createComment(DOMString data);
+
+ readonly attribute DOMString compatMode;
+ readonly attribute DocumentType? doctype;
+
+ readonly attribute Element? documentElement;
+ attribute HTMLElement? body;
+ readonly attribute HTMLHeadElement? head;
+
+ readonly attribute DOMString readyState;
+
+ attribute DOMString title;
+
+};
diff --git a/Userland/Libraries/LibWeb/DOM/DocumentFragment.cpp b/Userland/Libraries/LibWeb/DOM/DocumentFragment.cpp
new file mode 100644
index 0000000000..92400feb70
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/DocumentFragment.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, Luke Wilde <luke.wilde@live.co.uk>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/DOM/DocumentFragment.h>
+
+namespace Web::DOM {
+
+DocumentFragment::DocumentFragment(Document& document)
+ : ParentNode(document, NodeType::DOCUMENT_FRAGMENT_NODE)
+{
+}
+
+DocumentFragment::~DocumentFragment()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/DocumentFragment.h b/Userland/Libraries/LibWeb/DOM/DocumentFragment.h
new file mode 100644
index 0000000000..b5dbc81699
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/DocumentFragment.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/FlyString.h>
+#include <LibWeb/DOM/Element.h>
+#include <LibWeb/DOM/NonElementParentNode.h>
+#include <LibWeb/DOM/ParentNode.h>
+
+namespace Web::DOM {
+
+class DocumentFragment
+ : public ParentNode
+ , public NonElementParentNode<DocumentFragment> {
+public:
+ using WrapperType = Bindings::DocumentFragmentWrapper;
+
+ explicit DocumentFragment(Document& document);
+ virtual ~DocumentFragment() override;
+
+ virtual FlyString node_name() const override { return "#document-fragment"; }
+
+ RefPtr<Element> host() { return m_host; }
+ const RefPtr<Element> host() const { return m_host; }
+
+ void set_host(Element& host) { m_host = host; }
+
+private:
+ RefPtr<Element> m_host;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/DocumentFragment.idl b/Userland/Libraries/LibWeb/DOM/DocumentFragment.idl
new file mode 100644
index 0000000000..fc18e968be
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/DocumentFragment.idl
@@ -0,0 +1,11 @@
+interface DocumentFragment : Node {
+
+ Element? getElementById(DOMString id);
+
+ readonly attribute Element? firstElementChild;
+ readonly attribute Element? lastElementChild;
+
+ Element? querySelector(DOMString selectors);
+ ArrayFromVector querySelectorAll(DOMString selectors);
+
+};
diff --git a/Userland/Libraries/LibWeb/DOM/DocumentType.cpp b/Userland/Libraries/LibWeb/DOM/DocumentType.cpp
new file mode 100644
index 0000000000..da0f176efc
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/DocumentType.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/DOM/DocumentType.h>
+
+namespace Web::DOM {
+
+DocumentType::DocumentType(Document& document)
+ : Node(document, NodeType::DOCUMENT_TYPE_NODE)
+{
+}
+
+DocumentType::~DocumentType()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/DocumentType.h b/Userland/Libraries/LibWeb/DOM/DocumentType.h
new file mode 100644
index 0000000000..ffc4d82150
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/DocumentType.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/FlyString.h>
+#include <LibWeb/DOM/Node.h>
+
+namespace Web::DOM {
+
+class DocumentType final : public Node {
+public:
+ using WrapperType = Bindings::DocumentTypeWrapper;
+
+ explicit DocumentType(Document&);
+ virtual ~DocumentType() override;
+
+ virtual FlyString node_name() const override { return "#doctype"; }
+
+ const String& name() const { return m_name; }
+ void set_name(const String& name) { m_name = name; }
+
+ const String& public_id() const { return m_public_id; }
+ void set_public_id(const String& public_id) { m_public_id = public_id; }
+
+ const String& system_id() const { return m_system_id; }
+ void set_system_id(const String& system_id) { m_system_id = system_id; }
+
+private:
+ String m_name;
+ String m_public_id;
+ String m_system_id;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/DocumentType.idl b/Userland/Libraries/LibWeb/DOM/DocumentType.idl
new file mode 100644
index 0000000000..813b0e5b47
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/DocumentType.idl
@@ -0,0 +1,7 @@
+interface DocumentType : Node {
+
+ readonly attribute DOMString name;
+ readonly attribute DOMString publicId;
+ readonly attribute DOMString systemId;
+
+};
diff --git a/Userland/Libraries/LibWeb/DOM/Element.cpp b/Userland/Libraries/LibWeb/DOM/Element.cpp
new file mode 100644
index 0000000000..094d877475
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/Element.cpp
@@ -0,0 +1,335 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/StringBuilder.h>
+#include <LibWeb/CSS/Length.h>
+#include <LibWeb/CSS/Parser/CSSParser.h>
+#include <LibWeb/CSS/PropertyID.h>
+#include <LibWeb/CSS/StyleInvalidator.h>
+#include <LibWeb/CSS/StyleResolver.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/DOM/DocumentFragment.h>
+#include <LibWeb/DOM/Element.h>
+#include <LibWeb/DOM/Text.h>
+#include <LibWeb/Dump.h>
+#include <LibWeb/HTML/Parser/HTMLDocumentParser.h>
+#include <LibWeb/Layout/BlockBox.h>
+#include <LibWeb/Layout/InlineNode.h>
+#include <LibWeb/Layout/ListItemBox.h>
+#include <LibWeb/Layout/TableBox.h>
+#include <LibWeb/Layout/TableCellBox.h>
+#include <LibWeb/Layout/TableRowBox.h>
+#include <LibWeb/Layout/TableRowGroupBox.h>
+#include <LibWeb/Layout/TreeBuilder.h>
+#include <LibWeb/Layout/WidgetBox.h>
+
+namespace Web::DOM {
+
+Element::Element(Document& document, const QualifiedName& qualified_name)
+ : ParentNode(document, NodeType::ELEMENT_NODE)
+ , m_qualified_name(qualified_name)
+{
+}
+
+Element::~Element()
+{
+}
+
+Attribute* Element::find_attribute(const FlyString& name)
+{
+ for (auto& attribute : m_attributes) {
+ if (attribute.name() == name)
+ return &attribute;
+ }
+ return nullptr;
+}
+
+const Attribute* Element::find_attribute(const FlyString& name) const
+{
+ for (auto& attribute : m_attributes) {
+ if (attribute.name() == name)
+ return &attribute;
+ }
+ return nullptr;
+}
+
+String Element::attribute(const FlyString& name) const
+{
+ if (auto* attribute = find_attribute(name))
+ return attribute->value();
+ return {};
+}
+
+void Element::set_attribute(const FlyString& name, const String& value)
+{
+ CSS::StyleInvalidator style_invalidator(document());
+
+ if (auto* attribute = find_attribute(name))
+ attribute->set_value(value);
+ else
+ m_attributes.empend(name, value);
+
+ parse_attribute(name, value);
+}
+
+void Element::remove_attribute(const FlyString& name)
+{
+ CSS::StyleInvalidator style_invalidator(document());
+
+ m_attributes.remove_first_matching([&](auto& attribute) { return attribute.name() == name; });
+}
+
+bool Element::has_class(const FlyString& class_name) const
+{
+ for (auto& class_ : m_classes) {
+ if (class_ == class_name)
+ return true;
+ }
+ return false;
+}
+
+RefPtr<Layout::Node> Element::create_layout_node()
+{
+ auto style = document().style_resolver().resolve_style(*this);
+ const_cast<Element&>(*this).m_specified_css_values = style;
+ auto display = style->display();
+
+ if (display == CSS::Display::None)
+ return nullptr;
+
+ if (local_name() == "noscript" && document().is_scripting_enabled())
+ return nullptr;
+
+ if (display == CSS::Display::Block)
+ return adopt(*new Layout::BlockBox(document(), this, move(style)));
+
+ if (display == CSS::Display::Inline) {
+ if (style->float_().value_or(CSS::Float::None) != CSS::Float::None)
+ return adopt(*new Layout::BlockBox(document(), this, move(style)));
+ return adopt(*new Layout::InlineNode(document(), *this, move(style)));
+ }
+
+ if (display == CSS::Display::ListItem)
+ return adopt(*new Layout::ListItemBox(document(), *this, move(style)));
+ if (display == CSS::Display::Table)
+ return adopt(*new Layout::TableBox(document(), this, move(style)));
+ if (display == CSS::Display::TableRow)
+ return adopt(*new Layout::TableRowBox(document(), this, move(style)));
+ if (display == CSS::Display::TableCell)
+ return adopt(*new Layout::TableCellBox(document(), this, move(style)));
+ if (display == CSS::Display::TableRowGroup || display == CSS::Display::TableHeaderGroup || display == CSS::Display::TableFooterGroup)
+ return adopt(*new Layout::TableRowGroupBox(document(), *this, move(style)));
+ if (display == CSS::Display::InlineBlock) {
+ auto inline_block = adopt(*new Layout::BlockBox(document(), this, move(style)));
+ inline_block->set_inline(true);
+ return inline_block;
+ }
+ ASSERT_NOT_REACHED();
+}
+
+void Element::parse_attribute(const FlyString& name, const String& value)
+{
+ if (name == HTML::AttributeNames::class_) {
+ auto new_classes = value.split_view(' ');
+ m_classes.clear();
+ m_classes.ensure_capacity(new_classes.size());
+ for (auto& new_class : new_classes) {
+ m_classes.unchecked_append(new_class);
+ }
+ } else if (name == HTML::AttributeNames::style) {
+ m_inline_style = parse_css_declaration(CSS::ParsingContext(document()), value);
+ set_needs_style_update(true);
+ }
+}
+
+enum class StyleDifference {
+ None,
+ NeedsRepaint,
+ NeedsRelayout,
+};
+
+static StyleDifference compute_style_difference(const CSS::StyleProperties& old_style, const CSS::StyleProperties& new_style, const Document& document)
+{
+ if (old_style == new_style)
+ return StyleDifference::None;
+
+ bool needs_repaint = false;
+ bool needs_relayout = false;
+
+ if (new_style.display() != old_style.display())
+ needs_relayout = true;
+
+ if (new_style.color_or_fallback(CSS::PropertyID::Color, document, Color::Black) != old_style.color_or_fallback(CSS::PropertyID::Color, document, Color::Black))
+ needs_repaint = true;
+ else if (new_style.color_or_fallback(CSS::PropertyID::BackgroundColor, document, Color::Black) != old_style.color_or_fallback(CSS::PropertyID::BackgroundColor, document, Color::Black))
+ needs_repaint = true;
+
+ if (needs_relayout)
+ return StyleDifference::NeedsRelayout;
+ if (needs_repaint)
+ return StyleDifference::NeedsRepaint;
+ return StyleDifference::None;
+}
+
+void Element::recompute_style()
+{
+ set_needs_style_update(false);
+ ASSERT(parent());
+ auto old_specified_css_values = m_specified_css_values;
+ auto new_specified_css_values = document().style_resolver().resolve_style(*this);
+ m_specified_css_values = new_specified_css_values;
+ if (!layout_node()) {
+ if (new_specified_css_values->display() == CSS::Display::None)
+ return;
+ // We need a new layout tree here!
+ Layout::TreeBuilder tree_builder;
+ tree_builder.build(*this);
+ return;
+ }
+
+ // Don't bother with style on widgets. NATIVE LOOK & FEEL BABY!
+ if (is<Layout::WidgetBox>(layout_node()))
+ return;
+
+ auto diff = StyleDifference::NeedsRelayout;
+ if (old_specified_css_values)
+ diff = compute_style_difference(*old_specified_css_values, *new_specified_css_values, document());
+ if (diff == StyleDifference::None)
+ return;
+ layout_node()->apply_style(*new_specified_css_values);
+ if (diff == StyleDifference::NeedsRelayout) {
+ document().schedule_forced_layout();
+ return;
+ }
+ if (diff == StyleDifference::NeedsRepaint) {
+ layout_node()->set_needs_display();
+ }
+}
+
+NonnullRefPtr<CSS::StyleProperties> Element::computed_style()
+{
+ // FIXME: This implementation is not doing anything it's supposed to.
+ auto properties = m_specified_css_values->clone();
+ if (layout_node() && layout_node()->has_style()) {
+ CSS::PropertyID box_model_metrics[] = {
+ CSS::PropertyID::MarginTop,
+ CSS::PropertyID::MarginBottom,
+ CSS::PropertyID::MarginLeft,
+ CSS::PropertyID::MarginRight,
+ CSS::PropertyID::PaddingTop,
+ CSS::PropertyID::PaddingBottom,
+ CSS::PropertyID::PaddingLeft,
+ CSS::PropertyID::PaddingRight,
+ CSS::PropertyID::BorderTopWidth,
+ CSS::PropertyID::BorderBottomWidth,
+ CSS::PropertyID::BorderLeftWidth,
+ CSS::PropertyID::BorderRightWidth,
+ };
+ for (CSS::PropertyID id : box_model_metrics) {
+ auto prop = m_specified_css_values->property(id);
+ if (prop.has_value())
+ properties->set_property(id, prop.value());
+ }
+ }
+ return properties;
+}
+
+void Element::set_inner_html(StringView markup)
+{
+ auto new_children = HTML::HTMLDocumentParser::parse_html_fragment(*this, markup);
+ remove_all_children();
+ while (!new_children.is_empty()) {
+ append_child(new_children.take_first());
+ }
+
+ set_needs_style_update(true);
+ document().invalidate_layout();
+}
+
+String Element::inner_html() const
+{
+ auto escape_string = [](const StringView& string, bool attribute_mode) -> String {
+ // https://html.spec.whatwg.org/multipage/parsing.html#escapingString
+ StringBuilder builder;
+ for (auto& ch : string) {
+ if (ch == '&')
+ builder.append("&amp;");
+ // FIXME: also replace U+00A0 NO-BREAK SPACE with &nbsp;
+ else if (ch == '"' && attribute_mode)
+ builder.append("&quot;");
+ else if (ch == '<' && !attribute_mode)
+ builder.append("&lt;");
+ else if (ch == '>' && !attribute_mode)
+ builder.append("&gt;");
+ else
+ builder.append(ch);
+ }
+ return builder.to_string();
+ };
+
+ StringBuilder builder;
+
+ Function<void(const Node&)> recurse = [&](auto& node) {
+ for (auto* child = node.first_child(); child; child = child->next_sibling()) {
+ if (child->is_element()) {
+ auto& element = downcast<Element>(*child);
+ builder.append('<');
+ builder.append(element.local_name());
+ element.for_each_attribute([&](auto& name, auto& value) {
+ builder.append(' ');
+ builder.append(name);
+ builder.append('=');
+ builder.append('"');
+ builder.append(escape_string(value, true));
+ builder.append('"');
+ });
+ builder.append('>');
+
+ recurse(*child);
+
+ // FIXME: This should be skipped for void elements
+ builder.append("</");
+ builder.append(element.local_name());
+ builder.append('>');
+ }
+ if (child->is_text()) {
+ auto& text = downcast<Text>(*child);
+ builder.append(escape_string(text.data(), false));
+ }
+ // FIXME: Also handle Comment, ProcessingInstruction, DocumentType
+ }
+ };
+ recurse(*this);
+
+ return builder.to_string();
+}
+
+bool Element::is_focused() const
+{
+ return document().focused_element() == this;
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/Element.h b/Userland/Libraries/LibWeb/DOM/Element.h
new file mode 100644
index 0000000000..ddefb2ab93
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/Element.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/FlyString.h>
+#include <AK/String.h>
+#include <LibWeb/DOM/Attribute.h>
+#include <LibWeb/DOM/NonDocumentTypeChildNode.h>
+#include <LibWeb/DOM/ParentNode.h>
+#include <LibWeb/HTML/AttributeNames.h>
+#include <LibWeb/HTML/TagNames.h>
+#include <LibWeb/Layout/Node.h>
+#include <LibWeb/QualifiedName.h>
+
+namespace Web::DOM {
+
+class Element
+ : public ParentNode
+ , public NonDocumentTypeChildNode<Element> {
+
+public:
+ using WrapperType = Bindings::ElementWrapper;
+
+ Element(Document&, const QualifiedName& qualified_name);
+ virtual ~Element() override;
+
+ virtual FlyString node_name() const final { return m_qualified_name.local_name(); }
+ const FlyString& local_name() const { return m_qualified_name.local_name(); }
+
+ // NOTE: This is for the JS bindings
+ const FlyString& tag_name() const { return local_name(); }
+
+ const FlyString& namespace_() const { return m_qualified_name.namespace_(); }
+
+ // NOTE: This is for the JS bindings
+ const FlyString& namespace_uri() const { return namespace_(); }
+
+ bool has_attribute(const FlyString& name) const { return !attribute(name).is_null(); }
+ bool has_attributes() const { return !m_attributes.is_empty(); }
+ String attribute(const FlyString& name) const;
+ String get_attribute(const FlyString& name) const { return attribute(name); }
+ void set_attribute(const FlyString& name, const String& value);
+ void remove_attribute(const FlyString& name);
+
+ template<typename Callback>
+ void for_each_attribute(Callback callback) const
+ {
+ for (auto& attribute : m_attributes)
+ callback(attribute.name(), attribute.value());
+ }
+
+ bool has_class(const FlyString&) const;
+ const Vector<FlyString>& class_names() const { return m_classes; }
+
+ virtual void apply_presentational_hints(CSS::StyleProperties&) const { }
+ virtual void parse_attribute(const FlyString& name, const String& value);
+
+ void recompute_style();
+
+ Layout::NodeWithStyle* layout_node() { return static_cast<Layout::NodeWithStyle*>(Node::layout_node()); }
+ const Layout::NodeWithStyle* layout_node() const { return static_cast<const Layout::NodeWithStyle*>(Node::layout_node()); }
+
+ String name() const { return attribute(HTML::AttributeNames::name); }
+
+ const CSS::StyleProperties* specified_css_values() const { return m_specified_css_values.ptr(); }
+ NonnullRefPtr<CSS::StyleProperties> computed_style();
+
+ const CSS::StyleDeclaration* inline_style() const { return m_inline_style; }
+
+ // FIXME: innerHTML also appears on shadow roots. https://w3c.github.io/DOM-Parsing/#dom-innerhtml
+ String inner_html() const;
+ void set_inner_html(StringView);
+
+ bool is_focused() const;
+ virtual bool is_focusable() const { return false; }
+
+protected:
+ RefPtr<Layout::Node> create_layout_node() override;
+
+private:
+ Attribute* find_attribute(const FlyString& name);
+ const Attribute* find_attribute(const FlyString& name) const;
+
+ QualifiedName m_qualified_name;
+ Vector<Attribute> m_attributes;
+
+ RefPtr<CSS::StyleDeclaration> m_inline_style;
+
+ RefPtr<CSS::StyleProperties> m_specified_css_values;
+
+ Vector<FlyString> m_classes;
+};
+
+}
+
+namespace AK {
+template<>
+inline bool is<Web::DOM::Element>(const Web::DOM::Node& input)
+{
+ return input.is_element();
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/Element.idl b/Userland/Libraries/LibWeb/DOM/Element.idl
new file mode 100644
index 0000000000..e5de36e75b
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/Element.idl
@@ -0,0 +1,23 @@
+interface Element : Node {
+ readonly attribute DOMString? namespaceURI;
+ readonly attribute DOMString tagName;
+
+ DOMString? getAttribute(DOMString qualifiedName);
+ undefined setAttribute(DOMString qualifiedName, DOMString value);
+ undefined removeAttribute(DOMString qualifiedName);
+ boolean hasAttribute(DOMString qualifiedName);
+ boolean hasAttributes();
+
+ readonly attribute Element? firstElementChild;
+ readonly attribute Element? lastElementChild;
+
+ Element? querySelector(DOMString selectors);
+ ArrayFromVector querySelectorAll(DOMString selectors);
+
+ [LegacyNullToEmptyString] attribute DOMString innerHTML;
+ [Reflect] attribute DOMString id;
+ [Reflect=class] attribute DOMString className;
+
+ readonly attribute Element? nextElementSibling;
+ readonly attribute Element? previousElementSibling;
+};
diff --git a/Userland/Libraries/LibWeb/DOM/ElementFactory.cpp b/Userland/Libraries/LibWeb/DOM/ElementFactory.cpp
new file mode 100644
index 0000000000..810d82efdc
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/ElementFactory.cpp
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2020, Luke Wilde <luke.wilde@live.co.uk>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/DOM/ElementFactory.h>
+#include <LibWeb/HTML/HTMLAnchorElement.h>
+#include <LibWeb/HTML/HTMLAreaElement.h>
+#include <LibWeb/HTML/HTMLAudioElement.h>
+#include <LibWeb/HTML/HTMLBRElement.h>
+#include <LibWeb/HTML/HTMLBaseElement.h>
+#include <LibWeb/HTML/HTMLBlinkElement.h>
+#include <LibWeb/HTML/HTMLBodyElement.h>
+#include <LibWeb/HTML/HTMLButtonElement.h>
+#include <LibWeb/HTML/HTMLCanvasElement.h>
+#include <LibWeb/HTML/HTMLDListElement.h>
+#include <LibWeb/HTML/HTMLDataElement.h>
+#include <LibWeb/HTML/HTMLDataListElement.h>
+#include <LibWeb/HTML/HTMLDetailsElement.h>
+#include <LibWeb/HTML/HTMLDialogElement.h>
+#include <LibWeb/HTML/HTMLDirectoryElement.h>
+#include <LibWeb/HTML/HTMLDivElement.h>
+#include <LibWeb/HTML/HTMLEmbedElement.h>
+#include <LibWeb/HTML/HTMLFieldSetElement.h>
+#include <LibWeb/HTML/HTMLFontElement.h>
+#include <LibWeb/HTML/HTMLFormElement.h>
+#include <LibWeb/HTML/HTMLFrameElement.h>
+#include <LibWeb/HTML/HTMLFrameSetElement.h>
+#include <LibWeb/HTML/HTMLHRElement.h>
+#include <LibWeb/HTML/HTMLHeadElement.h>
+#include <LibWeb/HTML/HTMLHeadingElement.h>
+#include <LibWeb/HTML/HTMLHtmlElement.h>
+#include <LibWeb/HTML/HTMLIFrameElement.h>
+#include <LibWeb/HTML/HTMLImageElement.h>
+#include <LibWeb/HTML/HTMLInputElement.h>
+#include <LibWeb/HTML/HTMLLIElement.h>
+#include <LibWeb/HTML/HTMLLabelElement.h>
+#include <LibWeb/HTML/HTMLLegendElement.h>
+#include <LibWeb/HTML/HTMLLinkElement.h>
+#include <LibWeb/HTML/HTMLMapElement.h>
+#include <LibWeb/HTML/HTMLMarqueeElement.h>
+#include <LibWeb/HTML/HTMLMenuElement.h>
+#include <LibWeb/HTML/HTMLMetaElement.h>
+#include <LibWeb/HTML/HTMLMeterElement.h>
+#include <LibWeb/HTML/HTMLModElement.h>
+#include <LibWeb/HTML/HTMLOListElement.h>
+#include <LibWeb/HTML/HTMLObjectElement.h>
+#include <LibWeb/HTML/HTMLOptGroupElement.h>
+#include <LibWeb/HTML/HTMLOptionElement.h>
+#include <LibWeb/HTML/HTMLOutputElement.h>
+#include <LibWeb/HTML/HTMLParagraphElement.h>
+#include <LibWeb/HTML/HTMLParamElement.h>
+#include <LibWeb/HTML/HTMLPictureElement.h>
+#include <LibWeb/HTML/HTMLPreElement.h>
+#include <LibWeb/HTML/HTMLProgressElement.h>
+#include <LibWeb/HTML/HTMLQuoteElement.h>
+#include <LibWeb/HTML/HTMLScriptElement.h>
+#include <LibWeb/HTML/HTMLSelectElement.h>
+#include <LibWeb/HTML/HTMLSlotElement.h>
+#include <LibWeb/HTML/HTMLSourceElement.h>
+#include <LibWeb/HTML/HTMLSpanElement.h>
+#include <LibWeb/HTML/HTMLStyleElement.h>
+#include <LibWeb/HTML/HTMLTableCaptionElement.h>
+#include <LibWeb/HTML/HTMLTableCellElement.h>
+#include <LibWeb/HTML/HTMLTableColElement.h>
+#include <LibWeb/HTML/HTMLTableElement.h>
+#include <LibWeb/HTML/HTMLTableRowElement.h>
+#include <LibWeb/HTML/HTMLTableSectionElement.h>
+#include <LibWeb/HTML/HTMLTemplateElement.h>
+#include <LibWeb/HTML/HTMLTextAreaElement.h>
+#include <LibWeb/HTML/HTMLTimeElement.h>
+#include <LibWeb/HTML/HTMLTitleElement.h>
+#include <LibWeb/HTML/HTMLTrackElement.h>
+#include <LibWeb/HTML/HTMLUListElement.h>
+#include <LibWeb/HTML/HTMLUnknownElement.h>
+#include <LibWeb/HTML/HTMLVideoElement.h>
+#include <LibWeb/SVG/SVGPathElement.h>
+#include <LibWeb/SVG/SVGSVGElement.h>
+#include <LibWeb/SVG/TagNames.h>
+
+namespace Web::DOM {
+
+NonnullRefPtr<Element> create_element(Document& document, const FlyString& tag_name, const FlyString& namespace_)
+{
+ auto lowercase_tag_name = tag_name.to_lowercase();
+ // FIXME: Add prefix when we support it.
+ auto qualified_name = QualifiedName(tag_name, {}, namespace_);
+ if (lowercase_tag_name == HTML::TagNames::a)
+ return adopt(*new HTML::HTMLAnchorElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::area)
+ return adopt(*new HTML::HTMLAreaElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::audio)
+ return adopt(*new HTML::HTMLAudioElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::base)
+ return adopt(*new HTML::HTMLBaseElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::blink)
+ return adopt(*new HTML::HTMLBlinkElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::body)
+ return adopt(*new HTML::HTMLBodyElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::br)
+ return adopt(*new HTML::HTMLBRElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::button)
+ return adopt(*new HTML::HTMLButtonElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::canvas)
+ return adopt(*new HTML::HTMLCanvasElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::data)
+ return adopt(*new HTML::HTMLDataElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::datalist)
+ return adopt(*new HTML::HTMLDataListElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::details)
+ return adopt(*new HTML::HTMLDetailsElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::dialog)
+ return adopt(*new HTML::HTMLDialogElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::dir)
+ return adopt(*new HTML::HTMLDirectoryElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::div)
+ return adopt(*new HTML::HTMLDivElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::dl)
+ return adopt(*new HTML::HTMLDListElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::embed)
+ return adopt(*new HTML::HTMLEmbedElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::fieldset)
+ return adopt(*new HTML::HTMLFieldSetElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::font)
+ return adopt(*new HTML::HTMLFontElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::form)
+ return adopt(*new HTML::HTMLFormElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::frame)
+ return adopt(*new HTML::HTMLFrameElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::frameset)
+ return adopt(*new HTML::HTMLFrameSetElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::head)
+ return adopt(*new HTML::HTMLHeadElement(document, qualified_name));
+ if (lowercase_tag_name.is_one_of(HTML::TagNames::h1, HTML::TagNames::h2, HTML::TagNames::h3, HTML::TagNames::h4, HTML::TagNames::h5, HTML::TagNames::h6))
+ return adopt(*new HTML::HTMLHeadingElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::hr)
+ return adopt(*new HTML::HTMLHRElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::html)
+ return adopt(*new HTML::HTMLHtmlElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::iframe)
+ return adopt(*new HTML::HTMLIFrameElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::img)
+ return adopt(*new HTML::HTMLImageElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::input)
+ return adopt(*new HTML::HTMLInputElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::label)
+ return adopt(*new HTML::HTMLLabelElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::legend)
+ return adopt(*new HTML::HTMLLegendElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::li)
+ return adopt(*new HTML::HTMLLIElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::link)
+ return adopt(*new HTML::HTMLLinkElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::map)
+ return adopt(*new HTML::HTMLMapElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::marquee)
+ return adopt(*new HTML::HTMLMarqueeElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::menu)
+ return adopt(*new HTML::HTMLMenuElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::meta)
+ return adopt(*new HTML::HTMLMetaElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::meter)
+ return adopt(*new HTML::HTMLMeterElement(document, qualified_name));
+ if (lowercase_tag_name.is_one_of(HTML::TagNames::ins, HTML::TagNames::del))
+ return adopt(*new HTML::HTMLModElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::object)
+ return adopt(*new HTML::HTMLObjectElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::ol)
+ return adopt(*new HTML::HTMLOListElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::optgroup)
+ return adopt(*new HTML::HTMLOptGroupElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::option)
+ return adopt(*new HTML::HTMLOptionElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::output)
+ return adopt(*new HTML::HTMLOutputElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::p)
+ return adopt(*new HTML::HTMLParagraphElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::param)
+ return adopt(*new HTML::HTMLParamElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::picture)
+ return adopt(*new HTML::HTMLPictureElement(document, qualified_name));
+ // NOTE: The obsolete elements "listing" and "xmp" are explicitly mapped to HTMLPreElement in the specification.
+ if (lowercase_tag_name.is_one_of(HTML::TagNames::pre, HTML::TagNames::listing, HTML::TagNames::xmp))
+ return adopt(*new HTML::HTMLPreElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::progress)
+ return adopt(*new HTML::HTMLProgressElement(document, qualified_name));
+ if (lowercase_tag_name.is_one_of(HTML::TagNames::blockquote, HTML::TagNames::q))
+ return adopt(*new HTML::HTMLQuoteElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::script)
+ return adopt(*new HTML::HTMLScriptElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::select)
+ return adopt(*new HTML::HTMLSelectElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::slot)
+ return adopt(*new HTML::HTMLSlotElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::source)
+ return adopt(*new HTML::HTMLSourceElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::span)
+ return adopt(*new HTML::HTMLSpanElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::style)
+ return adopt(*new HTML::HTMLStyleElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::caption)
+ return adopt(*new HTML::HTMLTableCaptionElement(document, qualified_name));
+ if (lowercase_tag_name.is_one_of(Web::HTML::TagNames::td, Web::HTML::TagNames::th))
+ return adopt(*new HTML::HTMLTableCellElement(document, qualified_name));
+ if (lowercase_tag_name.is_one_of(HTML::TagNames::colgroup, HTML::TagNames::col))
+ return adopt(*new HTML::HTMLTableColElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::table)
+ return adopt(*new HTML::HTMLTableElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::tr)
+ return adopt(*new HTML::HTMLTableRowElement(document, qualified_name));
+ if (lowercase_tag_name.is_one_of(HTML::TagNames::tbody, HTML::TagNames::thead, HTML::TagNames::tfoot))
+ return adopt(*new HTML::HTMLTableSectionElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::template_)
+ return adopt(*new HTML::HTMLTemplateElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::textarea)
+ return adopt(*new HTML::HTMLTextAreaElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::time)
+ return adopt(*new HTML::HTMLTimeElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::title)
+ return adopt(*new HTML::HTMLTitleElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::track)
+ return adopt(*new HTML::HTMLTrackElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::ul)
+ return adopt(*new HTML::HTMLUListElement(document, qualified_name));
+ if (lowercase_tag_name == HTML::TagNames::video)
+ return adopt(*new HTML::HTMLVideoElement(document, qualified_name));
+ if (lowercase_tag_name.is_one_of(
+ HTML::TagNames::article, HTML::TagNames::section, HTML::TagNames::nav, HTML::TagNames::aside, HTML::TagNames::hgroup, HTML::TagNames::header, HTML::TagNames::footer, HTML::TagNames::address, HTML::TagNames::dt, HTML::TagNames::dd, HTML::TagNames::figure, HTML::TagNames::figcaption, HTML::TagNames::main, HTML::TagNames::em, HTML::TagNames::strong, HTML::TagNames::small, HTML::TagNames::s, HTML::TagNames::cite, HTML::TagNames::dfn, HTML::TagNames::abbr, HTML::TagNames::ruby, HTML::TagNames::rt, HTML::TagNames::rp, HTML::TagNames::code, HTML::TagNames::var, HTML::TagNames::samp, HTML::TagNames::kbd, HTML::TagNames::sub, HTML::TagNames::sup, HTML::TagNames::i, HTML::TagNames::b, HTML::TagNames::u, HTML::TagNames::mark, HTML::TagNames::bdi, HTML::TagNames::bdo, HTML::TagNames::wbr, HTML::TagNames::summary, HTML::TagNames::noscript,
+ // Obsolete
+ HTML::TagNames::acronym, HTML::TagNames::basefont, HTML::TagNames::big, HTML::TagNames::center, HTML::TagNames::nobr, HTML::TagNames::noembed, HTML::TagNames::noframes, HTML::TagNames::plaintext, HTML::TagNames::rb, HTML::TagNames::rtc, HTML::TagNames::strike, HTML::TagNames::tt))
+ return adopt(*new HTML::HTMLElement(document, qualified_name));
+ if (lowercase_tag_name == SVG::TagNames::svg)
+ return adopt(*new SVG::SVGSVGElement(document, qualified_name));
+ if (lowercase_tag_name == SVG::TagNames::path)
+ return adopt(*new SVG::SVGPathElement(document, qualified_name));
+
+ // FIXME: If name is a valid custom element name, then return HTMLElement.
+
+ return adopt(*new HTML::HTMLUnknownElement(document, qualified_name));
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/ElementFactory.h b/Userland/Libraries/LibWeb/DOM/ElementFactory.h
new file mode 100644
index 0000000000..c1c811a9a4
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/ElementFactory.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/DOM/Element.h>
+
+namespace Web::DOM {
+
+NonnullRefPtr<Element> create_element(Document&, const FlyString& tag_name, const FlyString& namespace_);
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/Event.cpp b/Userland/Libraries/LibWeb/DOM/Event.cpp
new file mode 100644
index 0000000000..9fd75ab226
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/Event.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2020, the SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/Assertions.h>
+#include <AK/TypeCasts.h>
+#include <LibWeb/DOM/Event.h>
+#include <LibWeb/DOM/Node.h>
+#include <LibWeb/DOM/ShadowRoot.h>
+
+namespace Web::DOM {
+
+void Event::append_to_path(EventTarget& invocation_target, RefPtr<EventTarget> shadow_adjusted_target, RefPtr<EventTarget> related_target, TouchTargetList& touch_targets, bool slot_in_closed_tree)
+{
+ bool invocation_target_in_shadow_tree = false;
+ bool root_of_closed_tree = false;
+
+ if (is<Node>(invocation_target)) {
+ auto& invocation_target_node = downcast<Node>(invocation_target);
+ if (is<ShadowRoot>(invocation_target_node.root()))
+ invocation_target_in_shadow_tree = true;
+ if (is<ShadowRoot>(invocation_target_node)) {
+ auto& invocation_target_shadow_root = downcast<ShadowRoot>(invocation_target_node);
+ root_of_closed_tree = invocation_target_shadow_root.closed();
+ }
+ }
+
+ m_path.append({ invocation_target, invocation_target_in_shadow_tree, shadow_adjusted_target, related_target, touch_targets, root_of_closed_tree, slot_in_closed_tree, m_path.size() });
+}
+
+void Event::set_cancelled_flag()
+{
+ if (m_cancelable && !m_in_passive_listener)
+ m_cancelled = true;
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/Event.h b/Userland/Libraries/LibWeb/DOM/Event.h
new file mode 100644
index 0000000000..1acc60a386
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/Event.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/FlyString.h>
+#include <LibWeb/Bindings/Wrappable.h>
+#include <LibWeb/DOM/EventTarget.h>
+
+namespace Web::DOM {
+
+class Event
+ : public RefCounted<Event>
+ , public Bindings::Wrappable {
+public:
+ using WrapperType = Bindings::EventWrapper;
+
+ enum Phase : u16 {
+ None = 0,
+ CapturingPhase = 1,
+ AtTarget = 2,
+ BubblingPhase = 3,
+ };
+
+ using TouchTargetList = Vector<RefPtr<EventTarget>>;
+
+ struct PathEntry {
+ RefPtr<EventTarget> invocation_target;
+ bool invocation_target_in_shadow_tree { false };
+ RefPtr<EventTarget> shadow_adjusted_target;
+ RefPtr<EventTarget> related_target;
+ TouchTargetList touch_target_list;
+ bool root_of_closed_tree { false };
+ bool slot_in_closed_tree { false };
+ size_t index;
+ };
+
+ using Path = Vector<PathEntry>;
+
+ static NonnullRefPtr<Event> create(const FlyString& event_name)
+ {
+ return adopt(*new Event(event_name));
+ }
+
+ virtual ~Event() { }
+
+ const FlyString& type() const { return m_type; }
+ void set_type(const StringView& type) { m_type = type; }
+
+ RefPtr<EventTarget> target() const { return m_target; }
+ void set_target(EventTarget* target) { m_target = target; }
+
+ // NOTE: This is intended for the JS bindings.
+ RefPtr<EventTarget> src_target() const { return target(); }
+
+ RefPtr<EventTarget> related_target() const { return m_related_target; }
+ void set_related_target(EventTarget* related_target) { m_related_target = related_target; }
+
+ bool should_stop_propagation() const { return m_stop_propagation; }
+ void set_stop_propagation(bool stop_propagation) { m_stop_propagation = stop_propagation; }
+
+ bool should_stop_immediate_propagation() const { return m_stop_immediate_propagation; }
+ void set_stop_immediate_propagation(bool stop_immediate_propagation) { m_stop_immediate_propagation = stop_immediate_propagation; }
+
+ bool cancelled() const { return m_cancelled; }
+ void set_cancelled(bool cancelled) { m_cancelled = cancelled; }
+
+ bool in_passive_listener() const { return m_in_passive_listener; }
+ void set_in_passive_listener(bool in_passive_listener) { m_in_passive_listener = in_passive_listener; }
+
+ bool composed() const { return m_composed; }
+ void set_composed(bool composed) { m_composed = composed; }
+
+ bool initialized() const { return m_initialized; }
+ void set_initialized(bool initialized) { m_initialized = initialized; }
+
+ bool dispatched() const { return m_dispatch; }
+ void set_dispatched(bool dispatched) { m_dispatch = dispatched; }
+
+ void prevent_default() { set_cancelled_flag(); }
+ bool default_prevented() const { return cancelled(); }
+
+ u16 event_phase() const { return m_phase; }
+ void set_phase(Phase phase) { m_phase = phase; }
+
+ RefPtr<EventTarget> current_target() const { return m_current_target; }
+ void set_current_target(EventTarget* current_target) { m_current_target = current_target; }
+
+ bool return_value() const { return !m_cancelled; }
+ void set_return_value(bool return_value)
+ {
+ if (!return_value)
+ set_cancelled_flag();
+ }
+
+ void append_to_path(EventTarget&, RefPtr<EventTarget>, RefPtr<EventTarget>, TouchTargetList&, bool);
+ Path& path() { return m_path; }
+ const Path& path() const { return m_path; }
+ void clear_path() { m_path.clear(); }
+
+ void set_touch_target_list(TouchTargetList& touch_target_list) { m_touch_target_list = touch_target_list; }
+ TouchTargetList& touch_target_list() { return m_touch_target_list; };
+ void clear_touch_target_list() { m_touch_target_list.clear(); }
+
+ bool bubbles() const { return m_bubbles; }
+ void set_bubbles(bool bubbles) { m_bubbles = bubbles; }
+
+ bool cancelable() const { return m_cancelable; }
+ void set_cancelable(bool cancelable) { m_cancelable = cancelable; }
+
+ bool is_trusted() const { return m_is_trusted; }
+ void set_is_trusted(bool is_trusted) { m_is_trusted = is_trusted; }
+
+ void stop_propagation() { m_stop_propagation = true; }
+
+ bool cancel_bubble() const { return m_stop_propagation; }
+ void set_cancel_bubble(bool cancel_bubble)
+ {
+ if (cancel_bubble)
+ m_stop_propagation = true;
+ }
+
+ void stop_immediate_propagation()
+ {
+ m_stop_propagation = true;
+ m_stop_immediate_propagation = true;
+ }
+
+protected:
+ explicit Event(const FlyString& type)
+ : m_type(type)
+ , m_initialized(true)
+ {
+ }
+
+private:
+ FlyString m_type;
+ RefPtr<EventTarget> m_target;
+ RefPtr<EventTarget> m_related_target;
+ RefPtr<EventTarget> m_current_target;
+
+ Phase m_phase { None };
+
+ bool m_bubbles { false };
+ bool m_cancelable { false };
+
+ bool m_stop_propagation { false };
+ bool m_stop_immediate_propagation { false };
+ bool m_cancelled { false };
+ bool m_in_passive_listener { false };
+ bool m_composed { false };
+ bool m_initialized { false };
+ bool m_dispatch { false };
+
+ bool m_is_trusted { true };
+
+ Path m_path;
+ TouchTargetList m_touch_target_list;
+
+ void set_cancelled_flag();
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/Event.idl b/Userland/Libraries/LibWeb/DOM/Event.idl
new file mode 100644
index 0000000000..a577092cae
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/Event.idl
@@ -0,0 +1,23 @@
+interface Event {
+
+ readonly attribute DOMString type;
+ readonly attribute EventTarget? target;
+ readonly attribute EventTarget? srcTarget;
+ readonly attribute EventTarget? currentTarget;
+
+ readonly attribute unsigned short eventPhase;
+
+ undefined stopPropagation();
+ attribute boolean cancelBubble;
+ undefined stopImmediatePropagation();
+
+ readonly attribute boolean bubbles;
+ readonly attribute boolean cancelable;
+ attribute boolean returnValue;
+ undefined preventDefault();
+ readonly attribute boolean defaultPrevented;
+ readonly attribute boolean composed;
+
+ readonly attribute boolean isTrusted;
+
+};
diff --git a/Userland/Libraries/LibWeb/DOM/EventDispatcher.cpp b/Userland/Libraries/LibWeb/DOM/EventDispatcher.cpp
new file mode 100644
index 0000000000..e45b42c23e
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/EventDispatcher.cpp
@@ -0,0 +1,332 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/Assertions.h>
+#include <AK/TypeCasts.h>
+#include <LibJS/Runtime/Function.h>
+#include <LibWeb/Bindings/EventTargetWrapper.h>
+#include <LibWeb/Bindings/EventTargetWrapperFactory.h>
+#include <LibWeb/Bindings/EventWrapper.h>
+#include <LibWeb/Bindings/EventWrapperFactory.h>
+#include <LibWeb/Bindings/ScriptExecutionContext.h>
+#include <LibWeb/Bindings/WindowObject.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/DOM/Event.h>
+#include <LibWeb/DOM/EventDispatcher.h>
+#include <LibWeb/DOM/EventListener.h>
+#include <LibWeb/DOM/EventTarget.h>
+#include <LibWeb/DOM/Node.h>
+#include <LibWeb/DOM/ShadowRoot.h>
+#include <LibWeb/DOM/Window.h>
+#include <LibWeb/HTML/EventNames.h>
+#include <LibWeb/UIEvents/MouseEvent.h>
+
+namespace Web::DOM {
+
+// FIXME: This shouldn't be here, as retargeting is not only used by the event dispatcher.
+// When moving this function, it needs to be generalized. https://dom.spec.whatwg.org/#retarget
+static EventTarget* retarget(EventTarget* left, [[maybe_unused]] EventTarget* right)
+{
+ // FIXME
+ for (;;) {
+ if (!is<Node>(left))
+ return left;
+
+ auto* left_node = downcast<Node>(left);
+ auto* left_root = left_node->root();
+ if (!is<ShadowRoot>(left_root))
+ return left;
+
+ // FIXME: If right is a node and left’s root is a shadow-including inclusive ancestor of right, return left.
+
+ auto* left_shadow_root = downcast<ShadowRoot>(left_root);
+ left = left_shadow_root->host();
+ }
+}
+
+// https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke
+bool EventDispatcher::inner_invoke(Event& event, Vector<EventTarget::EventListenerRegistration>& listeners, Event::Phase phase, bool invocation_target_in_shadow_tree)
+{
+ bool found = false;
+
+ for (auto& listener : listeners) {
+ if (listener.listener->removed())
+ continue;
+
+ if (event.type() != listener.listener->type())
+ continue;
+
+ found = true;
+
+ if (phase == Event::Phase::CapturingPhase && !listener.listener->capture())
+ continue;
+
+ if (phase == Event::Phase::BubblingPhase && listener.listener->capture())
+ continue;
+
+ if (listener.listener->once())
+ event.current_target()->remove_from_event_listener_list(listener.listener);
+
+ auto& function = listener.listener->function();
+ auto& global = function.global_object();
+
+ RefPtr<Event> current_event;
+
+ if (is<Bindings::WindowObject>(global)) {
+ auto& bindings_window_global = downcast<Bindings::WindowObject>(global);
+ auto& window_impl = bindings_window_global.impl();
+ current_event = window_impl.current_event();
+ if (!invocation_target_in_shadow_tree)
+ window_impl.set_current_event(&event);
+ }
+
+ if (listener.listener->passive())
+ event.set_in_passive_listener(true);
+
+ auto* this_value = Bindings::wrap(global, *event.current_target());
+ auto* wrapped_event = Bindings::wrap(global, event);
+ auto& vm = global.vm();
+ [[maybe_unused]] auto rc = vm.call(listener.listener->function(), this_value, wrapped_event);
+ if (vm.exception()) {
+ vm.clear_exception();
+ // FIXME: Set legacyOutputDidListenersThrowFlag if given. (Only used by IndexedDB currently)
+ }
+
+ event.set_in_passive_listener(false);
+ if (is<Bindings::WindowObject>(global)) {
+ auto& bindings_window_global = downcast<Bindings::WindowObject>(global);
+ auto& window_impl = bindings_window_global.impl();
+ window_impl.set_current_event(current_event);
+ }
+
+ if (event.should_stop_immediate_propagation())
+ return found;
+ }
+
+ return found;
+}
+
+// https://dom.spec.whatwg.org/#concept-event-listener-invoke
+void EventDispatcher::invoke(Event::PathEntry& struct_, Event& event, Event::Phase phase)
+{
+ auto last_valid_shadow_adjusted_target = event.path().last_matching([&struct_](auto& entry) {
+ return entry.index <= struct_.index && !entry.shadow_adjusted_target.is_null();
+ });
+
+ ASSERT(last_valid_shadow_adjusted_target.has_value());
+
+ event.set_target(last_valid_shadow_adjusted_target.value().shadow_adjusted_target);
+ event.set_related_target(struct_.related_target);
+ event.set_touch_target_list(struct_.touch_target_list);
+
+ if (event.should_stop_propagation())
+ return;
+
+ event.set_current_target(struct_.invocation_target);
+
+ // NOTE: This is an intentional copy. Any event listeners added after this point will not be invoked.
+ auto listeners = event.current_target()->listeners();
+ bool invocation_target_in_shadow_tree = struct_.invocation_target_in_shadow_tree;
+
+ bool found = inner_invoke(event, listeners, phase, invocation_target_in_shadow_tree);
+
+ if (!found && event.is_trusted()) {
+ auto original_event_type = event.type();
+
+ if (event.type() == "animationend")
+ event.set_type("webkitAnimationEnd");
+ else if (event.type() == "animationiteration")
+ event.set_type("webkitAnimationIteration");
+ else if (event.type() == "animationstart")
+ event.set_type("webkitAnimationStart");
+ else if (event.type() == "transitionend")
+ event.set_type("webkitTransitionEnd");
+ else
+ return;
+
+ inner_invoke(event, listeners, phase, invocation_target_in_shadow_tree);
+ event.set_type(original_event_type);
+ }
+}
+
+// https://dom.spec.whatwg.org/#concept-event-dispatch
+bool EventDispatcher::dispatch(NonnullRefPtr<EventTarget> target, NonnullRefPtr<Event> event, bool legacy_target_override)
+{
+ event->set_dispatched(true);
+ RefPtr<EventTarget> target_override;
+
+ if (!legacy_target_override) {
+ target_override = target;
+ } else {
+ // NOTE: This can be done because legacy_target_override is only set for events targeted at Window.
+ target_override = downcast<Window>(*target).document();
+ }
+
+ RefPtr<EventTarget> activation_target;
+ RefPtr<EventTarget> related_target = retarget(event->related_target(), target);
+
+ bool clear_targets = false;
+
+ if (related_target != target || event->related_target() == target) {
+ Event::TouchTargetList touch_targets;
+
+ for (auto& touch_target : event->touch_target_list()) {
+ touch_targets.append(retarget(touch_target, target));
+ }
+
+ event->append_to_path(*target, target_override, related_target, touch_targets, false);
+
+ bool is_activation_event = is<UIEvents::MouseEvent>(*event) && event->type() == HTML::EventNames::click;
+
+ if (is_activation_event && target->activation_behaviour)
+ activation_target = target;
+
+ // FIXME: Let slottable be target, if target is a slottable and is assigned, and null otherwise.
+
+ bool slot_in_closed_tree = false;
+ auto* parent = target->get_parent(event);
+
+ while (parent) {
+ // FIXME: If slottable is non-null:
+
+ // FIXME: If parent is a slottable and is assigned, then set slottable to parent.
+
+ related_target = retarget(event->related_target(), parent);
+ touch_targets.clear();
+
+ for (auto& touch_target : event->touch_target_list()) {
+ touch_targets.append(retarget(touch_target, parent));
+ }
+
+ // FIXME: or parent is a node and target’s root is a shadow-including inclusive ancestor of parent, then:
+ if (is<Window>(parent)) {
+ if (is_activation_event && event->bubbles() && !activation_target && parent->activation_behaviour)
+ activation_target = parent;
+
+ event->append_to_path(*parent, nullptr, related_target, touch_targets, slot_in_closed_tree);
+ } else if (related_target == parent) {
+ parent = nullptr;
+ } else {
+ target = *parent;
+
+ if (is_activation_event && !activation_target && target->activation_behaviour)
+ activation_target = target;
+
+ event->append_to_path(*parent, target, related_target, touch_targets, slot_in_closed_tree);
+ }
+
+ if (parent) {
+ parent = parent->get_parent(event);
+ }
+
+ slot_in_closed_tree = false;
+ }
+
+ auto clear_targets_struct = event->path().last_matching([](auto& entry) {
+ return !entry.shadow_adjusted_target.is_null();
+ });
+
+ ASSERT(clear_targets_struct.has_value());
+
+ if (is<Node>(clear_targets_struct.value().shadow_adjusted_target.ptr())) {
+ auto& shadow_adjusted_target_node = downcast<Node>(*clear_targets_struct.value().shadow_adjusted_target);
+ if (is<ShadowRoot>(shadow_adjusted_target_node.root()))
+ clear_targets = true;
+ }
+
+ if (!clear_targets && is<Node>(clear_targets_struct.value().related_target.ptr())) {
+ auto& related_target_node = downcast<Node>(*clear_targets_struct.value().related_target);
+ if (is<ShadowRoot>(related_target_node.root()))
+ clear_targets = true;
+ }
+
+ if (!clear_targets) {
+ for (auto touch_target : clear_targets_struct.value().touch_target_list) {
+ if (is<Node>(*touch_target.ptr())) {
+ auto& touch_target_node = downcast<Node>(*touch_target.ptr());
+ if (is<ShadowRoot>(touch_target_node.root())) {
+ clear_targets = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (activation_target && activation_target->legacy_pre_activation_behaviour)
+ activation_target->legacy_pre_activation_behaviour();
+
+ for (ssize_t i = event->path().size() - 1; i >= 0; --i) {
+ auto& entry = event->path().at(i);
+
+ if (entry.shadow_adjusted_target)
+ event->set_phase(Event::Phase::AtTarget);
+ else
+ event->set_phase(Event::Phase::CapturingPhase);
+
+ invoke(entry, event, Event::Phase::CapturingPhase);
+ }
+
+ for (auto& entry : event->path()) {
+ if (entry.shadow_adjusted_target) {
+ event->set_phase(Event::Phase::AtTarget);
+ } else {
+ if (!event->bubbles())
+ continue;
+
+ event->set_phase(Event::Phase::BubblingPhase);
+ }
+
+ invoke(entry, event, Event::Phase::BubblingPhase);
+ }
+ }
+
+ event->set_phase(Event::Phase::None);
+ event->set_current_target(nullptr);
+ event->clear_path();
+ event->set_dispatched(false);
+ event->set_stop_propagation(false);
+ event->set_stop_immediate_propagation(false);
+
+ if (clear_targets) {
+ event->set_target(nullptr);
+ event->set_related_target(nullptr);
+ event->clear_touch_target_list();
+ }
+
+ if (activation_target) {
+ if (!event->cancelled()) {
+ // NOTE: Since activation_target is set, it will have activation behaviour.
+ activation_target->activation_behaviour(event);
+ } else {
+ if (activation_target->legacy_cancelled_activation_behaviour)
+ activation_target->legacy_cancelled_activation_behaviour();
+ }
+ }
+
+ return !event->cancelled();
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/EventDispatcher.h b/Userland/Libraries/LibWeb/DOM/EventDispatcher.h
new file mode 100644
index 0000000000..c5c380c597
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/EventDispatcher.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/Forward.h>
+#include <LibWeb/Forward.h>
+
+namespace Web::DOM {
+
+class EventDispatcher {
+public:
+ static bool dispatch(NonnullRefPtr<EventTarget>, NonnullRefPtr<Event>, bool legacy_target_override = false);
+
+private:
+ static void invoke(Event::PathEntry&, Event&, Event::Phase);
+ static bool inner_invoke(Event&, Vector<EventTarget::EventListenerRegistration>&, Event::Phase, bool);
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/EventListener.cpp b/Userland/Libraries/LibWeb/DOM/EventListener.cpp
new file mode 100644
index 0000000000..eb64bfa503
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/EventListener.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibJS/Runtime/Function.h>
+#include <LibWeb/DOM/EventListener.h>
+
+namespace Web::DOM {
+
+JS::Function& EventListener::function()
+{
+ ASSERT(m_function.cell());
+ return *m_function.cell();
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/EventListener.h b/Userland/Libraries/LibWeb/DOM/EventListener.h
new file mode 100644
index 0000000000..1d5705c2b6
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/EventListener.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/RefCounted.h>
+#include <LibJS/Heap/Handle.h>
+#include <LibWeb/Bindings/Wrappable.h>
+
+namespace Web::DOM {
+
+class EventListener
+ : public RefCounted<EventListener>
+ , public Bindings::Wrappable {
+public:
+ using WrapperType = Bindings::EventListenerWrapper;
+
+ explicit EventListener(JS::Handle<JS::Function> function)
+ : m_function(move(function))
+ {
+ }
+
+ JS::Function& function();
+
+ const FlyString& type() const { return m_type; }
+ void set_type(const FlyString& type) { m_type = type; }
+
+ bool capture() const { return m_capture; }
+ void set_capture(bool capture) { m_capture = capture; }
+
+ bool passive() const { return m_passive; }
+ void set_passive(bool passive) { m_capture = passive; }
+
+ bool once() const { return m_once; }
+ void set_once(bool once) { m_once = once; }
+
+ bool removed() const { return m_removed; }
+ void set_removed(bool removed) { m_removed = removed; }
+
+private:
+ FlyString m_type;
+ JS::Handle<JS::Function> m_function;
+ bool m_capture { false };
+ bool m_passive { false };
+ bool m_once { false };
+ bool m_removed { false };
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/EventTarget.cpp b/Userland/Libraries/LibWeb/DOM/EventTarget.cpp
new file mode 100644
index 0000000000..5f8ca71c8a
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/EventTarget.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/Bindings/ScriptExecutionContext.h>
+#include <LibWeb/DOM/EventListener.h>
+#include <LibWeb/DOM/EventTarget.h>
+
+namespace Web::DOM {
+
+EventTarget::EventTarget(Bindings::ScriptExecutionContext& script_execution_context)
+ : m_script_execution_context(&script_execution_context)
+{
+}
+
+EventTarget::~EventTarget()
+{
+}
+
+void EventTarget::add_event_listener(const FlyString& event_name, NonnullRefPtr<EventListener> listener)
+{
+ auto existing_listener = m_listeners.first_matching([&](auto& entry) {
+ return entry.listener->type() == event_name && &entry.listener->function() == &listener->function() && entry.listener->capture() == listener->capture();
+ });
+ if (existing_listener.has_value())
+ return;
+ listener->set_type(event_name);
+ m_listeners.append({ event_name, move(listener) });
+}
+
+void EventTarget::remove_event_listener(const FlyString& event_name, NonnullRefPtr<EventListener> listener)
+{
+ m_listeners.remove_first_matching([&](auto& entry) {
+ auto matches = entry.event_name == event_name && &entry.listener->function() == &listener->function() && entry.listener->capture() == listener->capture();
+ if (matches)
+ entry.listener->set_removed(true);
+ return matches;
+ });
+}
+
+void EventTarget::remove_from_event_listener_list(NonnullRefPtr<EventListener> listener)
+{
+ m_listeners.remove_first_matching([&](auto& entry) {
+ return entry.listener->type() == listener->type() && &entry.listener->function() == &listener->function() && entry.listener->capture() == listener->capture();
+ });
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/EventTarget.h b/Userland/Libraries/LibWeb/DOM/EventTarget.h
new file mode 100644
index 0000000000..32399a6a7a
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/EventTarget.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/FlyString.h>
+#include <AK/Function.h>
+#include <AK/Noncopyable.h>
+#include <AK/Vector.h>
+#include <LibJS/Forward.h>
+#include <LibWeb/Forward.h>
+
+namespace Web::DOM {
+
+class EventTarget {
+ AK_MAKE_NONCOPYABLE(EventTarget);
+ AK_MAKE_NONMOVABLE(EventTarget);
+
+public:
+ virtual ~EventTarget();
+
+ void ref() { ref_event_target(); }
+ void unref() { unref_event_target(); }
+
+ void add_event_listener(const FlyString& event_name, NonnullRefPtr<EventListener>);
+ void remove_event_listener(const FlyString& event_name, NonnullRefPtr<EventListener>);
+
+ void remove_from_event_listener_list(NonnullRefPtr<EventListener>);
+
+ virtual bool dispatch_event(NonnullRefPtr<Event>) = 0;
+ virtual Bindings::EventTargetWrapper* create_wrapper(JS::GlobalObject&) = 0;
+ Bindings::ScriptExecutionContext* script_execution_context() { return m_script_execution_context; }
+
+ virtual EventTarget* get_parent(const Event&) { return nullptr; }
+
+ struct EventListenerRegistration {
+ FlyString event_name;
+ NonnullRefPtr<EventListener> listener;
+ };
+
+ const Vector<EventListenerRegistration>& listeners() const { return m_listeners; }
+
+ Function<void(const Event&)> activation_behaviour;
+
+ // NOTE: These only exist for checkbox and radio input elements.
+ Function<void()> legacy_pre_activation_behaviour;
+ Function<void()> legacy_cancelled_activation_behaviour;
+
+protected:
+ explicit EventTarget(Bindings::ScriptExecutionContext&);
+
+ virtual void ref_event_target() = 0;
+ virtual void unref_event_target() = 0;
+
+private:
+ // FIXME: This should not be a raw pointer.
+ Bindings::ScriptExecutionContext* m_script_execution_context { nullptr };
+
+ Vector<EventListenerRegistration> m_listeners;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/EventTarget.idl b/Userland/Libraries/LibWeb/DOM/EventTarget.idl
new file mode 100644
index 0000000000..2dacfae0e2
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/EventTarget.idl
@@ -0,0 +1,6 @@
+interface EventTarget {
+
+ undefined addEventListener(DOMString type, EventListener? callback);
+ undefined removeEventListener(DOMString type, EventListener? callback);
+
+};
diff --git a/Userland/Libraries/LibWeb/DOM/Node.cpp b/Userland/Libraries/LibWeb/DOM/Node.cpp
new file mode 100644
index 0000000000..6890e4fda5
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/Node.cpp
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/StringBuilder.h>
+#include <LibJS/AST.h>
+#include <LibJS/Runtime/Function.h>
+#include <LibJS/Runtime/ScriptFunction.h>
+#include <LibWeb/Bindings/EventWrapper.h>
+#include <LibWeb/Bindings/EventWrapperFactory.h>
+#include <LibWeb/Bindings/NodeWrapper.h>
+#include <LibWeb/Bindings/NodeWrapperFactory.h>
+#include <LibWeb/CSS/StyleResolver.h>
+#include <LibWeb/DOM/Element.h>
+#include <LibWeb/DOM/Event.h>
+#include <LibWeb/DOM/EventDispatcher.h>
+#include <LibWeb/DOM/EventListener.h>
+#include <LibWeb/DOM/Node.h>
+#include <LibWeb/DOM/ShadowRoot.h>
+#include <LibWeb/HTML/HTMLAnchorElement.h>
+#include <LibWeb/Layout/BlockBox.h>
+#include <LibWeb/Layout/InitialContainingBlockBox.h>
+#include <LibWeb/Layout/InlineNode.h>
+#include <LibWeb/Layout/Node.h>
+#include <LibWeb/Layout/TextNode.h>
+
+//#define EVENT_DEBUG
+
+namespace Web::DOM {
+
+Node::Node(Document& document, NodeType type)
+ : EventTarget(static_cast<Bindings::ScriptExecutionContext&>(document))
+ , m_document(&document)
+ , m_type(type)
+{
+ if (!is_document())
+ m_document->ref_from_node({});
+}
+
+Node::~Node()
+{
+ ASSERT(m_deletion_has_begun);
+ if (layout_node() && layout_node()->parent())
+ layout_node()->parent()->remove_child(*layout_node());
+
+ if (!is_document())
+ m_document->unref_from_node({});
+}
+
+const HTML::HTMLAnchorElement* Node::enclosing_link_element() const
+{
+ for (auto* node = this; node; node = node->parent()) {
+ if (is<HTML::HTMLAnchorElement>(*node) && downcast<HTML::HTMLAnchorElement>(*node).has_attribute(HTML::AttributeNames::href))
+ return downcast<HTML::HTMLAnchorElement>(node);
+ }
+ return nullptr;
+}
+
+const HTML::HTMLElement* Node::enclosing_html_element() const
+{
+ return first_ancestor_of_type<HTML::HTMLElement>();
+}
+
+String Node::text_content() const
+{
+ StringBuilder builder;
+ for (auto* child = first_child(); child; child = child->next_sibling()) {
+ builder.append(child->text_content());
+ }
+ return builder.to_string();
+}
+
+void Node::set_text_content(const String& content)
+{
+ if (is_text()) {
+ downcast<Text>(this)->set_data(content);
+ } else {
+ remove_all_children();
+ append_child(document().create_text_node(content));
+ }
+
+ set_needs_style_update(true);
+ document().invalidate_layout();
+}
+
+RefPtr<Layout::Node> Node::create_layout_node()
+{
+ return nullptr;
+}
+
+void Node::invalidate_style()
+{
+ for_each_in_subtree_of_type<Element>([&](auto& element) {
+ element.set_needs_style_update(true);
+ return IterationDecision::Continue;
+ });
+ document().schedule_style_update();
+}
+
+bool Node::is_link() const
+{
+ return enclosing_link_element();
+}
+
+bool Node::dispatch_event(NonnullRefPtr<Event> event)
+{
+ return EventDispatcher::dispatch(*this, event);
+}
+
+String Node::child_text_content() const
+{
+ if (!is<ParentNode>(*this))
+ return String::empty();
+
+ StringBuilder builder;
+ downcast<ParentNode>(*this).for_each_child([&](auto& child) {
+ if (is<Text>(child))
+ builder.append(downcast<Text>(child).text_content());
+ });
+ return builder.build();
+}
+
+Node* Node::root()
+{
+ Node* root = this;
+ while (root->parent())
+ root = root->parent();
+ return root;
+}
+
+Node* Node::shadow_including_root()
+{
+ auto node_root = root();
+ if (is<ShadowRoot>(node_root))
+ return downcast<ShadowRoot>(node_root)->host()->shadow_including_root();
+ return node_root;
+}
+
+bool Node::is_connected() const
+{
+ return shadow_including_root() && shadow_including_root()->is_document();
+}
+
+Element* Node::parent_element()
+{
+ if (!parent() || !is<Element>(parent()))
+ return nullptr;
+ return downcast<Element>(parent());
+}
+
+const Element* Node::parent_element() const
+{
+ if (!parent() || !is<Element>(parent()))
+ return nullptr;
+ return downcast<Element>(parent());
+}
+
+RefPtr<Node> Node::append_child(NonnullRefPtr<Node> node, bool notify)
+{
+ if (&node->document() != &document())
+ document().adopt_node(node);
+ TreeNode<Node>::append_child(node, notify);
+ return node;
+}
+
+RefPtr<Node> Node::insert_before(NonnullRefPtr<Node> node, RefPtr<Node> child, bool notify)
+{
+ if (!child)
+ return append_child(move(node), notify);
+ if (child->parent_node() != this) {
+ dbgln("FIXME: Trying to insert_before() a bogus child");
+ return nullptr;
+ }
+ if (&node->document() != &document())
+ document().adopt_node(node);
+ TreeNode<Node>::insert_before(node, child, notify);
+ return node;
+}
+
+void Node::set_document(Badge<Document>, Document& document)
+{
+ if (m_document == &document)
+ return;
+ document.ref_from_node({});
+ m_document->unref_from_node({});
+ m_document = &document;
+}
+
+bool Node::is_editable() const
+{
+ return parent() && parent()->is_editable();
+}
+
+Bindings::EventTargetWrapper* Node::create_wrapper(JS::GlobalObject& global_object)
+{
+ return wrap(global_object, *this);
+}
+
+void Node::removed_last_ref()
+{
+ if (is<Document>(*this)) {
+ downcast<Document>(*this).removed_last_ref();
+ return;
+ }
+ m_deletion_has_begun = true;
+ delete this;
+}
+
+void Node::set_layout_node(Badge<Layout::Node>, Layout::Node* layout_node) const
+{
+ if (layout_node)
+ m_layout_node = layout_node->make_weak_ptr();
+ else
+ m_layout_node = nullptr;
+}
+
+EventTarget* Node::get_parent(const Event&)
+{
+ // FIXME: returns the node’s assigned slot, if node is assigned, and node’s parent otherwise.
+ return parent();
+}
+
+void Node::set_needs_style_update(bool value)
+{
+ if (m_needs_style_update == value)
+ return;
+ m_needs_style_update = value;
+
+ if (m_needs_style_update) {
+ for (auto* ancestor = parent(); ancestor; ancestor = ancestor->parent())
+ ancestor->m_child_needs_style_update = true;
+ document().schedule_style_update();
+ }
+}
+
+void Node::inserted_into(Node&)
+{
+ set_needs_style_update(true);
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/Node.h b/Userland/Libraries/LibWeb/DOM/Node.h
new file mode 100644
index 0000000000..624b41b951
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/Node.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/Badge.h>
+#include <AK/RefPtr.h>
+#include <AK/String.h>
+#include <AK/TypeCasts.h>
+#include <AK/Vector.h>
+#include <LibWeb/Bindings/Wrappable.h>
+#include <LibWeb/DOM/EventTarget.h>
+#include <LibWeb/TreeNode.h>
+
+namespace Web::DOM {
+
+enum class NodeType : unsigned {
+ INVALID = 0,
+ ELEMENT_NODE = 1,
+ TEXT_NODE = 3,
+ COMMENT_NODE = 8,
+ DOCUMENT_NODE = 9,
+ DOCUMENT_TYPE_NODE = 10,
+ DOCUMENT_FRAGMENT_NODE = 11,
+};
+
+class Node
+ : public TreeNode<Node>
+ , public EventTarget
+ , public Bindings::Wrappable {
+public:
+ using WrapperType = Bindings::NodeWrapper;
+
+ using TreeNode<Node>::ref;
+ using TreeNode<Node>::unref;
+
+ // ^EventTarget
+ virtual void ref_event_target() final { ref(); }
+ virtual void unref_event_target() final { unref(); }
+ virtual bool dispatch_event(NonnullRefPtr<Event>) final;
+ virtual Bindings::EventTargetWrapper* create_wrapper(JS::GlobalObject&) override;
+
+ virtual ~Node();
+
+ void removed_last_ref();
+
+ NodeType type() const { return m_type; }
+ bool is_element() const { return type() == NodeType::ELEMENT_NODE; }
+ bool is_text() const { return type() == NodeType::TEXT_NODE; }
+ bool is_document() const { return type() == NodeType::DOCUMENT_NODE; }
+ bool is_document_type() const { return type() == NodeType::DOCUMENT_TYPE_NODE; }
+ bool is_comment() const { return type() == NodeType::COMMENT_NODE; }
+ bool is_character_data() const { return type() == NodeType::TEXT_NODE || type() == NodeType::COMMENT_NODE; }
+ bool is_document_fragment() const { return type() == NodeType::DOCUMENT_FRAGMENT_NODE; }
+ bool is_parent_node() const { return is_element() || is_document() || is_document_fragment(); }
+ bool is_slottable() const { return is_element() || is_text(); }
+
+ virtual bool is_editable() const;
+
+ RefPtr<Node> append_child(NonnullRefPtr<Node>, bool notify = true);
+ RefPtr<Node> insert_before(NonnullRefPtr<Node> node, RefPtr<Node> child, bool notify = true);
+
+ virtual RefPtr<Layout::Node> create_layout_node();
+
+ virtual FlyString node_name() const = 0;
+
+ virtual String text_content() const;
+ void set_text_content(const String&);
+
+ Document& document() { return *m_document; }
+ const Document& document() const { return *m_document; }
+
+ const HTML::HTMLAnchorElement* enclosing_link_element() const;
+ const HTML::HTMLElement* enclosing_html_element() const;
+
+ String child_text_content() const;
+
+ Node* root();
+ const Node* root() const
+ {
+ return const_cast<Node*>(this)->root();
+ }
+
+ Node* shadow_including_root();
+ const Node* shadow_including_root() const
+ {
+ return const_cast<Node*>(this)->shadow_including_root();
+ }
+
+ bool is_connected() const;
+
+ Node* parent_node() { return parent(); }
+ const Node* parent_node() const { return parent(); }
+
+ Element* parent_element();
+ const Element* parent_element() const;
+
+ virtual void inserted_into(Node&);
+ virtual void removed_from(Node&) { }
+ virtual void children_changed() { }
+
+ const Layout::Node* layout_node() const { return m_layout_node; }
+ Layout::Node* layout_node() { return m_layout_node; }
+
+ void set_layout_node(Badge<Layout::Node>, Layout::Node*) const;
+
+ virtual bool is_child_allowed(const Node&) const { return true; }
+
+ bool needs_style_update() const { return m_needs_style_update; }
+ void set_needs_style_update(bool);
+
+ bool child_needs_style_update() const { return m_child_needs_style_update; }
+ void set_child_needs_style_update(bool b) { m_child_needs_style_update = b; }
+
+ void invalidate_style();
+
+ bool is_link() const;
+
+ void set_document(Badge<Document>, Document&);
+
+ virtual EventTarget* get_parent(const Event&) override;
+
+protected:
+ Node(Document&, NodeType);
+
+ Document* m_document { nullptr };
+ mutable WeakPtr<Layout::Node> m_layout_node;
+ NodeType m_type { NodeType::INVALID };
+ bool m_needs_style_update { false };
+ bool m_child_needs_style_update { false };
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/Node.idl b/Userland/Libraries/LibWeb/DOM/Node.idl
new file mode 100644
index 0000000000..2ba27688e6
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/Node.idl
@@ -0,0 +1,16 @@
+interface Node : EventTarget {
+
+ readonly attribute DOMString nodeName;
+ readonly attribute Node? firstChild;
+ readonly attribute Node? lastChild;
+ readonly attribute Node? previousSibling;
+ readonly attribute Node? nextSibling;
+ readonly attribute Node? parentNode;
+ readonly attribute Element? parentElement;
+ attribute DOMString textContent;
+
+ Node appendChild(Node node);
+ Node insertBefore(Node node, Node? child);
+
+};
+
diff --git a/Userland/Libraries/LibWeb/DOM/NonDocumentTypeChildNode.h b/Userland/Libraries/LibWeb/DOM/NonDocumentTypeChildNode.h
new file mode 100644
index 0000000000..2e3b3996d9
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/NonDocumentTypeChildNode.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/Forward.h>
+#include <LibWeb/Forward.h>
+#include <LibWeb/TreeNode.h>
+
+namespace Web::DOM {
+
+template<typename NodeType>
+class NonDocumentTypeChildNode {
+public:
+ Element* previous_element_sibling()
+ {
+ for (auto* sibling = static_cast<NodeType*>(this)->previous_sibling(); sibling; sibling = sibling->previous_sibling()) {
+ if (is<Element>(*sibling))
+ return downcast<Element>(sibling);
+ }
+ return nullptr;
+ }
+
+ Element* next_element_sibling()
+ {
+ for (auto* sibling = static_cast<NodeType*>(this)->next_sibling(); sibling; sibling = sibling->next_sibling()) {
+ if (is<Element>(*sibling))
+ return downcast<Element>(sibling);
+ }
+ return nullptr;
+ }
+
+ Element* next_element_in_pre_order()
+ {
+ for (auto* node = static_cast<NodeType*>(this)->next_in_pre_order(); node; node = node->next_in_pre_order()) {
+ if (is<Element>(*node))
+ return downcast<Element>(node);
+ }
+ return nullptr;
+ }
+
+ const Element* previous_element_sibling() const { return const_cast<NonDocumentTypeChildNode*>(this)->previous_element_sibling(); }
+ const Element* next_element_sibling() const { return const_cast<NonDocumentTypeChildNode*>(this)->next_element_sibling(); }
+ const Element* next_element_in_pre_order() const { return const_cast<NonDocumentTypeChildNode*>(this)->next_element_in_pre_order(); }
+
+protected:
+ NonDocumentTypeChildNode() { }
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/NonElementParentNode.h b/Userland/Libraries/LibWeb/DOM/NonElementParentNode.h
new file mode 100644
index 0000000000..e362fbc5c0
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/NonElementParentNode.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/Forward.h>
+#include <LibWeb/Forward.h>
+#include <LibWeb/HTML/AttributeNames.h>
+#include <LibWeb/TreeNode.h>
+
+namespace Web::DOM {
+
+template<typename NodeType>
+class NonElementParentNode {
+public:
+ RefPtr<Element> get_element_by_id(const FlyString& id) const
+ {
+ RefPtr<Element> found_element;
+ static_cast<const NodeType*>(this)->template for_each_in_subtree_of_type<Element>([&](auto& element) {
+ if (element.attribute(HTML::AttributeNames::id) == id) {
+ found_element = &element;
+ return IterationDecision::Break;
+ }
+ return IterationDecision::Continue;
+ });
+ return found_element;
+ }
+ RefPtr<Element> get_element_by_id(const FlyString& id)
+ {
+ return const_cast<const NonElementParentNode*>(this)->get_element_by_id(id);
+ }
+
+protected:
+ NonElementParentNode() { }
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/ParentNode.cpp b/Userland/Libraries/LibWeb/DOM/ParentNode.cpp
new file mode 100644
index 0000000000..42f435c19e
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/ParentNode.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2020, Luke Wilde <luke.wilde@live.co.uk>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/CSS/Parser/CSSParser.h>
+#include <LibWeb/CSS/SelectorEngine.h>
+#include <LibWeb/DOM/ParentNode.h>
+#include <LibWeb/Dump.h>
+
+namespace Web::DOM {
+
+RefPtr<Element> ParentNode::query_selector(const StringView& selector_text)
+{
+ auto selector = parse_selector(CSS::ParsingContext(*this), selector_text);
+ if (!selector.has_value())
+ return {};
+
+ dump_selector(selector.value());
+
+ RefPtr<Element> result;
+ for_each_in_subtree_of_type<Element>([&](auto& element) {
+ if (SelectorEngine::matches(selector.value(), element)) {
+ result = element;
+ return IterationDecision::Break;
+ }
+ return IterationDecision::Continue;
+ });
+
+ return result;
+}
+
+NonnullRefPtrVector<Element> ParentNode::query_selector_all(const StringView& selector_text)
+{
+ auto selector = parse_selector(CSS::ParsingContext(*this), selector_text);
+ if (!selector.has_value())
+ return {};
+
+ dump_selector(selector.value());
+
+ NonnullRefPtrVector<Element> elements;
+ for_each_in_subtree_of_type<Element>([&](auto& element) {
+ if (SelectorEngine::matches(selector.value(), element)) {
+ elements.append(element);
+ }
+ return IterationDecision::Continue;
+ });
+
+ return elements;
+}
+
+RefPtr<Element> ParentNode::first_element_child()
+{
+ return first_child_of_type<Element>();
+}
+
+RefPtr<Element> ParentNode::last_element_child()
+{
+ return last_child_of_type<Element>();
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/ParentNode.h b/Userland/Libraries/LibWeb/DOM/ParentNode.h
new file mode 100644
index 0000000000..0fa6e8f1fe
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/ParentNode.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/NonnullRefPtrVector.h>
+#include <LibWeb/DOM/Node.h>
+
+namespace Web::DOM {
+
+class ParentNode : public Node {
+public:
+ template<typename F>
+ void for_each_child(F) const;
+ template<typename F>
+ void for_each_child(F);
+
+ RefPtr<Element> first_element_child();
+ RefPtr<Element> last_element_child();
+
+ RefPtr<Element> query_selector(const StringView&);
+ NonnullRefPtrVector<Element> query_selector_all(const StringView&);
+
+protected:
+ ParentNode(Document& document, NodeType type)
+ : Node(document, type)
+ {
+ }
+};
+
+template<typename Callback>
+inline void ParentNode::for_each_child(Callback callback) const
+{
+ for (auto* node = first_child(); node; node = node->next_sibling())
+ callback(*node);
+}
+
+template<typename Callback>
+inline void ParentNode::for_each_child(Callback callback)
+{
+ for (auto* node = first_child(); node; node = node->next_sibling())
+ callback(*node);
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/Position.cpp b/Userland/Libraries/LibWeb/DOM/Position.cpp
new file mode 100644
index 0000000000..e78139f788
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/Position.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/DOM/Node.h>
+#include <LibWeb/DOM/Position.h>
+
+namespace Web::DOM {
+
+Position::Position(Node& node, unsigned offset)
+ : m_node(node)
+ , m_offset(offset)
+{
+}
+
+Position::~Position()
+{
+}
+
+String Position::to_string() const
+{
+ if (!node())
+ return String::formatted("DOM::Position(nullptr, {})", offset());
+ return String::formatted("DOM::Position({} ({})), {})", node()->node_name(), node(), offset());
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/Position.h b/Userland/Libraries/LibWeb/DOM/Position.h
new file mode 100644
index 0000000000..b2c5a17ceb
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/Position.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/RefPtr.h>
+#include <LibWeb/DOM/Node.h>
+#include <LibWeb/Forward.h>
+
+namespace Web::DOM {
+
+class Position {
+public:
+ Position() { }
+ Position(Node&, unsigned offset);
+
+ ~Position();
+
+ bool is_valid() const { return m_node; }
+
+ Node* node() { return m_node; }
+ const Node* node() const { return m_node; }
+
+ unsigned offset() const { return m_offset; }
+ void set_offset(unsigned value) { m_offset = value; }
+
+ bool operator==(const Position& other) const
+ {
+ return m_node == other.m_node && m_offset == other.m_offset;
+ }
+
+ bool operator!=(const Position& other) const
+ {
+ return !(*this == other);
+ }
+
+ String to_string() const;
+
+private:
+ RefPtr<Node> m_node;
+ unsigned m_offset { 0 };
+};
+
+}
+
+namespace AK {
+template<>
+struct Formatter<Web::DOM::Position> : Formatter<StringView> {
+ void format(FormatBuilder& builder, const Web::DOM::Position& value)
+ {
+ Formatter<StringView>::format(builder, value.to_string());
+ }
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/Range.cpp b/Userland/Libraries/LibWeb/DOM/Range.cpp
new file mode 100644
index 0000000000..377eb7f882
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/Range.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2020, the SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/DOM/Node.h>
+#include <LibWeb/DOM/Range.h>
+#include <LibWeb/DOM/Window.h>
+
+namespace Web::DOM {
+
+Range::Range(Window& window)
+ : m_start_container(window.document())
+ , m_start_offset(0)
+ , m_end_container(window.document())
+ , m_end_offset(0)
+{
+}
+
+Range::Range(Node& start_container, size_t start_offset, Node& end_container, size_t end_offset)
+ : m_start_container(start_container)
+ , m_start_offset(start_offset)
+ , m_end_container(end_container)
+ , m_end_offset(end_offset)
+{
+}
+
+NonnullRefPtr<Range> Range::clone_range() const
+{
+ return adopt(*new Range(const_cast<Node&>(*m_start_container), m_start_offset, const_cast<Node&>(*m_end_container), m_end_offset));
+}
+
+NonnullRefPtr<Range> Range::inverted() const
+{
+ return adopt(*new Range(const_cast<Node&>(*m_end_container), m_end_offset, const_cast<Node&>(*m_start_container), m_start_offset));
+}
+
+NonnullRefPtr<Range> Range::normalized() const
+{
+ if (m_start_container.ptr() == m_end_container.ptr()) {
+ if (m_start_offset <= m_end_offset)
+ return clone_range();
+
+ return inverted();
+ }
+
+ if (m_start_container->is_before(m_end_container))
+ return clone_range();
+
+ return inverted();
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/Range.h b/Userland/Libraries/LibWeb/DOM/Range.h
new file mode 100644
index 0000000000..608112fb14
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/Range.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2020, the SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/RefCounted.h>
+#include <LibWeb/Bindings/Wrappable.h>
+#include <LibWeb/DOM/Node.h>
+
+namespace Web::DOM {
+
+class Range final
+ : public RefCounted<Range>
+ , public Bindings::Wrappable {
+public:
+ using WrapperType = Bindings::RangeWrapper;
+
+ static NonnullRefPtr<Range> create(Window& window)
+ {
+ return adopt(*new Range(window));
+ }
+ static NonnullRefPtr<Range> create(Node& start_container, size_t start_offset, Node& end_container, size_t end_offset)
+ {
+ return adopt(*new Range(start_container, start_offset, end_container, end_offset));
+ }
+
+ // FIXME: There are a ton of methods missing here.
+
+ Node* start_container() { return m_start_container; }
+ unsigned start_offset() { return m_start_offset; }
+
+ Node* end_container() { return m_end_container; }
+ unsigned end_offset() { return m_end_offset; }
+
+ bool collapsed()
+ {
+ return start_container() == end_container() && start_offset() == end_offset();
+ }
+
+ void set_start(Node& container, unsigned offset)
+ {
+ m_start_container = container;
+ m_start_offset = offset;
+ }
+
+ void set_end(Node& container, unsigned offset)
+ {
+ m_end_container = container;
+ m_end_offset = offset;
+ }
+
+ NonnullRefPtr<Range> inverted() const;
+ NonnullRefPtr<Range> normalized() const;
+ NonnullRefPtr<Range> clone_range() const;
+
+private:
+ explicit Range(Window&);
+ Range(Node& start_container, size_t start_offset, Node& end_container, size_t end_offset);
+
+ NonnullRefPtr<Node> m_start_container;
+ unsigned m_start_offset;
+
+ NonnullRefPtr<Node> m_end_container;
+ unsigned m_end_offset;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/ShadowRoot.cpp b/Userland/Libraries/LibWeb/DOM/ShadowRoot.cpp
new file mode 100644
index 0000000000..237216379b
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/ShadowRoot.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020, the SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/DOM/Event.h>
+#include <LibWeb/DOM/ShadowRoot.h>
+
+namespace Web::DOM {
+
+ShadowRoot::ShadowRoot(Document& document, Element& host)
+ : DocumentFragment(document)
+{
+ set_host(host);
+}
+
+EventTarget* ShadowRoot::get_parent(const Event& event)
+{
+ if (!event.composed()) {
+ auto& events_first_invocation_target = downcast<Node>(*event.path().first().invocation_target);
+ if (events_first_invocation_target.root() == this)
+ return nullptr;
+ }
+
+ return host();
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/ShadowRoot.h b/Userland/Libraries/LibWeb/DOM/ShadowRoot.h
new file mode 100644
index 0000000000..4bee76470d
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/ShadowRoot.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2020, the SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/DOM/DocumentFragment.h>
+
+namespace Web::DOM {
+
+class ShadowRoot final : public DocumentFragment {
+public:
+ ShadowRoot(Document&, Element&);
+
+ bool closed() const { return m_closed; }
+
+ bool delegates_focus() const { return m_delegates_focus; }
+ void set_delegates_focus(bool delegates_focus) { m_delegates_focus = delegates_focus; }
+
+ bool available_to_element_internals() const { return m_available_to_element_internals; }
+ void set_available_to_element_internals(bool available_to_element_internals) { m_available_to_element_internals = available_to_element_internals; }
+
+ // ^EventTarget
+ virtual EventTarget* get_parent(const Event&) override;
+
+ // NOTE: This is intended for the JS bindings.
+ String mode() const { return m_closed ? "closed" : "open"; }
+
+private:
+ // NOTE: The specification doesn't seem to specify a default value for closed. Assuming false for now.
+ bool m_closed { false };
+ bool m_delegates_focus { false };
+ bool m_available_to_element_internals { false };
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/ShadowRoot.idl b/Userland/Libraries/LibWeb/DOM/ShadowRoot.idl
new file mode 100644
index 0000000000..320ca53e80
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/ShadowRoot.idl
@@ -0,0 +1,6 @@
+interface ShadowRoot : DocumentFragment {
+
+ readonly attribute DOMString mode;
+ readonly attribute Element host;
+
+};
diff --git a/Userland/Libraries/LibWeb/DOM/Text.cpp b/Userland/Libraries/LibWeb/DOM/Text.cpp
new file mode 100644
index 0000000000..de7beec7b6
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/Text.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/DOM/Text.h>
+#include <LibWeb/Layout/TextNode.h>
+
+namespace Web::DOM {
+
+Text::Text(Document& document, const String& data)
+ : CharacterData(document, NodeType::TEXT_NODE, data)
+{
+}
+
+Text::~Text()
+{
+}
+
+RefPtr<Layout::Node> Text::create_layout_node()
+{
+ return adopt(*new Layout::TextNode(document(), *this));
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/Text.h b/Userland/Libraries/LibWeb/DOM/Text.h
new file mode 100644
index 0000000000..0e6f4a2dbd
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/Text.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/FlyString.h>
+#include <AK/String.h>
+#include <LibWeb/DOM/CharacterData.h>
+
+namespace Web::DOM {
+
+class Text final : public CharacterData {
+public:
+ using WrapperType = Bindings::TextWrapper;
+
+ explicit Text(Document&, const String&);
+ virtual ~Text() override;
+
+ virtual FlyString node_name() const override { return "#text"; }
+
+private:
+ virtual RefPtr<Layout::Node> create_layout_node() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/Text.idl b/Userland/Libraries/LibWeb/DOM/Text.idl
new file mode 100644
index 0000000000..25a6fdb405
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/Text.idl
@@ -0,0 +1,3 @@
+interface Text : CharacterData {
+
+};
diff --git a/Userland/Libraries/LibWeb/DOM/Timer.cpp b/Userland/Libraries/LibWeb/DOM/Timer.cpp
new file mode 100644
index 0000000000..04e3c778f5
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/Timer.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibCore/Timer.h>
+#include <LibJS/Runtime/Function.h>
+#include <LibWeb/DOM/Timer.h>
+#include <LibWeb/DOM/Window.h>
+
+namespace Web::DOM {
+
+NonnullRefPtr<Timer> Timer::create_interval(Window& window, int milliseconds, JS::Function& callback)
+{
+ return adopt(*new Timer(window, Type::Interval, milliseconds, callback));
+}
+
+NonnullRefPtr<Timer> Timer::create_timeout(Window& window, int milliseconds, JS::Function& callback)
+{
+ return adopt(*new Timer(window, Type::Timeout, milliseconds, callback));
+}
+
+Timer::Timer(Window& window, Type type, int milliseconds, JS::Function& callback)
+ : m_window(window)
+ , m_type(type)
+ , m_callback(JS::make_handle(&callback))
+{
+ m_id = window.allocate_timer_id({});
+ m_timer = Core::Timer::construct(milliseconds, [this] { m_window.timer_did_fire({}, *this); });
+ if (m_type == Type::Timeout)
+ m_timer->set_single_shot(true);
+}
+
+Timer::~Timer()
+{
+ m_window.deallocate_timer_id({}, m_id);
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/Timer.h b/Userland/Libraries/LibWeb/DOM/Timer.h
new file mode 100644
index 0000000000..bdecde92d8
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/Timer.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/Forward.h>
+#include <LibCore/Forward.h>
+#include <LibJS/Heap/Handle.h>
+#include <LibWeb/Forward.h>
+
+namespace Web::DOM {
+
+class Timer final : public RefCounted<Timer> {
+public:
+ enum class Type {
+ Interval,
+ Timeout,
+ };
+
+ static NonnullRefPtr<Timer> create_interval(Window&, int milliseconds, JS::Function&);
+ static NonnullRefPtr<Timer> create_timeout(Window&, int milliseconds, JS::Function&);
+
+ ~Timer();
+
+ i32 id() const { return m_id; }
+ Type type() const { return m_type; }
+
+ JS::Function& callback() { return *m_callback.cell(); }
+
+private:
+ Timer(Window&, Type, int ms, JS::Function&);
+
+ Window& m_window;
+ RefPtr<Core::Timer> m_timer;
+ Type m_type;
+ int m_id { 0 };
+ JS::Handle<JS::Function> m_callback;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/Window.cpp b/Userland/Libraries/LibWeb/DOM/Window.cpp
new file mode 100644
index 0000000000..d10620bc1e
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/Window.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibGUI/DisplayLink.h>
+#include <LibGUI/MessageBox.h>
+#include <LibJS/Runtime/Function.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/DOM/Event.h>
+#include <LibWeb/DOM/EventDispatcher.h>
+#include <LibWeb/DOM/Timer.h>
+#include <LibWeb/DOM/Window.h>
+#include <LibWeb/HighResolutionTime/Performance.h>
+#include <LibWeb/InProcessWebView.h>
+#include <LibWeb/Page/Frame.h>
+
+namespace Web::DOM {
+
+NonnullRefPtr<Window> Window::create_with_document(Document& document)
+{
+ return adopt(*new Window(document));
+}
+
+Window::Window(Document& document)
+ : EventTarget(static_cast<Bindings::ScriptExecutionContext&>(document))
+ , m_document(document)
+ , m_performance(make<HighResolutionTime::Performance>(*this))
+{
+}
+
+Window::~Window()
+{
+}
+
+void Window::set_wrapper(Badge<Bindings::WindowObject>, Bindings::WindowObject& wrapper)
+{
+ m_wrapper = wrapper.make_weak_ptr();
+}
+
+void Window::alert(const String& message)
+{
+ if (auto* page = m_document.page())
+ page->client().page_did_request_alert(message);
+}
+
+bool Window::confirm(const String& message)
+{
+ auto confirm_result = GUI::MessageBox::show(nullptr, message, "Confirm", GUI::MessageBox::Type::Warning, GUI::MessageBox::InputType::OKCancel);
+ return confirm_result == GUI::Dialog::ExecResult::ExecOK;
+}
+
+i32 Window::set_interval(JS::Function& callback, i32 interval)
+{
+ auto timer = Timer::create_interval(*this, interval, callback);
+ m_timers.set(timer->id(), timer);
+ return timer->id();
+}
+
+i32 Window::set_timeout(JS::Function& callback, i32 interval)
+{
+ auto timer = Timer::create_timeout(*this, interval, callback);
+ m_timers.set(timer->id(), timer);
+ return timer->id();
+}
+
+void Window::timer_did_fire(Badge<Timer>, Timer& timer)
+{
+ // We should not be here if there's no JS wrapper for the Window object.
+ ASSERT(wrapper());
+ auto& vm = wrapper()->vm();
+
+ // NOTE: This protector pointer keeps the timer alive until the end of this function no matter what.
+ NonnullRefPtr protector(timer);
+
+ if (timer.type() == Timer::Type::Timeout) {
+ m_timers.remove(timer.id());
+ }
+
+ [[maybe_unused]] auto rc = vm.call(timer.callback(), wrapper());
+ if (vm.exception())
+ vm.clear_exception();
+}
+
+i32 Window::allocate_timer_id(Badge<Timer>)
+{
+ return m_timer_id_allocator.allocate();
+}
+
+void Window::deallocate_timer_id(Badge<Timer>, i32 id)
+{
+ m_timer_id_allocator.deallocate(id);
+}
+
+void Window::clear_timeout(i32 timer_id)
+{
+ m_timers.remove(timer_id);
+}
+
+void Window::clear_interval(i32 timer_id)
+{
+ m_timers.remove(timer_id);
+}
+
+i32 Window::request_animation_frame(JS::Function& callback)
+{
+ // FIXME: This is extremely fake!
+ static double fake_timestamp = 0;
+
+ i32 link_id = GUI::DisplayLink::register_callback([handle = make_handle(&callback)](i32 link_id) {
+ auto& function = const_cast<JS::Function&>(static_cast<const JS::Function&>(*handle.cell()));
+ auto& vm = function.vm();
+ fake_timestamp += 10;
+ [[maybe_unused]] auto rc = vm.call(function, {}, JS::Value(fake_timestamp));
+ if (vm.exception())
+ vm.clear_exception();
+ GUI::DisplayLink::unregister_callback(link_id);
+ });
+
+ // FIXME: Don't hand out raw DisplayLink ID's to JavaScript!
+ return link_id;
+}
+
+void Window::cancel_animation_frame(i32 id)
+{
+ // FIXME: We should not be passing untrusted numbers to DisplayLink::unregister_callback()!
+ GUI::DisplayLink::unregister_callback(id);
+}
+
+void Window::did_set_location_href(Badge<Bindings::LocationObject>, const URL& new_href)
+{
+ auto* frame = document().frame();
+ if (!frame)
+ return;
+ frame->loader().load(new_href, FrameLoader::Type::Navigation);
+}
+
+void Window::did_call_location_reload(Badge<Bindings::LocationObject>)
+{
+ auto* frame = document().frame();
+ if (!frame)
+ return;
+ frame->loader().load(document().url(), FrameLoader::Type::Reload);
+}
+
+bool Window::dispatch_event(NonnullRefPtr<Event> event)
+{
+ return EventDispatcher::dispatch(*this, event, true);
+}
+
+Bindings::EventTargetWrapper* Window::create_wrapper(JS::GlobalObject&)
+{
+ ASSERT_NOT_REACHED();
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/Window.h b/Userland/Libraries/LibWeb/DOM/Window.h
new file mode 100644
index 0000000000..4118bebbe2
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/Window.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/Badge.h>
+#include <AK/IDAllocator.h>
+#include <AK/RefCounted.h>
+#include <AK/RefPtr.h>
+#include <LibWeb/Bindings/WindowObject.h>
+#include <LibWeb/Bindings/Wrappable.h>
+#include <LibWeb/DOM/Event.h>
+#include <LibWeb/DOM/EventTarget.h>
+
+namespace Web::DOM {
+
+class Window final
+ : public RefCounted<Window>
+ , public EventTarget {
+public:
+ static NonnullRefPtr<Window> create_with_document(Document&);
+ ~Window();
+
+ using RefCounted::ref;
+ using RefCounted::unref;
+
+ virtual void ref_event_target() override { RefCounted::ref(); }
+ virtual void unref_event_target() override { RefCounted::unref(); }
+ virtual bool dispatch_event(NonnullRefPtr<Event>) override;
+ virtual Bindings::EventTargetWrapper* create_wrapper(JS::GlobalObject&) override;
+
+ const Document& document() const { return m_document; }
+ Document& document() { return m_document; }
+
+ void alert(const String&);
+ bool confirm(const String&);
+ i32 request_animation_frame(JS::Function&);
+ void cancel_animation_frame(i32);
+
+ i32 set_timeout(JS::Function&, i32);
+ i32 set_interval(JS::Function&, i32);
+ void clear_timeout(i32);
+ void clear_interval(i32);
+
+ void did_set_location_href(Badge<Bindings::LocationObject>, const URL& new_href);
+ void did_call_location_reload(Badge<Bindings::LocationObject>);
+
+ Bindings::WindowObject* wrapper() { return m_wrapper; }
+ const Bindings::WindowObject* wrapper() const { return m_wrapper; }
+
+ void set_wrapper(Badge<Bindings::WindowObject>, Bindings::WindowObject&);
+
+ i32 allocate_timer_id(Badge<Timer>);
+ void deallocate_timer_id(Badge<Timer>, i32);
+ void timer_did_fire(Badge<Timer>, Timer&);
+
+ HighResolutionTime::Performance& performance() { return *m_performance; }
+
+ const Event* current_event() const { return m_current_event; }
+ void set_current_event(Event* event) { m_current_event = event; }
+
+private:
+ explicit Window(Document&);
+
+ Document& m_document;
+ WeakPtr<Bindings::WindowObject> m_wrapper;
+
+ IDAllocator m_timer_id_allocator;
+ HashMap<int, NonnullRefPtr<Timer>> m_timers;
+
+ NonnullOwnPtr<HighResolutionTime::Performance> m_performance;
+ RefPtr<Event> m_current_event;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/XMLHttpRequest.cpp b/Userland/Libraries/LibWeb/DOM/XMLHttpRequest.cpp
new file mode 100644
index 0000000000..f085ade4fd
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/XMLHttpRequest.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibJS/Runtime/Function.h>
+#include <LibWeb/Bindings/EventWrapper.h>
+#include <LibWeb/Bindings/EventWrapperFactory.h>
+#include <LibWeb/Bindings/XMLHttpRequestWrapper.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/DOM/Event.h>
+#include <LibWeb/DOM/EventDispatcher.h>
+#include <LibWeb/DOM/EventListener.h>
+#include <LibWeb/DOM/Window.h>
+#include <LibWeb/DOM/XMLHttpRequest.h>
+#include <LibWeb/HTML/EventNames.h>
+#include <LibWeb/Loader/ResourceLoader.h>
+#include <LibWeb/Origin.h>
+
+namespace Web {
+
+XMLHttpRequest::XMLHttpRequest(DOM::Window& window)
+ : EventTarget(static_cast<Bindings::ScriptExecutionContext&>(window.document()))
+ , m_window(window)
+{
+}
+
+XMLHttpRequest::~XMLHttpRequest()
+{
+}
+
+void XMLHttpRequest::set_ready_state(ReadyState ready_state)
+{
+ // FIXME: call onreadystatechange once we have that
+ m_ready_state = ready_state;
+}
+
+String XMLHttpRequest::response_text() const
+{
+ if (m_response.is_null())
+ return {};
+ return String::copy(m_response);
+}
+
+void XMLHttpRequest::open(const String& method, const String& url)
+{
+ m_method = method;
+ m_url = url;
+ set_ready_state(ReadyState::Opened);
+}
+
+void XMLHttpRequest::send()
+{
+ URL request_url = m_window->document().complete_url(m_url);
+ dbg() << "XHR send from " << m_window->document().url() << " to " << request_url;
+
+ // TODO: Add support for preflight requests to support CORS requests
+ Origin request_url_origin = Origin(request_url.protocol(), request_url.host(), request_url.port());
+
+ if (!m_window->document().origin().is_same(request_url_origin)) {
+ dbg() << "XHR failed to load: Same-Origin Policy violation: " << m_window->document().url() << " may not load " << request_url;
+ auto weak_this = make_weak_ptr();
+ if (!weak_this)
+ return;
+ const_cast<XMLHttpRequest&>(*weak_this).set_ready_state(ReadyState::Done);
+ const_cast<XMLHttpRequest&>(*weak_this).dispatch_event(DOM::Event::create(HTML::EventNames::error));
+ return;
+ }
+
+ // FIXME: in order to properly set ReadyState::HeadersReceived and ReadyState::Loading,
+ // we need to make ResourceLoader give us more detailed updates than just "done" and "error".
+ ResourceLoader::the().load(
+ m_window->document().complete_url(m_url),
+ [weak_this = make_weak_ptr()](auto data, auto&) {
+ if (!weak_this)
+ return;
+ const_cast<XMLHttpRequest&>(*weak_this).m_response = ByteBuffer::copy(data);
+ const_cast<XMLHttpRequest&>(*weak_this).set_ready_state(ReadyState::Done);
+ const_cast<XMLHttpRequest&>(*weak_this).dispatch_event(DOM::Event::create(HTML::EventNames::load));
+ },
+ [weak_this = make_weak_ptr()](auto& error) {
+ if (!weak_this)
+ return;
+ dbg() << "XHR failed to load: " << error;
+ const_cast<XMLHttpRequest&>(*weak_this).set_ready_state(ReadyState::Done);
+ const_cast<XMLHttpRequest&>(*weak_this).dispatch_event(DOM::Event::create(HTML::EventNames::error));
+ });
+}
+
+bool XMLHttpRequest::dispatch_event(NonnullRefPtr<DOM::Event> event)
+{
+ return DOM::EventDispatcher::dispatch(*this, move(event));
+}
+
+Bindings::EventTargetWrapper* XMLHttpRequest::create_wrapper(JS::GlobalObject& global_object)
+{
+ return wrap(global_object, *this);
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/DOM/XMLHttpRequest.h b/Userland/Libraries/LibWeb/DOM/XMLHttpRequest.h
new file mode 100644
index 0000000000..33a6038863
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOM/XMLHttpRequest.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/ByteBuffer.h>
+#include <AK/RefCounted.h>
+#include <AK/Weakable.h>
+#include <LibWeb/Bindings/Wrappable.h>
+#include <LibWeb/DOM/EventTarget.h>
+
+namespace Web {
+
+class XMLHttpRequest final
+ : public RefCounted<XMLHttpRequest>
+ , public Weakable<XMLHttpRequest>
+ , public DOM::EventTarget
+ , public Bindings::Wrappable {
+public:
+ enum class ReadyState {
+ Unsent,
+ Opened,
+ HeadersReceived,
+ Loading,
+ Done,
+ };
+
+ using WrapperType = Bindings::XMLHttpRequestWrapper;
+
+ static NonnullRefPtr<XMLHttpRequest> create(DOM::Window& window) { return adopt(*new XMLHttpRequest(window)); }
+
+ virtual ~XMLHttpRequest() override;
+
+ using RefCounted::ref;
+ using RefCounted::unref;
+
+ ReadyState ready_state() const { return m_ready_state; };
+ String response_text() const;
+ void open(const String& method, const String& url);
+ void send();
+
+private:
+ virtual void ref_event_target() override { ref(); }
+ virtual void unref_event_target() override { unref(); }
+ virtual bool dispatch_event(NonnullRefPtr<DOM::Event>) override;
+ virtual Bindings::EventTargetWrapper* create_wrapper(JS::GlobalObject&) override;
+
+ void set_ready_state(ReadyState);
+
+ explicit XMLHttpRequest(DOM::Window&);
+
+ NonnullRefPtr<DOM::Window> m_window;
+
+ ReadyState m_ready_state { ReadyState::Unsent };
+
+ String m_method;
+ String m_url;
+
+ ByteBuffer m_response;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/DOMTreeModel.cpp b/Userland/Libraries/LibWeb/DOMTreeModel.cpp
new file mode 100644
index 0000000000..76c0e39769
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOMTreeModel.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "DOMTreeModel.h"
+#include <AK/StringBuilder.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/DOM/Element.h>
+#include <LibWeb/DOM/Text.h>
+#include <ctype.h>
+#include <stdio.h>
+
+namespace Web {
+
+DOMTreeModel::DOMTreeModel(DOM::Document& document)
+ : m_document(document)
+{
+ m_document_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/filetype-html.png"));
+ m_element_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/inspector-object.png"));
+ m_text_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/filetype-unknown.png"));
+}
+
+DOMTreeModel::~DOMTreeModel()
+{
+}
+
+GUI::ModelIndex DOMTreeModel::index(int row, int column, const GUI::ModelIndex& parent) const
+{
+ if (!parent.is_valid()) {
+ return create_index(row, column, m_document.ptr());
+ }
+ auto& parent_node = *static_cast<DOM::Node*>(parent.internal_data());
+ return create_index(row, column, parent_node.child_at_index(row));
+}
+
+GUI::ModelIndex DOMTreeModel::parent_index(const GUI::ModelIndex& index) const
+{
+ if (!index.is_valid())
+ return {};
+ auto& node = *static_cast<DOM::Node*>(index.internal_data());
+ if (!node.parent())
+ return {};
+
+ // FIXME: Handle the template element (child elements are not stored in it, all of its children are in its document fragment "content")
+
+ // No grandparent? Parent is the document!
+ if (!node.parent()->parent()) {
+ return create_index(0, 0, m_document.ptr());
+ }
+
+ // Walk the grandparent's children to find the index of node's parent in its parent.
+ // (This is needed to produce the row number of the GUI::ModelIndex corresponding to node's parent.)
+ int grandparent_child_index = 0;
+ for (auto* grandparent_child = node.parent()->parent()->first_child(); grandparent_child; grandparent_child = grandparent_child->next_sibling()) {
+ if (grandparent_child == node.parent())
+ return create_index(grandparent_child_index, 0, node.parent());
+ ++grandparent_child_index;
+ }
+
+ ASSERT_NOT_REACHED();
+ return {};
+}
+
+int DOMTreeModel::row_count(const GUI::ModelIndex& index) const
+{
+ if (!index.is_valid())
+ return 1;
+ auto& node = *static_cast<DOM::Node*>(index.internal_data());
+ return node.child_count();
+}
+
+int DOMTreeModel::column_count(const GUI::ModelIndex&) const
+{
+ return 1;
+}
+
+static String with_whitespace_collapsed(const StringView& string)
+{
+ StringBuilder builder;
+ for (size_t i = 0; i < string.length(); ++i) {
+ if (isspace(string[i])) {
+ builder.append(' ');
+ while (i < string.length()) {
+ if (isspace(string[i])) {
+ ++i;
+ continue;
+ }
+ builder.append(string[i]);
+ break;
+ }
+ continue;
+ }
+ builder.append(string[i]);
+ }
+ return builder.to_string();
+}
+
+GUI::Variant DOMTreeModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const
+{
+ auto& node = *static_cast<DOM::Node*>(index.internal_data());
+ if (role == GUI::ModelRole::Icon) {
+ if (node.is_document())
+ return m_document_icon;
+ if (node.is_element())
+ return m_element_icon;
+ // FIXME: More node type icons?
+ return m_text_icon;
+ }
+ if (role == GUI::ModelRole::Display) {
+ if (node.is_text())
+ return with_whitespace_collapsed(downcast<DOM::Text>(node).data());
+ if (!node.is_element())
+ return node.node_name();
+ auto& element = downcast<DOM::Element>(node);
+ StringBuilder builder;
+ builder.append('<');
+ builder.append(element.local_name());
+ element.for_each_attribute([&](auto& name, auto& value) {
+ builder.append(' ');
+ builder.append(name);
+ builder.append('=');
+ builder.append('"');
+ builder.append(value);
+ builder.append('"');
+ });
+ builder.append('>');
+ return builder.to_string();
+ }
+ return {};
+}
+
+void DOMTreeModel::update()
+{
+ did_update();
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/DOMTreeModel.h b/Userland/Libraries/LibWeb/DOMTreeModel.h
new file mode 100644
index 0000000000..42a568b306
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DOMTreeModel.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibGUI/Model.h>
+#include <LibWeb/Forward.h>
+
+namespace Web {
+
+class DOMTreeModel final : public GUI::Model {
+public:
+ static NonnullRefPtr<DOMTreeModel> create(DOM::Document& document)
+ {
+ return adopt(*new DOMTreeModel(document));
+ }
+
+ virtual ~DOMTreeModel() override;
+
+ virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override;
+ virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override;
+ virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole) const override;
+ virtual GUI::ModelIndex index(int row, int column, const GUI::ModelIndex& parent = GUI::ModelIndex()) const override;
+ virtual GUI::ModelIndex parent_index(const GUI::ModelIndex&) const override;
+ virtual void update() override;
+
+private:
+ explicit DOMTreeModel(DOM::Document&);
+
+ NonnullRefPtr<DOM::Document> m_document;
+
+ GUI::Icon m_document_icon;
+ GUI::Icon m_element_icon;
+ GUI::Icon m_text_icon;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Dump.cpp b/Userland/Libraries/LibWeb/Dump.cpp
new file mode 100644
index 0000000000..f8f83ebc87
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Dump.cpp
@@ -0,0 +1,403 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/QuickSort.h>
+#include <AK/StringBuilder.h>
+#include <AK/Utf8View.h>
+#include <LibWeb/CSS/PropertyID.h>
+#include <LibWeb/CSS/StyleSheet.h>
+#include <LibWeb/DOM/Comment.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/DOM/DocumentFragment.h>
+#include <LibWeb/DOM/DocumentType.h>
+#include <LibWeb/DOM/Element.h>
+#include <LibWeb/DOM/Text.h>
+#include <LibWeb/Dump.h>
+#include <LibWeb/HTML/HTMLTemplateElement.h>
+#include <LibWeb/Layout/BlockBox.h>
+#include <LibWeb/Layout/Node.h>
+#include <LibWeb/Layout/TextNode.h>
+#include <stdio.h>
+
+namespace Web {
+
+void dump_tree(const DOM::Node& node)
+{
+ static int indent = 0;
+ for (int i = 0; i < indent; ++i)
+ dbgprintf(" ");
+ if (is<DOM::Document>(node)) {
+ dbgprintf("*Document*\n");
+ } else if (is<DOM::Element>(node)) {
+ dbgprintf("<%s", downcast<DOM::Element>(node).local_name().characters());
+ downcast<DOM::Element>(node).for_each_attribute([](auto& name, auto& value) {
+ dbgprintf(" %s=%s", name.characters(), value.characters());
+ });
+ dbgprintf(">\n");
+ } else if (is<DOM::Text>(node)) {
+ dbgprintf("\"%s\"\n", downcast<DOM::Text>(node).data().characters());
+ } else if (is<DOM::DocumentType>(node)) {
+ dbgprintf("<!DOCTYPE html>\n");
+ } else if (is<DOM::Comment>(node)) {
+ dbgprintf("<!--%s-->\n", downcast<DOM::Comment>(node).data().characters());
+ } else if (is<DOM::DocumentFragment>(node)) {
+ dbgprintf("#document-fragment\n");
+ }
+ ++indent;
+ if (is<DOM::ParentNode>(node)) {
+ if (!is<HTML::HTMLTemplateElement>(node)) {
+ static_cast<const DOM::ParentNode&>(node).for_each_child([](auto& child) {
+ dump_tree(child);
+ });
+ } else {
+ auto& template_element = downcast<HTML::HTMLTemplateElement>(node);
+ dump_tree(template_element.content());
+ }
+ }
+ --indent;
+}
+
+void dump_tree(const Layout::Node& layout_node, bool show_box_model, bool show_specified_style)
+{
+ StringBuilder builder;
+ dump_tree(builder, layout_node, show_box_model, show_specified_style, true);
+ dbgprintf("%s", builder.to_string().characters());
+}
+
+void dump_tree(StringBuilder& builder, const Layout::Node& layout_node, bool show_box_model, bool show_specified_style, bool interactive)
+{
+ static size_t indent = 0;
+ for (size_t i = 0; i < indent; ++i)
+ builder.append(" ");
+
+ FlyString tag_name;
+ if (layout_node.is_anonymous())
+ tag_name = "(anonymous)";
+ else if (is<DOM::Text>(layout_node.dom_node()))
+ tag_name = "#text";
+ else if (is<DOM::Document>(layout_node.dom_node()))
+ tag_name = "#document";
+ else if (is<DOM::Element>(layout_node.dom_node()))
+ tag_name = downcast<DOM::Element>(*layout_node.dom_node()).local_name();
+ else
+ tag_name = "???";
+
+ String identifier = "";
+ if (layout_node.dom_node() && is<DOM::Element>(*layout_node.dom_node())) {
+ auto& element = downcast<DOM::Element>(*layout_node.dom_node());
+ StringBuilder builder;
+ auto id = element.attribute(HTML::AttributeNames::id);
+ if (!id.is_empty()) {
+ builder.append('#');
+ builder.append(id);
+ }
+ for (auto& class_name : element.class_names()) {
+ builder.append('.');
+ builder.append(class_name);
+ }
+ identifier = builder.to_string();
+ }
+
+ const char* nonbox_color_on = "";
+ const char* box_color_on = "";
+ const char* positioned_color_on = "";
+ const char* floating_color_on = "";
+ const char* inline_block_color_on = "";
+ const char* line_box_color_on = "";
+ const char* fragment_color_on = "";
+ const char* color_off = "";
+
+ if (interactive) {
+ nonbox_color_on = "\033[33m";
+ box_color_on = "\033[34m";
+ positioned_color_on = "\033[31;1m";
+ floating_color_on = "\033[32;1m";
+ inline_block_color_on = "\033[36;1m";
+ line_box_color_on = "\033[34;1m";
+ fragment_color_on = "\033[35;1m";
+ color_off = "\033[0m";
+ }
+
+ if (!is<Layout::Box>(layout_node)) {
+ builder.appendff("{}{}{} <{}{}{}>",
+ nonbox_color_on,
+ layout_node.class_name().substring_view(13),
+ color_off,
+ tag_name,
+ nonbox_color_on,
+ identifier,
+ color_off);
+ if (interactive)
+ builder.appendff(" @{:p}", &layout_node);
+ builder.append("\n");
+ } else {
+ auto& box = downcast<Layout::Box>(layout_node);
+ builder.appendff("{}{}{} <{}{}{}{}> ",
+ box_color_on,
+ box.class_name().substring_view(13),
+ color_off,
+ box_color_on,
+ tag_name,
+ color_off,
+ identifier.characters());
+
+ if (interactive)
+ builder.appendff("@{:p} ", &layout_node);
+
+ builder.appendff("at ({},{}) size {}x{}",
+ (int)box.absolute_x(),
+ (int)box.absolute_y(),
+ (int)box.width(),
+ (int)box.height());
+
+ if (box.is_positioned())
+ builder.appendff(" {}positioned{}", positioned_color_on, color_off);
+ if (box.is_floating())
+ builder.appendff(" {}floating{}", floating_color_on, color_off);
+ if (box.is_inline_block())
+ builder.appendff(" {}inline-block{}", inline_block_color_on, color_off);
+
+ if (show_box_model) {
+ // Dump the horizontal box properties
+ builder.appendf(" [%g+%g+%g %g %g+%g+%g]",
+ box.box_model().margin.left,
+ box.box_model().border.left,
+ box.box_model().padding.left,
+ box.width(),
+ box.box_model().padding.right,
+ box.box_model().border.right,
+ box.box_model().margin.right);
+
+ // And the vertical box properties
+ builder.appendf(" [%g+%g+%g %g %g+%g+%g]",
+ box.box_model().margin.top,
+ box.box_model().border.top,
+ box.box_model().padding.top,
+ box.height(),
+ box.box_model().padding.bottom,
+ box.box_model().border.bottom,
+ box.box_model().margin.bottom);
+ }
+
+ builder.append("\n");
+ }
+
+ if (is<Layout::BlockBox>(layout_node) && static_cast<const Layout::BlockBox&>(layout_node).children_are_inline()) {
+ auto& block = static_cast<const Layout::BlockBox&>(layout_node);
+ for (size_t line_box_index = 0; line_box_index < block.line_boxes().size(); ++line_box_index) {
+ auto& line_box = block.line_boxes()[line_box_index];
+ for (size_t i = 0; i < indent; ++i)
+ builder.append(" ");
+ builder.appendff(" {}line {}{} width: {}\n",
+ line_box_color_on,
+ line_box_index,
+ color_off,
+ (int)line_box.width());
+ for (size_t fragment_index = 0; fragment_index < line_box.fragments().size(); ++fragment_index) {
+ auto& fragment = line_box.fragments()[fragment_index];
+ for (size_t i = 0; i < indent; ++i)
+ builder.append(" ");
+ builder.appendff(" {}frag {}{} from {} ",
+ fragment_color_on,
+ fragment_index,
+ color_off,
+ fragment.layout_node().class_name());
+ if (interactive)
+ builder.appendff("@{:p}, ", &fragment.layout_node());
+ builder.appendff("start: {}, length: {}, rect: {}\n",
+ fragment.start(),
+ fragment.length(),
+ enclosing_int_rect(fragment.absolute_rect()).to_string());
+ if (is<Layout::TextNode>(fragment.layout_node())) {
+ for (size_t i = 0; i < indent; ++i)
+ builder.append(" ");
+ auto& layout_text = static_cast<const Layout::TextNode&>(fragment.layout_node());
+ auto fragment_text = layout_text.text_for_rendering().substring(fragment.start(), fragment.length());
+ builder.appendff(" \"{}\"\n", fragment_text);
+ }
+ }
+ }
+ }
+
+ if (show_specified_style && layout_node.dom_node() && layout_node.dom_node()->is_element() && downcast<DOM::Element>(layout_node.dom_node())->specified_css_values()) {
+ struct NameAndValue {
+ String name;
+ String value;
+ };
+ Vector<NameAndValue> properties;
+ downcast<DOM::Element>(*layout_node.dom_node()).specified_css_values()->for_each_property([&](auto property_id, auto& value) {
+ properties.append({ CSS::string_from_property_id(property_id), value.to_string() });
+ });
+ quick_sort(properties, [](auto& a, auto& b) { return a.name < b.name; });
+
+ for (auto& property : properties) {
+ for (size_t i = 0; i < indent; ++i)
+ builder.append(" ");
+ builder.appendf(" (%s: %s)\n", property.name.characters(), property.value.characters());
+ }
+ }
+
+ ++indent;
+ layout_node.for_each_child([&](auto& child) {
+ dump_tree(builder, child, show_box_model, show_specified_style, interactive);
+ });
+ --indent;
+}
+
+void dump_selector(const CSS::Selector& selector)
+{
+ dbgprintf(" CSS::Selector:\n");
+
+ for (auto& complex_selector : selector.complex_selectors()) {
+ dbgprintf(" ");
+
+ const char* relation_description = "";
+ switch (complex_selector.relation) {
+ case CSS::Selector::ComplexSelector::Relation::None:
+ relation_description = "None";
+ break;
+ case CSS::Selector::ComplexSelector::Relation::ImmediateChild:
+ relation_description = "ImmediateChild";
+ break;
+ case CSS::Selector::ComplexSelector::Relation::Descendant:
+ relation_description = "Descendant";
+ break;
+ case CSS::Selector::ComplexSelector::Relation::AdjacentSibling:
+ relation_description = "AdjacentSibling";
+ break;
+ case CSS::Selector::ComplexSelector::Relation::GeneralSibling:
+ relation_description = "GeneralSibling";
+ break;
+ }
+
+ if (*relation_description)
+ dbgprintf("{%s} ", relation_description);
+
+ for (size_t i = 0; i < complex_selector.compound_selector.size(); ++i) {
+ auto& simple_selector = complex_selector.compound_selector[i];
+ const char* type_description = "Unknown";
+ switch (simple_selector.type) {
+ case CSS::Selector::SimpleSelector::Type::Invalid:
+ type_description = "Invalid";
+ break;
+ case CSS::Selector::SimpleSelector::Type::Universal:
+ type_description = "Universal";
+ break;
+ case CSS::Selector::SimpleSelector::Type::Id:
+ type_description = "Id";
+ break;
+ case CSS::Selector::SimpleSelector::Type::Class:
+ type_description = "Class";
+ break;
+ case CSS::Selector::SimpleSelector::Type::TagName:
+ type_description = "TagName";
+ break;
+ }
+ const char* attribute_match_type_description = "";
+ switch (simple_selector.attribute_match_type) {
+ case CSS::Selector::SimpleSelector::AttributeMatchType::None:
+ break;
+ case CSS::Selector::SimpleSelector::AttributeMatchType::HasAttribute:
+ attribute_match_type_description = "HasAttribute";
+ break;
+ case CSS::Selector::SimpleSelector::AttributeMatchType::ExactValueMatch:
+ attribute_match_type_description = "ExactValueMatch";
+ break;
+ case CSS::Selector::SimpleSelector::AttributeMatchType::Contains:
+ attribute_match_type_description = "Contains";
+ break;
+ }
+
+ const char* pseudo_class_description = "";
+ switch (simple_selector.pseudo_class) {
+ case CSS::Selector::SimpleSelector::PseudoClass::Link:
+ pseudo_class_description = "Link";
+ break;
+ case CSS::Selector::SimpleSelector::PseudoClass::Visited:
+ pseudo_class_description = "Visited";
+ break;
+ case CSS::Selector::SimpleSelector::PseudoClass::None:
+ pseudo_class_description = "None";
+ break;
+ case CSS::Selector::SimpleSelector::PseudoClass::Root:
+ pseudo_class_description = "Root";
+ break;
+ case CSS::Selector::SimpleSelector::PseudoClass::Focus:
+ pseudo_class_description = "Focus";
+ break;
+ case CSS::Selector::SimpleSelector::PseudoClass::Empty:
+ pseudo_class_description = "Empty";
+ break;
+ case CSS::Selector::SimpleSelector::PseudoClass::Hover:
+ pseudo_class_description = "Hover";
+ break;
+ case CSS::Selector::SimpleSelector::PseudoClass::LastChild:
+ pseudo_class_description = "LastChild";
+ break;
+ case CSS::Selector::SimpleSelector::PseudoClass::FirstChild:
+ pseudo_class_description = "FirstChild";
+ break;
+ case CSS::Selector::SimpleSelector::PseudoClass::OnlyChild:
+ pseudo_class_description = "OnlyChild";
+ break;
+ }
+
+ dbgprintf("%s:%s", type_description, simple_selector.value.characters());
+ if (simple_selector.pseudo_class != CSS::Selector::SimpleSelector::PseudoClass::None)
+ dbgprintf(" pseudo_class=%s", pseudo_class_description);
+ if (simple_selector.attribute_match_type != CSS::Selector::SimpleSelector::AttributeMatchType::None) {
+ dbgprintf(" [%s, name='%s', value='%s']", attribute_match_type_description, simple_selector.attribute_name.characters(), simple_selector.attribute_value.characters());
+ }
+
+ if (i != complex_selector.compound_selector.size() - 1)
+ dbgprintf(", ");
+ }
+ dbgprintf("\n");
+ }
+}
+
+void dump_rule(const CSS::StyleRule& rule)
+{
+ dbgprintf("Rule:\n");
+ for (auto& selector : rule.selectors()) {
+ dump_selector(selector);
+ }
+ dbgprintf(" Declarations:\n");
+ for (auto& property : rule.declaration().properties()) {
+ dbgprintf(" %s: '%s'\n", CSS::string_from_property_id(property.property_id), property.value->to_string().characters());
+ }
+}
+
+void dump_sheet(const CSS::StyleSheet& sheet)
+{
+ dbgprintf("StyleSheet{%p}: %zu rule(s)\n", &sheet, sheet.rules().size());
+
+ for (auto& rule : sheet.rules()) {
+ dump_rule(rule);
+ }
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Dump.h b/Userland/Libraries/LibWeb/Dump.h
new file mode 100644
index 0000000000..ab1d271a42
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Dump.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/Forward.h>
+#include <LibWeb/Forward.h>
+
+namespace Web {
+
+void dump_tree(const DOM::Node&);
+void dump_tree(StringBuilder&, const Layout::Node&, bool show_box_model = false, bool show_specified_style = false, bool colorize = false);
+void dump_tree(const Layout::Node&, bool show_box_model = false, bool show_specified_style = false);
+void dump_sheet(const CSS::StyleSheet&);
+void dump_rule(const CSS::StyleRule&);
+void dump_selector(const CSS::Selector&);
+
+}
diff --git a/Userland/Libraries/LibWeb/DumpLayoutTree/CMakeLists.txt b/Userland/Libraries/LibWeb/DumpLayoutTree/CMakeLists.txt
new file mode 100644
index 0000000000..11a7ce62f5
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DumpLayoutTree/CMakeLists.txt
@@ -0,0 +1,6 @@
+set(SOURCES
+ main.cpp
+)
+
+serenity_bin(DumpLayoutTree)
+target_link_libraries(DumpLayoutTree LibWeb)
diff --git a/Userland/Libraries/LibWeb/DumpLayoutTree/main.cpp b/Userland/Libraries/LibWeb/DumpLayoutTree/main.cpp
new file mode 100644
index 0000000000..f1def66b6e
--- /dev/null
+++ b/Userland/Libraries/LibWeb/DumpLayoutTree/main.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibCore/ArgsParser.h>
+#include <LibGUI/Application.h>
+#include <LibWeb/Dump.h>
+#include <LibWeb/InProcessWebView.h>
+#include <LibWeb/Layout/InitialContainingBlockBox.h>
+#include <stdlib.h>
+
+int main(int argc, char** argv)
+{
+ auto app = GUI::Application::construct(argc, argv);
+ auto window = GUI::Window::construct();
+ window->set_title("DumpLayoutTree");
+ window->resize(800, 600);
+ window->show();
+ auto& web_view = window->set_main_widget<Web::InProcessWebView>();
+ web_view.load(URL::create_with_file_protocol(argv[1]));
+ web_view.on_load_finish = [&](auto&) {
+ auto* document = web_view.document();
+ if (!document) {
+ warnln("No document.");
+ _exit(1);
+ }
+ auto* layout_root = document->layout_node();
+ if (!layout_root) {
+ warnln("No layout tree.");
+ _exit(1);
+ }
+ StringBuilder builder;
+ Web::dump_tree(builder, *layout_root);
+ write(STDOUT_FILENO, builder.string_view().characters_without_null_termination(), builder.length());
+ _exit(0);
+ };
+ return app->exec();
+}
diff --git a/Userland/Libraries/LibWeb/FontCache.cpp b/Userland/Libraries/LibWeb/FontCache.cpp
new file mode 100644
index 0000000000..c2ba8e7050
--- /dev/null
+++ b/Userland/Libraries/LibWeb/FontCache.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibGfx/Font.h>
+#include <LibWeb/FontCache.h>
+
+FontCache& FontCache::the()
+{
+ static FontCache cache;
+ return cache;
+}
+
+RefPtr<Gfx::Font> FontCache::get(const FontSelector& font_selector) const
+{
+ auto cached_font = m_fonts.get(font_selector);
+ if (cached_font.has_value())
+ return cached_font.value();
+ return nullptr;
+}
+
+void FontCache::set(const FontSelector& font_selector, NonnullRefPtr<Gfx::Font> font)
+{
+ m_fonts.set(font_selector, move(font));
+}
diff --git a/Userland/Libraries/LibWeb/FontCache.h b/Userland/Libraries/LibWeb/FontCache.h
new file mode 100644
index 0000000000..8cc554a8f4
--- /dev/null
+++ b/Userland/Libraries/LibWeb/FontCache.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/FlyString.h>
+#include <AK/HashMap.h>
+#include <AK/String.h>
+#include <LibGfx/Forward.h>
+
+struct FontSelector {
+ FlyString family;
+ int size { 0 };
+ int weight { 0 };
+
+ bool operator==(const FontSelector& other) const
+ {
+ return family == other.family && size == other.size && weight == other.weight;
+ }
+};
+
+namespace AK {
+template<>
+struct Traits<FontSelector> : public GenericTraits<FontSelector> {
+ static unsigned hash(const FontSelector& key) { return pair_int_hash(pair_int_hash(key.family.hash(), key.weight), key.size); }
+};
+}
+
+class FontCache {
+public:
+ static FontCache& the();
+ RefPtr<Gfx::Font> get(const FontSelector&) const;
+ void set(const FontSelector&, NonnullRefPtr<Gfx::Font>);
+
+private:
+ FontCache() { }
+ mutable HashMap<FontSelector, NonnullRefPtr<Gfx::Font>> m_fonts;
+};
diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h
new file mode 100644
index 0000000000..445b7222fa
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Forward.h
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+namespace Web::CSS {
+class Length;
+class Selector;
+class StyleDeclaration;
+class StyleProperties;
+class StyleResolver;
+class StyleRule;
+class StyleSheet;
+enum class Display;
+}
+
+namespace Web::DOM {
+class CharacterData;
+class Comment;
+class Document;
+class DocumentFragment;
+class DocumentType;
+class DOMImplementation;
+class Element;
+class Event;
+class EventHandler;
+class EventListener;
+class EventTarget;
+class MouseEvent;
+class Node;
+class ParentNode;
+class Position;
+class Text;
+class Timer;
+class Window;
+class Range;
+enum class QuirksMode;
+}
+
+namespace Web::HTML {
+class CanvasRenderingContext2D;
+class HTMLAnchorElement;
+class HTMLAreaElement;
+class HTMLAudioElement;
+class HTMLBaseElement;
+class HTMLBlinkElement;
+class HTMLBodyElement;
+class HTMLBRElement;
+class HTMLButtonElement;
+class HTMLCanvasElement;
+class HTMLDataElement;
+class HTMLDataListElement;
+class HTMLDetailsElement;
+class HTMLDialogElement;
+class HTMLDirectoryElement;
+class HTMLDivElement;
+class HTMLDListElement;
+class HTMLDocumentParser;
+class HTMLElement;
+class HTMLEmbedElement;
+class HTMLFieldSetElement;
+class HTMLFontElement;
+class HTMLFormElement;
+class HTMLFrameElement;
+class HTMLFrameSetElement;
+class HTMLHeadElement;
+class HTMLHeadingElement;
+class HTMLHRElement;
+class HTMLHtmlElement;
+class HTMLIFrameElement;
+class HTMLImageElement;
+class HTMLInputElement;
+class HTMLLabelElement;
+class HTMLLegendElement;
+class HTMLLIElement;
+class HTMLLinkElement;
+class HTMLMapElement;
+class HTMLMarqueeElement;
+class HTMLMediaElement;
+class HTMLMenuElement;
+class HTMLMetaElement;
+class HTMLMeterElement;
+class HTMLModElement;
+class HTMLObjectElement;
+class HTMLOListElement;
+class HTMLOptGroupElement;
+class HTMLOptionElement;
+class HTMLOutputElement;
+class HTMLParagraphElement;
+class HTMLParamElement;
+class HTMLPictureElement;
+class HTMLPreElement;
+class HTMLProgressElement;
+class HTMLQuoteElement;
+class HTMLScriptElement;
+class HTMLSelectElement;
+class HTMLSlotElement;
+class HTMLSourceElement;
+class HTMLSpanElement;
+class HTMLStyleElement;
+class HTMLTableCaptionElement;
+class HTMLTableCellElement;
+class HTMLTableColElement;
+class HTMLTableElement;
+class HTMLTableRowElement;
+class HTMLTableSectionElement;
+class HTMLTemplateElement;
+class HTMLTextAreaElement;
+class HTMLTimeElement;
+class HTMLTitleElement;
+class HTMLTrackElement;
+class HTMLUListElement;
+class HTMLUnknownElement;
+class HTMLVideoElement;
+class ImageData;
+}
+
+namespace Web::HighResolutionTime {
+class Performance;
+}
+
+namespace Web::SVG {
+class SVGElement;
+class SVGGeometryElement;
+class SVGGraphicsElement;
+class SVGPathElement;
+class SVGSVGElement;
+}
+
+namespace Web::Layout {
+enum class LayoutMode;
+enum class PaintPhase;
+class BlockBox;
+class BlockFormattingContext;
+class Box;
+class ButtonBox;
+class CheckBox;
+class FormattingContext;
+class InitialContainingBlockBox;
+class InlineFormattingContext;
+class LineBox;
+class LineBoxFragment;
+class Node;
+class NodeWithStyle;
+class ReplacedBox;
+}
+
+namespace Web {
+class EventHandler;
+class EditEventHandler;
+class Frame;
+class FrameLoader;
+class InProcessWebView;
+class LoadRequest;
+class Origin;
+class OutOfProcessWebView;
+class Page;
+class PageClient;
+class PaintContext;
+class Resource;
+class ResourceLoader;
+class StackingContext;
+class XMLHttpRequest;
+}
+
+namespace Web::Bindings {
+
+class CanvasRenderingContext2DWrapper;
+class CharacterDataWrapper;
+class CommentWrapper;
+class DocumentFragmentWrapper;
+class DocumentTypeWrapper;
+class DocumentWrapper;
+class DOMImplementationWrapper;
+class ElementWrapper;
+class EventListenerWrapper;
+class EventTargetWrapper;
+class EventWrapper;
+class HTMLAnchorElementWrapper;
+class HTMLAreaElementWrapper;
+class HTMLAudioElementWrapper;
+class HTMLBaseElementWrapper;
+class HTMLBodyElementWrapper;
+class HTMLBRElementWrapper;
+class HTMLButtonElementWrapper;
+class HTMLCanvasElementWrapper;
+class HTMLDataElementWrapper;
+class HTMLDataListElementWrapper;
+class HTMLDetailsElementWrapper;
+class HTMLDialogElementWrapper;
+class HTMLDirectoryElementWrapper;
+class HTMLDivElementWrapper;
+class HTMLDListElementWrapper;
+class HTMLElementWrapper;
+class HTMLEmbedElementWrapper;
+class HTMLFieldSetElementWrapper;
+class HTMLFontElementWrapper;
+class HTMLFormElementWrapper;
+class HTMLFrameElementWrapper;
+class HTMLFrameSetElementWrapper;
+class HTMLHRElementWrapper;
+class HTMLHeadElementWrapper;
+class HTMLHeadingElementWrapper;
+class HTMLHtmlElementWrapper;
+class HTMLIFrameElementWrapper;
+class HTMLImageElementWrapper;
+class HTMLInputElementWrapper;
+class HTMLLabelElementWrapper;
+class HTMLLegendElementWrapper;
+class HTMLLIElementWrapper;
+class HTMLLinkElementWrapper;
+class HTMLMapElementWrapper;
+class HTMLMarqueeElementWrapper;
+class HTMLMediaElementWrapper;
+class HTMLMenuElementWrapper;
+class HTMLMetaElementWrapper;
+class HTMLMeterElementWrapper;
+class HTMLModElementWrapper;
+class HTMLObjectElementWrapper;
+class HTMLOListElementWrapper;
+class HTMLOptGroupElementWrapper;
+class HTMLOptionElementWrapper;
+class HTMLOutputElementWrapper;
+class HTMLParagraphElementWrapper;
+class HTMLParamElementWrapper;
+class HTMLPictureElementWrapper;
+class HTMLPreElementWrapper;
+class HTMLProgressElementWrapper;
+class HTMLQuoteElementWrapper;
+class HTMLScriptElementWrapper;
+class HTMLSelectElementWrapper;
+class HTMLSlotElementWrapper;
+class HTMLSourceElementWrapper;
+class HTMLSpanElementWrapper;
+class HTMLStyleElementWrapper;
+class HTMLTableCaptionElementWrapper;
+class HTMLTableCellElementWrapper;
+class HTMLTableColElementWrapper;
+class HTMLTableElementWrapper;
+class HTMLTableRowElementWrapper;
+class HTMLTableSectionElementWrapper;
+class HTMLTemplateElementWrapper;
+class HTMLTextAreaElementWrapper;
+class HTMLTimeElementWrapper;
+class HTMLTitleElementWrapper;
+class HTMLTrackElementWrapper;
+class HTMLUListElementWrapper;
+class HTMLUnknownElementWrapper;
+class HTMLVideoElementWrapper;
+class ImageDataWrapper;
+class LocationObject;
+class MouseEventWrapper;
+class NodeWrapper;
+class PerformanceWrapper;
+class ScriptExecutionContext;
+class SubmitEventWrapper;
+class SVGElementWrapper;
+class SVGGeometryElementWrapper;
+class SVGGraphicsElementWrapper;
+class SVGPathElementWrapper;
+class SVGSVGElementWrapper;
+class TextWrapper;
+class UIEventWrapper;
+class WindowObject;
+class Wrappable;
+class Wrapper;
+class XMLHttpRequestConstructor;
+class XMLHttpRequestPrototype;
+class XMLHttpRequestWrapper;
+class RangeConstructor;
+class RangePrototype;
+class RangeWrapper;
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/AttributeNames.cpp b/Userland/Libraries/LibWeb/HTML/AttributeNames.cpp
new file mode 100644
index 0000000000..e7a21e6b39
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/AttributeNames.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/AttributeNames.h>
+
+namespace Web {
+namespace HTML {
+namespace AttributeNames {
+
+#define __ENUMERATE_HTML_ATTRIBUTE(name) FlyString name;
+ENUMERATE_HTML_ATTRIBUTES
+#undef __ENUMERATE_HTML_ATTRIBUTE
+
+[[gnu::constructor]] static void initialize()
+{
+ static bool s_initialized = false;
+ if (s_initialized)
+ return;
+
+#define __ENUMERATE_HTML_ATTRIBUTE(name) \
+ name = #name;
+ ENUMERATE_HTML_ATTRIBUTES
+#undef __ENUMERATE_HTML_ATTRIBUTE
+
+ // NOTE: Special cases for C++ keywords.
+ class_ = "class";
+ for_ = "for";
+ default_ = "default";
+ char_ = "char";
+
+ // NOTE: Special cases for attributes with dashes in them.
+ accept_charset = "accept-charset";
+ http_equiv = "http-equiv";
+
+ s_initialized = true;
+}
+
+}
+}
+}
diff --git a/Userland/Libraries/LibWeb/HTML/AttributeNames.h b/Userland/Libraries/LibWeb/HTML/AttributeNames.h
new file mode 100644
index 0000000000..670ec9bdb4
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/AttributeNames.h
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/FlyString.h>
+
+namespace Web {
+namespace HTML {
+namespace AttributeNames {
+
+#define ENUMERATE_HTML_ATTRIBUTES \
+ __ENUMERATE_HTML_ATTRIBUTE(abbr) \
+ __ENUMERATE_HTML_ATTRIBUTE(accept) \
+ __ENUMERATE_HTML_ATTRIBUTE(accept_charset) \
+ __ENUMERATE_HTML_ATTRIBUTE(action) \
+ __ENUMERATE_HTML_ATTRIBUTE(align) \
+ __ENUMERATE_HTML_ATTRIBUTE(alink) \
+ __ENUMERATE_HTML_ATTRIBUTE(allow) \
+ __ENUMERATE_HTML_ATTRIBUTE(allowfullscreen) \
+ __ENUMERATE_HTML_ATTRIBUTE(alt) \
+ __ENUMERATE_HTML_ATTRIBUTE(archive) \
+ __ENUMERATE_HTML_ATTRIBUTE(async) \
+ __ENUMERATE_HTML_ATTRIBUTE(autoplay) \
+ __ENUMERATE_HTML_ATTRIBUTE(axis) \
+ __ENUMERATE_HTML_ATTRIBUTE(background) \
+ __ENUMERATE_HTML_ATTRIBUTE(behaviour) \
+ __ENUMERATE_HTML_ATTRIBUTE(bgcolor) \
+ __ENUMERATE_HTML_ATTRIBUTE(border) \
+ __ENUMERATE_HTML_ATTRIBUTE(cellpadding) \
+ __ENUMERATE_HTML_ATTRIBUTE(cellspacing) \
+ __ENUMERATE_HTML_ATTRIBUTE(char_) \
+ __ENUMERATE_HTML_ATTRIBUTE(charoff) \
+ __ENUMERATE_HTML_ATTRIBUTE(charset) \
+ __ENUMERATE_HTML_ATTRIBUTE(checked) \
+ __ENUMERATE_HTML_ATTRIBUTE(cite) \
+ __ENUMERATE_HTML_ATTRIBUTE(class_) \
+ __ENUMERATE_HTML_ATTRIBUTE(clear) \
+ __ENUMERATE_HTML_ATTRIBUTE(code) \
+ __ENUMERATE_HTML_ATTRIBUTE(codetype) \
+ __ENUMERATE_HTML_ATTRIBUTE(color) \
+ __ENUMERATE_HTML_ATTRIBUTE(cols) \
+ __ENUMERATE_HTML_ATTRIBUTE(colspan) \
+ __ENUMERATE_HTML_ATTRIBUTE(compact) \
+ __ENUMERATE_HTML_ATTRIBUTE(content) \
+ __ENUMERATE_HTML_ATTRIBUTE(contenteditable) \
+ __ENUMERATE_HTML_ATTRIBUTE(controls) \
+ __ENUMERATE_HTML_ATTRIBUTE(coords) \
+ __ENUMERATE_HTML_ATTRIBUTE(data) \
+ __ENUMERATE_HTML_ATTRIBUTE(datetime) \
+ __ENUMERATE_HTML_ATTRIBUTE(declare) \
+ __ENUMERATE_HTML_ATTRIBUTE(default_) \
+ __ENUMERATE_HTML_ATTRIBUTE(defer) \
+ __ENUMERATE_HTML_ATTRIBUTE(disabled) \
+ __ENUMERATE_HTML_ATTRIBUTE(download) \
+ __ENUMERATE_HTML_ATTRIBUTE(direction) \
+ __ENUMERATE_HTML_ATTRIBUTE(dirname) \
+ __ENUMERATE_HTML_ATTRIBUTE(event) \
+ __ENUMERATE_HTML_ATTRIBUTE(face) \
+ __ENUMERATE_HTML_ATTRIBUTE(for_) \
+ __ENUMERATE_HTML_ATTRIBUTE(formnovalidate) \
+ __ENUMERATE_HTML_ATTRIBUTE(formtarget) \
+ __ENUMERATE_HTML_ATTRIBUTE(frame) \
+ __ENUMERATE_HTML_ATTRIBUTE(frameborder) \
+ __ENUMERATE_HTML_ATTRIBUTE(headers) \
+ __ENUMERATE_HTML_ATTRIBUTE(height) \
+ __ENUMERATE_HTML_ATTRIBUTE(hidden) \
+ __ENUMERATE_HTML_ATTRIBUTE(href) \
+ __ENUMERATE_HTML_ATTRIBUTE(hreflang) \
+ __ENUMERATE_HTML_ATTRIBUTE(http_equiv) \
+ __ENUMERATE_HTML_ATTRIBUTE(id) \
+ __ENUMERATE_HTML_ATTRIBUTE(imagesizes) \
+ __ENUMERATE_HTML_ATTRIBUTE(imagesrcset) \
+ __ENUMERATE_HTML_ATTRIBUTE(integrity) \
+ __ENUMERATE_HTML_ATTRIBUTE(ismap) \
+ __ENUMERATE_HTML_ATTRIBUTE(label) \
+ __ENUMERATE_HTML_ATTRIBUTE(lang) \
+ __ENUMERATE_HTML_ATTRIBUTE(link) \
+ __ENUMERATE_HTML_ATTRIBUTE(longdesc) \
+ __ENUMERATE_HTML_ATTRIBUTE(loop) \
+ __ENUMERATE_HTML_ATTRIBUTE(max) \
+ __ENUMERATE_HTML_ATTRIBUTE(marginheight) \
+ __ENUMERATE_HTML_ATTRIBUTE(marginwidth) \
+ __ENUMERATE_HTML_ATTRIBUTE(media) \
+ __ENUMERATE_HTML_ATTRIBUTE(method) \
+ __ENUMERATE_HTML_ATTRIBUTE(min) \
+ __ENUMERATE_HTML_ATTRIBUTE(multiple) \
+ __ENUMERATE_HTML_ATTRIBUTE(name) \
+ __ENUMERATE_HTML_ATTRIBUTE(nohref) \
+ __ENUMERATE_HTML_ATTRIBUTE(nomodule) \
+ __ENUMERATE_HTML_ATTRIBUTE(noshade) \
+ __ENUMERATE_HTML_ATTRIBUTE(novalidate) \
+ __ENUMERATE_HTML_ATTRIBUTE(nowrap) \
+ __ENUMERATE_HTML_ATTRIBUTE(open) \
+ __ENUMERATE_HTML_ATTRIBUTE(pattern) \
+ __ENUMERATE_HTML_ATTRIBUTE(ping) \
+ __ENUMERATE_HTML_ATTRIBUTE(placeholder) \
+ __ENUMERATE_HTML_ATTRIBUTE(playsinline) \
+ __ENUMERATE_HTML_ATTRIBUTE(poster) \
+ __ENUMERATE_HTML_ATTRIBUTE(readonly) \
+ __ENUMERATE_HTML_ATTRIBUTE(rel) \
+ __ENUMERATE_HTML_ATTRIBUTE(required) \
+ __ENUMERATE_HTML_ATTRIBUTE(rev) \
+ __ENUMERATE_HTML_ATTRIBUTE(reversed) \
+ __ENUMERATE_HTML_ATTRIBUTE(rows) \
+ __ENUMERATE_HTML_ATTRIBUTE(rules) \
+ __ENUMERATE_HTML_ATTRIBUTE(scheme) \
+ __ENUMERATE_HTML_ATTRIBUTE(scrolling) \
+ __ENUMERATE_HTML_ATTRIBUTE(selected) \
+ __ENUMERATE_HTML_ATTRIBUTE(shape) \
+ __ENUMERATE_HTML_ATTRIBUTE(size) \
+ __ENUMERATE_HTML_ATTRIBUTE(sizes) \
+ __ENUMERATE_HTML_ATTRIBUTE(src) \
+ __ENUMERATE_HTML_ATTRIBUTE(srcdoc) \
+ __ENUMERATE_HTML_ATTRIBUTE(srclang) \
+ __ENUMERATE_HTML_ATTRIBUTE(srcset) \
+ __ENUMERATE_HTML_ATTRIBUTE(standby) \
+ __ENUMERATE_HTML_ATTRIBUTE(step) \
+ __ENUMERATE_HTML_ATTRIBUTE(style) \
+ __ENUMERATE_HTML_ATTRIBUTE(summary) \
+ __ENUMERATE_HTML_ATTRIBUTE(target) \
+ __ENUMERATE_HTML_ATTRIBUTE(text) \
+ __ENUMERATE_HTML_ATTRIBUTE(title) \
+ __ENUMERATE_HTML_ATTRIBUTE(type) \
+ __ENUMERATE_HTML_ATTRIBUTE(usemap) \
+ __ENUMERATE_HTML_ATTRIBUTE(value) \
+ __ENUMERATE_HTML_ATTRIBUTE(valuetype) \
+ __ENUMERATE_HTML_ATTRIBUTE(valign) \
+ __ENUMERATE_HTML_ATTRIBUTE(version) \
+ __ENUMERATE_HTML_ATTRIBUTE(vlink) \
+ __ENUMERATE_HTML_ATTRIBUTE(width) \
+ __ENUMERATE_HTML_ATTRIBUTE(wrap)
+
+#define __ENUMERATE_HTML_ATTRIBUTE(name) extern FlyString name;
+ENUMERATE_HTML_ATTRIBUTES
+#undef __ENUMERATE_HTML_ATTRIBUTE
+
+}
+}
+}
diff --git a/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp b/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp
new file mode 100644
index 0000000000..d029e77eb8
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/OwnPtr.h>
+#include <LibGfx/Painter.h>
+#include <LibWeb/Bindings/CanvasRenderingContext2DWrapper.h>
+#include <LibWeb/HTML/CanvasRenderingContext2D.h>
+#include <LibWeb/HTML/HTMLCanvasElement.h>
+#include <LibWeb/HTML/HTMLImageElement.h>
+#include <LibWeb/HTML/ImageData.h>
+
+namespace Web::HTML {
+
+CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement& element)
+ : m_element(element)
+{
+}
+
+CanvasRenderingContext2D::~CanvasRenderingContext2D()
+{
+}
+
+void CanvasRenderingContext2D::set_fill_style(String style)
+{
+ m_fill_style = Gfx::Color::from_string(style).value_or(Color::Black);
+}
+
+String CanvasRenderingContext2D::fill_style() const
+{
+ return m_fill_style.to_string();
+}
+
+void CanvasRenderingContext2D::fill_rect(float x, float y, float width, float height)
+{
+ auto painter = this->painter();
+ if (!painter)
+ return;
+
+ auto rect = m_transform.map(Gfx::FloatRect(x, y, width, height));
+ painter->fill_rect(enclosing_int_rect(rect), m_fill_style);
+ did_draw(rect);
+}
+
+void CanvasRenderingContext2D::set_stroke_style(String style)
+{
+ m_stroke_style = Gfx::Color::from_string(style).value_or(Color::Black);
+}
+
+String CanvasRenderingContext2D::stroke_style() const
+{
+ return m_fill_style.to_string();
+}
+
+void CanvasRenderingContext2D::stroke_rect(float x, float y, float width, float height)
+{
+ auto painter = this->painter();
+ if (!painter)
+ return;
+
+ auto rect = m_transform.map(Gfx::FloatRect(x, y, width, height));
+
+ auto top_left = m_transform.map(Gfx::FloatPoint(x, y)).to_type<int>();
+ auto top_right = m_transform.map(Gfx::FloatPoint(x + width - 1, y)).to_type<int>();
+ auto bottom_left = m_transform.map(Gfx::FloatPoint(x, y + height - 1)).to_type<int>();
+ auto bottom_right = m_transform.map(Gfx::FloatPoint(x + width - 1, y + height - 1)).to_type<int>();
+
+ painter->draw_line(top_left, top_right, m_stroke_style, m_line_width);
+ painter->draw_line(top_right, bottom_right, m_stroke_style, m_line_width);
+ painter->draw_line(bottom_right, bottom_left, m_stroke_style, m_line_width);
+ painter->draw_line(bottom_left, top_left, m_stroke_style, m_line_width);
+
+ did_draw(rect);
+}
+
+void CanvasRenderingContext2D::draw_image(const HTMLImageElement& image_element, float x, float y)
+{
+ if (!image_element.bitmap())
+ return;
+
+ auto painter = this->painter();
+ if (!painter)
+ return;
+
+ auto src_rect = image_element.bitmap()->rect();
+ Gfx::FloatRect dst_rect = { x, y, (float)image_element.bitmap()->width(), (float)image_element.bitmap()->height() };
+ auto rect = m_transform.map(dst_rect);
+
+ painter->draw_scaled_bitmap(enclosing_int_rect(rect), *image_element.bitmap(), src_rect);
+}
+
+void CanvasRenderingContext2D::scale(float sx, float sy)
+{
+ dbg() << "CanvasRenderingContext2D::scale(): " << sx << ", " << sy;
+ m_transform.scale(sx, sy);
+}
+
+void CanvasRenderingContext2D::translate(float tx, float ty)
+{
+ dbg() << "CanvasRenderingContext2D::translate(): " << tx << ", " << ty;
+ m_transform.translate(tx, ty);
+}
+
+void CanvasRenderingContext2D::rotate(float radians)
+{
+ dbg() << "CanvasRenderingContext2D::rotate(): " << radians;
+ m_transform.rotate_radians(radians);
+}
+
+void CanvasRenderingContext2D::did_draw(const Gfx::FloatRect&)
+{
+ // FIXME: Make use of the rect to reduce the invalidated area when possible.
+ if (!m_element)
+ return;
+ if (!m_element->layout_node())
+ return;
+ m_element->layout_node()->set_needs_display();
+}
+
+OwnPtr<Gfx::Painter> CanvasRenderingContext2D::painter()
+{
+ if (!m_element)
+ return {};
+
+ if (!m_element->bitmap()) {
+ if (!m_element->create_bitmap())
+ return {};
+ }
+
+ return make<Gfx::Painter>(*m_element->bitmap());
+}
+
+void CanvasRenderingContext2D::begin_path()
+{
+ m_path = Gfx::Path();
+}
+
+void CanvasRenderingContext2D::close_path()
+{
+ m_path.close();
+}
+
+void CanvasRenderingContext2D::move_to(float x, float y)
+{
+ m_path.move_to({ x, y });
+}
+
+void CanvasRenderingContext2D::line_to(float x, float y)
+{
+ m_path.line_to({ x, y });
+}
+
+void CanvasRenderingContext2D::quadratic_curve_to(float cx, float cy, float x, float y)
+{
+ m_path.quadratic_bezier_curve_to({ cx, cy }, { x, y });
+}
+
+void CanvasRenderingContext2D::stroke()
+{
+ auto painter = this->painter();
+ if (!painter)
+ return;
+
+ painter->stroke_path(m_path, m_stroke_style, m_line_width);
+}
+
+void CanvasRenderingContext2D::fill(Gfx::Painter::WindingRule winding)
+{
+ auto painter = this->painter();
+ if (!painter)
+ return;
+
+ auto path = m_path;
+ path.close_all_subpaths();
+ painter->fill_path(path, m_fill_style, winding);
+}
+
+void CanvasRenderingContext2D::fill(const String& fill_rule)
+{
+ if (fill_rule == "evenodd")
+ return fill(Gfx::Painter::WindingRule::EvenOdd);
+ return fill(Gfx::Painter::WindingRule::Nonzero);
+}
+
+RefPtr<ImageData> CanvasRenderingContext2D::create_image_data(int width, int height) const
+{
+ if (!wrapper()) {
+ dbgln("Hmm! Attempted to create ImageData for wrapper-less CRC2D.");
+ return {};
+ }
+ return ImageData::create_with_size(wrapper()->global_object(), width, height);
+}
+
+void CanvasRenderingContext2D::put_image_data(const ImageData& image_data, float x, float y)
+{
+ auto painter = this->painter();
+ if (!painter)
+ return;
+
+ painter->blit(Gfx::IntPoint(x, y), image_data.bitmap(), image_data.bitmap().rect());
+
+ did_draw(Gfx::FloatRect(x, y, image_data.width(), image_data.height()));
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h b/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h
new file mode 100644
index 0000000000..58cccfc6e6
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/RefCounted.h>
+#include <LibGfx/AffineTransform.h>
+#include <LibGfx/Color.h>
+#include <LibGfx/Forward.h>
+#include <LibGfx/Painter.h>
+#include <LibGfx/Path.h>
+#include <LibWeb/Bindings/Wrappable.h>
+
+namespace Web::HTML {
+
+class CanvasRenderingContext2D
+ : public RefCounted<CanvasRenderingContext2D>
+ , public Bindings::Wrappable {
+
+ AK_MAKE_NONCOPYABLE(CanvasRenderingContext2D);
+ AK_MAKE_NONMOVABLE(CanvasRenderingContext2D);
+
+public:
+ using WrapperType = Bindings::CanvasRenderingContext2DWrapper;
+
+ static NonnullRefPtr<CanvasRenderingContext2D> create(HTMLCanvasElement& element) { return adopt(*new CanvasRenderingContext2D(element)); }
+ ~CanvasRenderingContext2D();
+
+ void set_fill_style(String);
+ String fill_style() const;
+
+ void set_stroke_style(String);
+ String stroke_style() const;
+
+ void fill_rect(float x, float y, float width, float height);
+ void stroke_rect(float x, float y, float width, float height);
+
+ void draw_image(const HTMLImageElement&, float x, float y);
+
+ void scale(float sx, float sy);
+ void translate(float x, float y);
+ void rotate(float degrees);
+
+ void set_line_width(float line_width) { m_line_width = line_width; }
+ float line_width() const { return m_line_width; }
+
+ void begin_path();
+ void close_path();
+ void move_to(float x, float y);
+ void line_to(float x, float y);
+ void quadratic_curve_to(float cx, float cy, float x, float y);
+ void stroke();
+
+ // FIXME: We should only have one fill(), really. Fix the wrapper generator!
+ void fill(Gfx::Painter::WindingRule);
+ void fill(const String& fill_rule);
+
+ RefPtr<ImageData> create_image_data(int width, int height) const;
+ void put_image_data(const ImageData&, float x, float y);
+
+ HTMLCanvasElement* canvas() { return m_element; }
+
+private:
+ explicit CanvasRenderingContext2D(HTMLCanvasElement&);
+
+ void did_draw(const Gfx::FloatRect&);
+
+ OwnPtr<Gfx::Painter> painter();
+
+ WeakPtr<HTMLCanvasElement> m_element;
+
+ Gfx::AffineTransform m_transform;
+ Gfx::Color m_fill_style;
+ Gfx::Color m_stroke_style;
+ float m_line_width { 1 };
+
+ Gfx::Path m_path;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.idl b/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.idl
new file mode 100644
index 0000000000..bcfa544c67
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.idl
@@ -0,0 +1,29 @@
+interface CanvasRenderingContext2D {
+
+ undefined fillRect(double x, double y, double w, double h);
+ undefined strokeRect(double x, double y, double w, double h);
+
+ undefined scale(double x, double y);
+ undefined translate(double x, double y);
+ undefined rotate(double radians);
+
+ undefined beginPath();
+ undefined closePath();
+ undefined fill(DOMString fillRule);
+ undefined stroke();
+ undefined moveTo(double x, double y);
+ undefined lineTo(double x, double y);
+ undefined quadraticCurveTo(double cpx, double cpy, double x, double y);
+
+ undefined drawImage(HTMLImageElement image, double dx, double dy);
+
+ attribute DOMString fillStyle;
+ attribute DOMString strokeStyle;
+ attribute double lineWidth;
+
+ ImageData createImageData(double sw, double sh);
+ undefined putImageData(ImageData imagedata, double dx, double dy);
+
+ readonly attribute HTMLCanvasElement canvas;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/EventNames.cpp b/Userland/Libraries/LibWeb/HTML/EventNames.cpp
new file mode 100644
index 0000000000..6856aa2d03
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/EventNames.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020, the SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/EventNames.h>
+
+namespace Web::HTML::EventNames {
+
+#define __ENUMERATE_HTML_EVENT(name) FlyString name;
+ENUMERATE_HTML_EVENTS
+#undef __ENUMERATE_HTML_EVENT
+
+[[gnu::constructor]] static void initialize()
+{
+ static bool s_initialized = false;
+ if (s_initialized)
+ return;
+
+#define __ENUMERATE_HTML_EVENT(name) \
+ name = #name;
+ ENUMERATE_HTML_EVENTS
+#undef __ENUMERATE_HTML_EVENT
+
+ s_initialized = true;
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/EventNames.h b/Userland/Libraries/LibWeb/HTML/EventNames.h
new file mode 100644
index 0000000000..d36056ef01
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/EventNames.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2020, the SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/FlyString.h>
+
+namespace Web::HTML::EventNames {
+
+// FIXME: Add media events https://html.spec.whatwg.org/multipage/media.html#mediaevents
+// FIXME: Add app cache events https://html.spec.whatwg.org/multipage/offline.html#appcacheevents
+// FIXME: Add drag and drop events https://html.spec.whatwg.org/multipage/dnd.html#dndevents
+
+#define ENUMERATE_HTML_EVENTS \
+ __ENUMERATE_HTML_EVENT(abort) \
+ __ENUMERATE_HTML_EVENT(DOMContentLoaded) \
+ __ENUMERATE_HTML_EVENT(afterprint) \
+ __ENUMERATE_HTML_EVENT(beforeprint) \
+ __ENUMERATE_HTML_EVENT(beforeunload) \
+ __ENUMERATE_HTML_EVENT(blur) \
+ __ENUMERATE_HTML_EVENT(cancel) \
+ __ENUMERATE_HTML_EVENT(change) \
+ __ENUMERATE_HTML_EVENT(click) \
+ __ENUMERATE_HTML_EVENT(close) \
+ __ENUMERATE_HTML_EVENT(connect) \
+ __ENUMERATE_HTML_EVENT(contextmenu) \
+ __ENUMERATE_HTML_EVENT(copy) \
+ __ENUMERATE_HTML_EVENT(cut) \
+ __ENUMERATE_HTML_EVENT(error) \
+ __ENUMERATE_HTML_EVENT(focus) \
+ __ENUMERATE_HTML_EVENT(formdata) \
+ __ENUMERATE_HTML_EVENT(hashchange) \
+ __ENUMERATE_HTML_EVENT(input) \
+ __ENUMERATE_HTML_EVENT(invalid) \
+ __ENUMERATE_HTML_EVENT(languagechange) \
+ __ENUMERATE_HTML_EVENT(load) \
+ __ENUMERATE_HTML_EVENT(message) \
+ __ENUMERATE_HTML_EVENT(messageerror) \
+ __ENUMERATE_HTML_EVENT(offline) \
+ __ENUMERATE_HTML_EVENT(online) \
+ __ENUMERATE_HTML_EVENT(open) \
+ __ENUMERATE_HTML_EVENT(pagehide) \
+ __ENUMERATE_HTML_EVENT(pageshow) \
+ __ENUMERATE_HTML_EVENT(paste) \
+ __ENUMERATE_HTML_EVENT(popstate) \
+ __ENUMERATE_HTML_EVENT(readystatechange) \
+ __ENUMERATE_HTML_EVENT(rejectionhandled) \
+ __ENUMERATE_HTML_EVENT(reset) \
+ __ENUMERATE_HTML_EVENT(securitypolicyviolation) \
+ __ENUMERATE_HTML_EVENT(select) \
+ __ENUMERATE_HTML_EVENT(slotchange) \
+ __ENUMERATE_HTML_EVENT(storage) \
+ __ENUMERATE_HTML_EVENT(submit) \
+ __ENUMERATE_HTML_EVENT(toggle) \
+ __ENUMERATE_HTML_EVENT(unhandledrejection) \
+ __ENUMERATE_HTML_EVENT(unload)
+
+#define __ENUMERATE_HTML_EVENT(name) extern FlyString name;
+ENUMERATE_HTML_EVENTS
+#undef __ENUMERATE_HTML_EVENT
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLAnchorElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLAnchorElement.cpp
new file mode 100644
index 0000000000..f79debf4cc
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLAnchorElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLAnchorElement.h>
+
+namespace Web::HTML {
+
+HTMLAnchorElement::HTMLAnchorElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLAnchorElement::~HTMLAnchorElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLAnchorElement.h b/Userland/Libraries/LibWeb/HTML/HTMLAnchorElement.h
new file mode 100644
index 0000000000..02d7349116
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLAnchorElement.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLAnchorElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLAnchorElementWrapper;
+
+ HTMLAnchorElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLAnchorElement() override;
+
+ String href() const { return attribute(HTML::AttributeNames::href); }
+ String target() const { return attribute(HTML::AttributeNames::target); }
+
+ virtual bool is_focusable() const override { return has_attribute(HTML::AttributeNames::href); }
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLAnchorElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLAnchorElement.idl
new file mode 100644
index 0000000000..829b9039ee
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLAnchorElement.idl
@@ -0,0 +1,16 @@
+interface HTMLAnchorElement : HTMLElement {
+
+ [Reflect] attribute DOMString target;
+ [Reflect] attribute DOMString download;
+ [Reflect] attribute DOMString ping;
+ [Reflect] attribute DOMString rel;
+ [Reflect] attribute DOMString hreflang;
+ [Reflect] attribute DOMString type;
+
+ [Reflect] attribute DOMString coords;
+ [Reflect] attribute DOMString charset;
+ [Reflect] attribute DOMString name;
+ [Reflect] attribute DOMString rev;
+ [Reflect] attribute DOMString shape;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLAreaElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLAreaElement.cpp
new file mode 100644
index 0000000000..fe9c9a2147
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLAreaElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLAreaElement.h>
+
+namespace Web::HTML {
+
+HTMLAreaElement::HTMLAreaElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLAreaElement::~HTMLAreaElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLAreaElement.h b/Userland/Libraries/LibWeb/HTML/HTMLAreaElement.h
new file mode 100644
index 0000000000..29fa82ffa0
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLAreaElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLAreaElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLAreaElementWrapper;
+
+ HTMLAreaElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLAreaElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLAreaElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLAreaElement.idl
new file mode 100644
index 0000000000..7cd0058305
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLAreaElement.idl
@@ -0,0 +1,5 @@
+interface HTMLAreaElement : HTMLElement {
+
+ [Reflect=nohref] attribute boolean noHref;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLAudioElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLAudioElement.cpp
new file mode 100644
index 0000000000..2653ebf944
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLAudioElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLAudioElement.h>
+
+namespace Web::HTML {
+
+HTMLAudioElement::HTMLAudioElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLMediaElement(document, qualified_name)
+{
+}
+
+HTMLAudioElement::~HTMLAudioElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLAudioElement.h b/Userland/Libraries/LibWeb/HTML/HTMLAudioElement.h
new file mode 100644
index 0000000000..ce6a43b021
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLAudioElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLMediaElement.h>
+
+namespace Web::HTML {
+
+class HTMLAudioElement final : public HTMLMediaElement {
+public:
+ using WrapperType = Bindings::HTMLAudioElementWrapper;
+
+ HTMLAudioElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLAudioElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLAudioElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLAudioElement.idl
new file mode 100644
index 0000000000..c56ca47938
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLAudioElement.idl
@@ -0,0 +1,5 @@
+interface HTMLAudioElement : HTMLMediaElement {
+
+
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLBRElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLBRElement.cpp
new file mode 100644
index 0000000000..d3a0bc1b9e
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLBRElement.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLBRElement.h>
+#include <LibWeb/Layout/BreakNode.h>
+
+namespace Web::HTML {
+
+HTMLBRElement::HTMLBRElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLBRElement::~HTMLBRElement()
+{
+}
+
+RefPtr<Layout::Node> HTMLBRElement::create_layout_node()
+{
+ return adopt(*new Layout::BreakNode(document(), *this));
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLBRElement.h b/Userland/Libraries/LibWeb/HTML/HTMLBRElement.h
new file mode 100644
index 0000000000..5e453c6ecb
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLBRElement.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLBRElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLBRElementWrapper;
+
+ HTMLBRElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLBRElement() override;
+
+ virtual RefPtr<Layout::Node> create_layout_node() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLBRElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLBRElement.idl
new file mode 100644
index 0000000000..a71383c890
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLBRElement.idl
@@ -0,0 +1,5 @@
+interface HTMLBRElement : HTMLElement {
+
+ [Reflect] attribute DOMString clear;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLBaseElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLBaseElement.cpp
new file mode 100644
index 0000000000..975d0bddda
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLBaseElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLBaseElement.h>
+
+namespace Web::HTML {
+
+HTMLBaseElement::HTMLBaseElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLBaseElement::~HTMLBaseElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLBaseElement.h b/Userland/Libraries/LibWeb/HTML/HTMLBaseElement.h
new file mode 100644
index 0000000000..ce2be21e0d
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLBaseElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLBaseElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLBaseElementWrapper;
+
+ HTMLBaseElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLBaseElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLBaseElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLBaseElement.idl
new file mode 100644
index 0000000000..868e4a84a4
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLBaseElement.idl
@@ -0,0 +1,5 @@
+interface HTMLBaseElement : HTMLElement {
+
+ [Reflect] attribute DOMString target;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLBlinkElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLBlinkElement.cpp
new file mode 100644
index 0000000000..911a8940bd
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLBlinkElement.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibCore/Timer.h>
+#include <LibWeb/CSS/StyleProperties.h>
+#include <LibWeb/CSS/StyleValue.h>
+#include <LibWeb/HTML/HTMLBlinkElement.h>
+#include <LibWeb/Layout/Node.h>
+
+namespace Web::HTML {
+
+HTMLBlinkElement::HTMLBlinkElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+ , m_timer(Core::Timer::construct())
+{
+ m_timer->set_interval(500);
+ m_timer->on_timeout = [this] { blink(); };
+ m_timer->start();
+}
+
+HTMLBlinkElement::~HTMLBlinkElement()
+{
+}
+
+void HTMLBlinkElement::blink()
+{
+ if (!layout_node())
+ return;
+
+ layout_node()->set_visible(!layout_node()->is_visible());
+ layout_node()->set_needs_display();
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLBlinkElement.h b/Userland/Libraries/LibWeb/HTML/HTMLBlinkElement.h
new file mode 100644
index 0000000000..e4c4c3421f
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLBlinkElement.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibCore/Forward.h>
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLBlinkElement final : public HTMLElement {
+public:
+ HTMLBlinkElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLBlinkElement() override;
+
+private:
+ void blink();
+
+ NonnullRefPtr<Core::Timer> m_timer;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLBodyElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLBodyElement.cpp
new file mode 100644
index 0000000000..e6b7272f8d
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLBodyElement.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/CSS/StyleProperties.h>
+#include <LibWeb/CSS/StyleValue.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/HTML/HTMLBodyElement.h>
+
+namespace Web::HTML {
+
+HTMLBodyElement::HTMLBodyElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLBodyElement::~HTMLBodyElement()
+{
+}
+
+void HTMLBodyElement::apply_presentational_hints(CSS::StyleProperties& style) const
+{
+ for_each_attribute([&](auto& name, auto& value) {
+ if (name.equals_ignoring_case("bgcolor")) {
+ auto color = Color::from_string(value);
+ if (color.has_value())
+ style.set_property(CSS::PropertyID::BackgroundColor, CSS::ColorStyleValue::create(color.value()));
+ } else if (name.equals_ignoring_case("text")) {
+ auto color = Color::from_string(value);
+ if (color.has_value())
+ style.set_property(CSS::PropertyID::Color, CSS::ColorStyleValue::create(color.value()));
+ } else if (name.equals_ignoring_case("background")) {
+ ASSERT(m_background_style_value);
+ style.set_property(CSS::PropertyID::BackgroundImage, *m_background_style_value);
+ }
+ });
+}
+
+void HTMLBodyElement::parse_attribute(const FlyString& name, const String& value)
+{
+ HTMLElement::parse_attribute(name, value);
+ if (name.equals_ignoring_case("link")) {
+ auto color = Color::from_string(value);
+ if (color.has_value())
+ document().set_link_color(color.value());
+ } else if (name.equals_ignoring_case("alink")) {
+ auto color = Color::from_string(value);
+ if (color.has_value())
+ document().set_active_link_color(color.value());
+ } else if (name.equals_ignoring_case("vlink")) {
+ auto color = Color::from_string(value);
+ if (color.has_value())
+ document().set_visited_link_color(color.value());
+ } else if (name.equals_ignoring_case("background")) {
+ m_background_style_value = CSS::ImageStyleValue::create(document().complete_url(value), const_cast<DOM::Document&>(document()));
+ }
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLBodyElement.h b/Userland/Libraries/LibWeb/HTML/HTMLBodyElement.h
new file mode 100644
index 0000000000..d8d41cbb7b
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLBodyElement.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLBodyElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLBodyElementWrapper;
+
+ HTMLBodyElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLBodyElement() override;
+
+ virtual void parse_attribute(const FlyString&, const String&) override;
+ virtual void apply_presentational_hints(CSS::StyleProperties&) const override;
+
+private:
+ RefPtr<CSS::ImageStyleValue> m_background_style_value;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLBodyElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLBodyElement.idl
new file mode 100644
index 0000000000..dcd0b4d753
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLBodyElement.idl
@@ -0,0 +1,10 @@
+interface HTMLBodyElement : HTMLElement {
+
+ [LegacyNullToEmptyString, Reflect] attribute DOMString text;
+ [LegacyNullToEmptyString, Reflect] attribute DOMString link;
+ [LegacyNullToEmptyString, Reflect=vlink] attribute DOMString vLink;
+ [LegacyNullToEmptyString, Reflect=alink] attribute DOMString aLink;
+ [LegacyNullToEmptyString, Reflect=bgcolor] attribute DOMString bgColor;
+ [Reflect] attribute DOMString background;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLButtonElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLButtonElement.cpp
new file mode 100644
index 0000000000..ec4925965b
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLButtonElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLButtonElement.h>
+
+namespace Web::HTML {
+
+HTMLButtonElement::HTMLButtonElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLButtonElement::~HTMLButtonElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLButtonElement.h b/Userland/Libraries/LibWeb/HTML/HTMLButtonElement.h
new file mode 100644
index 0000000000..234656a97d
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLButtonElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLButtonElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLButtonElementWrapper;
+
+ HTMLButtonElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLButtonElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLButtonElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLButtonElement.idl
new file mode 100644
index 0000000000..7e50852e64
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLButtonElement.idl
@@ -0,0 +1,8 @@
+interface HTMLButtonElement : HTMLElement {
+
+ [Reflect=formnovalidate] attribute boolean formNoValidate;
+ [Reflect=formtarget] attribute DOMString formTarget;
+ [Reflect] attribute DOMString name;
+ [Reflect] attribute DOMString value;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp
new file mode 100644
index 0000000000..d015225460
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/Checked.h>
+#include <LibGfx/Bitmap.h>
+#include <LibWeb/CSS/StyleResolver.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/HTML/CanvasRenderingContext2D.h>
+#include <LibWeb/HTML/HTMLCanvasElement.h>
+#include <LibWeb/Layout/CanvasBox.h>
+
+namespace Web::HTML {
+
+static constexpr auto max_canvas_area = 16384 * 16384;
+
+HTMLCanvasElement::HTMLCanvasElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLCanvasElement::~HTMLCanvasElement()
+{
+}
+
+unsigned HTMLCanvasElement::width() const
+{
+ return attribute(HTML::AttributeNames::width).to_uint().value_or(300);
+}
+
+unsigned HTMLCanvasElement::height() const
+{
+ return attribute(HTML::AttributeNames::height).to_uint().value_or(150);
+}
+
+RefPtr<Layout::Node> HTMLCanvasElement::create_layout_node()
+{
+ auto style = document().style_resolver().resolve_style(*this);
+ if (style->display() == CSS::Display::None)
+ return nullptr;
+ return adopt(*new Layout::CanvasBox(document(), *this, move(style)));
+}
+
+CanvasRenderingContext2D* HTMLCanvasElement::get_context(String type)
+{
+ ASSERT(type == "2d");
+ if (!m_context)
+ m_context = CanvasRenderingContext2D::create(*this);
+ return m_context;
+}
+
+static Gfx::IntSize bitmap_size_for_canvas(const HTMLCanvasElement& canvas)
+{
+ auto width = canvas.width();
+ auto height = canvas.height();
+
+ Checked<size_t> area = width;
+ area *= height;
+
+ if (area.has_overflow()) {
+ dbgln("Refusing to create {}x{} canvas (overflow)", width, height);
+ return {};
+ }
+ if (area.value() > max_canvas_area) {
+ dbgln("Refusing to create {}x{} canvas (exceeds maximum size)", width, height);
+ return {};
+ }
+ return Gfx::IntSize(width, height);
+}
+
+bool HTMLCanvasElement::create_bitmap()
+{
+ auto size = bitmap_size_for_canvas(*this);
+ if (size.is_empty()) {
+ m_bitmap = nullptr;
+ return false;
+ }
+ if (!m_bitmap || m_bitmap->size() != size)
+ m_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA32, size);
+ return m_bitmap;
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.h b/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.h
new file mode 100644
index 0000000000..9616af98b7
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/ByteBuffer.h>
+#include <LibGfx/Forward.h>
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLCanvasElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLCanvasElementWrapper;
+
+ HTMLCanvasElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLCanvasElement() override;
+
+ const Gfx::Bitmap* bitmap() const { return m_bitmap; }
+ Gfx::Bitmap* bitmap() { return m_bitmap; }
+ bool create_bitmap();
+
+ CanvasRenderingContext2D* get_context(String type);
+
+ unsigned width() const;
+ unsigned height() const;
+
+private:
+ virtual RefPtr<Layout::Node> create_layout_node() override;
+
+ RefPtr<Gfx::Bitmap> m_bitmap;
+ RefPtr<CanvasRenderingContext2D> m_context;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.idl
new file mode 100644
index 0000000000..5a04895fb4
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.idl
@@ -0,0 +1,7 @@
+interface HTMLCanvasElement : HTMLElement {
+
+ CanvasRenderingContext2D? getContext(DOMString contextId);
+ readonly attribute unsigned long width;
+ readonly attribute unsigned long height;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDListElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLDListElement.cpp
new file mode 100644
index 0000000000..ce68845ca6
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLDListElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLDListElement.h>
+
+namespace Web::HTML {
+
+HTMLDListElement::HTMLDListElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLDListElement::~HTMLDListElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDListElement.h b/Userland/Libraries/LibWeb/HTML/HTMLDListElement.h
new file mode 100644
index 0000000000..cf0fe2a9c2
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLDListElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLDListElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLDListElementWrapper;
+
+ HTMLDListElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLDListElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDListElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLDListElement.idl
new file mode 100644
index 0000000000..16b3b881ac
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLDListElement.idl
@@ -0,0 +1,5 @@
+interface HTMLDListElement : HTMLElement {
+
+ [Reflect] attribute boolean compact;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDataElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLDataElement.cpp
new file mode 100644
index 0000000000..1a23f48fd9
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLDataElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLDataElement.h>
+
+namespace Web::HTML {
+
+HTMLDataElement::HTMLDataElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLDataElement::~HTMLDataElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDataElement.h b/Userland/Libraries/LibWeb/HTML/HTMLDataElement.h
new file mode 100644
index 0000000000..21918958cd
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLDataElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLDataElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLDataElementWrapper;
+
+ HTMLDataElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLDataElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDataElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLDataElement.idl
new file mode 100644
index 0000000000..4edce306fa
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLDataElement.idl
@@ -0,0 +1,5 @@
+interface HTMLDataElement : HTMLElement {
+
+ [Reflect] attribute DOMString value;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDataListElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLDataListElement.cpp
new file mode 100644
index 0000000000..32bae03035
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLDataListElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLDataListElement.h>
+
+namespace Web::HTML {
+
+HTMLDataListElement::HTMLDataListElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLDataListElement::~HTMLDataListElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDataListElement.h b/Userland/Libraries/LibWeb/HTML/HTMLDataListElement.h
new file mode 100644
index 0000000000..85bc7ccf7d
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLDataListElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLDataListElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLDataListElementWrapper;
+
+ HTMLDataListElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLDataListElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDataListElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLDataListElement.idl
new file mode 100644
index 0000000000..c9383448e4
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLDataListElement.idl
@@ -0,0 +1,5 @@
+interface HTMLDataListElement : HTMLElement {
+
+
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDetailsElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLDetailsElement.cpp
new file mode 100644
index 0000000000..abcdf0801a
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLDetailsElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLDetailsElement.h>
+
+namespace Web::HTML {
+
+HTMLDetailsElement::HTMLDetailsElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLDetailsElement::~HTMLDetailsElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDetailsElement.h b/Userland/Libraries/LibWeb/HTML/HTMLDetailsElement.h
new file mode 100644
index 0000000000..2bdbf673f8
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLDetailsElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLDetailsElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLDetailsElementWrapper;
+
+ HTMLDetailsElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLDetailsElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDetailsElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLDetailsElement.idl
new file mode 100644
index 0000000000..2b0daa6c7f
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLDetailsElement.idl
@@ -0,0 +1,5 @@
+interface HTMLDetailsElement : HTMLElement {
+
+ [Reflect] attribute boolean open;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDialogElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLDialogElement.cpp
new file mode 100644
index 0000000000..7437ca2261
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLDialogElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLDialogElement.h>
+
+namespace Web::HTML {
+
+HTMLDialogElement::HTMLDialogElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLDialogElement::~HTMLDialogElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDialogElement.h b/Userland/Libraries/LibWeb/HTML/HTMLDialogElement.h
new file mode 100644
index 0000000000..133f65ddbd
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLDialogElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLDialogElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLDialogElementWrapper;
+
+ HTMLDialogElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLDialogElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDialogElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLDialogElement.idl
new file mode 100644
index 0000000000..0d37547ed3
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLDialogElement.idl
@@ -0,0 +1,5 @@
+interface HTMLDialogElement : HTMLElement {
+
+ [Reflect] attribute boolean open;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDirectoryElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLDirectoryElement.cpp
new file mode 100644
index 0000000000..53fee8b14a
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLDirectoryElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLDirectoryElement.h>
+
+namespace Web::HTML {
+
+HTMLDirectoryElement::HTMLDirectoryElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLDirectoryElement::~HTMLDirectoryElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDirectoryElement.h b/Userland/Libraries/LibWeb/HTML/HTMLDirectoryElement.h
new file mode 100644
index 0000000000..39a9f5ae94
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLDirectoryElement.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+// NOTE: This element is marked as obsolete, but is still listed as required by the specification.
+class HTMLDirectoryElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLDirectoryElementWrapper;
+
+ HTMLDirectoryElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLDirectoryElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDirectoryElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLDirectoryElement.idl
new file mode 100644
index 0000000000..9adb6ad670
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLDirectoryElement.idl
@@ -0,0 +1,5 @@
+interface HTMLDirectoryElement : HTMLElement {
+
+ [Reflect] attribute boolean compact;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDivElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLDivElement.cpp
new file mode 100644
index 0000000000..7392f8ec80
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLDivElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLDivElement.h>
+
+namespace Web::HTML {
+
+HTMLDivElement::HTMLDivElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLDivElement::~HTMLDivElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDivElement.h b/Userland/Libraries/LibWeb/HTML/HTMLDivElement.h
new file mode 100644
index 0000000000..eade002544
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLDivElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLDivElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLDivElementWrapper;
+
+ HTMLDivElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLDivElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDivElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLDivElement.idl
new file mode 100644
index 0000000000..bb5cfba9b4
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLDivElement.idl
@@ -0,0 +1,5 @@
+interface HTMLDivElement : HTMLElement {
+
+ [Reflect] attribute DOMString align;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp
new file mode 100644
index 0000000000..eb5356a594
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/StringBuilder.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/HTML/HTMLAnchorElement.h>
+#include <LibWeb/HTML/HTMLElement.h>
+#include <LibWeb/Layout/BreakNode.h>
+#include <LibWeb/Layout/TextNode.h>
+
+namespace Web::HTML {
+
+HTMLElement::HTMLElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : Element(document, qualified_name)
+{
+}
+
+HTMLElement::~HTMLElement()
+{
+}
+
+HTMLElement::ContentEditableState HTMLElement::content_editable_state() const
+{
+ auto contenteditable = attribute(HTML::AttributeNames::contenteditable);
+ // "true" and the empty string map to the "true" state.
+ if ((!contenteditable.is_null() && contenteditable.is_empty()) || contenteditable.equals_ignoring_case("true"))
+ return ContentEditableState::True;
+ // "false" maps to the "false" state.
+ if (contenteditable.equals_ignoring_case("false"))
+ return ContentEditableState::False;
+ // "inherit", an invalid value, and a missing value all map to the "inherit" state.
+ return ContentEditableState::Inherit;
+}
+
+bool HTMLElement::is_editable() const
+{
+ switch (content_editable_state()) {
+ case ContentEditableState::True:
+ return true;
+ case ContentEditableState::False:
+ return false;
+ case ContentEditableState::Inherit:
+ return parent() && parent()->is_editable();
+ default:
+ ASSERT_NOT_REACHED();
+ }
+}
+
+String HTMLElement::content_editable() const
+{
+ switch (content_editable_state()) {
+ case ContentEditableState::True:
+ return "true";
+ case ContentEditableState::False:
+ return "false";
+ case ContentEditableState::Inherit:
+ return "inherit";
+ default:
+ ASSERT_NOT_REACHED();
+ }
+}
+
+void HTMLElement::set_content_editable(const String& content_editable)
+{
+ if (content_editable.equals_ignoring_case("inherit")) {
+ remove_attribute(HTML::AttributeNames::contenteditable);
+ return;
+ }
+ if (content_editable.equals_ignoring_case("true")) {
+ set_attribute(HTML::AttributeNames::contenteditable, "true");
+ return;
+ }
+ if (content_editable.equals_ignoring_case("false")) {
+ set_attribute(HTML::AttributeNames::contenteditable, "false");
+ return;
+ }
+ // FIXME: otherwise the attribute setter must throw a "SyntaxError" DOMException.
+}
+
+void HTMLElement::set_inner_text(StringView text)
+{
+ remove_all_children();
+ append_child(document().create_text_node(text));
+
+ set_needs_style_update(true);
+ document().invalidate_layout();
+}
+
+String HTMLElement::inner_text()
+{
+ StringBuilder builder;
+
+ // innerText for element being rendered takes visibility into account, so force a layout and then walk the layout tree.
+ document().update_layout();
+ if (!layout_node())
+ return text_content();
+
+ Function<void(const Layout::Node&)> recurse = [&](auto& node) {
+ for (auto* child = node.first_child(); child; child = child->next_sibling()) {
+ if (is<Layout::TextNode>(child))
+ builder.append(downcast<Layout::TextNode>(*child).text_for_rendering());
+ if (is<Layout::BreakNode>(child))
+ builder.append('\n');
+ recurse(*child);
+ }
+ };
+ recurse(*layout_node());
+
+ return builder.to_string();
+}
+
+bool HTMLElement::cannot_navigate() const
+{
+ // FIXME: Return true if element's node document is not fully active
+ return !is<HTML::HTMLAnchorElement>(this) && !is_connected();
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLElement.h b/Userland/Libraries/LibWeb/HTML/HTMLElement.h
new file mode 100644
index 0000000000..ee602a62b5
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLElement.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/DOM/Element.h>
+
+namespace Web::HTML {
+
+class HTMLElement : public DOM::Element {
+public:
+ using WrapperType = Bindings::HTMLElementWrapper;
+
+ HTMLElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLElement() override;
+
+ String title() const { return attribute(HTML::AttributeNames::title); }
+
+ virtual bool is_editable() const final;
+ String content_editable() const;
+ void set_content_editable(const String&);
+
+ String inner_text();
+ void set_inner_text(StringView);
+
+ bool cannot_navigate() const;
+
+private:
+ enum class ContentEditableState {
+ True,
+ False,
+ Inherit,
+ };
+ ContentEditableState content_editable_state() const;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLElement.idl
new file mode 100644
index 0000000000..6b5de34cdb
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLElement.idl
@@ -0,0 +1,11 @@
+interface HTMLElement : Element {
+
+ [Reflect] attribute DOMString title;
+ [Reflect] attribute DOMString lang;
+
+ [Reflect] attribute boolean hidden;
+
+ attribute DOMString contentEditable;
+
+ [LegacyNullToEmptyString] attribute DOMString innerText;
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLEmbedElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLEmbedElement.cpp
new file mode 100644
index 0000000000..1ec87b0fd0
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLEmbedElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLEmbedElement.h>
+
+namespace Web::HTML {
+
+HTMLEmbedElement::HTMLEmbedElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLEmbedElement::~HTMLEmbedElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLEmbedElement.h b/Userland/Libraries/LibWeb/HTML/HTMLEmbedElement.h
new file mode 100644
index 0000000000..1ceaaae634
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLEmbedElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLEmbedElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLEmbedElementWrapper;
+
+ HTMLEmbedElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLEmbedElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLEmbedElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLEmbedElement.idl
new file mode 100644
index 0000000000..5eb48b3509
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLEmbedElement.idl
@@ -0,0 +1,11 @@
+interface HTMLEmbedElement : HTMLElement {
+
+ [Reflect] attribute DOMString src;
+ [Reflect] attribute DOMString type;
+ [Reflect] attribute DOMString width;
+ [Reflect] attribute DOMString height;
+
+ [Reflect] attribute DOMString align;
+ [Reflect] attribute DOMString name;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFieldSetElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLFieldSetElement.cpp
new file mode 100644
index 0000000000..6a6c5bf87e
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLFieldSetElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLFieldSetElement.h>
+
+namespace Web::HTML {
+
+HTMLFieldSetElement::HTMLFieldSetElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLFieldSetElement::~HTMLFieldSetElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFieldSetElement.h b/Userland/Libraries/LibWeb/HTML/HTMLFieldSetElement.h
new file mode 100644
index 0000000000..b9ffcf43be
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLFieldSetElement.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLFieldSetElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLFieldSetElementWrapper;
+
+ HTMLFieldSetElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLFieldSetElement() override;
+
+ const String& type() const
+ {
+ static String fieldset = "fieldset";
+ return fieldset;
+ }
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFieldSetElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLFieldSetElement.idl
new file mode 100644
index 0000000000..4901c8aae8
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLFieldSetElement.idl
@@ -0,0 +1,5 @@
+interface HTMLFieldSetElement : HTMLElement {
+
+ readonly attribute DOMString type;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFontElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLFontElement.cpp
new file mode 100644
index 0000000000..50ef0e37f5
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLFontElement.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/CSS/StyleProperties.h>
+#include <LibWeb/CSS/StyleValue.h>
+#include <LibWeb/HTML/HTMLFontElement.h>
+
+namespace Web::HTML {
+
+HTMLFontElement::HTMLFontElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLFontElement::~HTMLFontElement()
+{
+}
+
+void HTMLFontElement::apply_presentational_hints(CSS::StyleProperties& style) const
+{
+ for_each_attribute([&](auto& name, auto& value) {
+ if (name.equals_ignoring_case("color")) {
+ auto color = Color::from_string(value);
+ if (color.has_value())
+ style.set_property(CSS::PropertyID::Color, CSS::ColorStyleValue::create(color.value()));
+ }
+ });
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFontElement.h b/Userland/Libraries/LibWeb/HTML/HTMLFontElement.h
new file mode 100644
index 0000000000..d414abc5a8
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLFontElement.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLFontElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLFontElementWrapper;
+
+ HTMLFontElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLFontElement() override;
+
+ virtual void apply_presentational_hints(CSS::StyleProperties&) const override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFontElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLFontElement.idl
new file mode 100644
index 0000000000..578e313ed0
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLFontElement.idl
@@ -0,0 +1,7 @@
+interface HTMLFontElement : HTMLElement {
+
+ [LegacyNullToEmptyString, Reflect] attribute DOMString color;
+ [Reflect] attribute DOMString face;
+ [Reflect] attribute DOMString size;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFormElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLFormElement.cpp
new file mode 100644
index 0000000000..b632dab482
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLFormElement.cpp
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/StringBuilder.h>
+#include <LibWeb/HTML/EventNames.h>
+#include <LibWeb/HTML/HTMLFormElement.h>
+#include <LibWeb/HTML/HTMLInputElement.h>
+#include <LibWeb/HTML/SubmitEvent.h>
+#include <LibWeb/InProcessWebView.h>
+#include <LibWeb/Page/Frame.h>
+#include <LibWeb/URLEncoder.h>
+
+namespace Web::HTML {
+
+HTMLFormElement::HTMLFormElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLFormElement::~HTMLFormElement()
+{
+}
+
+void HTMLFormElement::submit_form(RefPtr<HTMLElement> submitter, bool from_submit_binding)
+{
+ if (cannot_navigate())
+ return;
+
+ if (action().is_null()) {
+ dbgln("Unsupported form action ''");
+ return;
+ }
+
+ auto effective_method = method().to_lowercase();
+
+ if (effective_method == "dialog") {
+ dbgln("Failed to submit form: Unsupported form method '{}'", method());
+ return;
+ }
+
+ if (effective_method != "get" && effective_method != "post") {
+ effective_method = "get";
+ }
+
+ if (!from_submit_binding) {
+ if (m_firing_submission_events)
+ return;
+
+ m_firing_submission_events = true;
+
+ // FIXME: If the submitter element's no-validate state is false...
+
+ RefPtr<HTMLElement> submitter_button;
+
+ if (submitter != this)
+ submitter_button = submitter;
+
+ auto submit_event = SubmitEvent::create(EventNames::submit, submitter_button);
+ submit_event->set_bubbles(true);
+ submit_event->set_cancelable(true);
+ bool continue_ = dispatch_event(submit_event);
+
+ m_firing_submission_events = false;
+
+ if (!continue_)
+ return;
+
+ // This is checked again because arbitrary JS may have run when handling submit,
+ // which may have changed the result.
+ if (cannot_navigate())
+ return;
+ }
+
+ URL url(document().complete_url(action()));
+
+ if (!url.is_valid()) {
+ dbgln("Failed to submit form: Invalid URL: {}", action());
+ return;
+ }
+
+ if (url.protocol() == "file") {
+ if (document().url().protocol() != "file") {
+ dbgln("Failed to submit form: Security violation: {} may not submit to {}", document().url(), url);
+ return;
+ }
+ if (effective_method != "get") {
+ dbgln("Failed to submit form: Unsupported form method '{}' for URL: {}", method(), url);
+ return;
+ }
+ } else if (url.protocol() != "http" && url.protocol() != "https") {
+ dbgln("Failed to submit form: Unsupported protocol for URL: {}", url);
+ return;
+ }
+
+ Vector<URLQueryParam> parameters;
+
+ for_each_in_subtree_of_type<HTMLInputElement>([&](auto& node) {
+ auto& input = downcast<HTMLInputElement>(node);
+ if (!input.name().is_null() && (input.type() != "submit" || &input == submitter))
+ parameters.append({ input.name(), input.value() });
+ return IterationDecision::Continue;
+ });
+
+ if (effective_method == "get") {
+ url.set_query(urlencode(parameters));
+ }
+
+ LoadRequest request;
+ request.set_url(url);
+
+ if (effective_method == "post") {
+ auto body = urlencode(parameters).to_byte_buffer();
+ request.set_method("POST");
+ request.set_header("Content-Type", "application/x-www-form-urlencoded");
+ request.set_header("Content-Length", String::number(body.size()));
+ request.set_body(body);
+ }
+
+ if (auto* page = document().page())
+ page->load(request);
+}
+
+void HTMLFormElement::submit()
+{
+ submit_form(this, true);
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFormElement.h b/Userland/Libraries/LibWeb/HTML/HTMLFormElement.h
new file mode 100644
index 0000000000..8f90e8f3f7
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLFormElement.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+#include <LibWeb/HTML/HTMLInputElement.h>
+
+namespace Web::HTML {
+
+class HTMLFormElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLFormElementWrapper;
+
+ HTMLFormElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLFormElement() override;
+
+ String action() const { return attribute(HTML::AttributeNames::action); }
+ String method() const { return attribute(HTML::AttributeNames::method); }
+
+ void submit_form(RefPtr<HTMLElement> submitter, bool from_submit_binding = false);
+
+ // NOTE: This is for the JS bindings. Use submit_form instead.
+ void submit();
+
+private:
+ bool m_firing_submission_events { false };
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFormElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLFormElement.idl
new file mode 100644
index 0000000000..81d4fe37f4
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLFormElement.idl
@@ -0,0 +1,10 @@
+interface HTMLFormElement : HTMLElement {
+
+ [Reflect] attribute DOMString name;
+ [Reflect] attribute DOMString rel;
+ [Reflect=accept-charset] attribute DOMString acceptCharset;
+ [Reflect=novalidate] attribute boolean noValidate;
+
+ undefined submit();
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFrameElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLFrameElement.cpp
new file mode 100644
index 0000000000..ba2e0fb470
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLFrameElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLFrameElement.h>
+
+namespace Web::HTML {
+
+HTMLFrameElement::HTMLFrameElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLFrameElement::~HTMLFrameElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFrameElement.h b/Userland/Libraries/LibWeb/HTML/HTMLFrameElement.h
new file mode 100644
index 0000000000..7541bc80b5
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLFrameElement.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+// NOTE: This element is marked as obsolete, but is still listed as required by the specification.
+class HTMLFrameElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLFrameElementWrapper;
+
+ HTMLFrameElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLFrameElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFrameElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLFrameElement.idl
new file mode 100644
index 0000000000..e1092ce79e
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLFrameElement.idl
@@ -0,0 +1,9 @@
+interface HTMLFrameElement : HTMLElement {
+
+ [Reflect] attribute DOMString name;
+ [Reflect] attribute DOMString scrolling;
+ [Reflect] attribute DOMString src;
+ [Reflect=frameborder] attribute DOMString frameBorder;
+ [Reflect=longdesc] attribute DOMString longDesc;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFrameSetElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLFrameSetElement.cpp
new file mode 100644
index 0000000000..128da69ccf
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLFrameSetElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLFrameSetElement.h>
+
+namespace Web::HTML {
+
+HTMLFrameSetElement::HTMLFrameSetElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLFrameSetElement::~HTMLFrameSetElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFrameSetElement.h b/Userland/Libraries/LibWeb/HTML/HTMLFrameSetElement.h
new file mode 100644
index 0000000000..0c2af01678
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLFrameSetElement.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+// NOTE: This element is marked as obsolete, but is still listed as required by the specification.
+class HTMLFrameSetElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLFrameSetElementWrapper;
+
+ HTMLFrameSetElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLFrameSetElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFrameSetElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLFrameSetElement.idl
new file mode 100644
index 0000000000..abf5699ec4
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLFrameSetElement.idl
@@ -0,0 +1,6 @@
+interface HTMLFrameSetElement : HTMLElement {
+
+ [Reflect] attribute DOMString cols;
+ [Reflect] attribute DOMString rows;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLHRElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLHRElement.cpp
new file mode 100644
index 0000000000..82574cf789
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLHRElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLHRElement.h>
+
+namespace Web::HTML {
+
+HTMLHRElement::HTMLHRElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLHRElement::~HTMLHRElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLHRElement.h b/Userland/Libraries/LibWeb/HTML/HTMLHRElement.h
new file mode 100644
index 0000000000..e8a23c1a19
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLHRElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLHRElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLHRElementWrapper;
+
+ HTMLHRElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLHRElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLHRElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLHRElement.idl
new file mode 100644
index 0000000000..5cfd1e700e
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLHRElement.idl
@@ -0,0 +1,9 @@
+interface HTMLHRElement : HTMLElement {
+
+ [Reflect] attribute DOMString align;
+ [Reflect] attribute DOMString color;
+ [Reflect=noshade] attribute boolean noShade;
+ [Reflect] attribute DOMString size;
+ [Reflect] attribute DOMString width;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLHeadElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLHeadElement.cpp
new file mode 100644
index 0000000000..a17ead858b
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLHeadElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLHeadElement.h>
+
+namespace Web::HTML {
+
+HTMLHeadElement::HTMLHeadElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLHeadElement::~HTMLHeadElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLHeadElement.h b/Userland/Libraries/LibWeb/HTML/HTMLHeadElement.h
new file mode 100644
index 0000000000..d378e1af77
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLHeadElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLHeadElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLHeadElementWrapper;
+
+ HTMLHeadElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLHeadElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLHeadElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLHeadElement.idl
new file mode 100644
index 0000000000..8730e7c1e4
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLHeadElement.idl
@@ -0,0 +1,5 @@
+interface HTMLHeadElement : HTMLElement {
+
+
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLHeadingElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLHeadingElement.cpp
new file mode 100644
index 0000000000..b899c873a5
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLHeadingElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLHeadingElement.h>
+
+namespace Web::HTML {
+
+HTMLHeadingElement::HTMLHeadingElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLHeadingElement::~HTMLHeadingElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLHeadingElement.h b/Userland/Libraries/LibWeb/HTML/HTMLHeadingElement.h
new file mode 100644
index 0000000000..654b75baf9
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLHeadingElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLHeadingElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLHeadingElementWrapper;
+
+ HTMLHeadingElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLHeadingElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLHeadingElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLHeadingElement.idl
new file mode 100644
index 0000000000..fa3d5a4ed7
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLHeadingElement.idl
@@ -0,0 +1,5 @@
+interface HTMLHeadingElement : HTMLElement {
+
+ [Reflect] attribute DOMString align;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLHtmlElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLHtmlElement.cpp
new file mode 100644
index 0000000000..ab1233f739
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLHtmlElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLHtmlElement.h>
+
+namespace Web::HTML {
+
+HTMLHtmlElement::HTMLHtmlElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLHtmlElement::~HTMLHtmlElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLHtmlElement.h b/Userland/Libraries/LibWeb/HTML/HTMLHtmlElement.h
new file mode 100644
index 0000000000..3e34c52f6c
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLHtmlElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLHtmlElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLHtmlElementWrapper;
+
+ HTMLHtmlElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLHtmlElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLHtmlElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLHtmlElement.idl
new file mode 100644
index 0000000000..dad97564c8
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLHtmlElement.idl
@@ -0,0 +1,5 @@
+interface HTMLHtmlElement : HTMLElement {
+
+ [Reflect] attribute DOMString version;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLIFrameElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLIFrameElement.cpp
new file mode 100644
index 0000000000..e59818ba82
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLIFrameElement.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibGUI/Button.h>
+#include <LibGUI/TextBox.h>
+#include <LibWeb/Bindings/WindowObject.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/DOM/Event.h>
+#include <LibWeb/DOM/Window.h>
+#include <LibWeb/Dump.h>
+#include <LibWeb/HTML/EventNames.h>
+#include <LibWeb/HTML/HTMLFormElement.h>
+#include <LibWeb/HTML/HTMLIFrameElement.h>
+#include <LibWeb/HTML/Parser/HTMLDocumentParser.h>
+#include <LibWeb/InProcessWebView.h>
+#include <LibWeb/Layout/FrameBox.h>
+#include <LibWeb/Loader/ResourceLoader.h>
+#include <LibWeb/Origin.h>
+#include <LibWeb/Page/Frame.h>
+
+namespace Web::HTML {
+
+HTMLIFrameElement::HTMLIFrameElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+ ASSERT(document.frame());
+ m_content_frame = Frame::create_subframe(*this, document.frame()->main_frame());
+}
+
+HTMLIFrameElement::~HTMLIFrameElement()
+{
+}
+
+RefPtr<Layout::Node> HTMLIFrameElement::create_layout_node()
+{
+ auto style = document().style_resolver().resolve_style(*this);
+ return adopt(*new Layout::FrameBox(document(), *this, move(style)));
+}
+
+void HTMLIFrameElement::parse_attribute(const FlyString& name, const String& value)
+{
+ HTMLElement::parse_attribute(name, value);
+ if (name == HTML::AttributeNames::src)
+ load_src(value);
+}
+
+void HTMLIFrameElement::load_src(const String& value)
+{
+ auto url = document().complete_url(value);
+ if (!url.is_valid()) {
+ dbg() << "iframe failed to load URL: Invalid URL: " << value;
+ return;
+ }
+ if (url.protocol() == "file" && document().origin().protocol() != "file") {
+ dbg() << "iframe failed to load URL: Security violation: " << document().url() << " may not load " << url;
+ return;
+ }
+
+ dbg() << "Loading iframe document from " << value;
+ m_content_frame->loader().load(url, FrameLoader::Type::IFrame);
+}
+
+Origin HTMLIFrameElement::content_origin() const
+{
+ if (!m_content_frame || !m_content_frame->document())
+ return {};
+ return m_content_frame->document()->origin();
+}
+
+bool HTMLIFrameElement::may_access_from_origin(const Origin& origin) const
+{
+ return origin.is_same(content_origin());
+}
+
+const DOM::Document* HTMLIFrameElement::content_document() const
+{
+ return m_content_frame ? m_content_frame->document() : nullptr;
+}
+
+void HTMLIFrameElement::content_frame_did_load(Badge<FrameLoader>)
+{
+ dispatch_event(DOM::Event::create(EventNames::load));
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLIFrameElement.h b/Userland/Libraries/LibWeb/HTML/HTMLIFrameElement.h
new file mode 100644
index 0000000000..0237ea3c91
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLIFrameElement.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLIFrameElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLIFrameElementWrapper;
+
+ HTMLIFrameElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLIFrameElement() override;
+
+ virtual RefPtr<Layout::Node> create_layout_node() override;
+
+ Frame* content_frame() { return m_content_frame; }
+ const Frame* content_frame() const { return m_content_frame; }
+
+ const DOM::Document* content_document() const;
+
+ Origin content_origin() const;
+ bool may_access_from_origin(const Origin&) const;
+
+ void content_frame_did_load(Badge<FrameLoader>);
+
+private:
+ virtual void parse_attribute(const FlyString& name, const String& value) override;
+
+ void load_src(const String&);
+
+ RefPtr<Frame> m_content_frame;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLIFrameElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLIFrameElement.idl
new file mode 100644
index 0000000000..a219b66071
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLIFrameElement.idl
@@ -0,0 +1,19 @@
+interface HTMLIFrameElement : HTMLElement {
+
+ [Reflect] attribute DOMString src;
+ [Reflect] attribute DOMString srcdoc;
+ [Reflect] attribute DOMString name;
+ [Reflect] attribute DOMString allow;
+ [Reflect] attribute DOMString width;
+ [Reflect] attribute DOMString height;
+ [Reflect=allowfullscreen] attribute boolean allowFullscreen;
+
+ [ReturnNullIfCrossOrigin] readonly attribute Document? contentDocument;
+
+ [Reflect] attribute DOMString align;
+ [Reflect] attribute DOMString scrolling;
+ [Reflect=frameborder] attribute DOMString frameBorder;
+
+ [LegacyNullToEmptyString, Reflect=marginheight] attribute DOMString marginHeight;
+ [LegacyNullToEmptyString, Reflect=marginwidth] attribute DOMString marginWidth;
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLImageElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLImageElement.cpp
new file mode 100644
index 0000000000..61b99f0ffa
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLImageElement.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibGfx/Bitmap.h>
+#include <LibGfx/ImageDecoder.h>
+#include <LibWeb/CSS/Parser/CSSParser.h>
+#include <LibWeb/CSS/StyleResolver.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/DOM/Event.h>
+#include <LibWeb/HTML/EventNames.h>
+#include <LibWeb/HTML/HTMLImageElement.h>
+#include <LibWeb/Layout/ImageBox.h>
+#include <LibWeb/Loader/ResourceLoader.h>
+
+namespace Web::HTML {
+
+HTMLImageElement::HTMLImageElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+ m_image_loader.on_load = [this] {
+ this->document().update_layout();
+ dispatch_event(DOM::Event::create(EventNames::load));
+ };
+
+ m_image_loader.on_fail = [this] {
+ dbgln("HTMLImageElement: Resource did fail: {}", src());
+ this->document().update_layout();
+ dispatch_event(DOM::Event::create(EventNames::error));
+ };
+
+ m_image_loader.on_animate = [this] {
+ if (layout_node())
+ layout_node()->set_needs_display();
+ };
+}
+
+HTMLImageElement::~HTMLImageElement()
+{
+}
+
+void HTMLImageElement::apply_presentational_hints(CSS::StyleProperties& style) const
+{
+ for_each_attribute([&](auto& name, auto& value) {
+ if (name == HTML::AttributeNames::width) {
+ if (auto parsed_value = parse_html_length(document(), value)) {
+ style.set_property(CSS::PropertyID::Width, parsed_value.release_nonnull());
+ }
+ } else if (name == HTML::AttributeNames::height) {
+ if (auto parsed_value = parse_html_length(document(), value)) {
+ style.set_property(CSS::PropertyID::Height, parsed_value.release_nonnull());
+ }
+ }
+ });
+}
+
+void HTMLImageElement::parse_attribute(const FlyString& name, const String& value)
+{
+ HTMLElement::parse_attribute(name, value);
+
+ if (name == HTML::AttributeNames::src)
+ m_image_loader.load(document().complete_url(value));
+}
+
+RefPtr<Layout::Node> HTMLImageElement::create_layout_node()
+{
+ auto style = document().style_resolver().resolve_style(*this);
+ if (style->display() == CSS::Display::None)
+ return nullptr;
+ return adopt(*new Layout::ImageBox(document(), *this, move(style), m_image_loader));
+}
+
+const Gfx::Bitmap* HTMLImageElement::bitmap() const
+{
+ return m_image_loader.bitmap();
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLImageElement.h b/Userland/Libraries/LibWeb/HTML/HTMLImageElement.h
new file mode 100644
index 0000000000..7cc97f1983
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLImageElement.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/ByteBuffer.h>
+#include <AK/OwnPtr.h>
+#include <LibGfx/Forward.h>
+#include <LibWeb/HTML/HTMLElement.h>
+#include <LibWeb/Loader/ImageLoader.h>
+
+namespace Web::HTML {
+
+class HTMLImageElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLImageElementWrapper;
+
+ HTMLImageElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLImageElement() override;
+
+ virtual void parse_attribute(const FlyString& name, const String& value) override;
+
+ String alt() const { return attribute(HTML::AttributeNames::alt); }
+ String src() const { return attribute(HTML::AttributeNames::src); }
+
+ const Gfx::Bitmap* bitmap() const;
+
+private:
+ virtual void apply_presentational_hints(CSS::StyleProperties&) const override;
+
+ void animate();
+
+ virtual RefPtr<Layout::Node> create_layout_node() override;
+
+ ImageLoader m_image_loader;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLImageElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLImageElement.idl
new file mode 100644
index 0000000000..d0d4330a9a
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLImageElement.idl
@@ -0,0 +1,14 @@
+interface HTMLImageElement : HTMLElement {
+
+ [Reflect] attribute DOMString src;
+ [Reflect] attribute DOMString alt;
+ [Reflect] attribute DOMString srcset;
+ [Reflect] attribute DOMString sizes;
+ [Reflect=usemap] attribute DOMString useMap;
+ [Reflect=ismap] attribute boolean isMap;
+
+ [Reflect] attribute DOMString name;
+ [Reflect] attribute DOMString align;
+ [LegacyNullToEmptyString, Reflect] attribute DOMString border;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp
new file mode 100644
index 0000000000..c5654c3530
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibGUI/Button.h>
+#include <LibGUI/TextBox.h>
+#include <LibGfx/FontDatabase.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/DOM/Event.h>
+#include <LibWeb/HTML/EventNames.h>
+#include <LibWeb/HTML/HTMLFormElement.h>
+#include <LibWeb/HTML/HTMLInputElement.h>
+#include <LibWeb/InProcessWebView.h>
+#include <LibWeb/Layout/ButtonBox.h>
+#include <LibWeb/Layout/CheckBox.h>
+#include <LibWeb/Layout/WidgetBox.h>
+#include <LibWeb/Page/Frame.h>
+
+namespace Web::HTML {
+
+HTMLInputElement::HTMLInputElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLInputElement::~HTMLInputElement()
+{
+}
+
+void HTMLInputElement::did_click_button(Badge<Layout::ButtonBox>)
+{
+ // FIXME: This should be a PointerEvent.
+ dispatch_event(DOM::Event::create(EventNames::click));
+
+ if (type().equals_ignoring_case("submit")) {
+ if (auto* form = first_ancestor_of_type<HTMLFormElement>()) {
+ form->submit_form(this);
+ }
+ return;
+ }
+}
+
+RefPtr<Layout::Node> HTMLInputElement::create_layout_node()
+{
+ ASSERT(document().page());
+ auto& page = *document().page();
+ auto& page_view = const_cast<InProcessWebView&>(static_cast<const InProcessWebView&>(page.client()));
+
+ if (type() == "hidden")
+ return nullptr;
+
+ auto style = document().style_resolver().resolve_style(*this);
+ if (style->display() == CSS::Display::None)
+ return nullptr;
+
+ if (type().equals_ignoring_case("submit") || type().equals_ignoring_case("button"))
+ return adopt(*new Layout::ButtonBox(document(), *this, move(style)));
+
+ if (type() == "checkbox")
+ return adopt(*new Layout::CheckBox(document(), *this, move(style)));
+
+ auto& text_box = page_view.add<GUI::TextBox>();
+ text_box.set_text(value());
+ text_box.on_change = [this] {
+ auto& widget = downcast<Layout::WidgetBox>(layout_node())->widget();
+ const_cast<HTMLInputElement*>(this)->set_attribute(HTML::AttributeNames::value, static_cast<const GUI::TextBox&>(widget).text());
+ };
+ int text_width = Gfx::FontDatabase::default_font().width(value());
+ auto size_value = attribute(HTML::AttributeNames::size);
+ if (!size_value.is_null()) {
+ auto size = size_value.to_uint();
+ if (size.has_value())
+ text_width = Gfx::FontDatabase::default_font().glyph_width('x') * size.value();
+ }
+ text_box.set_relative_rect(0, 0, text_width + 20, 20);
+ return adopt(*new Layout::WidgetBox(document(), *this, text_box));
+}
+
+void HTMLInputElement::set_checked(bool checked)
+{
+ if (m_checked == checked)
+ return;
+ m_checked = checked;
+ if (layout_node())
+ layout_node()->set_needs_display();
+
+ dispatch_event(DOM::Event::create(EventNames::change));
+}
+
+bool HTMLInputElement::enabled() const
+{
+ return !has_attribute(HTML::AttributeNames::disabled);
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h
new file mode 100644
index 0000000000..b887118263
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLInputElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLInputElementWrapper;
+
+ HTMLInputElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLInputElement() override;
+
+ virtual RefPtr<Layout::Node> create_layout_node() override;
+
+ String type() const { return attribute(HTML::AttributeNames::type); }
+ String value() const { return attribute(HTML::AttributeNames::value); }
+ String name() const { return attribute(HTML::AttributeNames::name); }
+
+ bool checked() const { return m_checked; }
+ void set_checked(bool);
+
+ bool enabled() const;
+
+ void did_click_button(Badge<Layout::ButtonBox>);
+
+private:
+ bool m_checked { false };
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.idl
new file mode 100644
index 0000000000..037b65c280
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.idl
@@ -0,0 +1,27 @@
+interface HTMLInputElement : HTMLElement {
+
+ [Reflect] attribute DOMString accept;
+ [Reflect] attribute DOMString alt;
+ [Reflect] attribute DOMString max;
+ [Reflect] attribute DOMString min;
+ [Reflect] attribute DOMString pattern;
+ [Reflect] attribute DOMString placeholder;
+ [Reflect] attribute DOMString src;
+ [Reflect] attribute DOMString step;
+ [Reflect=dirname] attribute DOMString dirName;
+ [Reflect=value] attribute DOMString defaultValue;
+
+ attribute boolean checked;
+
+ [Reflect] attribute boolean disabled;
+ [Reflect=checked] attribute boolean defaultChecked;
+ [Reflect=formnovalidate] attribute boolean formNoValidate;
+ [Reflect=formtarget] attribute DOMString formTarget;
+ [Reflect] attribute boolean multiple;
+ [Reflect=readonly] attribute boolean readOnly;
+ [Reflect] attribute boolean required;
+
+ [Reflect] attribute DOMString align;
+ [Reflect=usemap] attribute DOMString useMap;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLLIElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLLIElement.cpp
new file mode 100644
index 0000000000..fbdb4d0444
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLLIElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLLIElement.h>
+
+namespace Web::HTML {
+
+HTMLLIElement::HTMLLIElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLLIElement::~HTMLLIElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLLIElement.h b/Userland/Libraries/LibWeb/HTML/HTMLLIElement.h
new file mode 100644
index 0000000000..b0113aef9c
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLLIElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLLIElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLLIElementWrapper;
+
+ HTMLLIElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLLIElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLLIElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLLIElement.idl
new file mode 100644
index 0000000000..9330aa88b1
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLLIElement.idl
@@ -0,0 +1,5 @@
+interface HTMLLIElement : HTMLElement {
+
+ [Reflect] attribute DOMString type;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLLabelElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLLabelElement.cpp
new file mode 100644
index 0000000000..766c64eecd
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLLabelElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLLabelElement.h>
+
+namespace Web::HTML {
+
+HTMLLabelElement::HTMLLabelElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLLabelElement::~HTMLLabelElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLLabelElement.h b/Userland/Libraries/LibWeb/HTML/HTMLLabelElement.h
new file mode 100644
index 0000000000..210e366ee0
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLLabelElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLLabelElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLLabelElementWrapper;
+
+ HTMLLabelElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLLabelElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLLabelElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLLabelElement.idl
new file mode 100644
index 0000000000..bbddd2052f
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLLabelElement.idl
@@ -0,0 +1,5 @@
+interface HTMLLabelElement : HTMLElement {
+
+ [Reflect=for] attribute DOMString htmlFor;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLLegendElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLLegendElement.cpp
new file mode 100644
index 0000000000..aa2bbc2049
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLLegendElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLLegendElement.h>
+
+namespace Web::HTML {
+
+HTMLLegendElement::HTMLLegendElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLLegendElement::~HTMLLegendElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLLegendElement.h b/Userland/Libraries/LibWeb/HTML/HTMLLegendElement.h
new file mode 100644
index 0000000000..02a8b24585
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLLegendElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLLegendElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLLegendElementWrapper;
+
+ HTMLLegendElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLLegendElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLLegendElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLLegendElement.idl
new file mode 100644
index 0000000000..49ee03cd21
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLLegendElement.idl
@@ -0,0 +1,5 @@
+interface HTMLLegendElement : HTMLElement {
+
+ [Reflect] attribute DOMString align;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLLinkElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLLinkElement.cpp
new file mode 100644
index 0000000000..323c642d61
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLLinkElement.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/ByteBuffer.h>
+#include <AK/URL.h>
+#include <LibCore/File.h>
+#include <LibWeb/CSS/Parser/CSSParser.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/HTML/HTMLLinkElement.h>
+#include <LibWeb/Loader/ResourceLoader.h>
+
+namespace Web::HTML {
+
+HTMLLinkElement::HTMLLinkElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLLinkElement::~HTMLLinkElement()
+{
+}
+
+void HTMLLinkElement::inserted_into(Node& node)
+{
+ HTMLElement::inserted_into(node);
+
+ if (m_relationship & Relationship::Stylesheet && !(m_relationship & Relationship::Alternate))
+ load_stylesheet(document().complete_url(href()));
+}
+
+void HTMLLinkElement::resource_did_fail()
+{
+}
+
+void HTMLLinkElement::resource_did_load()
+{
+ ASSERT(resource());
+ if (!resource()->has_encoded_data())
+ return;
+
+ dbg() << "HTMLLinkElement: Resource did load, looks good! " << href();
+
+ auto sheet = parse_css(CSS::ParsingContext(document()), resource()->encoded_data());
+ if (!sheet) {
+ dbg() << "HTMLLinkElement: Failed to parse stylesheet: " << href();
+ return;
+ }
+
+ // Transfer the rules from the successfully parsed sheet into the sheet we've already inserted.
+ m_style_sheet->rules() = sheet->rules();
+
+ document().update_style();
+}
+
+void HTMLLinkElement::load_stylesheet(const URL& url)
+{
+ // First insert an empty style sheet in the document sheet list.
+ // There's probably a nicer way to do this, but this ensures that sheets are in document order.
+ m_style_sheet = CSS::StyleSheet::create({});
+ document().style_sheets().add_sheet(*m_style_sheet);
+
+ LoadRequest request;
+ request.set_url(url);
+ set_resource(ResourceLoader::the().load_resource(Resource::Type::Generic, request));
+}
+
+void HTMLLinkElement::parse_attribute(const FlyString& name, const String& value)
+{
+ if (name == HTML::AttributeNames::rel) {
+ m_relationship = 0;
+ auto parts = value.split_view(' ');
+ for (auto& part : parts) {
+ if (part == "stylesheet")
+ m_relationship |= Relationship::Stylesheet;
+ else if (part == "alternate")
+ m_relationship |= Relationship::Alternate;
+ }
+ }
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLLinkElement.h b/Userland/Libraries/LibWeb/HTML/HTMLLinkElement.h
new file mode 100644
index 0000000000..ac91ecbe21
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLLinkElement.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+#include <LibWeb/Loader/Resource.h>
+
+namespace Web::HTML {
+
+class HTMLLinkElement final
+ : public HTMLElement
+ , public ResourceClient {
+public:
+ using WrapperType = Bindings::HTMLLinkElementWrapper;
+
+ HTMLLinkElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLLinkElement() override;
+
+ virtual void inserted_into(Node&) override;
+
+ String rel() const { return attribute(HTML::AttributeNames::rel); }
+ String type() const { return attribute(HTML::AttributeNames::type); }
+ String href() const { return attribute(HTML::AttributeNames::href); }
+
+private:
+ // ^ResourceClient
+ virtual void resource_did_fail() override;
+ virtual void resource_did_load() override;
+
+ void parse_attribute(const FlyString&, const String&) override;
+
+ void load_stylesheet(const URL&);
+
+ struct Relationship {
+ enum {
+ Alternate = 1 << 0,
+ Stylesheet = 1 << 1,
+ };
+ };
+
+ unsigned m_relationship { 0 };
+ RefPtr<CSS::StyleSheet> m_style_sheet;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLLinkElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLLinkElement.idl
new file mode 100644
index 0000000000..e1999c74b8
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLLinkElement.idl
@@ -0,0 +1,17 @@
+interface HTMLLinkElement : HTMLElement {
+
+ [Reflect] attribute DOMString href;
+ [Reflect] attribute DOMString hreflang;
+ [Reflect] attribute DOMString integrity;
+ [Reflect] attribute DOMString media;
+ [Reflect] attribute DOMString rel;
+ [Reflect] attribute DOMString type;
+ [Reflect=imagesrcset] attribute DOMString imageSrcset;
+ [Reflect=imagesizes] attribute DOMString imageSizes;
+ [Reflect] attribute boolean disabled;
+
+ [Reflect] attribute DOMString charset;
+ [Reflect] attribute DOMString rev;
+ [Reflect] attribute DOMString target;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMapElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLMapElement.cpp
new file mode 100644
index 0000000000..c85b871c71
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLMapElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLMapElement.h>
+
+namespace Web::HTML {
+
+HTMLMapElement::HTMLMapElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLMapElement::~HTMLMapElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMapElement.h b/Userland/Libraries/LibWeb/HTML/HTMLMapElement.h
new file mode 100644
index 0000000000..23b59ce6fc
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLMapElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLMapElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLMapElementWrapper;
+
+ HTMLMapElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLMapElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMapElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLMapElement.idl
new file mode 100644
index 0000000000..636a96f8a5
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLMapElement.idl
@@ -0,0 +1,5 @@
+interface HTMLMapElement : HTMLElement {
+
+ [Reflect] attribute DOMString name;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMarqueeElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLMarqueeElement.cpp
new file mode 100644
index 0000000000..02e5e2ec6c
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLMarqueeElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLMarqueeElement.h>
+
+namespace Web::HTML {
+
+HTMLMarqueeElement::HTMLMarqueeElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLMarqueeElement::~HTMLMarqueeElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMarqueeElement.h b/Userland/Libraries/LibWeb/HTML/HTMLMarqueeElement.h
new file mode 100644
index 0000000000..0a2e5f5e32
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLMarqueeElement.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+// NOTE: This element is marked as obsolete, but is still listed as required by the specification.
+class HTMLMarqueeElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLMarqueeElementWrapper;
+
+ HTMLMarqueeElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLMarqueeElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMarqueeElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLMarqueeElement.idl
new file mode 100644
index 0000000000..49ff70654d
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLMarqueeElement.idl
@@ -0,0 +1,9 @@
+interface HTMLMarqueeElement : HTMLElement {
+
+ [Reflect] attribute DOMString behaviour;
+ [Reflect=bgcolor] attribute DOMString bgColor;
+ [Reflect] attribute DOMString direction;
+ [Reflect] attribute DOMString height;
+ [Reflect] attribute DOMString width;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMediaElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLMediaElement.cpp
new file mode 100644
index 0000000000..15e4c01195
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLMediaElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLMediaElement.h>
+
+namespace Web::HTML {
+
+HTMLMediaElement::HTMLMediaElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLMediaElement::~HTMLMediaElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMediaElement.h b/Userland/Libraries/LibWeb/HTML/HTMLMediaElement.h
new file mode 100644
index 0000000000..53adde12e3
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLMediaElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLMediaElement : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLMediaElementWrapper;
+
+ HTMLMediaElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLMediaElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMediaElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLMediaElement.idl
new file mode 100644
index 0000000000..dda7615580
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLMediaElement.idl
@@ -0,0 +1,10 @@
+interface HTMLMediaElement : HTMLElement {
+
+ [Reflect] attribute DOMString src;
+
+ [Reflect] attribute boolean autoplay;
+ [Reflect] attribute boolean loop;
+
+ [Reflect] attribute boolean controls;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMenuElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLMenuElement.cpp
new file mode 100644
index 0000000000..bcda7e5f56
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLMenuElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLMenuElement.h>
+
+namespace Web::HTML {
+
+HTMLMenuElement::HTMLMenuElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLMenuElement::~HTMLMenuElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMenuElement.h b/Userland/Libraries/LibWeb/HTML/HTMLMenuElement.h
new file mode 100644
index 0000000000..8fe2acca90
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLMenuElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLMenuElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLMenuElementWrapper;
+
+ HTMLMenuElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLMenuElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMenuElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLMenuElement.idl
new file mode 100644
index 0000000000..f9dc7ed7a0
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLMenuElement.idl
@@ -0,0 +1,5 @@
+interface HTMLMenuElement : HTMLElement {
+
+ [Reflect] attribute boolean compact;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMetaElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLMetaElement.cpp
new file mode 100644
index 0000000000..64c50a19e3
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLMetaElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLMetaElement.h>
+
+namespace Web::HTML {
+
+HTMLMetaElement::HTMLMetaElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLMetaElement::~HTMLMetaElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMetaElement.h b/Userland/Libraries/LibWeb/HTML/HTMLMetaElement.h
new file mode 100644
index 0000000000..e48271c826
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLMetaElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLMetaElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLMetaElementWrapper;
+
+ HTMLMetaElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLMetaElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMetaElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLMetaElement.idl
new file mode 100644
index 0000000000..de1868b65c
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLMetaElement.idl
@@ -0,0 +1,9 @@
+interface HTMLMetaElement : HTMLElement {
+
+ [Reflect] attribute DOMString name;
+ [Reflect] attribute DOMString content;
+ [Reflect=http-equiv] attribute DOMString httpEquiv;
+
+ [Reflect] attribute DOMString scheme;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.cpp
new file mode 100644
index 0000000000..07bfeee98f
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLMeterElement.h>
+
+namespace Web::HTML {
+
+HTMLMeterElement::HTMLMeterElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLMeterElement::~HTMLMeterElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.h b/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.h
new file mode 100644
index 0000000000..f6d983a6b0
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLMeterElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLMeterElementWrapper;
+
+ HTMLMeterElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLMeterElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.idl
new file mode 100644
index 0000000000..72f6c1d4fe
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.idl
@@ -0,0 +1,5 @@
+interface HTMLMeterElement : HTMLElement {
+
+
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLModElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLModElement.cpp
new file mode 100644
index 0000000000..5fcd7603b0
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLModElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLModElement.h>
+
+namespace Web::HTML {
+
+HTMLModElement::HTMLModElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLModElement::~HTMLModElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLModElement.h b/Userland/Libraries/LibWeb/HTML/HTMLModElement.h
new file mode 100644
index 0000000000..f75c8bef9e
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLModElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLModElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLModElementWrapper;
+
+ HTMLModElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLModElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLModElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLModElement.idl
new file mode 100644
index 0000000000..8e3a489fa4
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLModElement.idl
@@ -0,0 +1,6 @@
+interface HTMLModElement : HTMLElement {
+
+ [Reflect] attribute DOMString cite;
+ [Reflect=datetime] attribute DOMString dateTime;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLOListElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLOListElement.cpp
new file mode 100644
index 0000000000..48ac66fd8d
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLOListElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLOListElement.h>
+
+namespace Web::HTML {
+
+HTMLOListElement::HTMLOListElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLOListElement::~HTMLOListElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLOListElement.h b/Userland/Libraries/LibWeb/HTML/HTMLOListElement.h
new file mode 100644
index 0000000000..8f5a849c75
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLOListElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLOListElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLOListElementWrapper;
+
+ HTMLOListElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLOListElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLOListElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLOListElement.idl
new file mode 100644
index 0000000000..c81faf9c0e
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLOListElement.idl
@@ -0,0 +1,8 @@
+interface HTMLOListElement : HTMLElement {
+
+ [Reflect] attribute boolean reversed;
+ [Reflect] attribute DOMString type;
+
+ [Reflect] attribute boolean compact;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLObjectElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLObjectElement.cpp
new file mode 100644
index 0000000000..a7514179d4
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLObjectElement.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibGfx/Bitmap.h>
+#include <LibGfx/ImageDecoder.h>
+#include <LibWeb/CSS/StyleResolver.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/DOM/Event.h>
+#include <LibWeb/HTML/HTMLObjectElement.h>
+#include <LibWeb/Layout/ImageBox.h>
+#include <LibWeb/Loader/ResourceLoader.h>
+
+namespace Web::HTML {
+
+HTMLObjectElement::HTMLObjectElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+ m_image_loader.on_load = [this] {
+ m_should_show_fallback_content = false;
+ this->document().force_layout();
+ };
+
+ m_image_loader.on_fail = [this] {
+ m_should_show_fallback_content = true;
+ this->document().force_layout();
+ };
+}
+
+HTMLObjectElement::~HTMLObjectElement()
+{
+}
+
+void HTMLObjectElement::parse_attribute(const FlyString& name, const String& value)
+{
+ HTMLElement::parse_attribute(name, value);
+
+ if (name == HTML::AttributeNames::data)
+ m_image_loader.load(document().complete_url(value));
+}
+
+RefPtr<Layout::Node> HTMLObjectElement::create_layout_node()
+{
+ if (m_should_show_fallback_content)
+ return HTMLElement::create_layout_node();
+
+ auto style = document().style_resolver().resolve_style(*this);
+ if (style->display() == CSS::Display::None)
+ return nullptr;
+ if (m_image_loader.has_image())
+ return adopt(*new Layout::ImageBox(document(), *this, move(style), m_image_loader));
+ return nullptr;
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLObjectElement.h b/Userland/Libraries/LibWeb/HTML/HTMLObjectElement.h
new file mode 100644
index 0000000000..f07e122ff7
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLObjectElement.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibCore/Forward.h>
+#include <LibGfx/Forward.h>
+#include <LibWeb/HTML/HTMLElement.h>
+#include <LibWeb/Loader/ImageLoader.h>
+
+namespace Web::HTML {
+
+class HTMLObjectElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLObjectElementWrapper;
+
+ HTMLObjectElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLObjectElement() override;
+
+ virtual void parse_attribute(const FlyString& name, const String& value) override;
+
+ String data() const { return attribute(HTML::AttributeNames::data); }
+ String type() const { return attribute(HTML::AttributeNames::type); }
+
+private:
+ virtual RefPtr<Layout::Node> create_layout_node() override;
+
+ ImageLoader m_image_loader;
+ bool m_should_show_fallback_content { false };
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLObjectElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLObjectElement.idl
new file mode 100644
index 0000000000..3c5790bcc5
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLObjectElement.idl
@@ -0,0 +1,19 @@
+interface HTMLObjectElement : HTMLElement {
+
+ [Reflect] attribute DOMString data;
+ [Reflect] attribute DOMString type;
+ [Reflect] attribute DOMString name;
+ [Reflect=usemap] attribute DOMString useMap;
+ [Reflect] attribute DOMString width;
+ [Reflect] attribute DOMString height;
+
+ [Reflect] attribute DOMString align;
+ [Reflect] attribute DOMString archive;
+ [Reflect] attribute DOMString code;
+ [Reflect] attribute boolean declare;
+ [Reflect] attribute DOMString standby;
+ [Reflect=codetype] attribute DOMString codeType;
+
+ [LegacyNullToEmptyString, Reflect] attribute DOMString border;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLOptGroupElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLOptGroupElement.cpp
new file mode 100644
index 0000000000..50c7d23b6f
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLOptGroupElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLOptGroupElement.h>
+
+namespace Web::HTML {
+
+HTMLOptGroupElement::HTMLOptGroupElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLOptGroupElement::~HTMLOptGroupElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLOptGroupElement.h b/Userland/Libraries/LibWeb/HTML/HTMLOptGroupElement.h
new file mode 100644
index 0000000000..77a90aebcb
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLOptGroupElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLOptGroupElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLOptGroupElementWrapper;
+
+ HTMLOptGroupElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLOptGroupElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLOptGroupElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLOptGroupElement.idl
new file mode 100644
index 0000000000..d442091b1a
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLOptGroupElement.idl
@@ -0,0 +1,6 @@
+interface HTMLOptGroupElement : HTMLElement {
+
+ [Reflect] attribute boolean disabled;
+ [Reflect] attribute DOMString label;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLOptionElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLOptionElement.cpp
new file mode 100644
index 0000000000..c22fbb3668
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLOptionElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLOptionElement.h>
+
+namespace Web::HTML {
+
+HTMLOptionElement::HTMLOptionElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLOptionElement::~HTMLOptionElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLOptionElement.h b/Userland/Libraries/LibWeb/HTML/HTMLOptionElement.h
new file mode 100644
index 0000000000..e2bead49f6
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLOptionElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLOptionElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLOptionElementWrapper;
+
+ HTMLOptionElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLOptionElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLOptionElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLOptionElement.idl
new file mode 100644
index 0000000000..96dcaec548
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLOptionElement.idl
@@ -0,0 +1,6 @@
+interface HTMLOptionElement : HTMLElement {
+
+ [Reflect] attribute boolean disabled;
+ [Reflect=selected] attribute boolean defaultSelected;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLOutputElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLOutputElement.cpp
new file mode 100644
index 0000000000..80f9d82dd5
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLOutputElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLOutputElement.h>
+
+namespace Web::HTML {
+
+HTMLOutputElement::HTMLOutputElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLOutputElement::~HTMLOutputElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLOutputElement.h b/Userland/Libraries/LibWeb/HTML/HTMLOutputElement.h
new file mode 100644
index 0000000000..110225535f
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLOutputElement.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLOutputElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLOutputElementWrapper;
+
+ HTMLOutputElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLOutputElement() override;
+
+ const String& type() const
+ {
+ static String output = "output";
+ return output;
+ }
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLOutputElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLOutputElement.idl
new file mode 100644
index 0000000000..94c45e99f3
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLOutputElement.idl
@@ -0,0 +1,5 @@
+interface HTMLOutputElement : HTMLElement {
+
+ readonly attribute DOMString type;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLParagraphElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLParagraphElement.cpp
new file mode 100644
index 0000000000..9e0fab9859
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLParagraphElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLParagraphElement.h>
+
+namespace Web::HTML {
+
+HTMLParagraphElement::HTMLParagraphElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLParagraphElement::~HTMLParagraphElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLParagraphElement.h b/Userland/Libraries/LibWeb/HTML/HTMLParagraphElement.h
new file mode 100644
index 0000000000..660855edb9
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLParagraphElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLParagraphElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLParagraphElementWrapper;
+
+ HTMLParagraphElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLParagraphElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLParagraphElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLParagraphElement.idl
new file mode 100644
index 0000000000..e1248da905
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLParagraphElement.idl
@@ -0,0 +1,5 @@
+interface HTMLParagraphElement : HTMLElement {
+
+ [Reflect] attribute DOMString align;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLParamElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLParamElement.cpp
new file mode 100644
index 0000000000..838ae13ef8
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLParamElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLParamElement.h>
+
+namespace Web::HTML {
+
+HTMLParamElement::HTMLParamElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLParamElement::~HTMLParamElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLParamElement.h b/Userland/Libraries/LibWeb/HTML/HTMLParamElement.h
new file mode 100644
index 0000000000..642551cdd0
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLParamElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLParamElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLParamElementWrapper;
+
+ HTMLParamElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLParamElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLParamElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLParamElement.idl
new file mode 100644
index 0000000000..a848fc5364
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLParamElement.idl
@@ -0,0 +1,9 @@
+interface HTMLParamElement : HTMLElement {
+
+ [Reflect] attribute DOMString name;
+ [Reflect] attribute DOMString value;
+
+ [Reflect] attribute DOMString type;
+ [Reflect=valuetype] attribute DOMString valueType;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLPictureElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLPictureElement.cpp
new file mode 100644
index 0000000000..5aec65b51a
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLPictureElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLPictureElement.h>
+
+namespace Web::HTML {
+
+HTMLPictureElement::HTMLPictureElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLPictureElement::~HTMLPictureElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLPictureElement.h b/Userland/Libraries/LibWeb/HTML/HTMLPictureElement.h
new file mode 100644
index 0000000000..8d12df2b16
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLPictureElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLPictureElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLPictureElementWrapper;
+
+ HTMLPictureElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLPictureElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLPictureElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLPictureElement.idl
new file mode 100644
index 0000000000..9bd6cefe16
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLPictureElement.idl
@@ -0,0 +1,5 @@
+interface HTMLPictureElement : HTMLElement {
+
+
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLPreElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLPreElement.cpp
new file mode 100644
index 0000000000..69349d7238
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLPreElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLPreElement.h>
+
+namespace Web::HTML {
+
+HTMLPreElement::HTMLPreElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLPreElement::~HTMLPreElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLPreElement.h b/Userland/Libraries/LibWeb/HTML/HTMLPreElement.h
new file mode 100644
index 0000000000..b87197f600
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLPreElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLPreElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLPreElementWrapper;
+
+ HTMLPreElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLPreElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLPreElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLPreElement.idl
new file mode 100644
index 0000000000..27a0404ba5
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLPreElement.idl
@@ -0,0 +1,5 @@
+interface HTMLPreElement : HTMLElement {
+
+
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLProgressElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLProgressElement.cpp
new file mode 100644
index 0000000000..cfad730312
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLProgressElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLProgressElement.h>
+
+namespace Web::HTML {
+
+HTMLProgressElement::HTMLProgressElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLProgressElement::~HTMLProgressElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLProgressElement.h b/Userland/Libraries/LibWeb/HTML/HTMLProgressElement.h
new file mode 100644
index 0000000000..cb18e317f2
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLProgressElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLProgressElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLProgressElementWrapper;
+
+ HTMLProgressElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLProgressElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLProgressElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLProgressElement.idl
new file mode 100644
index 0000000000..1d00f9b3fc
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLProgressElement.idl
@@ -0,0 +1,5 @@
+interface HTMLProgressElement : HTMLElement {
+
+
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLQuoteElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLQuoteElement.cpp
new file mode 100644
index 0000000000..07bf149640
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLQuoteElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLQuoteElement.h>
+
+namespace Web::HTML {
+
+HTMLQuoteElement::HTMLQuoteElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLQuoteElement::~HTMLQuoteElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLQuoteElement.h b/Userland/Libraries/LibWeb/HTML/HTMLQuoteElement.h
new file mode 100644
index 0000000000..98e254f213
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLQuoteElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLQuoteElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLQuoteElementWrapper;
+
+ HTMLQuoteElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLQuoteElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLQuoteElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLQuoteElement.idl
new file mode 100644
index 0000000000..390074f746
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLQuoteElement.idl
@@ -0,0 +1,5 @@
+interface HTMLQuoteElement : HTMLElement {
+
+ [Reflect] attribute DOMString cite;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLScriptElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLScriptElement.cpp
new file mode 100644
index 0000000000..cc3031984c
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLScriptElement.cpp
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/StringBuilder.h>
+#include <LibJS/Parser.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/DOM/Event.h>
+#include <LibWeb/DOM/Text.h>
+#include <LibWeb/HTML/EventNames.h>
+#include <LibWeb/HTML/HTMLScriptElement.h>
+#include <LibWeb/Loader/ResourceLoader.h>
+
+namespace Web::HTML {
+
+HTMLScriptElement::HTMLScriptElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLScriptElement::~HTMLScriptElement()
+{
+}
+
+void HTMLScriptElement::set_parser_document(Badge<HTMLDocumentParser>, DOM::Document& document)
+{
+ m_parser_document = document;
+}
+
+void HTMLScriptElement::set_non_blocking(Badge<HTMLDocumentParser>, bool non_blocking)
+{
+ m_non_blocking = non_blocking;
+}
+
+void HTMLScriptElement::execute_script()
+{
+ document().run_javascript(m_script_source);
+
+ if (has_attribute(HTML::AttributeNames::src))
+ dispatch_event(DOM::Event::create(EventNames::load));
+}
+
+void HTMLScriptElement::prepare_script(Badge<HTMLDocumentParser>)
+{
+ if (m_already_started)
+ return;
+ RefPtr<DOM::Document> parser_document = m_parser_document.ptr();
+ m_parser_document = nullptr;
+
+ if (parser_document && !has_attribute(HTML::AttributeNames::async)) {
+ m_non_blocking = true;
+ }
+
+ auto source_text = child_text_content();
+ if (!has_attribute(HTML::AttributeNames::src) && source_text.is_empty())
+ return;
+
+ if (!is_connected())
+ return;
+
+ // FIXME: Check the "type" and "language" attributes
+
+ if (parser_document) {
+ m_parser_document = *parser_document;
+ m_non_blocking = false;
+ }
+
+ m_already_started = true;
+ m_preparation_time_document = document();
+
+ if (parser_document && parser_document.ptr() != m_preparation_time_document.ptr()) {
+ return;
+ }
+
+ // FIXME: Check if scripting is disabled, if so return
+ // FIXME: Check the "nomodule" content attribute
+ // FIXME: Check CSP
+ // FIXME: Check "event" and "for" attributes
+ // FIXME: Check "charset" attribute
+ // FIXME: Check CORS
+ // FIXME: Module script credentials mode
+ // FIXME: Cryptographic nonce
+ // FIXME: Check "integrity" attribute
+ // FIXME: Check "referrerpolicy" attribute
+
+ m_parser_inserted = !!m_parser_document;
+
+ // FIXME: Check fetch options
+
+ if (has_attribute(HTML::AttributeNames::src)) {
+ auto src = attribute(HTML::AttributeNames::src);
+ if (src.is_empty()) {
+ // FIXME: Fire an "error" event at the element and return
+ ASSERT_NOT_REACHED();
+ }
+ m_from_an_external_file = true;
+
+ auto url = document().complete_url(src);
+ if (!url.is_valid()) {
+ // FIXME: Fire an "error" event at the element and return
+ ASSERT_NOT_REACHED();
+ }
+
+ // FIXME: Check classic vs. module script type
+
+ // FIXME: This load should be made asynchronous and the parser should spin an event loop etc.
+ ResourceLoader::the().load_sync(
+ url,
+ [this, url](auto data, auto&) {
+ if (data.is_null()) {
+ dbgln("HTMLScriptElement: Failed to load {}", url);
+ return;
+ }
+ m_script_source = String::copy(data);
+ script_became_ready();
+ },
+ [this](auto&) {
+ m_failed_to_load = true;
+ });
+ } else {
+ // FIXME: Check classic vs. module script type
+ m_script_source = source_text;
+ script_became_ready();
+ }
+
+ // FIXME: Check classic vs. module
+ if (has_attribute(HTML::AttributeNames::src) && has_attribute(HTML::AttributeNames::defer) && m_parser_inserted && !has_attribute(HTML::AttributeNames::async)) {
+ document().add_script_to_execute_when_parsing_has_finished({}, *this);
+ }
+
+ else if (has_attribute(HTML::AttributeNames::src) && m_parser_inserted && !has_attribute(HTML::AttributeNames::async)) {
+
+ document().set_pending_parsing_blocking_script({}, this);
+ when_the_script_is_ready([this] {
+ m_ready_to_be_parser_executed = true;
+ });
+ }
+
+ else if (has_attribute(HTML::AttributeNames::src) && !has_attribute(HTML::AttributeNames::async) && !m_non_blocking) {
+ ASSERT_NOT_REACHED();
+ }
+
+ else if (has_attribute(HTML::AttributeNames::src)) {
+ m_preparation_time_document->add_script_to_execute_as_soon_as_possible({}, *this);
+ }
+
+ else {
+ // Immediately execute the script block, even if other scripts are already executing.
+ execute_script();
+ }
+}
+
+void HTMLScriptElement::script_became_ready()
+{
+ m_script_ready = true;
+ if (!m_script_ready_callback)
+ return;
+ m_script_ready_callback();
+ m_script_ready_callback = nullptr;
+}
+
+void HTMLScriptElement::when_the_script_is_ready(Function<void()> callback)
+{
+ if (m_script_ready) {
+ callback();
+ return;
+ }
+ m_script_ready_callback = move(callback);
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLScriptElement.h b/Userland/Libraries/LibWeb/HTML/HTMLScriptElement.h
new file mode 100644
index 0000000000..b15afcdd7a
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLScriptElement.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/Function.h>
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLScriptElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLScriptElementWrapper;
+
+ HTMLScriptElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLScriptElement() override;
+
+ bool is_non_blocking() const { return m_non_blocking; }
+ bool is_ready_to_be_parser_executed() const { return m_ready_to_be_parser_executed; }
+ bool failed_to_load() const { return m_failed_to_load; }
+
+ void set_parser_document(Badge<HTMLDocumentParser>, DOM::Document&);
+ void set_non_blocking(Badge<HTMLDocumentParser>, bool);
+ void set_already_started(Badge<HTMLDocumentParser>, bool b) { m_already_started = b; }
+ void prepare_script(Badge<HTMLDocumentParser>);
+ void execute_script();
+
+private:
+ void script_became_ready();
+ void when_the_script_is_ready(Function<void()>);
+
+ WeakPtr<DOM::Document> m_parser_document;
+ WeakPtr<DOM::Document> m_preparation_time_document;
+ bool m_non_blocking { false };
+ bool m_already_started { false };
+ bool m_parser_inserted { false };
+ bool m_from_an_external_file { false };
+ bool m_script_ready { false };
+ bool m_ready_to_be_parser_executed { false };
+ bool m_failed_to_load { false };
+
+ Function<void()> m_script_ready_callback;
+
+ String m_script_source;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLScriptElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLScriptElement.idl
new file mode 100644
index 0000000000..370dadd29a
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLScriptElement.idl
@@ -0,0 +1,13 @@
+interface HTMLScriptElement : HTMLElement {
+
+ [Reflect] attribute DOMString src;
+ [Reflect] attribute DOMString type;
+ [Reflect=nomodule] attribute boolean noModule;
+ [Reflect] attribute boolean defer;
+ [Reflect] attribute DOMString integrity;
+
+ [Reflect] attribute DOMString charset;
+ [Reflect] attribute DOMString event;
+ [Reflect=for] attribute DOMString htmlFor;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.cpp
new file mode 100644
index 0000000000..9c4b486b05
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLSelectElement.h>
+
+namespace Web::HTML {
+
+HTMLSelectElement::HTMLSelectElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLSelectElement::~HTMLSelectElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.h b/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.h
new file mode 100644
index 0000000000..49d66970b9
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLSelectElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLSelectElementWrapper;
+
+ HTMLSelectElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLSelectElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.idl
new file mode 100644
index 0000000000..808db0b45f
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.idl
@@ -0,0 +1,7 @@
+interface HTMLSelectElement : HTMLElement {
+
+ [Reflect] attribute boolean disabled;
+ [Reflect] attribute boolean multiple;
+ [Reflect] attribute boolean required;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLSlotElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLSlotElement.cpp
new file mode 100644
index 0000000000..cb32ab7772
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLSlotElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLSlotElement.h>
+
+namespace Web::HTML {
+
+HTMLSlotElement::HTMLSlotElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLSlotElement::~HTMLSlotElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLSlotElement.h b/Userland/Libraries/LibWeb/HTML/HTMLSlotElement.h
new file mode 100644
index 0000000000..96c42ff6f2
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLSlotElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLSlotElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLSlotElementWrapper;
+
+ HTMLSlotElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLSlotElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLSlotElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLSlotElement.idl
new file mode 100644
index 0000000000..04dc7a7111
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLSlotElement.idl
@@ -0,0 +1,5 @@
+interface HTMLSlotElement : HTMLElement {
+
+ [Reflect] attribute DOMString name;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLSourceElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLSourceElement.cpp
new file mode 100644
index 0000000000..3dcf89815e
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLSourceElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLSourceElement.h>
+
+namespace Web::HTML {
+
+HTMLSourceElement::HTMLSourceElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLSourceElement::~HTMLSourceElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLSourceElement.h b/Userland/Libraries/LibWeb/HTML/HTMLSourceElement.h
new file mode 100644
index 0000000000..f126968ddd
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLSourceElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLSourceElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLSourceElementWrapper;
+
+ HTMLSourceElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLSourceElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLSourceElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLSourceElement.idl
new file mode 100644
index 0000000000..5496d55a1a
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLSourceElement.idl
@@ -0,0 +1,9 @@
+interface HTMLSourceElement : HTMLElement {
+
+ [Reflect] attribute DOMString src;
+ [Reflect] attribute DOMString type;
+ [Reflect] attribute DOMString srcset;
+ [Reflect] attribute DOMString sizes;
+ [Reflect] attribute DOMString media;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLSpanElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLSpanElement.cpp
new file mode 100644
index 0000000000..568d24f891
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLSpanElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLSpanElement.h>
+
+namespace Web::HTML {
+
+HTMLSpanElement::HTMLSpanElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLSpanElement::~HTMLSpanElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLSpanElement.h b/Userland/Libraries/LibWeb/HTML/HTMLSpanElement.h
new file mode 100644
index 0000000000..1906eb2a56
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLSpanElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLSpanElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLSpanElementWrapper;
+
+ HTMLSpanElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLSpanElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLSpanElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLSpanElement.idl
new file mode 100644
index 0000000000..a87dda0eb7
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLSpanElement.idl
@@ -0,0 +1,5 @@
+interface HTMLSpanElement : HTMLElement {
+
+
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLStyleElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLStyleElement.cpp
new file mode 100644
index 0000000000..44fa6daea0
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLStyleElement.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/StringBuilder.h>
+#include <LibWeb/CSS/Parser/CSSParser.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/DOM/Text.h>
+#include <LibWeb/HTML/HTMLStyleElement.h>
+
+namespace Web::HTML {
+
+HTMLStyleElement::HTMLStyleElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLStyleElement::~HTMLStyleElement()
+{
+}
+
+void HTMLStyleElement::children_changed()
+{
+ StringBuilder builder;
+ for_each_child([&](auto& child) {
+ if (is<DOM::Text>(child))
+ builder.append(downcast<DOM::Text>(child).text_content());
+ });
+ m_stylesheet = parse_css(CSS::ParsingContext(document()), builder.to_string());
+ if (m_stylesheet)
+ document().style_sheets().add_sheet(*m_stylesheet);
+ else
+ document().style_sheets().add_sheet(CSS::StyleSheet::create({}));
+ HTMLElement::children_changed();
+}
+
+void HTMLStyleElement::removed_from(Node& old_parent)
+{
+ if (m_stylesheet) {
+ // FIXME: Remove the sheet from the document
+ }
+ return HTMLElement::removed_from(old_parent);
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLStyleElement.h b/Userland/Libraries/LibWeb/HTML/HTMLStyleElement.h
new file mode 100644
index 0000000000..9e3b615c0c
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLStyleElement.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLStyleElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLStyleElementWrapper;
+
+ HTMLStyleElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLStyleElement() override;
+
+ virtual void children_changed() override;
+ virtual void removed_from(Node&) override;
+
+private:
+ RefPtr<CSS::StyleSheet> m_stylesheet;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLStyleElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLStyleElement.idl
new file mode 100644
index 0000000000..a5ab2934f3
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLStyleElement.idl
@@ -0,0 +1,7 @@
+interface HTMLStyleElement : HTMLElement {
+
+ [Reflect] attribute DOMString media;
+
+ [Reflect] attribute DOMString type;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableCaptionElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLTableCaptionElement.cpp
new file mode 100644
index 0000000000..a9c3e62a98
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLTableCaptionElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLTableCaptionElement.h>
+
+namespace Web::HTML {
+
+HTMLTableCaptionElement::HTMLTableCaptionElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLTableCaptionElement::~HTMLTableCaptionElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableCaptionElement.h b/Userland/Libraries/LibWeb/HTML/HTMLTableCaptionElement.h
new file mode 100644
index 0000000000..691b49ab3c
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLTableCaptionElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLTableCaptionElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLTableCaptionElementWrapper;
+
+ HTMLTableCaptionElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLTableCaptionElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableCaptionElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLTableCaptionElement.idl
new file mode 100644
index 0000000000..97d9419ccf
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLTableCaptionElement.idl
@@ -0,0 +1,5 @@
+interface HTMLTableCaptionElement : HTMLElement {
+
+ [Reflect] attribute DOMString align;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableCellElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLTableCellElement.cpp
new file mode 100644
index 0000000000..5dd60f2cbb
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLTableCellElement.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/CSS/Parser/CSSParser.h>
+#include <LibWeb/HTML/HTMLTableCellElement.h>
+
+namespace Web::HTML {
+
+HTMLTableCellElement::HTMLTableCellElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLTableCellElement::~HTMLTableCellElement()
+{
+}
+
+void HTMLTableCellElement::apply_presentational_hints(CSS::StyleProperties& style) const
+{
+ for_each_attribute([&](auto& name, auto& value) {
+ if (name == HTML::AttributeNames::bgcolor) {
+ auto color = Color::from_string(value);
+ if (color.has_value())
+ style.set_property(CSS::PropertyID::BackgroundColor, CSS::ColorStyleValue::create(color.value()));
+ return;
+ }
+ if (name == HTML::AttributeNames::align) {
+ if (value.equals_ignoring_case("center") || value.equals_ignoring_case("middle"))
+ style.set_property(CSS::PropertyID::TextAlign, StringView("-libweb-center"));
+ else
+ style.set_property(CSS::PropertyID::TextAlign, value.view());
+ return;
+ }
+ if (name == HTML::AttributeNames::width) {
+ if (auto parsed_value = parse_html_length(document(), value))
+ style.set_property(CSS::PropertyID::Width, parsed_value.release_nonnull());
+ return;
+ }
+ });
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableCellElement.h b/Userland/Libraries/LibWeb/HTML/HTMLTableCellElement.h
new file mode 100644
index 0000000000..7a2b9e4f09
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLTableCellElement.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLTableCellElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLTableCellElementWrapper;
+
+ HTMLTableCellElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLTableCellElement() override;
+
+private:
+ virtual void apply_presentational_hints(CSS::StyleProperties&) const override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableCellElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLTableCellElement.idl
new file mode 100644
index 0000000000..bcf8f1a04a
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLTableCellElement.idl
@@ -0,0 +1,18 @@
+interface HTMLTableCellElement : HTMLElement {
+
+ [Reflect] attribute DOMString headers;
+ [Reflect] attribute DOMString abbr;
+
+ [Reflect] attribute DOMString align;
+ [Reflect] attribute DOMString axis;
+ [Reflect] attribute DOMString height;
+ [Reflect] attribute DOMString width;
+
+ [Reflect=char] attribute DOMString ch;
+ [Reflect=charoff] attribute DOMString chOff;
+ [Reflect=nowrap] attribute boolean noWrap;
+ [Reflect=valign] attribute DOMString vAlign;
+
+ [LegacyNullToEmptyString, Reflect=bgcolor] attribute DOMString bgColor;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableColElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLTableColElement.cpp
new file mode 100644
index 0000000000..17a3e494c8
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLTableColElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLTableColElement.h>
+
+namespace Web::HTML {
+
+HTMLTableColElement::HTMLTableColElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLTableColElement::~HTMLTableColElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableColElement.h b/Userland/Libraries/LibWeb/HTML/HTMLTableColElement.h
new file mode 100644
index 0000000000..c6b8e706ff
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLTableColElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLTableColElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLTableColElementWrapper;
+
+ HTMLTableColElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLTableColElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableColElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLTableColElement.idl
new file mode 100644
index 0000000000..d76f0da76b
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLTableColElement.idl
@@ -0,0 +1,9 @@
+interface HTMLTableColElement : HTMLElement {
+
+ [Reflect] attribute DOMString align;
+ [Reflect=char] attribute DOMString ch;
+ [Reflect=charoff] attribute DOMString chOff;
+ [Reflect=valign] attribute DOMString vAlign;
+ [Reflect] attribute DOMString width;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLTableElement.cpp
new file mode 100644
index 0000000000..d31641af42
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLTableElement.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/CSS/Parser/CSSParser.h>
+#include <LibWeb/HTML/HTMLTableElement.h>
+
+namespace Web::HTML {
+
+HTMLTableElement::HTMLTableElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLTableElement::~HTMLTableElement()
+{
+}
+
+void HTMLTableElement::apply_presentational_hints(CSS::StyleProperties& style) const
+{
+ for_each_attribute([&](auto& name, auto& value) {
+ if (name == HTML::AttributeNames::width) {
+ if (auto parsed_value = parse_html_length(document(), value))
+ style.set_property(CSS::PropertyID::Width, parsed_value.release_nonnull());
+ return;
+ }
+ if (name == HTML::AttributeNames::height) {
+ if (auto parsed_value = parse_html_length(document(), value))
+ style.set_property(CSS::PropertyID::Height, parsed_value.release_nonnull());
+ return;
+ }
+ if (name == HTML::AttributeNames::bgcolor) {
+ auto color = Color::from_string(value);
+ if (color.has_value())
+ style.set_property(CSS::PropertyID::BackgroundColor, CSS::ColorStyleValue::create(color.value()));
+ return;
+ }
+ });
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableElement.h b/Userland/Libraries/LibWeb/HTML/HTMLTableElement.h
new file mode 100644
index 0000000000..39d65833c6
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLTableElement.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLTableElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLTableElementWrapper;
+
+ HTMLTableElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLTableElement() override;
+
+private:
+ virtual void apply_presentational_hints(CSS::StyleProperties&) const override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLTableElement.idl
new file mode 100644
index 0000000000..0990efa4a1
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLTableElement.idl
@@ -0,0 +1,14 @@
+interface HTMLTableElement : HTMLElement {
+
+ [Reflect] attribute DOMString align;
+ [Reflect] attribute DOMString border;
+ [Reflect] attribute DOMString frame;
+ [Reflect] attribute DOMString rules;
+ [Reflect] attribute DOMString summary;
+ [Reflect] attribute DOMString width;
+
+ [LegacyNullToEmptyString, Reflect=bgcolor] attribute DOMString bgColor;
+ [LegacyNullToEmptyString, Reflect=cellpadding] attribute DOMString cellPadding;
+ [LegacyNullToEmptyString, Reflect=cellspacing] attribute DOMString cellSpacing;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableRowElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLTableRowElement.cpp
new file mode 100644
index 0000000000..28f0d6d910
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLTableRowElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLTableRowElement.h>
+
+namespace Web::HTML {
+
+HTMLTableRowElement::HTMLTableRowElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLTableRowElement::~HTMLTableRowElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableRowElement.h b/Userland/Libraries/LibWeb/HTML/HTMLTableRowElement.h
new file mode 100644
index 0000000000..dc64d13322
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLTableRowElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLTableRowElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLTableRowElementWrapper;
+
+ HTMLTableRowElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLTableRowElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableRowElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLTableRowElement.idl
new file mode 100644
index 0000000000..1b999948f4
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLTableRowElement.idl
@@ -0,0 +1,10 @@
+interface HTMLTableRowElement : HTMLElement {
+
+ [Reflect] attribute DOMString align;
+ [Reflect=char] attribute DOMString ch;
+ [Reflect=charoff] attribute DOMString chOff;
+ [Reflect=valign] attribute DOMString vAlign;
+
+ [LegacyNullToEmptyString, Reflect=bgcolor] attribute DOMString bgColor;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableSectionElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLTableSectionElement.cpp
new file mode 100644
index 0000000000..2ce4394b94
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLTableSectionElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLTableSectionElement.h>
+
+namespace Web::HTML {
+
+HTMLTableSectionElement::HTMLTableSectionElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLTableSectionElement::~HTMLTableSectionElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableSectionElement.h b/Userland/Libraries/LibWeb/HTML/HTMLTableSectionElement.h
new file mode 100644
index 0000000000..dc58ea8a1b
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLTableSectionElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLTableSectionElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLTableSectionElementWrapper;
+
+ HTMLTableSectionElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLTableSectionElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTableSectionElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLTableSectionElement.idl
new file mode 100644
index 0000000000..4201411b8c
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLTableSectionElement.idl
@@ -0,0 +1,8 @@
+interface HTMLTableSectionElement : HTMLElement {
+
+ [Reflect] attribute DOMString align;
+ [Reflect=char] attribute DOMString ch;
+ [Reflect=charoff] attribute DOMString chOff;
+ [Reflect=valign] attribute DOMString vAlign;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTemplateElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLTemplateElement.cpp
new file mode 100644
index 0000000000..8a0209337b
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLTemplateElement.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/HTML/HTMLTemplateElement.h>
+
+namespace Web::HTML {
+
+HTMLTemplateElement::HTMLTemplateElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+ m_content = adopt(*new DOM::DocumentFragment(appropriate_template_contents_owner_document(document)));
+ m_content->set_host(*this);
+}
+
+HTMLTemplateElement::~HTMLTemplateElement()
+{
+}
+
+DOM::Document& HTMLTemplateElement::appropriate_template_contents_owner_document(DOM::Document& document)
+{
+ if (!document.created_for_appropriate_template_contents()) {
+ if (!document.associated_inert_template_document()) {
+ auto new_document = DOM::Document::create();
+ new_document->set_created_for_appropriate_template_contents(true);
+
+ // FIXME: If doc is an HTML document, mark new doc as an HTML document also.
+
+ document.set_associated_inert_template_document(new_document);
+ }
+
+ return *document.associated_inert_template_document();
+ }
+
+ return document;
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTemplateElement.h b/Userland/Libraries/LibWeb/HTML/HTMLTemplateElement.h
new file mode 100644
index 0000000000..8e26320684
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLTemplateElement.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/DOM/DocumentFragment.h>
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLTemplateElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLTemplateElementWrapper;
+
+ HTMLTemplateElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLTemplateElement() override;
+
+ NonnullRefPtr<DOM::DocumentFragment> content() { return *m_content; }
+ const NonnullRefPtr<DOM::DocumentFragment> content() const { return *m_content; }
+
+private:
+ DOM::Document& appropriate_template_contents_owner_document(DOM::Document&);
+
+ RefPtr<DOM::DocumentFragment> m_content;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTemplateElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLTemplateElement.idl
new file mode 100644
index 0000000000..efbbc57e02
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLTemplateElement.idl
@@ -0,0 +1,5 @@
+interface HTMLTemplateElement : HTMLElement {
+
+ readonly attribute DocumentFragment content;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp
new file mode 100644
index 0000000000..29433042c4
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLTextAreaElement.h>
+
+namespace Web::HTML {
+
+HTMLTextAreaElement::HTMLTextAreaElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLTextAreaElement::~HTMLTextAreaElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.h b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.h
new file mode 100644
index 0000000000..e09b95541a
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLTextAreaElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLTextAreaElementWrapper;
+
+ HTMLTextAreaElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLTextAreaElement() override;
+
+ const String& type() const
+ {
+ static String textarea = "textarea";
+ return textarea;
+ }
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.idl
new file mode 100644
index 0000000000..878cc67db0
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.idl
@@ -0,0 +1,12 @@
+interface HTMLTextAreaElement : HTMLElement {
+
+ [Reflect] attribute DOMString placeholder;
+ [Reflect] attribute DOMString name;
+ [Reflect] attribute DOMString wrap;
+ readonly attribute DOMString type;
+
+ [Reflect] attribute boolean disabled;
+ [Reflect=readonly] attribute boolean readOnly;
+ [Reflect] attribute boolean required;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTimeElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLTimeElement.cpp
new file mode 100644
index 0000000000..613f8aadbe
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLTimeElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLTimeElement.h>
+
+namespace Web::HTML {
+
+HTMLTimeElement::HTMLTimeElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLTimeElement::~HTMLTimeElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTimeElement.h b/Userland/Libraries/LibWeb/HTML/HTMLTimeElement.h
new file mode 100644
index 0000000000..aacef209a9
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLTimeElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLTimeElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLTimeElementWrapper;
+
+ HTMLTimeElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLTimeElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTimeElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLTimeElement.idl
new file mode 100644
index 0000000000..f82d1e1aea
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLTimeElement.idl
@@ -0,0 +1,5 @@
+interface HTMLTimeElement : HTMLElement {
+
+ [Reflect=datetime] attribute DOMString dateTime;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTitleElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLTitleElement.cpp
new file mode 100644
index 0000000000..a1404dfe9e
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLTitleElement.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/HTML/HTMLTitleElement.h>
+#include <LibWeb/Page/Page.h>
+
+namespace Web::HTML {
+
+HTMLTitleElement::HTMLTitleElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLTitleElement::~HTMLTitleElement()
+{
+}
+
+void HTMLTitleElement::children_changed()
+{
+ HTMLElement::children_changed();
+ if (auto* page = document().page())
+ page->client().page_did_change_title(document().title());
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTitleElement.h b/Userland/Libraries/LibWeb/HTML/HTMLTitleElement.h
new file mode 100644
index 0000000000..42c28f467f
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLTitleElement.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLTitleElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLTitleElementWrapper;
+
+ HTMLTitleElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLTitleElement() override;
+
+private:
+ virtual void children_changed() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTitleElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLTitleElement.idl
new file mode 100644
index 0000000000..6cfbc9ef29
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLTitleElement.idl
@@ -0,0 +1,5 @@
+interface HTMLTitleElement : HTMLElement {
+
+
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTrackElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLTrackElement.cpp
new file mode 100644
index 0000000000..2f0cee7f28
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLTrackElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLTrackElement.h>
+
+namespace Web::HTML {
+
+HTMLTrackElement::HTMLTrackElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLTrackElement::~HTMLTrackElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTrackElement.h b/Userland/Libraries/LibWeb/HTML/HTMLTrackElement.h
new file mode 100644
index 0000000000..6a0c78c176
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLTrackElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLTrackElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLTrackElementWrapper;
+
+ HTMLTrackElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLTrackElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTrackElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLTrackElement.idl
new file mode 100644
index 0000000000..d58d662cb0
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLTrackElement.idl
@@ -0,0 +1,8 @@
+interface HTMLTrackElement : HTMLElement {
+
+ [Reflect] attribute DOMString src;
+ [Reflect] attribute DOMString srclang;
+ [Reflect] attribute DOMString label;
+ [Reflect] attribute boolean default;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLUListElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLUListElement.cpp
new file mode 100644
index 0000000000..d9cbcb9cd7
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLUListElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLUListElement.h>
+
+namespace Web::HTML {
+
+HTMLUListElement::HTMLUListElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLUListElement::~HTMLUListElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLUListElement.h b/Userland/Libraries/LibWeb/HTML/HTMLUListElement.h
new file mode 100644
index 0000000000..c4efa7949a
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLUListElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLUListElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLUListElementWrapper;
+
+ HTMLUListElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLUListElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLUListElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLUListElement.idl
new file mode 100644
index 0000000000..6490a6d5c8
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLUListElement.idl
@@ -0,0 +1,6 @@
+interface HTMLUListElement : HTMLElement {
+
+ [Reflect] attribute boolean compact;
+ [Reflect] attribute DOMString type;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLUnknownElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLUnknownElement.cpp
new file mode 100644
index 0000000000..3ef559692d
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLUnknownElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLUnknownElement.h>
+
+namespace Web::HTML {
+
+HTMLUnknownElement::HTMLUnknownElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLElement(document, qualified_name)
+{
+}
+
+HTMLUnknownElement::~HTMLUnknownElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLUnknownElement.h b/Userland/Libraries/LibWeb/HTML/HTMLUnknownElement.h
new file mode 100644
index 0000000000..896df0d064
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLUnknownElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLElement.h>
+
+namespace Web::HTML {
+
+class HTMLUnknownElement final : public HTMLElement {
+public:
+ using WrapperType = Bindings::HTMLUnknownElementWrapper;
+
+ HTMLUnknownElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLUnknownElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLUnknownElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLUnknownElement.idl
new file mode 100644
index 0000000000..7a6672d4f4
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLUnknownElement.idl
@@ -0,0 +1,5 @@
+interface HTMLUnknownElement : HTMLElement {
+
+
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLVideoElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLVideoElement.cpp
new file mode 100644
index 0000000000..cbd97199cf
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLVideoElement.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/HTMLVideoElement.h>
+
+namespace Web::HTML {
+
+HTMLVideoElement::HTMLVideoElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : HTMLMediaElement(document, qualified_name)
+{
+}
+
+HTMLVideoElement::~HTMLVideoElement()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLVideoElement.h b/Userland/Libraries/LibWeb/HTML/HTMLVideoElement.h
new file mode 100644
index 0000000000..9349f0feb6
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLVideoElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, The SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLMediaElement.h>
+
+namespace Web::HTML {
+
+class HTMLVideoElement final : public HTMLMediaElement {
+public:
+ using WrapperType = Bindings::HTMLVideoElementWrapper;
+
+ HTMLVideoElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~HTMLVideoElement() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLVideoElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLVideoElement.idl
new file mode 100644
index 0000000000..6fb6ba3a50
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/HTMLVideoElement.idl
@@ -0,0 +1,6 @@
+interface HTMLVideoElement : HTMLMediaElement {
+
+ [Reflect] attribute DOMString poster;
+ [Reflect=playsinline] attribute boolean playsInline;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/ImageData.cpp b/Userland/Libraries/LibWeb/HTML/ImageData.cpp
new file mode 100644
index 0000000000..085cc88728
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/ImageData.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibGfx/Bitmap.h>
+#include <LibJS/Runtime/Uint8ClampedArray.h>
+#include <LibWeb/HTML/ImageData.h>
+
+namespace Web::HTML {
+
+RefPtr<ImageData> ImageData::create_with_size(JS::GlobalObject& global_object, int width, int height)
+{
+ if (width <= 0 || height <= 0)
+ return nullptr;
+
+ if (width > 16384 || height > 16384)
+ return nullptr;
+
+ dbgln("Creating ImageData with {}x{}", width, height);
+
+ auto* data = JS::Uint8ClampedArray::create(global_object, width * height * 4);
+ if (!data)
+ return nullptr;
+
+ auto data_handle = JS::make_handle(data);
+
+ auto bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::RGBA32, Gfx::IntSize(width, height), width * sizeof(u32), (u32*)data->data());
+ if (!bitmap)
+ return nullptr;
+ return adopt(*new ImageData(bitmap.release_nonnull(), move(data_handle)));
+}
+
+ImageData::ImageData(NonnullRefPtr<Gfx::Bitmap> bitmap, JS::Handle<JS::Uint8ClampedArray> data)
+ : m_bitmap(move(bitmap))
+ , m_data(move(data))
+{
+}
+
+ImageData::~ImageData()
+{
+}
+
+unsigned ImageData::width() const
+{
+ return m_bitmap->width();
+}
+
+unsigned ImageData::height() const
+{
+ return m_bitmap->height();
+}
+
+JS::Uint8ClampedArray* ImageData::data()
+{
+ return m_data.cell();
+}
+
+const JS::Uint8ClampedArray* ImageData::data() const
+{
+ return m_data.cell();
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/ImageData.h b/Userland/Libraries/LibWeb/HTML/ImageData.h
new file mode 100644
index 0000000000..810400d187
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/ImageData.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibGfx/Forward.h>
+#include <LibJS/Heap/Handle.h>
+#include <LibWeb/Bindings/Wrappable.h>
+
+namespace Web::HTML {
+
+class ImageData
+ : public RefCounted<ImageData>
+ , public Bindings::Wrappable {
+public:
+ using WrapperType = Bindings::ImageDataWrapper;
+
+ static RefPtr<ImageData> create_with_size(JS::GlobalObject&, int width, int height);
+
+ ~ImageData();
+
+ unsigned width() const;
+ unsigned height() const;
+
+ Gfx::Bitmap& bitmap() { return m_bitmap; }
+ const Gfx::Bitmap& bitmap() const { return m_bitmap; }
+
+ JS::Uint8ClampedArray* data();
+ const JS::Uint8ClampedArray* data() const;
+
+private:
+ explicit ImageData(NonnullRefPtr<Gfx::Bitmap>, JS::Handle<JS::Uint8ClampedArray>);
+
+ NonnullRefPtr<Gfx::Bitmap> m_bitmap;
+ JS::Handle<JS::Uint8ClampedArray> m_data;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/ImageData.idl b/Userland/Libraries/LibWeb/HTML/ImageData.idl
new file mode 100644
index 0000000000..29b683b547
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/ImageData.idl
@@ -0,0 +1,7 @@
+interface ImageData {
+
+ readonly attribute unsigned long width;
+ readonly attribute unsigned long height;
+ readonly attribute Uint8ClampedArray data;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/Parser/Entities.cpp b/Userland/Libraries/LibWeb/HTML/Parser/Entities.cpp
new file mode 100644
index 0000000000..edda468b0a
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/Parser/Entities.cpp
@@ -0,0 +1,2302 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/LogStream.h>
+#include <AK/StringView.h>
+#include <LibWeb/HTML/Parser/Entities.h>
+
+namespace Web {
+namespace HTML {
+
+Optional<EntityMatch> code_points_from_entity(const StringView& entity)
+{
+ constexpr struct {
+ StringView entity;
+ u32 code_point;
+ } single_code_point_entities[] = {
+ { "AElig;", 0x000C6 },
+ { "AElig", 0x000C6 },
+ { "AMP;", 0x00026 },
+ { "AMP", 0x00026 },
+ { "Aacute;", 0x000C1 },
+ { "Aacute", 0x000C1 },
+ { "Abreve;", 0x00102 },
+ { "Acirc;", 0x000C2 },
+ { "Acirc", 0x000C2 },
+ { "Acy;", 0x00410 },
+ { "Afr;", 0x1D504 },
+ { "Agrave;", 0x000C0 },
+ { "Agrave", 0x000C0 },
+ { "Alpha;", 0x00391 },
+ { "Amacr;", 0x00100 },
+ { "And;", 0x02A53 },
+ { "Aogon;", 0x00104 },
+ { "Aopf;", 0x1D538 },
+ { "ApplyFunction;", 0x02061 },
+ { "Aring;", 0x000C5 },
+ { "Aring", 0x000C5 },
+ { "Ascr;", 0x1D49C },
+ { "Assign;", 0x02254 },
+ { "Atilde;", 0x000C3 },
+ { "Atilde", 0x000C3 },
+ { "Auml;", 0x000C4 },
+ { "Auml", 0x000C4 },
+ { "Backslash;", 0x02216 },
+ { "Barv;", 0x02AE7 },
+ { "Barwed;", 0x02306 },
+ { "Bcy;", 0x00411 },
+ { "Because;", 0x02235 },
+ { "Bernoullis;", 0x0212C },
+ { "Beta;", 0x00392 },
+ { "Bfr;", 0x1D505 },
+ { "Bopf;", 0x1D539 },
+ { "Breve;", 0x002D8 },
+ { "Bscr;", 0x0212C },
+ { "Bumpeq;", 0x0224E },
+ { "CHcy;", 0x00427 },
+ { "COPY;", 0x000A9 },
+ { "COPY", 0x000A9 },
+ { "Cacute;", 0x00106 },
+ { "Cap;", 0x022D2 },
+ { "CapitalDifferentialD;", 0x02145 },
+ { "Cayleys;", 0x0212D },
+ { "Ccaron;", 0x0010C },
+ { "Ccedil;", 0x000C7 },
+ { "Ccedil", 0x000C7 },
+ { "Ccirc;", 0x00108 },
+ { "Cconint;", 0x02230 },
+ { "Cdot;", 0x0010A },
+ { "Cedilla;", 0x000B8 },
+ { "CenterDot;", 0x000B7 },
+ { "Cfr;", 0x0212D },
+ { "Chi;", 0x003A7 },
+ { "CircleDot;", 0x02299 },
+ { "CircleMinus;", 0x02296 },
+ { "CirclePlus;", 0x02295 },
+ { "CircleTimes;", 0x02297 },
+ { "ClockwiseContourIntegral;", 0x02232 },
+ { "CloseCurlyDoubleQuote;", 0x0201D },
+ { "CloseCurlyQuote;", 0x02019 },
+ { "Colon;", 0x02237 },
+ { "Colone;", 0x02A74 },
+ { "Congruent;", 0x02261 },
+ { "Conint;", 0x0222F },
+ { "ContourIntegral;", 0x0222E },
+ { "Copf;", 0x02102 },
+ { "Coproduct;", 0x02210 },
+ { "CounterClockwiseContourIntegral;", 0x02233 },
+ { "Cross;", 0x02A2F },
+ { "Cscr;", 0x1D49E },
+ { "Cup;", 0x022D3 },
+ { "CupCap;", 0x0224D },
+ { "DD;", 0x02145 },
+ { "DDotrahd;", 0x02911 },
+ { "DJcy;", 0x00402 },
+ { "DScy;", 0x00405 },
+ { "DZcy;", 0x0040F },
+ { "Dagger;", 0x02021 },
+ { "Darr;", 0x021A1 },
+ { "Dashv;", 0x02AE4 },
+ { "Dcaron;", 0x0010E },
+ { "Dcy;", 0x00414 },
+ { "Del;", 0x02207 },
+ { "Delta;", 0x00394 },
+ { "Dfr;", 0x1D507 },
+ { "DiacriticalAcute;", 0x000B4 },
+ { "DiacriticalDot;", 0x002D9 },
+ { "DiacriticalDoubleAcute;", 0x002DD },
+ { "DiacriticalGrave;", 0x00060 },
+ { "DiacriticalTilde;", 0x002DC },
+ { "Diamond;", 0x022C4 },
+ { "DifferentialD;", 0x02146 },
+ { "Dopf;", 0x1D53B },
+ { "Dot;", 0x000A8 },
+ { "DotDot;", 0x020DC },
+ { "DotEqual;", 0x02250 },
+ { "DoubleContourIntegral;", 0x0222F },
+ { "DoubleDot;", 0x000A8 },
+ { "DoubleDownArrow;", 0x021D3 },
+ { "DoubleLeftArrow;", 0x021D0 },
+ { "DoubleLeftRightArrow;", 0x021D4 },
+ { "DoubleLeftTee;", 0x02AE4 },
+ { "DoubleLongLeftArrow;", 0x027F8 },
+ { "DoubleLongLeftRightArrow;", 0x027FA },
+ { "DoubleLongRightArrow;", 0x027F9 },
+ { "DoubleRightArrow;", 0x021D2 },
+ { "DoubleRightTee;", 0x022A8 },
+ { "DoubleUpArrow;", 0x021D1 },
+ { "DoubleUpDownArrow;", 0x021D5 },
+ { "DoubleVerticalBar;", 0x02225 },
+ { "DownArrow;", 0x02193 },
+ { "DownArrowBar;", 0x02913 },
+ { "DownArrowUpArrow;", 0x021F5 },
+ { "DownBreve;", 0x00311 },
+ { "DownLeftRightVector;", 0x02950 },
+ { "DownLeftTeeVector;", 0x0295E },
+ { "DownLeftVector;", 0x021BD },
+ { "DownLeftVectorBar;", 0x02956 },
+ { "DownRightTeeVector;", 0x0295F },
+ { "DownRightVector;", 0x021C1 },
+ { "DownRightVectorBar;", 0x02957 },
+ { "DownTee;", 0x022A4 },
+ { "DownTeeArrow;", 0x021A7 },
+ { "Downarrow;", 0x021D3 },
+ { "Dscr;", 0x1D49F },
+ { "Dstrok;", 0x00110 },
+ { "ENG;", 0x0014A },
+ { "ETH;", 0x000D0 },
+ { "ETH", 0x000D0 },
+ { "Eacute;", 0x000C9 },
+ { "Eacute", 0x000C9 },
+ { "Ecaron;", 0x0011A },
+ { "Ecirc;", 0x000CA },
+ { "Ecirc", 0x000CA },
+ { "Ecy;", 0x0042D },
+ { "Edot;", 0x00116 },
+ { "Efr;", 0x1D508 },
+ { "Egrave;", 0x000C8 },
+ { "Egrave", 0x000C8 },
+ { "Element;", 0x02208 },
+ { "Emacr;", 0x00112 },
+ { "EmptySmallSquare;", 0x025FB },
+ { "EmptyVerySmallSquare;", 0x025AB },
+ { "Eogon;", 0x00118 },
+ { "Eopf;", 0x1D53C },
+ { "Epsilon;", 0x00395 },
+ { "Equal;", 0x02A75 },
+ { "EqualTilde;", 0x02242 },
+ { "Equilibrium;", 0x021CC },
+ { "Escr;", 0x02130 },
+ { "Esim;", 0x02A73 },
+ { "Eta;", 0x00397 },
+ { "Euml;", 0x000CB },
+ { "Euml", 0x000CB },
+ { "Exists;", 0x02203 },
+ { "ExponentialE;", 0x02147 },
+ { "Fcy;", 0x00424 },
+ { "Ffr;", 0x1D509 },
+ { "FilledSmallSquare;", 0x025FC },
+ { "FilledVerySmallSquare;", 0x025AA },
+ { "Fopf;", 0x1D53D },
+ { "ForAll;", 0x02200 },
+ { "Fouriertrf;", 0x02131 },
+ { "Fscr;", 0x02131 },
+ { "GJcy;", 0x00403 },
+ { "GT;", 0x0003E },
+ { "GT", 0x0003E },
+ { "Gamma;", 0x00393 },
+ { "Gammad;", 0x003DC },
+ { "Gbreve;", 0x0011E },
+ { "Gcedil;", 0x00122 },
+ { "Gcirc;", 0x0011C },
+ { "Gcy;", 0x00413 },
+ { "Gdot;", 0x00120 },
+ { "Gfr;", 0x1D50A },
+ { "Gg;", 0x022D9 },
+ { "Gopf;", 0x1D53E },
+ { "GreaterEqual;", 0x02265 },
+ { "GreaterEqualLess;", 0x022DB },
+ { "GreaterFullEqual;", 0x02267 },
+ { "GreaterGreater;", 0x02AA2 },
+ { "GreaterLess;", 0x02277 },
+ { "GreaterSlantEqual;", 0x02A7E },
+ { "GreaterTilde;", 0x02273 },
+ { "Gscr;", 0x1D4A2 },
+ { "Gt;", 0x0226B },
+ { "HARDcy;", 0x0042A },
+ { "Hacek;", 0x002C7 },
+ { "Hat;", 0x0005E },
+ { "Hcirc;", 0x00124 },
+ { "Hfr;", 0x0210C },
+ { "HilbertSpace;", 0x0210B },
+ { "Hopf;", 0x0210D },
+ { "HorizontalLine;", 0x02500 },
+ { "Hscr;", 0x0210B },
+ { "Hstrok;", 0x00126 },
+ { "HumpDownHump;", 0x0224E },
+ { "HumpEqual;", 0x0224F },
+ { "IEcy;", 0x00415 },
+ { "IJlig;", 0x00132 },
+ { "IOcy;", 0x00401 },
+ { "Iacute;", 0x000CD },
+ { "Iacute", 0x000CD },
+ { "Icirc;", 0x000CE },
+ { "Icirc", 0x000CE },
+ { "Icy;", 0x00418 },
+ { "Idot;", 0x00130 },
+ { "Ifr;", 0x02111 },
+ { "Igrave;", 0x000CC },
+ { "Igrave", 0x000CC },
+ { "Im;", 0x02111 },
+ { "Imacr;", 0x0012A },
+ { "ImaginaryI;", 0x02148 },
+ { "Implies;", 0x021D2 },
+ { "Int;", 0x0222C },
+ { "Integral;", 0x0222B },
+ { "Intersection;", 0x022C2 },
+ { "InvisibleComma;", 0x02063 },
+ { "InvisibleTimes;", 0x02062 },
+ { "Iogon;", 0x0012E },
+ { "Iopf;", 0x1D540 },
+ { "Iota;", 0x00399 },
+ { "Iscr;", 0x02110 },
+ { "Itilde;", 0x00128 },
+ { "Iukcy;", 0x00406 },
+ { "Iuml;", 0x000CF },
+ { "Iuml", 0x000CF },
+ { "Jcirc;", 0x00134 },
+ { "Jcy;", 0x00419 },
+ { "Jfr;", 0x1D50D },
+ { "Jopf;", 0x1D541 },
+ { "Jscr;", 0x1D4A5 },
+ { "Jsercy;", 0x00408 },
+ { "Jukcy;", 0x00404 },
+ { "KHcy;", 0x00425 },
+ { "KJcy;", 0x0040C },
+ { "Kappa;", 0x0039A },
+ { "Kcedil;", 0x00136 },
+ { "Kcy;", 0x0041A },
+ { "Kfr;", 0x1D50E },
+ { "Kopf;", 0x1D542 },
+ { "Kscr;", 0x1D4A6 },
+ { "LJcy;", 0x00409 },
+ { "LT;", 0x0003C },
+ { "LT", 0x0003C },
+ { "Lacute;", 0x00139 },
+ { "Lambda;", 0x0039B },
+ { "Lang;", 0x027EA },
+ { "Laplacetrf;", 0x02112 },
+ { "Larr;", 0x0219E },
+ { "Lcaron;", 0x0013D },
+ { "Lcedil;", 0x0013B },
+ { "Lcy;", 0x0041B },
+ { "LeftAngleBracket;", 0x027E8 },
+ { "LeftArrow;", 0x02190 },
+ { "LeftArrowBar;", 0x021E4 },
+ { "LeftArrowRightArrow;", 0x021C6 },
+ { "LeftCeiling;", 0x02308 },
+ { "LeftDoubleBracket;", 0x027E6 },
+ { "LeftDownTeeVector;", 0x02961 },
+ { "LeftDownVector;", 0x021C3 },
+ { "LeftDownVectorBar;", 0x02959 },
+ { "LeftFloor;", 0x0230A },
+ { "LeftRightArrow;", 0x02194 },
+ { "LeftRightVector;", 0x0294E },
+ { "LeftTee;", 0x022A3 },
+ { "LeftTeeArrow;", 0x021A4 },
+ { "LeftTeeVector;", 0x0295A },
+ { "LeftTriangle;", 0x022B2 },
+ { "LeftTriangleBar;", 0x029CF },
+ { "LeftTriangleEqual;", 0x022B4 },
+ { "LeftUpDownVector;", 0x02951 },
+ { "LeftUpTeeVector;", 0x02960 },
+ { "LeftUpVector;", 0x021BF },
+ { "LeftUpVectorBar;", 0x02958 },
+ { "LeftVector;", 0x021BC },
+ { "LeftVectorBar;", 0x02952 },
+ { "Leftarrow;", 0x021D0 },
+ { "Leftrightarrow;", 0x021D4 },
+ { "LessEqualGreater;", 0x022DA },
+ { "LessFullEqual;", 0x02266 },
+ { "LessGreater;", 0x02276 },
+ { "LessLess;", 0x02AA1 },
+ { "LessSlantEqual;", 0x02A7D },
+ { "LessTilde;", 0x02272 },
+ { "Lfr;", 0x1D50F },
+ { "Ll;", 0x022D8 },
+ { "Lleftarrow;", 0x021DA },
+ { "Lmidot;", 0x0013F },
+ { "LongLeftArrow;", 0x027F5 },
+ { "LongLeftRightArrow;", 0x027F7 },
+ { "LongRightArrow;", 0x027F6 },
+ { "Longleftarrow;", 0x027F8 },
+ { "Longleftrightarrow;", 0x027FA },
+ { "Longrightarrow;", 0x027F9 },
+ { "Lopf;", 0x1D543 },
+ { "LowerLeftArrow;", 0x02199 },
+ { "LowerRightArrow;", 0x02198 },
+ { "Lscr;", 0x02112 },
+ { "Lsh;", 0x021B0 },
+ { "Lstrok;", 0x00141 },
+ { "Lt;", 0x0226A },
+ { "Map;", 0x02905 },
+ { "Mcy;", 0x0041C },
+ { "MediumSpace;", 0x0205F },
+ { "Mellintrf;", 0x02133 },
+ { "Mfr;", 0x1D510 },
+ { "MinusPlus;", 0x02213 },
+ { "Mopf;", 0x1D544 },
+ { "Mscr;", 0x02133 },
+ { "Mu;", 0x0039C },
+ { "NJcy;", 0x0040A },
+ { "Nacute;", 0x00143 },
+ { "Ncaron;", 0x00147 },
+ { "Ncedil;", 0x00145 },
+ { "Ncy;", 0x0041D },
+ { "NegativeMediumSpace;", 0x0200B },
+ { "NegativeThickSpace;", 0x0200B },
+ { "NegativeThinSpace;", 0x0200B },
+ { "NegativeVeryThinSpace;", 0x0200B },
+ { "NestedGreaterGreater;", 0x0226B },
+ { "NestedLessLess;", 0x0226A },
+ { "NewLine;", 0x0000A },
+ { "Nfr;", 0x1D511 },
+ { "NoBreak;", 0x02060 },
+ { "NonBreakingSpace;", 0x000A0 },
+ { "Nopf;", 0x02115 },
+ { "Not;", 0x02AEC },
+ { "NotCongruent;", 0x02262 },
+ { "NotCupCap;", 0x0226D },
+ { "NotDoubleVerticalBar;", 0x02226 },
+ { "NotElement;", 0x02209 },
+ { "NotEqual;", 0x02260 },
+ { "NotExists;", 0x02204 },
+ { "NotGreater;", 0x0226F },
+ { "NotGreaterEqual;", 0x02271 },
+ { "NotGreaterLess;", 0x02279 },
+ { "NotGreaterTilde;", 0x02275 },
+ { "NotLeftTriangle;", 0x022EA },
+ { "NotLeftTriangleEqual;", 0x022EC },
+ { "NotLess;", 0x0226E },
+ { "NotLessEqual;", 0x02270 },
+ { "NotLessGreater;", 0x02278 },
+ { "NotLessTilde;", 0x02274 },
+ { "NotPrecedes;", 0x02280 },
+ { "NotPrecedesSlantEqual;", 0x022E0 },
+ { "NotReverseElement;", 0x0220C },
+ { "NotRightTriangle;", 0x022EB },
+ { "NotRightTriangleEqual;", 0x022ED },
+ { "NotSquareSubsetEqual;", 0x022E2 },
+ { "NotSquareSupersetEqual;", 0x022E3 },
+ { "NotSubsetEqual;", 0x02288 },
+ { "NotSucceeds;", 0x02281 },
+ { "NotSucceedsSlantEqual;", 0x022E1 },
+ { "NotSupersetEqual;", 0x02289 },
+ { "NotTilde;", 0x02241 },
+ { "NotTildeEqual;", 0x02244 },
+ { "NotTildeFullEqual;", 0x02247 },
+ { "NotTildeTilde;", 0x02249 },
+ { "NotVerticalBar;", 0x02224 },
+ { "Nscr;", 0x1D4A9 },
+ { "Ntilde;", 0x000D1 },
+ { "Ntilde", 0x000D1 },
+ { "Nu;", 0x0039D },
+ { "OElig;", 0x00152 },
+ { "Oacute;", 0x000D3 },
+ { "Oacute", 0x000D3 },
+ { "Ocirc;", 0x000D4 },
+ { "Ocirc", 0x000D4 },
+ { "Ocy;", 0x0041E },
+ { "Odblac;", 0x00150 },
+ { "Ofr;", 0x1D512 },
+ { "Ograve;", 0x000D2 },
+ { "Ograve", 0x000D2 },
+ { "Omacr;", 0x0014C },
+ { "Omega;", 0x003A9 },
+ { "Omicron;", 0x0039F },
+ { "Oopf;", 0x1D546 },
+ { "OpenCurlyDoubleQuote;", 0x0201C },
+ { "OpenCurlyQuote;", 0x02018 },
+ { "Or;", 0x02A54 },
+ { "Oscr;", 0x1D4AA },
+ { "Oslash;", 0x000D8 },
+ { "Oslash", 0x000D8 },
+ { "Otilde;", 0x000D5 },
+ { "Otilde", 0x000D5 },
+ { "Otimes;", 0x02A37 },
+ { "Ouml;", 0x000D6 },
+ { "Ouml", 0x000D6 },
+ { "OverBar;", 0x0203E },
+ { "OverBrace;", 0x023DE },
+ { "OverBracket;", 0x023B4 },
+ { "OverParenthesis;", 0x023DC },
+ { "PartialD;", 0x02202 },
+ { "Pcy;", 0x0041F },
+ { "Pfr;", 0x1D513 },
+ { "Phi;", 0x003A6 },
+ { "Pi;", 0x003A0 },
+ { "PlusMinus;", 0x000B1 },
+ { "Poincareplane;", 0x0210C },
+ { "Popf;", 0x02119 },
+ { "Pr;", 0x02ABB },
+ { "Precedes;", 0x0227A },
+ { "PrecedesEqual;", 0x02AAF },
+ { "PrecedesSlantEqual;", 0x0227C },
+ { "PrecedesTilde;", 0x0227E },
+ { "Prime;", 0x02033 },
+ { "Product;", 0x0220F },
+ { "Proportion;", 0x02237 },
+ { "Proportional;", 0x0221D },
+ { "Pscr;", 0x1D4AB },
+ { "Psi;", 0x003A8 },
+ { "QUOT;", 0x00022 },
+ { "QUOT", 0x00022 },
+ { "Qfr;", 0x1D514 },
+ { "Qopf;", 0x0211A },
+ { "Qscr;", 0x1D4AC },
+ { "RBarr;", 0x02910 },
+ { "REG;", 0x000AE },
+ { "REG", 0x000AE },
+ { "Racute;", 0x00154 },
+ { "Rang;", 0x027EB },
+ { "Rarr;", 0x021A0 },
+ { "Rarrtl;", 0x02916 },
+ { "Rcaron;", 0x00158 },
+ { "Rcedil;", 0x00156 },
+ { "Rcy;", 0x00420 },
+ { "Re;", 0x0211C },
+ { "ReverseElement;", 0x0220B },
+ { "ReverseEquilibrium;", 0x021CB },
+ { "ReverseUpEquilibrium;", 0x0296F },
+ { "Rfr;", 0x0211C },
+ { "Rho;", 0x003A1 },
+ { "RightAngleBracket;", 0x027E9 },
+ { "RightArrow;", 0x02192 },
+ { "RightArrowBar;", 0x021E5 },
+ { "RightArrowLeftArrow;", 0x021C4 },
+ { "RightCeiling;", 0x02309 },
+ { "RightDoubleBracket;", 0x027E7 },
+ { "RightDownTeeVector;", 0x0295D },
+ { "RightDownVector;", 0x021C2 },
+ { "RightDownVectorBar;", 0x02955 },
+ { "RightFloor;", 0x0230B },
+ { "RightTee;", 0x022A2 },
+ { "RightTeeArrow;", 0x021A6 },
+ { "RightTeeVector;", 0x0295B },
+ { "RightTriangle;", 0x022B3 },
+ { "RightTriangleBar;", 0x029D0 },
+ { "RightTriangleEqual;", 0x022B5 },
+ { "RightUpDownVector;", 0x0294F },
+ { "RightUpTeeVector;", 0x0295C },
+ { "RightUpVector;", 0x021BE },
+ { "RightUpVectorBar;", 0x02954 },
+ { "RightVector;", 0x021C0 },
+ { "RightVectorBar;", 0x02953 },
+ { "Rightarrow;", 0x021D2 },
+ { "Ropf;", 0x0211D },
+ { "RoundImplies;", 0x02970 },
+ { "Rrightarrow;", 0x021DB },
+ { "Rscr;", 0x0211B },
+ { "Rsh;", 0x021B1 },
+ { "RuleDelayed;", 0x029F4 },
+ { "SHCHcy;", 0x00429 },
+ { "SHcy;", 0x00428 },
+ { "SOFTcy;", 0x0042C },
+ { "Sacute;", 0x0015A },
+ { "Sc;", 0x02ABC },
+ { "Scaron;", 0x00160 },
+ { "Scedil;", 0x0015E },
+ { "Scirc;", 0x0015C },
+ { "Scy;", 0x00421 },
+ { "Sfr;", 0x1D516 },
+ { "ShortDownArrow;", 0x02193 },
+ { "ShortLeftArrow;", 0x02190 },
+ { "ShortRightArrow;", 0x02192 },
+ { "ShortUpArrow;", 0x02191 },
+ { "Sigma;", 0x003A3 },
+ { "SmallCircle;", 0x02218 },
+ { "Sopf;", 0x1D54A },
+ { "Sqrt;", 0x0221A },
+ { "Square;", 0x025A1 },
+ { "SquareIntersection;", 0x02293 },
+ { "SquareSubset;", 0x0228F },
+ { "SquareSubsetEqual;", 0x02291 },
+ { "SquareSuperset;", 0x02290 },
+ { "SquareSupersetEqual;", 0x02292 },
+ { "SquareUnion;", 0x02294 },
+ { "Sscr;", 0x1D4AE },
+ { "Star;", 0x022C6 },
+ { "Sub;", 0x022D0 },
+ { "Subset;", 0x022D0 },
+ { "SubsetEqual;", 0x02286 },
+ { "Succeeds;", 0x0227B },
+ { "SucceedsEqual;", 0x02AB0 },
+ { "SucceedsSlantEqual;", 0x0227D },
+ { "SucceedsTilde;", 0x0227F },
+ { "SuchThat;", 0x0220B },
+ { "Sum;", 0x02211 },
+ { "Sup;", 0x022D1 },
+ { "Superset;", 0x02283 },
+ { "SupersetEqual;", 0x02287 },
+ { "Supset;", 0x022D1 },
+ { "THORN;", 0x000DE },
+ { "THORN", 0x000DE },
+ { "TRADE;", 0x02122 },
+ { "TSHcy;", 0x0040B },
+ { "TScy;", 0x00426 },
+ { "Tab;", 0x00009 },
+ { "Tau;", 0x003A4 },
+ { "Tcaron;", 0x00164 },
+ { "Tcedil;", 0x00162 },
+ { "Tcy;", 0x00422 },
+ { "Tfr;", 0x1D517 },
+ { "Therefore;", 0x02234 },
+ { "Theta;", 0x00398 },
+ { "ThinSpace;", 0x02009 },
+ { "Tilde;", 0x0223C },
+ { "TildeEqual;", 0x02243 },
+ { "TildeFullEqual;", 0x02245 },
+ { "TildeTilde;", 0x02248 },
+ { "Topf;", 0x1D54B },
+ { "TripleDot;", 0x020DB },
+ { "Tscr;", 0x1D4AF },
+ { "Tstrok;", 0x00166 },
+ { "Uacute;", 0x000DA },
+ { "Uacute", 0x000DA },
+ { "Uarr;", 0x0219F },
+ { "Uarrocir;", 0x02949 },
+ { "Ubrcy;", 0x0040E },
+ { "Ubreve;", 0x0016C },
+ { "Ucirc;", 0x000DB },
+ { "Ucirc", 0x000DB },
+ { "Ucy;", 0x00423 },
+ { "Udblac;", 0x00170 },
+ { "Ufr;", 0x1D518 },
+ { "Ugrave;", 0x000D9 },
+ { "Ugrave", 0x000D9 },
+ { "Umacr;", 0x0016A },
+ { "UnderBar;", 0x0005F },
+ { "UnderBrace;", 0x023DF },
+ { "UnderBracket;", 0x023B5 },
+ { "UnderParenthesis;", 0x023DD },
+ { "Union;", 0x022C3 },
+ { "UnionPlus;", 0x0228E },
+ { "Uogon;", 0x00172 },
+ { "Uopf;", 0x1D54C },
+ { "UpArrow;", 0x02191 },
+ { "UpArrowBar;", 0x02912 },
+ { "UpArrowDownArrow;", 0x021C5 },
+ { "UpDownArrow;", 0x02195 },
+ { "UpEquilibrium;", 0x0296E },
+ { "UpTee;", 0x022A5 },
+ { "UpTeeArrow;", 0x021A5 },
+ { "Uparrow;", 0x021D1 },
+ { "Updownarrow;", 0x021D5 },
+ { "UpperLeftArrow;", 0x02196 },
+ { "UpperRightArrow;", 0x02197 },
+ { "Upsi;", 0x003D2 },
+ { "Upsilon;", 0x003A5 },
+ { "Uring;", 0x0016E },
+ { "Uscr;", 0x1D4B0 },
+ { "Utilde;", 0x00168 },
+ { "Uuml;", 0x000DC },
+ { "Uuml", 0x000DC },
+ { "VDash;", 0x022AB },
+ { "Vbar;", 0x02AEB },
+ { "Vcy;", 0x00412 },
+ { "Vdash;", 0x022A9 },
+ { "Vdashl;", 0x02AE6 },
+ { "Vee;", 0x022C1 },
+ { "Verbar;", 0x02016 },
+ { "Vert;", 0x02016 },
+ { "VerticalBar;", 0x02223 },
+ { "VerticalLine;", 0x0007C },
+ { "VerticalSeparator;", 0x02758 },
+ { "VerticalTilde;", 0x02240 },
+ { "VeryThinSpace;", 0x0200A },
+ { "Vfr;", 0x1D519 },
+ { "Vopf;", 0x1D54D },
+ { "Vscr;", 0x1D4B1 },
+ { "Vvdash;", 0x022AA },
+ { "Wcirc;", 0x00174 },
+ { "Wedge;", 0x022C0 },
+ { "Wfr;", 0x1D51A },
+ { "Wopf;", 0x1D54E },
+ { "Wscr;", 0x1D4B2 },
+ { "Xfr;", 0x1D51B },
+ { "Xi;", 0x0039E },
+ { "Xopf;", 0x1D54F },
+ { "Xscr;", 0x1D4B3 },
+ { "YAcy;", 0x0042F },
+ { "YIcy;", 0x00407 },
+ { "YUcy;", 0x0042E },
+ { "Yacute;", 0x000DD },
+ { "Yacute", 0x000DD },
+ { "Ycirc;", 0x00176 },
+ { "Ycy;", 0x0042B },
+ { "Yfr;", 0x1D51C },
+ { "Yopf;", 0x1D550 },
+ { "Yscr;", 0x1D4B4 },
+ { "Yuml;", 0x00178 },
+ { "ZHcy;", 0x00416 },
+ { "Zacute;", 0x00179 },
+ { "Zcaron;", 0x0017D },
+ { "Zcy;", 0x00417 },
+ { "Zdot;", 0x0017B },
+ { "ZeroWidthSpace;", 0x0200B },
+ { "Zeta;", 0x00396 },
+ { "Zfr;", 0x02128 },
+ { "Zopf;", 0x02124 },
+ { "Zscr;", 0x1D4B5 },
+ { "aacute;", 0x000E1 },
+ { "aacute", 0x000E1 },
+ { "abreve;", 0x00103 },
+ { "ac;", 0x0223E },
+ { "acd;", 0x0223F },
+ { "acirc;", 0x000E2 },
+ { "acirc", 0x000E2 },
+ { "acute;", 0x000B4 },
+ { "acute", 0x000B4 },
+ { "acy;", 0x00430 },
+ { "aelig;", 0x000E6 },
+ { "aelig", 0x000E6 },
+ { "af;", 0x02061 },
+ { "afr;", 0x1D51E },
+ { "agrave;", 0x000E0 },
+ { "agrave", 0x000E0 },
+ { "alefsym;", 0x02135 },
+ { "aleph;", 0x02135 },
+ { "alpha;", 0x003B1 },
+ { "amacr;", 0x00101 },
+ { "amalg;", 0x02A3F },
+ { "amp;", 0x00026 },
+ { "amp", 0x00026 },
+ { "and;", 0x02227 },
+ { "andand;", 0x02A55 },
+ { "andd;", 0x02A5C },
+ { "andslope;", 0x02A58 },
+ { "andv;", 0x02A5A },
+ { "ang;", 0x02220 },
+ { "ange;", 0x029A4 },
+ { "angle;", 0x02220 },
+ { "angmsd;", 0x02221 },
+ { "angmsdaa;", 0x029A8 },
+ { "angmsdab;", 0x029A9 },
+ { "angmsdac;", 0x029AA },
+ { "angmsdad;", 0x029AB },
+ { "angmsdae;", 0x029AC },
+ { "angmsdaf;", 0x029AD },
+ { "angmsdag;", 0x029AE },
+ { "angmsdah;", 0x029AF },
+ { "angrt;", 0x0221F },
+ { "angrtvb;", 0x022BE },
+ { "angrtvbd;", 0x0299D },
+ { "angsph;", 0x02222 },
+ { "angst;", 0x000C5 },
+ { "angzarr;", 0x0237C },
+ { "aogon;", 0x00105 },
+ { "aopf;", 0x1D552 },
+ { "ap;", 0x02248 },
+ { "apE;", 0x02A70 },
+ { "apacir;", 0x02A6F },
+ { "ape;", 0x0224A },
+ { "apid;", 0x0224B },
+ { "apos;", 0x00027 },
+ { "approx;", 0x02248 },
+ { "approxeq;", 0x0224A },
+ { "aring;", 0x000E5 },
+ { "aring", 0x000E5 },
+ { "ascr;", 0x1D4B6 },
+ { "ast;", 0x0002A },
+ { "asymp;", 0x02248 },
+ { "asympeq;", 0x0224D },
+ { "atilde;", 0x000E3 },
+ { "atilde", 0x000E3 },
+ { "auml;", 0x000E4 },
+ { "auml", 0x000E4 },
+ { "awconint;", 0x02233 },
+ { "awint;", 0x02A11 },
+ { "bNot;", 0x02AED },
+ { "backcong;", 0x0224C },
+ { "backepsilon;", 0x003F6 },
+ { "backprime;", 0x02035 },
+ { "backsim;", 0x0223D },
+ { "backsimeq;", 0x022CD },
+ { "barvee;", 0x022BD },
+ { "barwed;", 0x02305 },
+ { "barwedge;", 0x02305 },
+ { "bbrk;", 0x023B5 },
+ { "bbrktbrk;", 0x023B6 },
+ { "bcong;", 0x0224C },
+ { "bcy;", 0x00431 },
+ { "bdquo;", 0x0201E },
+ { "becaus;", 0x02235 },
+ { "because;", 0x02235 },
+ { "bemptyv;", 0x029B0 },
+ { "bepsi;", 0x003F6 },
+ { "bernou;", 0x0212C },
+ { "beta;", 0x003B2 },
+ { "beth;", 0x02136 },
+ { "between;", 0x0226C },
+ { "bfr;", 0x1D51F },
+ { "bigcap;", 0x022C2 },
+ { "bigcirc;", 0x025EF },
+ { "bigcup;", 0x022C3 },
+ { "bigodot;", 0x02A00 },
+ { "bigoplus;", 0x02A01 },
+ { "bigotimes;", 0x02A02 },
+ { "bigsqcup;", 0x02A06 },
+ { "bigstar;", 0x02605 },
+ { "bigtriangledown;", 0x025BD },
+ { "bigtriangleup;", 0x025B3 },
+ { "biguplus;", 0x02A04 },
+ { "bigvee;", 0x022C1 },
+ { "bigwedge;", 0x022C0 },
+ { "bkarow;", 0x0290D },
+ { "blacklozenge;", 0x029EB },
+ { "blacksquare;", 0x025AA },
+ { "blacktriangle;", 0x025B4 },
+ { "blacktriangledown;", 0x025BE },
+ { "blacktriangleleft;", 0x025C2 },
+ { "blacktriangleright;", 0x025B8 },
+ { "blank;", 0x02423 },
+ { "blk12;", 0x02592 },
+ { "blk14;", 0x02591 },
+ { "blk34;", 0x02593 },
+ { "block;", 0x02588 },
+ { "bnot;", 0x02310 },
+ { "bopf;", 0x1D553 },
+ { "bot;", 0x022A5 },
+ { "bottom;", 0x022A5 },
+ { "bowtie;", 0x022C8 },
+ { "boxDL;", 0x02557 },
+ { "boxDR;", 0x02554 },
+ { "boxDl;", 0x02556 },
+ { "boxDr;", 0x02553 },
+ { "boxH;", 0x02550 },
+ { "boxHD;", 0x02566 },
+ { "boxHU;", 0x02569 },
+ { "boxHd;", 0x02564 },
+ { "boxHu;", 0x02567 },
+ { "boxUL;", 0x0255D },
+ { "boxUR;", 0x0255A },
+ { "boxUl;", 0x0255C },
+ { "boxUr;", 0x02559 },
+ { "boxV;", 0x02551 },
+ { "boxVH;", 0x0256C },
+ { "boxVL;", 0x02563 },
+ { "boxVR;", 0x02560 },
+ { "boxVh;", 0x0256B },
+ { "boxVl;", 0x02562 },
+ { "boxVr;", 0x0255F },
+ { "boxbox;", 0x029C9 },
+ { "boxdL;", 0x02555 },
+ { "boxdR;", 0x02552 },
+ { "boxdl;", 0x02510 },
+ { "boxdr;", 0x0250C },
+ { "boxh;", 0x02500 },
+ { "boxhD;", 0x02565 },
+ { "boxhU;", 0x02568 },
+ { "boxhd;", 0x0252C },
+ { "boxhu;", 0x02534 },
+ { "boxminus;", 0x0229F },
+ { "boxplus;", 0x0229E },
+ { "boxtimes;", 0x022A0 },
+ { "boxuL;", 0x0255B },
+ { "boxuR;", 0x02558 },
+ { "boxul;", 0x02518 },
+ { "boxur;", 0x02514 },
+ { "boxv;", 0x02502 },
+ { "boxvH;", 0x0256A },
+ { "boxvL;", 0x02561 },
+ { "boxvR;", 0x0255E },
+ { "boxvh;", 0x0253C },
+ { "boxvl;", 0x02524 },
+ { "boxvr;", 0x0251C },
+ { "bprime;", 0x02035 },
+ { "breve;", 0x002D8 },
+ { "brvbar;", 0x000A6 },
+ { "brvbar", 0x000A6 },
+ { "bscr;", 0x1D4B7 },
+ { "bsemi;", 0x0204F },
+ { "bsim;", 0x0223D },
+ { "bsime;", 0x022CD },
+ { "bsol;", 0x0005C },
+ { "bsolb;", 0x029C5 },
+ { "bsolhsub;", 0x027C8 },
+ { "bull;", 0x02022 },
+ { "bullet;", 0x02022 },
+ { "bump;", 0x0224E },
+ { "bumpE;", 0x02AAE },
+ { "bumpe;", 0x0224F },
+ { "bumpeq;", 0x0224F },
+ { "cacute;", 0x00107 },
+ { "cap;", 0x02229 },
+ { "capand;", 0x02A44 },
+ { "capbrcup;", 0x02A49 },
+ { "capcap;", 0x02A4B },
+ { "capcup;", 0x02A47 },
+ { "capdot;", 0x02A40 },
+ { "caret;", 0x02041 },
+ { "caron;", 0x002C7 },
+ { "ccaps;", 0x02A4D },
+ { "ccaron;", 0x0010D },
+ { "ccedil;", 0x000E7 },
+ { "ccedil", 0x000E7 },
+ { "ccirc;", 0x00109 },
+ { "ccups;", 0x02A4C },
+ { "ccupssm;", 0x02A50 },
+ { "cdot;", 0x0010B },
+ { "cedil;", 0x000B8 },
+ { "cedil", 0x000B8 },
+ { "cemptyv;", 0x029B2 },
+ { "cent;", 0x000A2 },
+ { "cent", 0x000A2 },
+ { "centerdot;", 0x000B7 },
+ { "cfr;", 0x1D520 },
+ { "chcy;", 0x00447 },
+ { "check;", 0x02713 },
+ { "checkmark;", 0x02713 },
+ { "chi;", 0x003C7 },
+ { "cir;", 0x025CB },
+ { "cirE;", 0x029C3 },
+ { "circ;", 0x002C6 },
+ { "circeq;", 0x02257 },
+ { "circlearrowleft;", 0x021BA },
+ { "circlearrowright;", 0x021BB },
+ { "circledR;", 0x000AE },
+ { "circledS;", 0x024C8 },
+ { "circledast;", 0x0229B },
+ { "circledcirc;", 0x0229A },
+ { "circleddash;", 0x0229D },
+ { "cire;", 0x02257 },
+ { "cirfnint;", 0x02A10 },
+ { "cirmid;", 0x02AEF },
+ { "cirscir;", 0x029C2 },
+ { "clubs;", 0x02663 },
+ { "clubsuit;", 0x02663 },
+ { "colon;", 0x0003A },
+ { "colone;", 0x02254 },
+ { "coloneq;", 0x02254 },
+ { "comma;", 0x0002C },
+ { "commat;", 0x00040 },
+ { "comp;", 0x02201 },
+ { "compfn;", 0x02218 },
+ { "complement;", 0x02201 },
+ { "complexes;", 0x02102 },
+ { "cong;", 0x02245 },
+ { "congdot;", 0x02A6D },
+ { "conint;", 0x0222E },
+ { "copf;", 0x1D554 },
+ { "coprod;", 0x02210 },
+ { "copy;", 0x000A9 },
+ { "copy", 0x000A9 },
+ { "copysr;", 0x02117 },
+ { "crarr;", 0x021B5 },
+ { "cross;", 0x02717 },
+ { "cscr;", 0x1D4B8 },
+ { "csub;", 0x02ACF },
+ { "csube;", 0x02AD1 },
+ { "csup;", 0x02AD0 },
+ { "csupe;", 0x02AD2 },
+ { "ctdot;", 0x022EF },
+ { "cudarrl;", 0x02938 },
+ { "cudarrr;", 0x02935 },
+ { "cuepr;", 0x022DE },
+ { "cuesc;", 0x022DF },
+ { "cularr;", 0x021B6 },
+ { "cularrp;", 0x0293D },
+ { "cup;", 0x0222A },
+ { "cupbrcap;", 0x02A48 },
+ { "cupcap;", 0x02A46 },
+ { "cupcup;", 0x02A4A },
+ { "cupdot;", 0x0228D },
+ { "cupor;", 0x02A45 },
+ { "curarr;", 0x021B7 },
+ { "curarrm;", 0x0293C },
+ { "curlyeqprec;", 0x022DE },
+ { "curlyeqsucc;", 0x022DF },
+ { "curlyvee;", 0x022CE },
+ { "curlywedge;", 0x022CF },
+ { "curren;", 0x000A4 },
+ { "curren", 0x000A4 },
+ { "curvearrowleft;", 0x021B6 },
+ { "curvearrowright;", 0x021B7 },
+ { "cuvee;", 0x022CE },
+ { "cuwed;", 0x022CF },
+ { "cwconint;", 0x02232 },
+ { "cwint;", 0x02231 },
+ { "cylcty;", 0x0232D },
+ { "dArr;", 0x021D3 },
+ { "dHar;", 0x02965 },
+ { "dagger;", 0x02020 },
+ { "daleth;", 0x02138 },
+ { "darr;", 0x02193 },
+ { "dash;", 0x02010 },
+ { "dashv;", 0x022A3 },
+ { "dbkarow;", 0x0290F },
+ { "dblac;", 0x002DD },
+ { "dcaron;", 0x0010F },
+ { "dcy;", 0x00434 },
+ { "dd;", 0x02146 },
+ { "ddagger;", 0x02021 },
+ { "ddarr;", 0x021CA },
+ { "ddotseq;", 0x02A77 },
+ { "deg;", 0x000B0 },
+ { "deg", 0x000B0 },
+ { "delta;", 0x003B4 },
+ { "demptyv;", 0x029B1 },
+ { "dfisht;", 0x0297F },
+ { "dfr;", 0x1D521 },
+ { "dharl;", 0x021C3 },
+ { "dharr;", 0x021C2 },
+ { "diam;", 0x022C4 },
+ { "diamond;", 0x022C4 },
+ { "diamondsuit;", 0x02666 },
+ { "diams;", 0x02666 },
+ { "die;", 0x000A8 },
+ { "digamma;", 0x003DD },
+ { "disin;", 0x022F2 },
+ { "div;", 0x000F7 },
+ { "divide;", 0x000F7 },
+ { "divide", 0x000F7 },
+ { "divideontimes;", 0x022C7 },
+ { "divonx;", 0x022C7 },
+ { "djcy;", 0x00452 },
+ { "dlcorn;", 0x0231E },
+ { "dlcrop;", 0x0230D },
+ { "dollar;", 0x00024 },
+ { "dopf;", 0x1D555 },
+ { "dot;", 0x002D9 },
+ { "doteq;", 0x02250 },
+ { "doteqdot;", 0x02251 },
+ { "dotminus;", 0x02238 },
+ { "dotplus;", 0x02214 },
+ { "dotsquare;", 0x022A1 },
+ { "doublebarwedge;", 0x02306 },
+ { "downarrow;", 0x02193 },
+ { "downdownarrows;", 0x021CA },
+ { "downharpoonleft;", 0x021C3 },
+ { "downharpoonright;", 0x021C2 },
+ { "drbkarow;", 0x02910 },
+ { "drcorn;", 0x0231F },
+ { "drcrop;", 0x0230C },
+ { "dscr;", 0x1D4B9 },
+ { "dscy;", 0x00455 },
+ { "dsol;", 0x029F6 },
+ { "dstrok;", 0x00111 },
+ { "dtdot;", 0x022F1 },
+ { "dtri;", 0x025BF },
+ { "dtrif;", 0x025BE },
+ { "duarr;", 0x021F5 },
+ { "duhar;", 0x0296F },
+ { "dwangle;", 0x029A6 },
+ { "dzcy;", 0x0045F },
+ { "dzigrarr;", 0x027FF },
+ { "eDDot;", 0x02A77 },
+ { "eDot;", 0x02251 },
+ { "eacute;", 0x000E9 },
+ { "eacute", 0x000E9 },
+ { "easter;", 0x02A6E },
+ { "ecaron;", 0x0011B },
+ { "ecir;", 0x02256 },
+ { "ecirc;", 0x000EA },
+ { "ecirc", 0x000EA },
+ { "ecolon;", 0x02255 },
+ { "ecy;", 0x0044D },
+ { "edot;", 0x00117 },
+ { "ee;", 0x02147 },
+ { "efDot;", 0x02252 },
+ { "efr;", 0x1D522 },
+ { "eg;", 0x02A9A },
+ { "egrave;", 0x000E8 },
+ { "egrave", 0x000E8 },
+ { "egs;", 0x02A96 },
+ { "egsdot;", 0x02A98 },
+ { "el;", 0x02A99 },
+ { "elinters;", 0x023E7 },
+ { "ell;", 0x02113 },
+ { "els;", 0x02A95 },
+ { "elsdot;", 0x02A97 },
+ { "emacr;", 0x00113 },
+ { "empty;", 0x02205 },
+ { "emptyset;", 0x02205 },
+ { "emptyv;", 0x02205 },
+ { "emsp13;", 0x02004 },
+ { "emsp14;", 0x02005 },
+ { "emsp;", 0x02003 },
+ { "eng;", 0x0014B },
+ { "ensp;", 0x02002 },
+ { "eogon;", 0x00119 },
+ { "eopf;", 0x1D556 },
+ { "epar;", 0x022D5 },
+ { "eparsl;", 0x029E3 },
+ { "eplus;", 0x02A71 },
+ { "epsi;", 0x003B5 },
+ { "epsilon;", 0x003B5 },
+ { "epsiv;", 0x003F5 },
+ { "eqcirc;", 0x02256 },
+ { "eqcolon;", 0x02255 },
+ { "eqsim;", 0x02242 },
+ { "eqslantgtr;", 0x02A96 },
+ { "eqslantless;", 0x02A95 },
+ { "equals;", 0x0003D },
+ { "equest;", 0x0225F },
+ { "equiv;", 0x02261 },
+ { "equivDD;", 0x02A78 },
+ { "eqvparsl;", 0x029E5 },
+ { "erDot;", 0x02253 },
+ { "erarr;", 0x02971 },
+ { "escr;", 0x0212F },
+ { "esdot;", 0x02250 },
+ { "esim;", 0x02242 },
+ { "eta;", 0x003B7 },
+ { "eth;", 0x000F0 },
+ { "eth", 0x000F0 },
+ { "euml;", 0x000EB },
+ { "euml", 0x000EB },
+ { "euro;", 0x020AC },
+ { "excl;", 0x00021 },
+ { "exist;", 0x02203 },
+ { "expectation;", 0x02130 },
+ { "exponentiale;", 0x02147 },
+ { "fallingdotseq;", 0x02252 },
+ { "fcy;", 0x00444 },
+ { "female;", 0x02640 },
+ { "ffilig;", 0x0FB03 },
+ { "fflig;", 0x0FB00 },
+ { "ffllig;", 0x0FB04 },
+ { "ffr;", 0x1D523 },
+ { "filig;", 0x0FB01 },
+ { "flat;", 0x0266D },
+ { "fllig;", 0x0FB02 },
+ { "fltns;", 0x025B1 },
+ { "fnof;", 0x00192 },
+ { "fopf;", 0x1D557 },
+ { "forall;", 0x02200 },
+ { "fork;", 0x022D4 },
+ { "forkv;", 0x02AD9 },
+ { "fpartint;", 0x02A0D },
+ { "frac12;", 0x000BD },
+ { "frac12", 0x000BD },
+ { "frac13;", 0x02153 },
+ { "frac14;", 0x000BC },
+ { "frac14", 0x000BC },
+ { "frac15;", 0x02155 },
+ { "frac16;", 0x02159 },
+ { "frac18;", 0x0215B },
+ { "frac23;", 0x02154 },
+ { "frac25;", 0x02156 },
+ { "frac34;", 0x000BE },
+ { "frac34", 0x000BE },
+ { "frac35;", 0x02157 },
+ { "frac38;", 0x0215C },
+ { "frac45;", 0x02158 },
+ { "frac56;", 0x0215A },
+ { "frac58;", 0x0215D },
+ { "frac78;", 0x0215E },
+ { "frasl;", 0x02044 },
+ { "frown;", 0x02322 },
+ { "fscr;", 0x1D4BB },
+ { "gE;", 0x02267 },
+ { "gEl;", 0x02A8C },
+ { "gacute;", 0x001F5 },
+ { "gamma;", 0x003B3 },
+ { "gammad;", 0x003DD },
+ { "gap;", 0x02A86 },
+ { "gbreve;", 0x0011F },
+ { "gcirc;", 0x0011D },
+ { "gcy;", 0x00433 },
+ { "gdot;", 0x00121 },
+ { "ge;", 0x02265 },
+ { "gel;", 0x022DB },
+ { "geq;", 0x02265 },
+ { "geqq;", 0x02267 },
+ { "geqslant;", 0x02A7E },
+ { "ges;", 0x02A7E },
+ { "gescc;", 0x02AA9 },
+ { "gesdot;", 0x02A80 },
+ { "gesdoto;", 0x02A82 },
+ { "gesdotol;", 0x02A84 },
+ { "gesles;", 0x02A94 },
+ { "gfr;", 0x1D524 },
+ { "gg;", 0x0226B },
+ { "ggg;", 0x022D9 },
+ { "gimel;", 0x02137 },
+ { "gjcy;", 0x00453 },
+ { "gl;", 0x02277 },
+ { "glE;", 0x02A92 },
+ { "gla;", 0x02AA5 },
+ { "glj;", 0x02AA4 },
+ { "gnE;", 0x02269 },
+ { "gnap;", 0x02A8A },
+ { "gnapprox;", 0x02A8A },
+ { "gne;", 0x02A88 },
+ { "gneq;", 0x02A88 },
+ { "gneqq;", 0x02269 },
+ { "gnsim;", 0x022E7 },
+ { "gopf;", 0x1D558 },
+ { "grave;", 0x00060 },
+ { "gscr;", 0x0210A },
+ { "gsim;", 0x02273 },
+ { "gsime;", 0x02A8E },
+ { "gsiml;", 0x02A90 },
+ { "gt;", 0x0003E },
+ { "gt", 0x0003E },
+ { "gtcc;", 0x02AA7 },
+ { "gtcir;", 0x02A7A },
+ { "gtdot;", 0x022D7 },
+ { "gtlPar;", 0x02995 },
+ { "gtquest;", 0x02A7C },
+ { "gtrapprox;", 0x02A86 },
+ { "gtrarr;", 0x02978 },
+ { "gtrdot;", 0x022D7 },
+ { "gtreqless;", 0x022DB },
+ { "gtreqqless;", 0x02A8C },
+ { "gtrless;", 0x02277 },
+ { "gtrsim;", 0x02273 },
+ { "hArr;", 0x021D4 },
+ { "hairsp;", 0x0200A },
+ { "half;", 0x000BD },
+ { "hamilt;", 0x0210B },
+ { "hardcy;", 0x0044A },
+ { "harr;", 0x02194 },
+ { "harrcir;", 0x02948 },
+ { "harrw;", 0x021AD },
+ { "hbar;", 0x0210F },
+ { "hcirc;", 0x00125 },
+ { "hearts;", 0x02665 },
+ { "heartsuit;", 0x02665 },
+ { "hellip;", 0x02026 },
+ { "hercon;", 0x022B9 },
+ { "hfr;", 0x1D525 },
+ { "hksearow;", 0x02925 },
+ { "hkswarow;", 0x02926 },
+ { "hoarr;", 0x021FF },
+ { "homtht;", 0x0223B },
+ { "hookleftarrow;", 0x021A9 },
+ { "hookrightarrow;", 0x021AA },
+ { "hopf;", 0x1D559 },
+ { "horbar;", 0x02015 },
+ { "hscr;", 0x1D4BD },
+ { "hslash;", 0x0210F },
+ { "hstrok;", 0x00127 },
+ { "hybull;", 0x02043 },
+ { "hyphen;", 0x02010 },
+ { "iacute;", 0x000ED },
+ { "iacute", 0x000ED },
+ { "ic;", 0x02063 },
+ { "icirc;", 0x000EE },
+ { "icirc", 0x000EE },
+ { "icy;", 0x00438 },
+ { "iecy;", 0x00435 },
+ { "iexcl;", 0x000A1 },
+ { "iexcl", 0x000A1 },
+ { "iff;", 0x021D4 },
+ { "ifr;", 0x1D526 },
+ { "igrave;", 0x000EC },
+ { "igrave", 0x000EC },
+ { "ii;", 0x02148 },
+ { "iiiint;", 0x02A0C },
+ { "iiint;", 0x0222D },
+ { "iinfin;", 0x029DC },
+ { "iiota;", 0x02129 },
+ { "ijlig;", 0x00133 },
+ { "imacr;", 0x0012B },
+ { "image;", 0x02111 },
+ { "imagline;", 0x02110 },
+ { "imagpart;", 0x02111 },
+ { "imath;", 0x00131 },
+ { "imof;", 0x022B7 },
+ { "imped;", 0x001B5 },
+ { "in;", 0x02208 },
+ { "incare;", 0x02105 },
+ { "infin;", 0x0221E },
+ { "infintie;", 0x029DD },
+ { "inodot;", 0x00131 },
+ { "int;", 0x0222B },
+ { "intcal;", 0x022BA },
+ { "integers;", 0x02124 },
+ { "intercal;", 0x022BA },
+ { "intlarhk;", 0x02A17 },
+ { "intprod;", 0x02A3C },
+ { "iocy;", 0x00451 },
+ { "iogon;", 0x0012F },
+ { "iopf;", 0x1D55A },
+ { "iota;", 0x003B9 },
+ { "iprod;", 0x02A3C },
+ { "iquest;", 0x000BF },
+ { "iquest", 0x000BF },
+ { "iscr;", 0x1D4BE },
+ { "isin;", 0x02208 },
+ { "isinE;", 0x022F9 },
+ { "isindot;", 0x022F5 },
+ { "isins;", 0x022F4 },
+ { "isinsv;", 0x022F3 },
+ { "isinv;", 0x02208 },
+ { "it;", 0x02062 },
+ { "itilde;", 0x00129 },
+ { "iukcy;", 0x00456 },
+ { "iuml;", 0x000EF },
+ { "iuml", 0x000EF },
+ { "jcirc;", 0x00135 },
+ { "jcy;", 0x00439 },
+ { "jfr;", 0x1D527 },
+ { "jmath;", 0x00237 },
+ { "jopf;", 0x1D55B },
+ { "jscr;", 0x1D4BF },
+ { "jsercy;", 0x00458 },
+ { "jukcy;", 0x00454 },
+ { "kappa;", 0x003BA },
+ { "kappav;", 0x003F0 },
+ { "kcedil;", 0x00137 },
+ { "kcy;", 0x0043A },
+ { "kfr;", 0x1D528 },
+ { "kgreen;", 0x00138 },
+ { "khcy;", 0x00445 },
+ { "kjcy;", 0x0045C },
+ { "kopf;", 0x1D55C },
+ { "kscr;", 0x1D4C0 },
+ { "lAarr;", 0x021DA },
+ { "lArr;", 0x021D0 },
+ { "lAtail;", 0x0291B },
+ { "lBarr;", 0x0290E },
+ { "lE;", 0x02266 },
+ { "lEg;", 0x02A8B },
+ { "lHar;", 0x02962 },
+ { "lacute;", 0x0013A },
+ { "laemptyv;", 0x029B4 },
+ { "lagran;", 0x02112 },
+ { "lambda;", 0x003BB },
+ { "lang;", 0x027E8 },
+ { "langd;", 0x02991 },
+ { "langle;", 0x027E8 },
+ { "lap;", 0x02A85 },
+ { "laquo;", 0x000AB },
+ { "laquo", 0x000AB },
+ { "larr;", 0x02190 },
+ { "larrb;", 0x021E4 },
+ { "larrbfs;", 0x0291F },
+ { "larrfs;", 0x0291D },
+ { "larrhk;", 0x021A9 },
+ { "larrlp;", 0x021AB },
+ { "larrpl;", 0x02939 },
+ { "larrsim;", 0x02973 },
+ { "larrtl;", 0x021A2 },
+ { "lat;", 0x02AAB },
+ { "latail;", 0x02919 },
+ { "late;", 0x02AAD },
+ { "lbarr;", 0x0290C },
+ { "lbbrk;", 0x02772 },
+ { "lbrace;", 0x0007B },
+ { "lbrack;", 0x0005B },
+ { "lbrke;", 0x0298B },
+ { "lbrksld;", 0x0298F },
+ { "lbrkslu;", 0x0298D },
+ { "lcaron;", 0x0013E },
+ { "lcedil;", 0x0013C },
+ { "lceil;", 0x02308 },
+ { "lcub;", 0x0007B },
+ { "lcy;", 0x0043B },
+ { "ldca;", 0x02936 },
+ { "ldquo;", 0x0201C },
+ { "ldquor;", 0x0201E },
+ { "ldrdhar;", 0x02967 },
+ { "ldrushar;", 0x0294B },
+ { "ldsh;", 0x021B2 },
+ { "le;", 0x02264 },
+ { "leftarrow;", 0x02190 },
+ { "leftarrowtail;", 0x021A2 },
+ { "leftharpoondown;", 0x021BD },
+ { "leftharpoonup;", 0x021BC },
+ { "leftleftarrows;", 0x021C7 },
+ { "leftrightarrow;", 0x02194 },
+ { "leftrightarrows;", 0x021C6 },
+ { "leftrightharpoons;", 0x021CB },
+ { "leftrightsquigarrow;", 0x021AD },
+ { "leftthreetimes;", 0x022CB },
+ { "leg;", 0x022DA },
+ { "leq;", 0x02264 },
+ { "leqq;", 0x02266 },
+ { "leqslant;", 0x02A7D },
+ { "les;", 0x02A7D },
+ { "lescc;", 0x02AA8 },
+ { "lesdot;", 0x02A7F },
+ { "lesdoto;", 0x02A81 },
+ { "lesdotor;", 0x02A83 },
+ { "lesges;", 0x02A93 },
+ { "lessapprox;", 0x02A85 },
+ { "lessdot;", 0x022D6 },
+ { "lesseqgtr;", 0x022DA },
+ { "lesseqqgtr;", 0x02A8B },
+ { "lessgtr;", 0x02276 },
+ { "lesssim;", 0x02272 },
+ { "lfisht;", 0x0297C },
+ { "lfloor;", 0x0230A },
+ { "lfr;", 0x1D529 },
+ { "lg;", 0x02276 },
+ { "lgE;", 0x02A91 },
+ { "lhard;", 0x021BD },
+ { "lharu;", 0x021BC },
+ { "lharul;", 0x0296A },
+ { "lhblk;", 0x02584 },
+ { "ljcy;", 0x00459 },
+ { "ll;", 0x0226A },
+ { "llarr;", 0x021C7 },
+ { "llcorner;", 0x0231E },
+ { "llhard;", 0x0296B },
+ { "lltri;", 0x025FA },
+ { "lmidot;", 0x00140 },
+ { "lmoust;", 0x023B0 },
+ { "lmoustache;", 0x023B0 },
+ { "lnE;", 0x02268 },
+ { "lnap;", 0x02A89 },
+ { "lnapprox;", 0x02A89 },
+ { "lne;", 0x02A87 },
+ { "lneq;", 0x02A87 },
+ { "lneqq;", 0x02268 },
+ { "lnsim;", 0x022E6 },
+ { "loang;", 0x027EC },
+ { "loarr;", 0x021FD },
+ { "lobrk;", 0x027E6 },
+ { "longleftarrow;", 0x027F5 },
+ { "longleftrightarrow;", 0x027F7 },
+ { "longmapsto;", 0x027FC },
+ { "longrightarrow;", 0x027F6 },
+ { "looparrowleft;", 0x021AB },
+ { "looparrowright;", 0x021AC },
+ { "lopar;", 0x02985 },
+ { "lopf;", 0x1D55D },
+ { "loplus;", 0x02A2D },
+ { "lotimes;", 0x02A34 },
+ { "lowast;", 0x02217 },
+ { "lowbar;", 0x0005F },
+ { "loz;", 0x025CA },
+ { "lozenge;", 0x025CA },
+ { "lozf;", 0x029EB },
+ { "lpar;", 0x00028 },
+ { "lparlt;", 0x02993 },
+ { "lrarr;", 0x021C6 },
+ { "lrcorner;", 0x0231F },
+ { "lrhar;", 0x021CB },
+ { "lrhard;", 0x0296D },
+ { "lrm;", 0x0200E },
+ { "lrtri;", 0x022BF },
+ { "lsaquo;", 0x02039 },
+ { "lscr;", 0x1D4C1 },
+ { "lsh;", 0x021B0 },
+ { "lsim;", 0x02272 },
+ { "lsime;", 0x02A8D },
+ { "lsimg;", 0x02A8F },
+ { "lsqb;", 0x0005B },
+ { "lsquo;", 0x02018 },
+ { "lsquor;", 0x0201A },
+ { "lstrok;", 0x00142 },
+ { "lt;", 0x0003C },
+ { "lt", 0x0003C },
+ { "ltcc;", 0x02AA6 },
+ { "ltcir;", 0x02A79 },
+ { "ltdot;", 0x022D6 },
+ { "lthree;", 0x022CB },
+ { "ltimes;", 0x022C9 },
+ { "ltlarr;", 0x02976 },
+ { "ltquest;", 0x02A7B },
+ { "ltrPar;", 0x02996 },
+ { "ltri;", 0x025C3 },
+ { "ltrie;", 0x022B4 },
+ { "ltrif;", 0x025C2 },
+ { "lurdshar;", 0x0294A },
+ { "luruhar;", 0x02966 },
+ { "mDDot;", 0x0223A },
+ { "macr;", 0x000AF },
+ { "macr", 0x000AF },
+ { "male;", 0x02642 },
+ { "malt;", 0x02720 },
+ { "maltese;", 0x02720 },
+ { "map;", 0x021A6 },
+ { "mapsto;", 0x021A6 },
+ { "mapstodown;", 0x021A7 },
+ { "mapstoleft;", 0x021A4 },
+ { "mapstoup;", 0x021A5 },
+ { "marker;", 0x025AE },
+ { "mcomma;", 0x02A29 },
+ { "mcy;", 0x0043C },
+ { "mdash;", 0x02014 },
+ { "measuredangle;", 0x02221 },
+ { "mfr;", 0x1D52A },
+ { "mho;", 0x02127 },
+ { "micro;", 0x000B5 },
+ { "micro", 0x000B5 },
+ { "mid;", 0x02223 },
+ { "midast;", 0x0002A },
+ { "midcir;", 0x02AF0 },
+ { "middot;", 0x000B7 },
+ { "middot", 0x000B7 },
+ { "minus;", 0x02212 },
+ { "minusb;", 0x0229F },
+ { "minusd;", 0x02238 },
+ { "minusdu;", 0x02A2A },
+ { "mlcp;", 0x02ADB },
+ { "mldr;", 0x02026 },
+ { "mnplus;", 0x02213 },
+ { "models;", 0x022A7 },
+ { "mopf;", 0x1D55E },
+ { "mp;", 0x02213 },
+ { "mscr;", 0x1D4C2 },
+ { "mstpos;", 0x0223E },
+ { "mu;", 0x003BC },
+ { "multimap;", 0x022B8 },
+ { "mumap;", 0x022B8 },
+ { "nLeftarrow;", 0x021CD },
+ { "nLeftrightarrow;", 0x021CE },
+ { "nRightarrow;", 0x021CF },
+ { "nVDash;", 0x022AF },
+ { "nVdash;", 0x022AE },
+ { "nabla;", 0x02207 },
+ { "nacute;", 0x00144 },
+ { "nap;", 0x02249 },
+ { "napos;", 0x00149 },
+ { "napprox;", 0x02249 },
+ { "natur;", 0x0266E },
+ { "natural;", 0x0266E },
+ { "naturals;", 0x02115 },
+ { "nbsp;", 0x000A0 },
+ { "nbsp", 0x000A0 },
+ { "ncap;", 0x02A43 },
+ { "ncaron;", 0x00148 },
+ { "ncedil;", 0x00146 },
+ { "ncong;", 0x02247 },
+ { "ncup;", 0x02A42 },
+ { "ncy;", 0x0043D },
+ { "ndash;", 0x02013 },
+ { "ne;", 0x02260 },
+ { "neArr;", 0x021D7 },
+ { "nearhk;", 0x02924 },
+ { "nearr;", 0x02197 },
+ { "nearrow;", 0x02197 },
+ { "nequiv;", 0x02262 },
+ { "nesear;", 0x02928 },
+ { "nexist;", 0x02204 },
+ { "nexists;", 0x02204 },
+ { "nfr;", 0x1D52B },
+ { "nge;", 0x02271 },
+ { "ngeq;", 0x02271 },
+ { "ngsim;", 0x02275 },
+ { "ngt;", 0x0226F },
+ { "ngtr;", 0x0226F },
+ { "nhArr;", 0x021CE },
+ { "nharr;", 0x021AE },
+ { "nhpar;", 0x02AF2 },
+ { "ni;", 0x0220B },
+ { "nis;", 0x022FC },
+ { "nisd;", 0x022FA },
+ { "niv;", 0x0220B },
+ { "njcy;", 0x0045A },
+ { "nlArr;", 0x021CD },
+ { "nlarr;", 0x0219A },
+ { "nldr;", 0x02025 },
+ { "nle;", 0x02270 },
+ { "nleftarrow;", 0x0219A },
+ { "nleftrightarrow;", 0x021AE },
+ { "nleq;", 0x02270 },
+ { "nless;", 0x0226E },
+ { "nlsim;", 0x02274 },
+ { "nlt;", 0x0226E },
+ { "nltri;", 0x022EA },
+ { "nltrie;", 0x022EC },
+ { "nmid;", 0x02224 },
+ { "nopf;", 0x1D55F },
+ { "not;", 0x000AC },
+ { "not", 0x000AC },
+ { "notin;", 0x02209 },
+ { "notinva;", 0x02209 },
+ { "notinvb;", 0x022F7 },
+ { "notinvc;", 0x022F6 },
+ { "notni;", 0x0220C },
+ { "notniva;", 0x0220C },
+ { "notnivb;", 0x022FE },
+ { "notnivc;", 0x022FD },
+ { "npar;", 0x02226 },
+ { "nparallel;", 0x02226 },
+ { "npolint;", 0x02A14 },
+ { "npr;", 0x02280 },
+ { "nprcue;", 0x022E0 },
+ { "nprec;", 0x02280 },
+ { "nrArr;", 0x021CF },
+ { "nrarr;", 0x0219B },
+ { "nrightarrow;", 0x0219B },
+ { "nrtri;", 0x022EB },
+ { "nrtrie;", 0x022ED },
+ { "nsc;", 0x02281 },
+ { "nsccue;", 0x022E1 },
+ { "nscr;", 0x1D4C3 },
+ { "nshortmid;", 0x02224 },
+ { "nshortparallel;", 0x02226 },
+ { "nsim;", 0x02241 },
+ { "nsime;", 0x02244 },
+ { "nsimeq;", 0x02244 },
+ { "nsmid;", 0x02224 },
+ { "nspar;", 0x02226 },
+ { "nsqsube;", 0x022E2 },
+ { "nsqsupe;", 0x022E3 },
+ { "nsub;", 0x02284 },
+ { "nsube;", 0x02288 },
+ { "nsubseteq;", 0x02288 },
+ { "nsucc;", 0x02281 },
+ { "nsup;", 0x02285 },
+ { "nsupe;", 0x02289 },
+ { "nsupseteq;", 0x02289 },
+ { "ntgl;", 0x02279 },
+ { "ntilde;", 0x000F1 },
+ { "ntilde", 0x000F1 },
+ { "ntlg;", 0x02278 },
+ { "ntriangleleft;", 0x022EA },
+ { "ntrianglelefteq;", 0x022EC },
+ { "ntriangleright;", 0x022EB },
+ { "ntrianglerighteq;", 0x022ED },
+ { "nu;", 0x003BD },
+ { "num;", 0x00023 },
+ { "numero;", 0x02116 },
+ { "numsp;", 0x02007 },
+ { "nvDash;", 0x022AD },
+ { "nvHarr;", 0x02904 },
+ { "nvdash;", 0x022AC },
+ { "nvinfin;", 0x029DE },
+ { "nvlArr;", 0x02902 },
+ { "nvrArr;", 0x02903 },
+ { "nwArr;", 0x021D6 },
+ { "nwarhk;", 0x02923 },
+ { "nwarr;", 0x02196 },
+ { "nwarrow;", 0x02196 },
+ { "nwnear;", 0x02927 },
+ { "oS;", 0x024C8 },
+ { "oacute;", 0x000F3 },
+ { "oacute", 0x000F3 },
+ { "oast;", 0x0229B },
+ { "ocir;", 0x0229A },
+ { "ocirc;", 0x000F4 },
+ { "ocirc", 0x000F4 },
+ { "ocy;", 0x0043E },
+ { "odash;", 0x0229D },
+ { "odblac;", 0x00151 },
+ { "odiv;", 0x02A38 },
+ { "odot;", 0x02299 },
+ { "odsold;", 0x029BC },
+ { "oelig;", 0x00153 },
+ { "ofcir;", 0x029BF },
+ { "ofr;", 0x1D52C },
+ { "ogon;", 0x002DB },
+ { "ograve;", 0x000F2 },
+ { "ograve", 0x000F2 },
+ { "ogt;", 0x029C1 },
+ { "ohbar;", 0x029B5 },
+ { "ohm;", 0x003A9 },
+ { "oint;", 0x0222E },
+ { "olarr;", 0x021BA },
+ { "olcir;", 0x029BE },
+ { "olcross;", 0x029BB },
+ { "oline;", 0x0203E },
+ { "olt;", 0x029C0 },
+ { "omacr;", 0x0014D },
+ { "omega;", 0x003C9 },
+ { "omicron;", 0x003BF },
+ { "omid;", 0x029B6 },
+ { "ominus;", 0x02296 },
+ { "oopf;", 0x1D560 },
+ { "opar;", 0x029B7 },
+ { "operp;", 0x029B9 },
+ { "oplus;", 0x02295 },
+ { "or;", 0x02228 },
+ { "orarr;", 0x021BB },
+ { "ord;", 0x02A5D },
+ { "order;", 0x02134 },
+ { "orderof;", 0x02134 },
+ { "ordf;", 0x000AA },
+ { "ordf", 0x000AA },
+ { "ordm;", 0x000BA },
+ { "ordm", 0x000BA },
+ { "origof;", 0x022B6 },
+ { "oror;", 0x02A56 },
+ { "orslope;", 0x02A57 },
+ { "orv;", 0x02A5B },
+ { "oscr;", 0x02134 },
+ { "oslash;", 0x000F8 },
+ { "oslash", 0x000F8 },
+ { "osol;", 0x02298 },
+ { "otilde;", 0x000F5 },
+ { "otilde", 0x000F5 },
+ { "otimes;", 0x02297 },
+ { "otimesas;", 0x02A36 },
+ { "ouml;", 0x000F6 },
+ { "ouml", 0x000F6 },
+ { "ovbar;", 0x0233D },
+ { "par;", 0x02225 },
+ { "para;", 0x000B6 },
+ { "para", 0x000B6 },
+ { "parallel;", 0x02225 },
+ { "parsim;", 0x02AF3 },
+ { "parsl;", 0x02AFD },
+ { "part;", 0x02202 },
+ { "pcy;", 0x0043F },
+ { "percnt;", 0x00025 },
+ { "period;", 0x0002E },
+ { "permil;", 0x02030 },
+ { "perp;", 0x022A5 },
+ { "pertenk;", 0x02031 },
+ { "pfr;", 0x1D52D },
+ { "phi;", 0x003C6 },
+ { "phiv;", 0x003D5 },
+ { "phmmat;", 0x02133 },
+ { "phone;", 0x0260E },
+ { "pi;", 0x003C0 },
+ { "pitchfork;", 0x022D4 },
+ { "piv;", 0x003D6 },
+ { "planck;", 0x0210F },
+ { "planckh;", 0x0210E },
+ { "plankv;", 0x0210F },
+ { "plus;", 0x0002B },
+ { "plusacir;", 0x02A23 },
+ { "plusb;", 0x0229E },
+ { "pluscir;", 0x02A22 },
+ { "plusdo;", 0x02214 },
+ { "plusdu;", 0x02A25 },
+ { "pluse;", 0x02A72 },
+ { "plusmn;", 0x000B1 },
+ { "plusmn", 0x000B1 },
+ { "plussim;", 0x02A26 },
+ { "plustwo;", 0x02A27 },
+ { "pm;", 0x000B1 },
+ { "pointint;", 0x02A15 },
+ { "popf;", 0x1D561 },
+ { "pound;", 0x000A3 },
+ { "pound", 0x000A3 },
+ { "pr;", 0x0227A },
+ { "prE;", 0x02AB3 },
+ { "prap;", 0x02AB7 },
+ { "prcue;", 0x0227C },
+ { "pre;", 0x02AAF },
+ { "prec;", 0x0227A },
+ { "precapprox;", 0x02AB7 },
+ { "preccurlyeq;", 0x0227C },
+ { "preceq;", 0x02AAF },
+ { "precnapprox;", 0x02AB9 },
+ { "precneqq;", 0x02AB5 },
+ { "precnsim;", 0x022E8 },
+ { "precsim;", 0x0227E },
+ { "prime;", 0x02032 },
+ { "primes;", 0x02119 },
+ { "prnE;", 0x02AB5 },
+ { "prnap;", 0x02AB9 },
+ { "prnsim;", 0x022E8 },
+ { "prod;", 0x0220F },
+ { "profalar;", 0x0232E },
+ { "profline;", 0x02312 },
+ { "profsurf;", 0x02313 },
+ { "prop;", 0x0221D },
+ { "propto;", 0x0221D },
+ { "prsim;", 0x0227E },
+ { "prurel;", 0x022B0 },
+ { "pscr;", 0x1D4C5 },
+ { "psi;", 0x003C8 },
+ { "puncsp;", 0x02008 },
+ { "qfr;", 0x1D52E },
+ { "qint;", 0x02A0C },
+ { "qopf;", 0x1D562 },
+ { "qprime;", 0x02057 },
+ { "qscr;", 0x1D4C6 },
+ { "quaternions;", 0x0210D },
+ { "quatint;", 0x02A16 },
+ { "quest;", 0x0003F },
+ { "questeq;", 0x0225F },
+ { "quot;", 0x00022 },
+ { "quot", 0x00022 },
+ { "rAarr;", 0x021DB },
+ { "rArr;", 0x021D2 },
+ { "rAtail;", 0x0291C },
+ { "rBarr;", 0x0290F },
+ { "rHar;", 0x02964 },
+ { "racute;", 0x00155 },
+ { "radic;", 0x0221A },
+ { "raemptyv;", 0x029B3 },
+ { "rang;", 0x027E9 },
+ { "rangd;", 0x02992 },
+ { "range;", 0x029A5 },
+ { "rangle;", 0x027E9 },
+ { "raquo;", 0x000BB },
+ { "raquo", 0x000BB },
+ { "rarr;", 0x02192 },
+ { "rarrap;", 0x02975 },
+ { "rarrb;", 0x021E5 },
+ { "rarrbfs;", 0x02920 },
+ { "rarrc;", 0x02933 },
+ { "rarrfs;", 0x0291E },
+ { "rarrhk;", 0x021AA },
+ { "rarrlp;", 0x021AC },
+ { "rarrpl;", 0x02945 },
+ { "rarrsim;", 0x02974 },
+ { "rarrtl;", 0x021A3 },
+ { "rarrw;", 0x0219D },
+ { "ratail;", 0x0291A },
+ { "ratio;", 0x02236 },
+ { "rationals;", 0x0211A },
+ { "rbarr;", 0x0290D },
+ { "rbbrk;", 0x02773 },
+ { "rbrace;", 0x0007D },
+ { "rbrack;", 0x0005D },
+ { "rbrke;", 0x0298C },
+ { "rbrksld;", 0x0298E },
+ { "rbrkslu;", 0x02990 },
+ { "rcaron;", 0x00159 },
+ { "rcedil;", 0x00157 },
+ { "rceil;", 0x02309 },
+ { "rcub;", 0x0007D },
+ { "rcy;", 0x00440 },
+ { "rdca;", 0x02937 },
+ { "rdldhar;", 0x02969 },
+ { "rdquo;", 0x0201D },
+ { "rdquor;", 0x0201D },
+ { "rdsh;", 0x021B3 },
+ { "real;", 0x0211C },
+ { "realine;", 0x0211B },
+ { "realpart;", 0x0211C },
+ { "reals;", 0x0211D },
+ { "rect;", 0x025AD },
+ { "reg;", 0x000AE },
+ { "reg", 0x000AE },
+ { "rfisht;", 0x0297D },
+ { "rfloor;", 0x0230B },
+ { "rfr;", 0x1D52F },
+ { "rhard;", 0x021C1 },
+ { "rharu;", 0x021C0 },
+ { "rharul;", 0x0296C },
+ { "rho;", 0x003C1 },
+ { "rhov;", 0x003F1 },
+ { "rightarrow;", 0x02192 },
+ { "rightarrowtail;", 0x021A3 },
+ { "rightharpoondown;", 0x021C1 },
+ { "rightharpoonup;", 0x021C0 },
+ { "rightleftarrows;", 0x021C4 },
+ { "rightleftharpoons;", 0x021CC },
+ { "rightrightarrows;", 0x021C9 },
+ { "rightsquigarrow;", 0x0219D },
+ { "rightthreetimes;", 0x022CC },
+ { "ring;", 0x002DA },
+ { "risingdotseq;", 0x02253 },
+ { "rlarr;", 0x021C4 },
+ { "rlhar;", 0x021CC },
+ { "rlm;", 0x0200F },
+ { "rmoust;", 0x023B1 },
+ { "rmoustache;", 0x023B1 },
+ { "rnmid;", 0x02AEE },
+ { "roang;", 0x027ED },
+ { "roarr;", 0x021FE },
+ { "robrk;", 0x027E7 },
+ { "ropar;", 0x02986 },
+ { "ropf;", 0x1D563 },
+ { "roplus;", 0x02A2E },
+ { "rotimes;", 0x02A35 },
+ { "rpar;", 0x00029 },
+ { "rpargt;", 0x02994 },
+ { "rppolint;", 0x02A12 },
+ { "rrarr;", 0x021C9 },
+ { "rsaquo;", 0x0203A },
+ { "rscr;", 0x1D4C7 },
+ { "rsh;", 0x021B1 },
+ { "rsqb;", 0x0005D },
+ { "rsquo;", 0x02019 },
+ { "rsquor;", 0x02019 },
+ { "rthree;", 0x022CC },
+ { "rtimes;", 0x022CA },
+ { "rtri;", 0x025B9 },
+ { "rtrie;", 0x022B5 },
+ { "rtrif;", 0x025B8 },
+ { "rtriltri;", 0x029CE },
+ { "ruluhar;", 0x02968 },
+ { "rx;", 0x0211E },
+ { "sacute;", 0x0015B },
+ { "sbquo;", 0x0201A },
+ { "sc;", 0x0227B },
+ { "scE;", 0x02AB4 },
+ { "scap;", 0x02AB8 },
+ { "scaron;", 0x00161 },
+ { "sccue;", 0x0227D },
+ { "sce;", 0x02AB0 },
+ { "scedil;", 0x0015F },
+ { "scirc;", 0x0015D },
+ { "scnE;", 0x02AB6 },
+ { "scnap;", 0x02ABA },
+ { "scnsim;", 0x022E9 },
+ { "scpolint;", 0x02A13 },
+ { "scsim;", 0x0227F },
+ { "scy;", 0x00441 },
+ { "sdot;", 0x022C5 },
+ { "sdotb;", 0x022A1 },
+ { "sdote;", 0x02A66 },
+ { "seArr;", 0x021D8 },
+ { "searhk;", 0x02925 },
+ { "searr;", 0x02198 },
+ { "searrow;", 0x02198 },
+ { "sect;", 0x000A7 },
+ { "sect", 0x000A7 },
+ { "semi;", 0x0003B },
+ { "seswar;", 0x02929 },
+ { "setminus;", 0x02216 },
+ { "setmn;", 0x02216 },
+ { "sext;", 0x02736 },
+ { "sfr;", 0x1D530 },
+ { "sfrown;", 0x02322 },
+ { "sharp;", 0x0266F },
+ { "shchcy;", 0x00449 },
+ { "shcy;", 0x00448 },
+ { "shortmid;", 0x02223 },
+ { "shortparallel;", 0x02225 },
+ { "shy;", 0x000AD },
+ { "shy", 0x000AD },
+ { "sigma;", 0x003C3 },
+ { "sigmaf;", 0x003C2 },
+ { "sigmav;", 0x003C2 },
+ { "sim;", 0x0223C },
+ { "simdot;", 0x02A6A },
+ { "sime;", 0x02243 },
+ { "simeq;", 0x02243 },
+ { "simg;", 0x02A9E },
+ { "simgE;", 0x02AA0 },
+ { "siml;", 0x02A9D },
+ { "simlE;", 0x02A9F },
+ { "simne;", 0x02246 },
+ { "simplus;", 0x02A24 },
+ { "simrarr;", 0x02972 },
+ { "slarr;", 0x02190 },
+ { "smallsetminus;", 0x02216 },
+ { "smashp;", 0x02A33 },
+ { "smeparsl;", 0x029E4 },
+ { "smid;", 0x02223 },
+ { "smile;", 0x02323 },
+ { "smt;", 0x02AAA },
+ { "smte;", 0x02AAC },
+ { "softcy;", 0x0044C },
+ { "sol;", 0x0002F },
+ { "solb;", 0x029C4 },
+ { "solbar;", 0x0233F },
+ { "sopf;", 0x1D564 },
+ { "spades;", 0x02660 },
+ { "spadesuit;", 0x02660 },
+ { "spar;", 0x02225 },
+ { "sqcap;", 0x02293 },
+ { "sqcup;", 0x02294 },
+ { "sqsub;", 0x0228F },
+ { "sqsube;", 0x02291 },
+ { "sqsubset;", 0x0228F },
+ { "sqsubseteq;", 0x02291 },
+ { "sqsup;", 0x02290 },
+ { "sqsupe;", 0x02292 },
+ { "sqsupset;", 0x02290 },
+ { "sqsupseteq;", 0x02292 },
+ { "squ;", 0x025A1 },
+ { "square;", 0x025A1 },
+ { "squarf;", 0x025AA },
+ { "squf;", 0x025AA },
+ { "srarr;", 0x02192 },
+ { "sscr;", 0x1D4C8 },
+ { "ssetmn;", 0x02216 },
+ { "ssmile;", 0x02323 },
+ { "sstarf;", 0x022C6 },
+ { "star;", 0x02606 },
+ { "starf;", 0x02605 },
+ { "straightepsilon;", 0x003F5 },
+ { "straightphi;", 0x003D5 },
+ { "strns;", 0x000AF },
+ { "sub;", 0x02282 },
+ { "subE;", 0x02AC5 },
+ { "subdot;", 0x02ABD },
+ { "sube;", 0x02286 },
+ { "subedot;", 0x02AC3 },
+ { "submult;", 0x02AC1 },
+ { "subnE;", 0x02ACB },
+ { "subne;", 0x0228A },
+ { "subplus;", 0x02ABF },
+ { "subrarr;", 0x02979 },
+ { "subset;", 0x02282 },
+ { "subseteq;", 0x02286 },
+ { "subseteqq;", 0x02AC5 },
+ { "subsetneq;", 0x0228A },
+ { "subsetneqq;", 0x02ACB },
+ { "subsim;", 0x02AC7 },
+ { "subsub;", 0x02AD5 },
+ { "subsup;", 0x02AD3 },
+ { "succ;", 0x0227B },
+ { "succapprox;", 0x02AB8 },
+ { "succcurlyeq;", 0x0227D },
+ { "succeq;", 0x02AB0 },
+ { "succnapprox;", 0x02ABA },
+ { "succneqq;", 0x02AB6 },
+ { "succnsim;", 0x022E9 },
+ { "succsim;", 0x0227F },
+ { "sum;", 0x02211 },
+ { "sung;", 0x0266A },
+ { "sup1;", 0x000B9 },
+ { "sup1", 0x000B9 },
+ { "sup2;", 0x000B2 },
+ { "sup2", 0x000B2 },
+ { "sup3;", 0x000B3 },
+ { "sup3", 0x000B3 },
+ { "sup;", 0x02283 },
+ { "supE;", 0x02AC6 },
+ { "supdot;", 0x02ABE },
+ { "supdsub;", 0x02AD8 },
+ { "supe;", 0x02287 },
+ { "supedot;", 0x02AC4 },
+ { "suphsol;", 0x027C9 },
+ { "suphsub;", 0x02AD7 },
+ { "suplarr;", 0x0297B },
+ { "supmult;", 0x02AC2 },
+ { "supnE;", 0x02ACC },
+ { "supne;", 0x0228B },
+ { "supplus;", 0x02AC0 },
+ { "supset;", 0x02283 },
+ { "supseteq;", 0x02287 },
+ { "supseteqq;", 0x02AC6 },
+ { "supsetneq;", 0x0228B },
+ { "supsetneqq;", 0x02ACC },
+ { "supsim;", 0x02AC8 },
+ { "supsub;", 0x02AD4 },
+ { "supsup;", 0x02AD6 },
+ { "swArr;", 0x021D9 },
+ { "swarhk;", 0x02926 },
+ { "swarr;", 0x02199 },
+ { "swarrow;", 0x02199 },
+ { "swnwar;", 0x0292A },
+ { "szlig;", 0x000DF },
+ { "szlig", 0x000DF },
+ { "target;", 0x02316 },
+ { "tau;", 0x003C4 },
+ { "tbrk;", 0x023B4 },
+ { "tcaron;", 0x00165 },
+ { "tcedil;", 0x00163 },
+ { "tcy;", 0x00442 },
+ { "tdot;", 0x020DB },
+ { "telrec;", 0x02315 },
+ { "tfr;", 0x1D531 },
+ { "there4;", 0x02234 },
+ { "therefore;", 0x02234 },
+ { "theta;", 0x003B8 },
+ { "thetasym;", 0x003D1 },
+ { "thetav;", 0x003D1 },
+ { "thickapprox;", 0x02248 },
+ { "thicksim;", 0x0223C },
+ { "thinsp;", 0x02009 },
+ { "thkap;", 0x02248 },
+ { "thksim;", 0x0223C },
+ { "thorn;", 0x000FE },
+ { "thorn", 0x000FE },
+ { "tilde;", 0x002DC },
+ { "times;", 0x000D7 },
+ { "times", 0x000D7 },
+ { "timesb;", 0x022A0 },
+ { "timesbar;", 0x02A31 },
+ { "timesd;", 0x02A30 },
+ { "tint;", 0x0222D },
+ { "toea;", 0x02928 },
+ { "top;", 0x022A4 },
+ { "topbot;", 0x02336 },
+ { "topcir;", 0x02AF1 },
+ { "topf;", 0x1D565 },
+ { "topfork;", 0x02ADA },
+ { "tosa;", 0x02929 },
+ { "tprime;", 0x02034 },
+ { "trade;", 0x02122 },
+ { "triangle;", 0x025B5 },
+ { "triangledown;", 0x025BF },
+ { "triangleleft;", 0x025C3 },
+ { "trianglelefteq;", 0x022B4 },
+ { "triangleq;", 0x0225C },
+ { "triangleright;", 0x025B9 },
+ { "trianglerighteq;", 0x022B5 },
+ { "tridot;", 0x025EC },
+ { "trie;", 0x0225C },
+ { "triminus;", 0x02A3A },
+ { "triplus;", 0x02A39 },
+ { "trisb;", 0x029CD },
+ { "tritime;", 0x02A3B },
+ { "trpezium;", 0x023E2 },
+ { "tscr;", 0x1D4C9 },
+ { "tscy;", 0x00446 },
+ { "tshcy;", 0x0045B },
+ { "tstrok;", 0x00167 },
+ { "twixt;", 0x0226C },
+ { "twoheadleftarrow;", 0x0219E },
+ { "twoheadrightarrow;", 0x021A0 },
+ { "uArr;", 0x021D1 },
+ { "uHar;", 0x02963 },
+ { "uacute;", 0x000FA },
+ { "uacute", 0x000FA },
+ { "uarr;", 0x02191 },
+ { "ubrcy;", 0x0045E },
+ { "ubreve;", 0x0016D },
+ { "ucirc;", 0x000FB },
+ { "ucirc", 0x000FB },
+ { "ucy;", 0x00443 },
+ { "udarr;", 0x021C5 },
+ { "udblac;", 0x00171 },
+ { "udhar;", 0x0296E },
+ { "ufisht;", 0x0297E },
+ { "ufr;", 0x1D532 },
+ { "ugrave;", 0x000F9 },
+ { "ugrave", 0x000F9 },
+ { "uharl;", 0x021BF },
+ { "uharr;", 0x021BE },
+ { "uhblk;", 0x02580 },
+ { "ulcorn;", 0x0231C },
+ { "ulcorner;", 0x0231C },
+ { "ulcrop;", 0x0230F },
+ { "ultri;", 0x025F8 },
+ { "umacr;", 0x0016B },
+ { "uml;", 0x000A8 },
+ { "uml", 0x000A8 },
+ { "uogon;", 0x00173 },
+ { "uopf;", 0x1D566 },
+ { "uparrow;", 0x02191 },
+ { "updownarrow;", 0x02195 },
+ { "upharpoonleft;", 0x021BF },
+ { "upharpoonright;", 0x021BE },
+ { "uplus;", 0x0228E },
+ { "upsi;", 0x003C5 },
+ { "upsih;", 0x003D2 },
+ { "upsilon;", 0x003C5 },
+ { "upuparrows;", 0x021C8 },
+ { "urcorn;", 0x0231D },
+ { "urcorner;", 0x0231D },
+ { "urcrop;", 0x0230E },
+ { "uring;", 0x0016F },
+ { "urtri;", 0x025F9 },
+ { "uscr;", 0x1D4CA },
+ { "utdot;", 0x022F0 },
+ { "utilde;", 0x00169 },
+ { "utri;", 0x025B5 },
+ { "utrif;", 0x025B4 },
+ { "uuarr;", 0x021C8 },
+ { "uuml;", 0x000FC },
+ { "uuml", 0x000FC },
+ { "uwangle;", 0x029A7 },
+ { "vArr;", 0x021D5 },
+ { "vBar;", 0x02AE8 },
+ { "vBarv;", 0x02AE9 },
+ { "vDash;", 0x022A8 },
+ { "vangrt;", 0x0299C },
+ { "varepsilon;", 0x003F5 },
+ { "varkappa;", 0x003F0 },
+ { "varnothing;", 0x02205 },
+ { "varphi;", 0x003D5 },
+ { "varpi;", 0x003D6 },
+ { "varpropto;", 0x0221D },
+ { "varr;", 0x02195 },
+ { "varrho;", 0x003F1 },
+ { "varsigma;", 0x003C2 },
+ { "vartheta;", 0x003D1 },
+ { "vartriangleleft;", 0x022B2 },
+ { "vartriangleright;", 0x022B3 },
+ { "vcy;", 0x00432 },
+ { "vdash;", 0x022A2 },
+ { "vee;", 0x02228 },
+ { "veebar;", 0x022BB },
+ { "veeeq;", 0x0225A },
+ { "vellip;", 0x022EE },
+ { "verbar;", 0x0007C },
+ { "vert;", 0x0007C },
+ { "vfr;", 0x1D533 },
+ { "vltri;", 0x022B2 },
+ { "vopf;", 0x1D567 },
+ { "vprop;", 0x0221D },
+ { "vrtri;", 0x022B3 },
+ { "vscr;", 0x1D4CB },
+ { "vzigzag;", 0x0299A },
+ { "wcirc;", 0x00175 },
+ { "wedbar;", 0x02A5F },
+ { "wedge;", 0x02227 },
+ { "wedgeq;", 0x02259 },
+ { "weierp;", 0x02118 },
+ { "wfr;", 0x1D534 },
+ { "wopf;", 0x1D568 },
+ { "wp;", 0x02118 },
+ { "wr;", 0x02240 },
+ { "wreath;", 0x02240 },
+ { "wscr;", 0x1D4CC },
+ { "xcap;", 0x022C2 },
+ { "xcirc;", 0x025EF },
+ { "xcup;", 0x022C3 },
+ { "xdtri;", 0x025BD },
+ { "xfr;", 0x1D535 },
+ { "xhArr;", 0x027FA },
+ { "xharr;", 0x027F7 },
+ { "xi;", 0x003BE },
+ { "xlArr;", 0x027F8 },
+ { "xlarr;", 0x027F5 },
+ { "xmap;", 0x027FC },
+ { "xnis;", 0x022FB },
+ { "xodot;", 0x02A00 },
+ { "xopf;", 0x1D569 },
+ { "xoplus;", 0x02A01 },
+ { "xotime;", 0x02A02 },
+ { "xrArr;", 0x027F9 },
+ { "xrarr;", 0x027F6 },
+ { "xscr;", 0x1D4CD },
+ { "xsqcup;", 0x02A06 },
+ { "xuplus;", 0x02A04 },
+ { "xutri;", 0x025B3 },
+ { "xvee;", 0x022C1 },
+ { "xwedge;", 0x022C0 },
+ { "yacute;", 0x000FD },
+ { "yacute", 0x000FD },
+ { "yacy;", 0x0044F },
+ { "ycirc;", 0x00177 },
+ { "ycy;", 0x0044B },
+ { "yen;", 0x000A5 },
+ { "yen", 0x000A5 },
+ { "yfr;", 0x1D536 },
+ { "yicy;", 0x00457 },
+ { "yopf;", 0x1D56A },
+ { "yscr;", 0x1D4CE },
+ { "yucy;", 0x0044E },
+ { "yuml;", 0x000FF },
+ { "yuml", 0x000FF },
+ { "zacute;", 0x0017A },
+ { "zcaron;", 0x0017E },
+ { "zcy;", 0x00437 },
+ { "zdot;", 0x0017C },
+ { "zeetrf;", 0x02128 },
+ { "zeta;", 0x003B6 },
+ { "zfr;", 0x1D537 },
+ { "zhcy;", 0x00436 },
+ { "zigrarr;", 0x021DD },
+ { "zopf;", 0x1D56B },
+ { "zscr;", 0x1D4CF },
+ { "zwj;", 0x0200D },
+ { "zwnj;", 0x0200C }
+ };
+
+ constexpr struct {
+ StringView entity;
+ u32 code_point1;
+ u32 code_point2;
+ } double_code_point_entities[] = {
+ { "NotEqualTilde;", 0x02242, 0x00338 },
+ { "NotGreaterFullEqual;", 0x02267, 0x00338 },
+ { "NotGreaterGreater;", 0x0226B, 0x00338 },
+ { "NotGreaterSlantEqual;", 0x02A7E, 0x00338 },
+ { "NotHumpDownHump;", 0x0224E, 0x00338 },
+ { "NotHumpEqual;", 0x0224F, 0x00338 },
+ { "NotLeftTriangleBar;", 0x029CF, 0x00338 },
+ { "NotLessLess;", 0x0226A, 0x00338 },
+ { "NotLessSlantEqual;", 0x02A7D, 0x00338 },
+ { "NotNestedGreaterGreater;", 0x02AA2, 0x00338 },
+ { "NotNestedLessLess;", 0x02AA1, 0x00338 },
+ { "NotPrecedesEqual;", 0x02AAF, 0x00338 },
+ { "NotRightTriangleBar;", 0x029D0, 0x00338 },
+ { "NotSquareSubset;", 0x0228F, 0x00338 },
+ { "NotSquareSuperset;", 0x02290, 0x00338 },
+ { "NotSubset;", 0x02282, 0x020D2 },
+ { "NotSucceedsEqual;", 0x02AB0, 0x00338 },
+ { "NotSucceedsTilde;", 0x0227F, 0x00338 },
+ { "NotSuperset;", 0x02283, 0x020D2 },
+ { "ThickSpace;", 0x0205F, 0x0200A },
+ { "acE;", 0x0223E, 0x00333 },
+ { "bne;", 0x0003D, 0x020E5 },
+ { "bnequiv;", 0x02261, 0x020E5 },
+ { "caps;", 0x02229, 0x0FE00 },
+ { "cups;", 0x0222A, 0x0FE00 },
+ { "fjlig;", 0x00066, 0x0006A },
+ { "gesl;", 0x022DB, 0x0FE00 },
+ { "gvertneqq;", 0x02269, 0x0FE00 },
+ { "gvnE;", 0x02269, 0x0FE00 },
+ { "lates;", 0x02AAD, 0x0FE00 },
+ { "lesg;", 0x022DA, 0x0FE00 },
+ { "lvertneqq;", 0x02268, 0x0FE00 },
+ { "lvnE;", 0x02268, 0x0FE00 },
+ { "nGg;", 0x022D9, 0x00338 },
+ { "nGt;", 0x0226B, 0x020D2 },
+ { "nGtv;", 0x0226B, 0x00338 },
+ { "nLl;", 0x022D8, 0x00338 },
+ { "nLt;", 0x0226A, 0x020D2 },
+ { "nLtv;", 0x0226A, 0x00338 },
+ { "nang;", 0x02220, 0x020D2 },
+ { "napE;", 0x02A70, 0x00338 },
+ { "napid;", 0x0224B, 0x00338 },
+ { "nbump;", 0x0224E, 0x00338 },
+ { "nbumpe;", 0x0224F, 0x00338 },
+ { "ncongdot;", 0x02A6D, 0x00338 },
+ { "nedot;", 0x02250, 0x00338 },
+ { "nesim;", 0x02242, 0x00338 },
+ { "ngE;", 0x02267, 0x00338 },
+ { "ngeqq;", 0x02267, 0x00338 },
+ { "ngeqslant;", 0x02A7E, 0x00338 },
+ { "nges;", 0x02A7E, 0x00338 },
+ { "nlE;", 0x02266, 0x00338 },
+ { "nleqq;", 0x02266, 0x00338 },
+ { "nleqslant;", 0x02A7D, 0x00338 },
+ { "nles;", 0x02A7D, 0x00338 },
+ { "notinE;", 0x022F9, 0x00338 },
+ { "notindot;", 0x022F5, 0x00338 },
+ { "nparsl;", 0x02AFD, 0x020E5 },
+ { "npart;", 0x02202, 0x00338 },
+ { "npre;", 0x02AAF, 0x00338 },
+ { "npreceq;", 0x02AAF, 0x00338 },
+ { "nrarrc;", 0x02933, 0x00338 },
+ { "nrarrw;", 0x0219D, 0x00338 },
+ { "nsce;", 0x02AB0, 0x00338 },
+ { "nsubE;", 0x02AC5, 0x00338 },
+ { "nsubset;", 0x02282, 0x020D2 },
+ { "nsubseteqq;", 0x02AC5, 0x00338 },
+ { "nsucceq;", 0x02AB0, 0x00338 },
+ { "nsupE;", 0x02AC6, 0x00338 },
+ { "nsupset;", 0x02283, 0x020D2 },
+ { "nsupseteqq;", 0x02AC6, 0x00338 },
+ { "nvap;", 0x0224D, 0x020D2 },
+ { "nvge;", 0x02265, 0x020D2 },
+ { "nvgt;", 0x0003E, 0x020D2 },
+ { "nvle;", 0x02264, 0x020D2 },
+ { "nvlt;", 0x0003C, 0x020D2 },
+ { "nvltrie;", 0x022B4, 0x020D2 },
+ { "nvrtrie;", 0x022B5, 0x020D2 },
+ { "nvsim;", 0x0223C, 0x020D2 },
+ { "race;", 0x0223D, 0x00331 },
+ { "smtes;", 0x02AAC, 0x0FE00 },
+ { "sqcaps;", 0x02293, 0x0FE00 },
+ { "sqcups;", 0x02294, 0x0FE00 },
+ { "varsubsetneq;", 0x0228A, 0x0FE00 },
+ { "varsubsetneqq;", 0x02ACB, 0x0FE00 },
+ { "varsupsetneq;", 0x0228B, 0x0FE00 },
+ { "varsupsetneqq;", 0x02ACC, 0x0FE00 },
+ { "vnsub;", 0x02282, 0x020D2 },
+ { "vnsup;", 0x02283, 0x020D2 },
+ { "vsubnE;", 0x02ACB, 0x0FE00 },
+ { "vsubne;", 0x0228A, 0x0FE00 },
+ { "vsupnE;", 0x02ACC, 0x0FE00 },
+ { "vsupne;", 0x0228B, 0x0FE00 },
+ };
+
+ EntityMatch match;
+
+ for (auto& single_code_point_entity : single_code_point_entities) {
+ if (entity.starts_with(single_code_point_entity.entity)) {
+ if (match.entity.is_null() || single_code_point_entity.entity.length() > match.entity.length())
+ match = { { single_code_point_entity.code_point }, single_code_point_entity.entity };
+ }
+ }
+
+ for (auto& double_code_point_entity : double_code_point_entities) {
+ if (entity.starts_with(double_code_point_entity.entity)) {
+ if (match.entity.is_null() || double_code_point_entity.entity.length() > match.entity.length())
+ match = EntityMatch { { double_code_point_entity.code_point1, double_code_point_entity.code_point2 }, StringView(double_code_point_entity.entity) };
+ }
+ }
+
+ if (match.entity.is_empty())
+ return {};
+ return match;
+}
+
+}
+}
diff --git a/Userland/Libraries/LibWeb/HTML/Parser/Entities.h b/Userland/Libraries/LibWeb/HTML/Parser/Entities.h
new file mode 100644
index 0000000000..0701f3be8d
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/Parser/Entities.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/StringView.h>
+#include <AK/Vector.h>
+
+namespace Web {
+namespace HTML {
+
+struct EntityMatch {
+ Vector<u32, 2> code_points;
+ StringView entity;
+};
+
+Optional<EntityMatch> code_points_from_entity(const StringView&);
+
+}
+}
diff --git a/Userland/Libraries/LibWeb/HTML/Parser/HTMLDocumentParser.cpp b/Userland/Libraries/LibWeb/HTML/Parser/HTMLDocumentParser.cpp
new file mode 100644
index 0000000000..446888aeb3
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/Parser/HTMLDocumentParser.cpp
@@ -0,0 +1,3027 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+//#define PARSER_DEBUG
+
+#include <AK/Utf32View.h>
+#include <LibTextCodec/Decoder.h>
+#include <LibWeb/DOM/Comment.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/DOM/DocumentType.h>
+#include <LibWeb/DOM/ElementFactory.h>
+#include <LibWeb/DOM/Event.h>
+#include <LibWeb/DOM/Text.h>
+#include <LibWeb/DOM/Window.h>
+#include <LibWeb/HTML/EventNames.h>
+#include <LibWeb/HTML/HTMLFormElement.h>
+#include <LibWeb/HTML/HTMLHeadElement.h>
+#include <LibWeb/HTML/HTMLScriptElement.h>
+#include <LibWeb/HTML/HTMLTemplateElement.h>
+#include <LibWeb/HTML/Parser/HTMLDocumentParser.h>
+#include <LibWeb/HTML/Parser/HTMLToken.h>
+#include <LibWeb/Namespace.h>
+#include <LibWeb/SVG/TagNames.h>
+
+namespace Web::HTML {
+
+#define PARSE_ERROR() \
+ do { \
+ dbg() << "Parse error! " << __PRETTY_FUNCTION__ << " @ " << __LINE__; \
+ } while (0)
+
+static Vector<FlyString> s_quirks_public_ids = {
+ "+//Silmaril//dtd html Pro v0r11 19970101//",
+ "-//AS//DTD HTML 3.0 asWedit + extensions//",
+ "-//AdvaSoft Ltd//DTD HTML 3.0 asWedit + extensions//",
+ "-//IETF//DTD HTML 2.0 Level 1//",
+ "-//IETF//DTD HTML 2.0 Level 2//",
+ "-//IETF//DTD HTML 2.0 Strict Level 1//",
+ "-//IETF//DTD HTML 2.0 Strict Level 2//",
+ "-//IETF//DTD HTML 2.0 Strict//",
+ "-//IETF//DTD HTML 2.0//",
+ "-//IETF//DTD HTML 2.1E//",
+ "-//IETF//DTD HTML 3.0//",
+ "-//IETF//DTD HTML 3.2 Final//",
+ "-//IETF//DTD HTML 3.2//",
+ "-//IETF//DTD HTML 3//",
+ "-//IETF//DTD HTML Level 0//",
+ "-//IETF//DTD HTML Level 1//",
+ "-//IETF//DTD HTML Level 2//",
+ "-//IETF//DTD HTML Level 3//",
+ "-//IETF//DTD HTML Strict Level 0//",
+ "-//IETF//DTD HTML Strict Level 1//",
+ "-//IETF//DTD HTML Strict Level 2//",
+ "-//IETF//DTD HTML Strict Level 3//",
+ "-//IETF//DTD HTML Strict//",
+ "-//IETF//DTD HTML//",
+ "-//Metrius//DTD Metrius Presentational//",
+ "-//Microsoft//DTD Internet Explorer 2.0 HTML Strict//",
+ "-//Microsoft//DTD Internet Explorer 2.0 HTML//",
+ "-//Microsoft//DTD Internet Explorer 2.0 Tables//",
+ "-//Microsoft//DTD Internet Explorer 3.0 HTML Strict//",
+ "-//Microsoft//DTD Internet Explorer 3.0 HTML//",
+ "-//Microsoft//DTD Internet Explorer 3.0 Tables//",
+ "-//Netscape Comm. Corp.//DTD HTML//",
+ "-//Netscape Comm. Corp.//DTD Strict HTML//",
+ "-//O'Reilly and Associates//DTD HTML 2.0//",
+ "-//O'Reilly and Associates//DTD HTML Extended 1.0//",
+ "-//O'Reilly and Associates//DTD HTML Extended Relaxed 1.0//",
+ "-//SQ//DTD HTML 2.0 HoTMetaL + extensions//",
+ "-//SoftQuad Software//DTD HoTMetaL PRO 6.0::19990601::extensions to HTML 4.0//",
+ "-//SoftQuad//DTD HoTMetaL PRO 4.0::19971010::extensions to HTML 4.0//",
+ "-//Spyglass//DTD HTML 2.0 Extended//",
+ "-//Sun Microsystems Corp.//DTD HotJava HTML//",
+ "-//Sun Microsystems Corp.//DTD HotJava Strict HTML//",
+ "-//W3C//DTD HTML 3 1995-03-24//",
+ "-//W3C//DTD HTML 3.2 Draft//",
+ "-//W3C//DTD HTML 3.2 Final//",
+ "-//W3C//DTD HTML 3.2//",
+ "-//W3C//DTD HTML 3.2S Draft//",
+ "-//W3C//DTD HTML 4.0 Frameset//",
+ "-//W3C//DTD HTML 4.0 Transitional//",
+ "-//W3C//DTD HTML Experimental 19960712//",
+ "-//W3C//DTD HTML Experimental 970421//",
+ "-//W3C//DTD W3 HTML//",
+ "-//W3O//DTD W3 HTML 3.0//",
+ "-//WebTechs//DTD Mozilla HTML 2.0//",
+ "-//WebTechs//DTD Mozilla HTML//"
+};
+
+RefPtr<DOM::Document> parse_html_document(const StringView& data, const URL& url, const String& encoding)
+{
+ auto document = DOM::Document::create(url);
+ HTMLDocumentParser parser(document, data, encoding);
+ parser.run(url);
+ return document;
+}
+
+HTMLDocumentParser::HTMLDocumentParser(DOM::Document& document, const StringView& input, const String& encoding)
+ : m_tokenizer(input, encoding)
+ , m_document(document)
+{
+ m_document->set_should_invalidate_styles_on_attribute_changes(false);
+ m_document->set_encoding(TextCodec::get_standardized_encoding(encoding));
+}
+
+HTMLDocumentParser::~HTMLDocumentParser()
+{
+ m_document->set_should_invalidate_styles_on_attribute_changes(true);
+}
+
+void HTMLDocumentParser::run(const URL& url)
+{
+ m_document->set_url(url);
+ m_document->set_source(m_tokenizer.source());
+
+ for (;;) {
+ auto optional_token = m_tokenizer.next_token();
+ if (!optional_token.has_value())
+ break;
+ auto& token = optional_token.value();
+
+#ifdef PARSER_DEBUG
+ dbg() << "[" << insertion_mode_name() << "] " << token.to_string();
+#endif
+ // FIXME: If the adjusted current node is a MathML text integration point and the token is a start tag whose tag name is neither "mglyph" nor "malignmark"
+ // FIXME: If the adjusted current node is a MathML text integration point and the token is a character token
+ // FIXME: If the adjusted current node is a MathML annotation-xml element and the token is a start tag whose tag name is "svg"
+ // FIXME: If the adjusted current node is an HTML integration point and the token is a start tag
+ // FIXME: If the adjusted current node is an HTML integration point and the token is a character token
+ if (m_stack_of_open_elements.is_empty()
+ || adjusted_current_node().namespace_() == Namespace::HTML
+ || token.is_end_of_file()) {
+ process_using_the_rules_for(m_insertion_mode, token);
+ } else {
+ process_using_the_rules_for_foreign_content(token);
+ }
+
+ if (m_stop_parsing) {
+#ifdef PARSER_DEBUG
+ dbg() << "Stop parsing" << (m_parsing_fragment ? " fragment" : "") << "! :^)";
+#endif
+ break;
+ }
+ }
+
+ flush_character_insertions();
+
+ // "The end"
+
+ m_document->set_ready_state("interactive");
+
+ auto scripts_to_execute_when_parsing_has_finished = m_document->take_scripts_to_execute_when_parsing_has_finished({});
+ for (auto& script : scripts_to_execute_when_parsing_has_finished) {
+ script.execute_script();
+ }
+
+ auto content_loaded_event = DOM::Event::create(HTML::EventNames::DOMContentLoaded);
+ content_loaded_event->set_bubbles(true);
+ m_document->dispatch_event(content_loaded_event);
+
+ auto scripts_to_execute_as_soon_as_possible = m_document->take_scripts_to_execute_as_soon_as_possible({});
+ for (auto& script : scripts_to_execute_as_soon_as_possible) {
+ script.execute_script();
+ }
+
+ // FIXME: Spin the event loop until there is nothing that delays the load event in the Document.
+
+ m_document->set_ready_state("complete");
+ m_document->window().dispatch_event(DOM::Event::create(HTML::EventNames::load));
+
+ m_document->set_ready_for_post_load_tasks(true);
+ m_document->completely_finish_loading();
+}
+
+void HTMLDocumentParser::process_using_the_rules_for(InsertionMode mode, HTMLToken& token)
+{
+ switch (mode) {
+ case InsertionMode::Initial:
+ handle_initial(token);
+ break;
+ case InsertionMode::BeforeHTML:
+ handle_before_html(token);
+ break;
+ case InsertionMode::BeforeHead:
+ handle_before_head(token);
+ break;
+ case InsertionMode::InHead:
+ handle_in_head(token);
+ break;
+ case InsertionMode::InHeadNoscript:
+ handle_in_head_noscript(token);
+ break;
+ case InsertionMode::AfterHead:
+ handle_after_head(token);
+ break;
+ case InsertionMode::InBody:
+ handle_in_body(token);
+ break;
+ case InsertionMode::AfterBody:
+ handle_after_body(token);
+ break;
+ case InsertionMode::AfterAfterBody:
+ handle_after_after_body(token);
+ break;
+ case InsertionMode::Text:
+ handle_text(token);
+ break;
+ case InsertionMode::InTable:
+ handle_in_table(token);
+ break;
+ case InsertionMode::InTableBody:
+ handle_in_table_body(token);
+ break;
+ case InsertionMode::InRow:
+ handle_in_row(token);
+ break;
+ case InsertionMode::InCell:
+ handle_in_cell(token);
+ break;
+ case InsertionMode::InTableText:
+ handle_in_table_text(token);
+ break;
+ case InsertionMode::InSelectInTable:
+ handle_in_select_in_table(token);
+ break;
+ case InsertionMode::InSelect:
+ handle_in_select(token);
+ break;
+ case InsertionMode::InCaption:
+ handle_in_caption(token);
+ break;
+ case InsertionMode::InColumnGroup:
+ handle_in_column_group(token);
+ break;
+ case InsertionMode::InTemplate:
+ handle_in_template(token);
+ break;
+ case InsertionMode::InFrameset:
+ handle_in_frameset(token);
+ break;
+ case InsertionMode::AfterFrameset:
+ handle_after_frameset(token);
+ break;
+ case InsertionMode::AfterAfterFrameset:
+ handle_after_after_frameset(token);
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ }
+}
+
+DOM::QuirksMode HTMLDocumentParser::which_quirks_mode(const HTMLToken& doctype_token) const
+{
+ if (doctype_token.m_doctype.force_quirks)
+ return DOM::QuirksMode::Yes;
+
+ // NOTE: The tokenizer puts the name into lower case for us.
+ if (doctype_token.m_doctype.name.to_string() != "html")
+ return DOM::QuirksMode::Yes;
+
+ auto public_identifier = doctype_token.m_doctype.public_identifier.to_string();
+ auto system_identifier = doctype_token.m_doctype.system_identifier.to_string();
+
+ if (public_identifier.equals_ignoring_case("-//W3O//DTD W3 HTML Strict 3.0//EN//"))
+ return DOM::QuirksMode::Yes;
+
+ if (public_identifier.equals_ignoring_case("-/W3C/DTD HTML 4.0 Transitional/EN"))
+ return DOM::QuirksMode::Yes;
+
+ if (public_identifier.equals_ignoring_case("HTML"))
+ return DOM::QuirksMode::Yes;
+
+ if (system_identifier.equals_ignoring_case("http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd"))
+ return DOM::QuirksMode::Yes;
+
+ for (auto& public_id : s_quirks_public_ids) {
+ if (public_identifier.starts_with(public_id, CaseSensitivity::CaseInsensitive))
+ return DOM::QuirksMode::Yes;
+ }
+
+ if (doctype_token.m_doctype.missing_system_identifier) {
+ if (public_identifier.starts_with("-//W3C//DTD HTML 4.01 Frameset//", CaseSensitivity::CaseInsensitive))
+ return DOM::QuirksMode::Yes;
+
+ if (public_identifier.starts_with("-//W3C//DTD HTML 4.01 Transitional//", CaseSensitivity::CaseInsensitive))
+ return DOM::QuirksMode::Yes;
+ }
+
+ if (public_identifier.starts_with("-//W3C//DTD XHTML 1.0 Frameset//", CaseSensitivity::CaseInsensitive))
+ return DOM::QuirksMode::Limited;
+
+ if (public_identifier.starts_with("-//W3C//DTD XHTML 1.0 Transitional//", CaseSensitivity::CaseInsensitive))
+ return DOM::QuirksMode::Limited;
+
+ if (!doctype_token.m_doctype.missing_system_identifier) {
+ if (public_identifier.starts_with("-//W3C//DTD HTML 4.01 Frameset//", CaseSensitivity::CaseInsensitive))
+ return DOM::QuirksMode::Limited;
+
+ if (public_identifier.starts_with("-//W3C//DTD HTML 4.01 Transitional//", CaseSensitivity::CaseInsensitive))
+ return DOM::QuirksMode::Limited;
+ }
+
+ return DOM::QuirksMode::No;
+}
+
+void HTMLDocumentParser::handle_initial(HTMLToken& token)
+{
+ if (token.is_character() && token.is_parser_whitespace()) {
+ return;
+ }
+
+ if (token.is_comment()) {
+ auto comment = adopt(*new DOM::Comment(document(), token.m_comment_or_character.data.to_string()));
+ document().append_child(move(comment));
+ return;
+ }
+
+ if (token.is_doctype()) {
+ auto doctype = adopt(*new DOM::DocumentType(document()));
+ doctype->set_name(token.m_doctype.name.to_string());
+ doctype->set_public_id(token.m_doctype.public_identifier.to_string());
+ doctype->set_system_id(token.m_doctype.system_identifier.to_string());
+ document().append_child(move(doctype));
+ document().set_quirks_mode(which_quirks_mode(token));
+ m_insertion_mode = InsertionMode::BeforeHTML;
+ return;
+ }
+
+ PARSE_ERROR();
+ document().set_quirks_mode(DOM::QuirksMode::Yes);
+ m_insertion_mode = InsertionMode::BeforeHTML;
+ process_using_the_rules_for(InsertionMode::BeforeHTML, token);
+}
+
+void HTMLDocumentParser::handle_before_html(HTMLToken& token)
+{
+ if (token.is_doctype()) {
+ PARSE_ERROR();
+ return;
+ }
+
+ if (token.is_comment()) {
+ auto comment = adopt(*new DOM::Comment(document(), token.m_comment_or_character.data.to_string()));
+ document().append_child(move(comment));
+ return;
+ }
+
+ if (token.is_character() && token.is_parser_whitespace()) {
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::html) {
+ auto element = create_element_for(token, Namespace::HTML);
+ document().append_child(element);
+ m_stack_of_open_elements.push(move(element));
+ m_insertion_mode = InsertionMode::BeforeHead;
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::head, HTML::TagNames::body, HTML::TagNames::html, HTML::TagNames::br)) {
+ goto AnythingElse;
+ }
+
+ if (token.is_end_tag()) {
+ PARSE_ERROR();
+ return;
+ }
+
+AnythingElse:
+ auto element = create_element(document(), HTML::TagNames::html, Namespace::HTML);
+ document().append_child(element);
+ m_stack_of_open_elements.push(element);
+ // FIXME: If the Document is being loaded as part of navigation of a browsing context, then: run the application cache selection algorithm with no manifest, passing it the Document object.
+ m_insertion_mode = InsertionMode::BeforeHead;
+ process_using_the_rules_for(InsertionMode::BeforeHead, token);
+ return;
+}
+
+DOM::Element& HTMLDocumentParser::current_node()
+{
+ return m_stack_of_open_elements.current_node();
+}
+
+DOM::Element& HTMLDocumentParser::adjusted_current_node()
+{
+ if (m_parsing_fragment && m_stack_of_open_elements.elements().size() == 1)
+ return *m_context_element;
+
+ return current_node();
+}
+
+DOM::Element& HTMLDocumentParser::node_before_current_node()
+{
+ return m_stack_of_open_elements.elements().at(m_stack_of_open_elements.elements().size() - 2);
+}
+
+HTMLDocumentParser::AdjustedInsertionLocation HTMLDocumentParser::find_appropriate_place_for_inserting_node()
+{
+ auto& target = current_node();
+ HTMLDocumentParser::AdjustedInsertionLocation adjusted_insertion_location;
+
+ if (m_foster_parenting && target.local_name().is_one_of(HTML::TagNames::table, HTML::TagNames::tbody, HTML::TagNames::tfoot, HTML::TagNames::thead, HTML::TagNames::tr)) {
+ auto last_template = m_stack_of_open_elements.last_element_with_tag_name(HTML::TagNames::template_);
+ auto last_table = m_stack_of_open_elements.last_element_with_tag_name(HTML::TagNames::table);
+ if (last_template.element && (!last_table.element || last_template.index > last_table.index)) {
+ // This returns the template content, so no need to check the parent is a template.
+ return { downcast<HTMLTemplateElement>(last_template.element)->content(), nullptr };
+ }
+ if (!last_table.element) {
+ ASSERT(m_parsing_fragment);
+ // Guaranteed not to be a template element (it will be the html element),
+ // so no need to check the parent is a template.
+ return { m_stack_of_open_elements.elements().first(), nullptr };
+ }
+ if (last_table.element->parent_node())
+ adjusted_insertion_location = { last_table.element->parent_node(), last_table.element };
+ else
+ adjusted_insertion_location = { m_stack_of_open_elements.element_before(*last_table.element), nullptr };
+ } else {
+ adjusted_insertion_location = { target, nullptr };
+ }
+
+ if (is<HTMLTemplateElement>(*adjusted_insertion_location.parent))
+ return { downcast<HTMLTemplateElement>(*adjusted_insertion_location.parent).content(), nullptr };
+
+ return adjusted_insertion_location;
+}
+
+NonnullRefPtr<DOM::Element> HTMLDocumentParser::create_element_for(const HTMLToken& token, const FlyString& namespace_)
+{
+ auto element = create_element(document(), token.tag_name(), namespace_);
+ for (auto& attribute : token.m_tag.attributes) {
+ element->set_attribute(attribute.local_name_builder.to_string(), attribute.value_builder.to_string());
+ }
+ return element;
+}
+
+RefPtr<DOM::Element> HTMLDocumentParser::insert_foreign_element(const HTMLToken& token, const FlyString& namespace_)
+{
+ auto adjusted_insertion_location = find_appropriate_place_for_inserting_node();
+ auto element = create_element_for(token, namespace_);
+ // FIXME: Check if it's possible to insert `element` at `adjusted_insertion_location`
+ adjusted_insertion_location.parent->insert_before(element, adjusted_insertion_location.insert_before_sibling);
+ m_stack_of_open_elements.push(element);
+ return element;
+}
+
+RefPtr<DOM::Element> HTMLDocumentParser::insert_html_element(const HTMLToken& token)
+{
+ return insert_foreign_element(token, Namespace::HTML);
+}
+
+void HTMLDocumentParser::handle_before_head(HTMLToken& token)
+{
+ if (token.is_character() && token.is_parser_whitespace()) {
+ return;
+ }
+
+ if (token.is_comment()) {
+ insert_comment(token);
+ return;
+ }
+
+ if (token.is_doctype()) {
+ PARSE_ERROR();
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::html) {
+ process_using_the_rules_for(InsertionMode::InBody, token);
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::head) {
+ auto element = insert_html_element(token);
+ m_head_element = downcast<HTMLHeadElement>(*element);
+ m_insertion_mode = InsertionMode::InHead;
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::head, HTML::TagNames::body, HTML::TagNames::html, HTML::TagNames::br)) {
+ goto AnythingElse;
+ }
+
+ if (token.is_end_tag()) {
+ PARSE_ERROR();
+ return;
+ }
+
+AnythingElse:
+ m_head_element = downcast<HTMLHeadElement>(*insert_html_element(HTMLToken::make_start_tag(HTML::TagNames::head)));
+ m_insertion_mode = InsertionMode::InHead;
+ process_using_the_rules_for(InsertionMode::InHead, token);
+ return;
+}
+
+void HTMLDocumentParser::insert_comment(HTMLToken& token)
+{
+ auto data = token.m_comment_or_character.data.to_string();
+ auto adjusted_insertion_location = find_appropriate_place_for_inserting_node();
+ adjusted_insertion_location.parent->insert_before(adopt(*new DOM::Comment(document(), data)), adjusted_insertion_location.insert_before_sibling);
+}
+
+void HTMLDocumentParser::handle_in_head(HTMLToken& token)
+{
+ if (token.is_parser_whitespace()) {
+ insert_character(token.code_point());
+ return;
+ }
+
+ if (token.is_comment()) {
+ insert_comment(token);
+ return;
+ }
+
+ if (token.is_doctype()) {
+ PARSE_ERROR();
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::html) {
+ process_using_the_rules_for(InsertionMode::InBody, token);
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::base, HTML::TagNames::basefont, HTML::TagNames::bgsound, HTML::TagNames::link)) {
+ insert_html_element(token);
+ m_stack_of_open_elements.pop();
+ token.acknowledge_self_closing_flag_if_set();
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::meta) {
+ auto element = insert_html_element(token);
+ m_stack_of_open_elements.pop();
+ token.acknowledge_self_closing_flag_if_set();
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::title) {
+ insert_html_element(token);
+ m_tokenizer.switch_to({}, HTMLTokenizer::State::RCDATA);
+ m_original_insertion_mode = m_insertion_mode;
+ m_insertion_mode = InsertionMode::Text;
+ return;
+ }
+
+ if (token.is_start_tag() && ((token.tag_name() == HTML::TagNames::noscript && m_scripting_enabled) || token.tag_name() == HTML::TagNames::noframes || token.tag_name() == HTML::TagNames::style)) {
+ parse_generic_raw_text_element(token);
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::noscript && !m_scripting_enabled) {
+ insert_html_element(token);
+ m_insertion_mode = InsertionMode::InHeadNoscript;
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::script) {
+ auto adjusted_insertion_location = find_appropriate_place_for_inserting_node();
+ auto element = create_element_for(token, Namespace::HTML);
+ auto& script_element = downcast<HTMLScriptElement>(*element);
+ script_element.set_parser_document({}, document());
+ script_element.set_non_blocking({}, false);
+
+ if (m_parsing_fragment) {
+ TODO();
+ }
+
+ if (m_invoked_via_document_write) {
+ TODO();
+ }
+
+ adjusted_insertion_location.parent->insert_before(element, adjusted_insertion_location.insert_before_sibling, false);
+ m_stack_of_open_elements.push(element);
+ m_tokenizer.switch_to({}, HTMLTokenizer::State::ScriptData);
+ m_original_insertion_mode = m_insertion_mode;
+ m_insertion_mode = InsertionMode::Text;
+ return;
+ }
+ if (token.is_end_tag() && token.tag_name() == HTML::TagNames::head) {
+ m_stack_of_open_elements.pop();
+ m_insertion_mode = InsertionMode::AfterHead;
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::body, HTML::TagNames::html, HTML::TagNames::br)) {
+ goto AnythingElse;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::template_) {
+ insert_html_element(token);
+ m_list_of_active_formatting_elements.add_marker();
+ m_frameset_ok = false;
+ m_insertion_mode = InsertionMode::InTemplate;
+ m_stack_of_template_insertion_modes.append(InsertionMode::InTemplate);
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name() == HTML::TagNames::template_) {
+ if (!m_stack_of_open_elements.contains(HTML::TagNames::template_)) {
+ PARSE_ERROR();
+ return;
+ }
+
+ generate_all_implied_end_tags_thoroughly();
+
+ if (current_node().local_name() != HTML::TagNames::template_)
+ PARSE_ERROR();
+
+ m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::template_);
+ m_list_of_active_formatting_elements.clear_up_to_the_last_marker();
+ m_stack_of_template_insertion_modes.take_last();
+ reset_the_insertion_mode_appropriately();
+ return;
+ }
+
+ if ((token.is_start_tag() && token.tag_name() == HTML::TagNames::head) || token.is_end_tag()) {
+ PARSE_ERROR();
+ return;
+ }
+
+AnythingElse:
+ m_stack_of_open_elements.pop();
+ m_insertion_mode = InsertionMode::AfterHead;
+ process_using_the_rules_for(m_insertion_mode, token);
+}
+
+void HTMLDocumentParser::handle_in_head_noscript(HTMLToken& token)
+{
+ if (token.is_doctype()) {
+ PARSE_ERROR();
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::html) {
+ process_using_the_rules_for(InsertionMode::InBody, token);
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name() == HTML::TagNames::noscript) {
+ m_stack_of_open_elements.pop();
+ m_insertion_mode = InsertionMode::InHead;
+ return;
+ }
+
+ if (token.is_parser_whitespace() || token.is_comment() || (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::basefont, HTML::TagNames::bgsound, HTML::TagNames::link, HTML::TagNames::meta, HTML::TagNames::noframes, HTML::TagNames::style))) {
+ process_using_the_rules_for(InsertionMode::InHead, token);
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name() == HTML::TagNames::br) {
+ goto AnythingElse;
+ }
+
+ if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::head, HTML::TagNames::noscript)) {
+ PARSE_ERROR();
+ return;
+ }
+
+AnythingElse:
+ PARSE_ERROR();
+ m_stack_of_open_elements.pop();
+ m_insertion_mode = InsertionMode::InHead;
+ process_using_the_rules_for(m_insertion_mode, token);
+}
+
+void HTMLDocumentParser::parse_generic_raw_text_element(HTMLToken& token)
+{
+ insert_html_element(token);
+ m_tokenizer.switch_to({}, HTMLTokenizer::State::RAWTEXT);
+ m_original_insertion_mode = m_insertion_mode;
+ m_insertion_mode = InsertionMode::Text;
+}
+
+DOM::Text* HTMLDocumentParser::find_character_insertion_node()
+{
+ auto adjusted_insertion_location = find_appropriate_place_for_inserting_node();
+ if (adjusted_insertion_location.insert_before_sibling) {
+ TODO();
+ }
+ if (adjusted_insertion_location.parent->is_document())
+ return nullptr;
+ if (adjusted_insertion_location.parent->last_child() && adjusted_insertion_location.parent->last_child()->is_text())
+ return downcast<DOM::Text>(adjusted_insertion_location.parent->last_child());
+ auto new_text_node = adopt(*new DOM::Text(document(), ""));
+ adjusted_insertion_location.parent->append_child(new_text_node);
+ return new_text_node;
+}
+
+void HTMLDocumentParser::flush_character_insertions()
+{
+ if (m_character_insertion_builder.is_empty())
+ return;
+ m_character_insertion_node->set_data(m_character_insertion_builder.to_string());
+ m_character_insertion_node->parent()->children_changed();
+ m_character_insertion_builder.clear();
+}
+
+void HTMLDocumentParser::insert_character(u32 data)
+{
+ auto node = find_character_insertion_node();
+ if (node == m_character_insertion_node) {
+ m_character_insertion_builder.append(Utf32View { &data, 1 });
+ return;
+ }
+ if (!m_character_insertion_node) {
+ m_character_insertion_node = node;
+ m_character_insertion_builder.append(Utf32View { &data, 1 });
+ return;
+ }
+ flush_character_insertions();
+ m_character_insertion_node = node;
+ m_character_insertion_builder.append(Utf32View { &data, 1 });
+}
+
+void HTMLDocumentParser::handle_after_head(HTMLToken& token)
+{
+ if (token.is_character() && token.is_parser_whitespace()) {
+ insert_character(token.code_point());
+ return;
+ }
+
+ if (token.is_comment()) {
+ insert_comment(token);
+ return;
+ }
+
+ if (token.is_doctype()) {
+ PARSE_ERROR();
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::html) {
+ process_using_the_rules_for(InsertionMode::InBody, token);
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::body) {
+ insert_html_element(token);
+ m_frameset_ok = false;
+ m_insertion_mode = InsertionMode::InBody;
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::frameset) {
+ insert_html_element(token);
+ m_insertion_mode = InsertionMode::InFrameset;
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::base, HTML::TagNames::basefont, HTML::TagNames::bgsound, HTML::TagNames::link, HTML::TagNames::meta, HTML::TagNames::noframes, HTML::TagNames::script, HTML::TagNames::style, HTML::TagNames::template_, HTML::TagNames::title)) {
+ PARSE_ERROR();
+ m_stack_of_open_elements.push(*m_head_element);
+ process_using_the_rules_for(InsertionMode::InHead, token);
+ m_stack_of_open_elements.elements().remove_first_matching([&](auto& entry) {
+ return entry.ptr() == m_head_element;
+ });
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name() == HTML::TagNames::template_) {
+ process_using_the_rules_for(InsertionMode::InHead, token);
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::body, HTML::TagNames::html, HTML::TagNames::br)) {
+ goto AnythingElse;
+ }
+
+ if ((token.is_start_tag() && token.tag_name() == HTML::TagNames::head) || token.is_end_tag()) {
+ PARSE_ERROR();
+ return;
+ }
+
+AnythingElse:
+ insert_html_element(HTMLToken::make_start_tag(HTML::TagNames::body));
+ m_insertion_mode = InsertionMode::InBody;
+ process_using_the_rules_for(m_insertion_mode, token);
+}
+
+void HTMLDocumentParser::generate_implied_end_tags(const FlyString& exception)
+{
+ while (current_node().local_name() != exception && current_node().local_name().is_one_of(HTML::TagNames::dd, HTML::TagNames::dt, HTML::TagNames::li, HTML::TagNames::optgroup, HTML::TagNames::option, HTML::TagNames::p, HTML::TagNames::rb, HTML::TagNames::rp, HTML::TagNames::rt, HTML::TagNames::rtc))
+ m_stack_of_open_elements.pop();
+}
+
+void HTMLDocumentParser::generate_all_implied_end_tags_thoroughly()
+{
+ while (current_node().local_name().is_one_of(HTML::TagNames::caption, HTML::TagNames::colgroup, HTML::TagNames::dd, HTML::TagNames::dt, HTML::TagNames::li, HTML::TagNames::optgroup, HTML::TagNames::option, HTML::TagNames::p, HTML::TagNames::rb, HTML::TagNames::rp, HTML::TagNames::rt, HTML::TagNames::rtc, HTML::TagNames::tbody, HTML::TagNames::td, HTML::TagNames::tfoot, HTML::TagNames::th, HTML::TagNames::thead, HTML::TagNames::tr))
+ m_stack_of_open_elements.pop();
+}
+
+void HTMLDocumentParser::close_a_p_element()
+{
+ generate_implied_end_tags(HTML::TagNames::p);
+ if (current_node().local_name() != HTML::TagNames::p) {
+ PARSE_ERROR();
+ }
+ m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::p);
+}
+
+void HTMLDocumentParser::handle_after_body(HTMLToken& token)
+{
+ if (token.is_character() && token.is_parser_whitespace()) {
+ process_using_the_rules_for(InsertionMode::InBody, token);
+ return;
+ }
+
+ if (token.is_comment()) {
+ auto data = token.m_comment_or_character.data.to_string();
+ auto& insertion_location = m_stack_of_open_elements.first();
+ insertion_location.append_child(adopt(*new DOM::Comment(document(), data)));
+ return;
+ }
+
+ if (token.is_doctype()) {
+ PARSE_ERROR();
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::html) {
+ process_using_the_rules_for(InsertionMode::InBody, token);
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name() == HTML::TagNames::html) {
+ if (m_parsing_fragment) {
+ PARSE_ERROR();
+ return;
+ }
+ m_insertion_mode = InsertionMode::AfterAfterBody;
+ return;
+ }
+
+ if (token.is_end_of_file()) {
+ stop_parsing();
+ return;
+ }
+
+ PARSE_ERROR();
+ m_insertion_mode = InsertionMode::InBody;
+ process_using_the_rules_for(InsertionMode::InBody, token);
+}
+
+void HTMLDocumentParser::handle_after_after_body(HTMLToken& token)
+{
+ if (token.is_comment()) {
+ auto comment = adopt(*new DOM::Comment(document(), token.m_comment_or_character.data.to_string()));
+ document().append_child(move(comment));
+ return;
+ }
+
+ if (token.is_doctype() || token.is_parser_whitespace() || (token.is_start_tag() && token.tag_name() == HTML::TagNames::html)) {
+ process_using_the_rules_for(InsertionMode::InBody, token);
+ return;
+ }
+
+ if (token.is_end_of_file()) {
+ stop_parsing();
+ return;
+ }
+
+ PARSE_ERROR();
+ m_insertion_mode = InsertionMode::InBody;
+ process_using_the_rules_for(m_insertion_mode, token);
+}
+
+void HTMLDocumentParser::reconstruct_the_active_formatting_elements()
+{
+ // FIXME: This needs to care about "markers"
+
+ if (m_list_of_active_formatting_elements.is_empty())
+ return;
+
+ if (m_list_of_active_formatting_elements.entries().last().is_marker())
+ return;
+
+ if (m_stack_of_open_elements.contains(*m_list_of_active_formatting_elements.entries().last().element))
+ return;
+
+ ssize_t index = m_list_of_active_formatting_elements.entries().size() - 1;
+ RefPtr<DOM::Element> entry = m_list_of_active_formatting_elements.entries().at(index).element;
+ ASSERT(entry);
+
+Rewind:
+ if (index == 0) {
+ goto Create;
+ }
+
+ --index;
+ entry = m_list_of_active_formatting_elements.entries().at(index).element;
+ ASSERT(entry);
+
+ if (!m_stack_of_open_elements.contains(*entry))
+ goto Rewind;
+
+Advance:
+ ++index;
+ entry = m_list_of_active_formatting_elements.entries().at(index).element;
+ ASSERT(entry);
+
+Create:
+ // FIXME: Hold on to the real token!
+ auto new_element = insert_html_element(HTMLToken::make_start_tag(entry->local_name()));
+
+ m_list_of_active_formatting_elements.entries().at(index).element = *new_element;
+
+ if (index != (ssize_t)m_list_of_active_formatting_elements.entries().size() - 1)
+ goto Advance;
+}
+
+HTMLDocumentParser::AdoptionAgencyAlgorithmOutcome HTMLDocumentParser::run_the_adoption_agency_algorithm(HTMLToken& token)
+{
+ auto subject = token.tag_name();
+
+ // If the current node is an HTML element whose tag name is subject,
+ // and the current node is not in the list of active formatting elements,
+ // then pop the current node off the stack of open elements, and return.
+ if (current_node().local_name() == subject && !m_list_of_active_formatting_elements.contains(current_node())) {
+ m_stack_of_open_elements.pop();
+ return AdoptionAgencyAlgorithmOutcome::DoNothing;
+ }
+
+ size_t outer_loop_counter = 0;
+
+ //OuterLoop:
+ if (outer_loop_counter >= 8)
+ return AdoptionAgencyAlgorithmOutcome::DoNothing;
+
+ ++outer_loop_counter;
+
+ auto formatting_element = m_list_of_active_formatting_elements.last_element_with_tag_name_before_marker(subject);
+ if (!formatting_element)
+ return AdoptionAgencyAlgorithmOutcome::RunAnyOtherEndTagSteps;
+
+ if (!m_stack_of_open_elements.contains(*formatting_element)) {
+ PARSE_ERROR();
+ // FIXME: If formatting element is not in the stack of open elements,
+ // then this is a parse error; remove the element from the list, and return.
+ TODO();
+ }
+
+ if (!m_stack_of_open_elements.has_in_scope(*formatting_element)) {
+ PARSE_ERROR();
+ return AdoptionAgencyAlgorithmOutcome::DoNothing;
+ }
+
+ if (formatting_element != &current_node()) {
+ PARSE_ERROR();
+ }
+
+ RefPtr<DOM::Element> furthest_block = m_stack_of_open_elements.topmost_special_node_below(*formatting_element);
+
+ if (!furthest_block) {
+ while (&current_node() != formatting_element)
+ m_stack_of_open_elements.pop();
+ m_stack_of_open_elements.pop();
+
+ m_list_of_active_formatting_elements.remove(*formatting_element);
+ return AdoptionAgencyAlgorithmOutcome::DoNothing;
+ }
+
+ // FIXME: Implement the rest of the AAA :^)
+
+ TODO();
+}
+
+bool HTMLDocumentParser::is_special_tag(const FlyString& tag_name, const FlyString& namespace_)
+{
+ if (namespace_ == Namespace::HTML) {
+ return tag_name.is_one_of(
+ HTML::TagNames::address,
+ HTML::TagNames::applet,
+ HTML::TagNames::area,
+ HTML::TagNames::article,
+ HTML::TagNames::aside,
+ HTML::TagNames::base,
+ HTML::TagNames::basefont,
+ HTML::TagNames::bgsound,
+ HTML::TagNames::blockquote,
+ HTML::TagNames::body,
+ HTML::TagNames::br,
+ HTML::TagNames::button,
+ HTML::TagNames::caption,
+ HTML::TagNames::center,
+ HTML::TagNames::col,
+ HTML::TagNames::colgroup,
+ HTML::TagNames::dd,
+ HTML::TagNames::details,
+ HTML::TagNames::dir,
+ HTML::TagNames::div,
+ HTML::TagNames::dl,
+ HTML::TagNames::dt,
+ HTML::TagNames::embed,
+ HTML::TagNames::fieldset,
+ HTML::TagNames::figcaption,
+ HTML::TagNames::figure,
+ HTML::TagNames::footer,
+ HTML::TagNames::form,
+ HTML::TagNames::frame,
+ HTML::TagNames::frameset,
+ HTML::TagNames::h1,
+ HTML::TagNames::h2,
+ HTML::TagNames::h3,
+ HTML::TagNames::h4,
+ HTML::TagNames::h5,
+ HTML::TagNames::h6,
+ HTML::TagNames::head,
+ HTML::TagNames::header,
+ HTML::TagNames::hgroup,
+ HTML::TagNames::hr,
+ HTML::TagNames::html,
+ HTML::TagNames::iframe,
+ HTML::TagNames::img,
+ HTML::TagNames::input,
+ HTML::TagNames::keygen,
+ HTML::TagNames::li,
+ HTML::TagNames::link,
+ HTML::TagNames::listing,
+ HTML::TagNames::main,
+ HTML::TagNames::marquee,
+ HTML::TagNames::menu,
+ HTML::TagNames::meta,
+ HTML::TagNames::nav,
+ HTML::TagNames::noembed,
+ HTML::TagNames::noframes,
+ HTML::TagNames::noscript,
+ HTML::TagNames::object,
+ HTML::TagNames::ol,
+ HTML::TagNames::p,
+ HTML::TagNames::param,
+ HTML::TagNames::plaintext,
+ HTML::TagNames::pre,
+ HTML::TagNames::script,
+ HTML::TagNames::section,
+ HTML::TagNames::select,
+ HTML::TagNames::source,
+ HTML::TagNames::style,
+ HTML::TagNames::summary,
+ HTML::TagNames::table,
+ HTML::TagNames::tbody,
+ HTML::TagNames::td,
+ HTML::TagNames::template_,
+ HTML::TagNames::textarea,
+ HTML::TagNames::tfoot,
+ HTML::TagNames::th,
+ HTML::TagNames::thead,
+ HTML::TagNames::title,
+ HTML::TagNames::tr,
+ HTML::TagNames::track,
+ HTML::TagNames::ul,
+ HTML::TagNames::wbr,
+ HTML::TagNames::xmp);
+ } else if (namespace_ == Namespace::SVG) {
+ return tag_name.is_one_of(
+ SVG::TagNames::desc,
+ SVG::TagNames::foreignObject,
+ SVG::TagNames::title);
+ } else if (namespace_ == Namespace::MathML) {
+ TODO();
+ }
+
+ return false;
+}
+
+void HTMLDocumentParser::handle_in_body(HTMLToken& token)
+{
+ if (token.is_character()) {
+ if (token.code_point() == 0) {
+ PARSE_ERROR();
+ return;
+ }
+ if (token.is_parser_whitespace()) {
+ reconstruct_the_active_formatting_elements();
+ insert_character(token.code_point());
+ return;
+ }
+ reconstruct_the_active_formatting_elements();
+ insert_character(token.code_point());
+ m_frameset_ok = false;
+ return;
+ }
+
+ if (token.is_comment()) {
+ insert_comment(token);
+ return;
+ }
+
+ if (token.is_doctype()) {
+ PARSE_ERROR();
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::html) {
+ PARSE_ERROR();
+ if (m_stack_of_open_elements.contains(HTML::TagNames::template_))
+ return;
+ for (auto& attribute : token.m_tag.attributes) {
+ if (current_node().has_attribute(attribute.local_name_builder.string_view()))
+ continue;
+ current_node().set_attribute(attribute.local_name_builder.to_string(), attribute.value_builder.to_string());
+ }
+ return;
+ }
+ if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::base, HTML::TagNames::basefont, HTML::TagNames::bgsound, HTML::TagNames::link, HTML::TagNames::meta, HTML::TagNames::noframes, HTML::TagNames::script, HTML::TagNames::style, HTML::TagNames::template_, HTML::TagNames::title)) {
+ process_using_the_rules_for(InsertionMode::InHead, token);
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name() == HTML::TagNames::template_) {
+ process_using_the_rules_for(InsertionMode::InHead, token);
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::body) {
+ PARSE_ERROR();
+ if (m_stack_of_open_elements.elements().size() == 1
+ || m_stack_of_open_elements.elements().at(1).local_name() != HTML::TagNames::body
+ || m_stack_of_open_elements.contains(HTML::TagNames::template_)) {
+ ASSERT(m_parsing_fragment);
+ return;
+ }
+ m_frameset_ok = false;
+ auto& body_element = m_stack_of_open_elements.elements().at(1);
+ for (auto& attribute : token.m_tag.attributes) {
+ if (body_element.has_attribute(attribute.local_name_builder.string_view()))
+ continue;
+ body_element.set_attribute(attribute.local_name_builder.to_string(), attribute.value_builder.to_string());
+ }
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::frameset) {
+ PARSE_ERROR();
+
+ if (m_stack_of_open_elements.elements().size() == 1
+ || m_stack_of_open_elements.elements().at(1).local_name() != HTML::TagNames::body) {
+ ASSERT(m_parsing_fragment);
+ return;
+ }
+
+ if (!m_frameset_ok)
+ return;
+
+ TODO();
+ }
+
+ if (token.is_end_of_file()) {
+ if (!m_stack_of_template_insertion_modes.is_empty()) {
+ process_using_the_rules_for(InsertionMode::InTemplate, token);
+ return;
+ }
+
+ for (auto& node : m_stack_of_open_elements.elements()) {
+ if (!node.local_name().is_one_of(HTML::TagNames::dd, HTML::TagNames::dt, HTML::TagNames::li, HTML::TagNames::optgroup, HTML::TagNames::option, HTML::TagNames::p, HTML::TagNames::rb, HTML::TagNames::rp, HTML::TagNames::rt, HTML::TagNames::rtc, HTML::TagNames::tbody, HTML::TagNames::td, HTML::TagNames::tfoot, HTML::TagNames::th, HTML::TagNames::thead, HTML::TagNames::tr, HTML::TagNames::body, HTML::TagNames::html)) {
+ PARSE_ERROR();
+ break;
+ }
+ }
+
+ stop_parsing();
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name() == HTML::TagNames::body) {
+ if (!m_stack_of_open_elements.has_in_scope(HTML::TagNames::body)) {
+ PARSE_ERROR();
+ return;
+ }
+
+ for (auto& node : m_stack_of_open_elements.elements()) {
+ if (!node.local_name().is_one_of(HTML::TagNames::dd, HTML::TagNames::dt, HTML::TagNames::li, HTML::TagNames::optgroup, HTML::TagNames::option, HTML::TagNames::p, HTML::TagNames::rb, HTML::TagNames::rp, HTML::TagNames::rt, HTML::TagNames::rtc, HTML::TagNames::tbody, HTML::TagNames::td, HTML::TagNames::tfoot, HTML::TagNames::th, HTML::TagNames::thead, HTML::TagNames::tr, HTML::TagNames::body, HTML::TagNames::html)) {
+ PARSE_ERROR();
+ break;
+ }
+ }
+
+ m_insertion_mode = InsertionMode::AfterBody;
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name() == HTML::TagNames::html) {
+ if (!m_stack_of_open_elements.has_in_scope(HTML::TagNames::body)) {
+ PARSE_ERROR();
+ return;
+ }
+
+ for (auto& node : m_stack_of_open_elements.elements()) {
+ if (!node.local_name().is_one_of(HTML::TagNames::dd, HTML::TagNames::dt, HTML::TagNames::li, HTML::TagNames::optgroup, HTML::TagNames::option, HTML::TagNames::p, HTML::TagNames::rb, HTML::TagNames::rp, HTML::TagNames::rt, HTML::TagNames::rtc, HTML::TagNames::tbody, HTML::TagNames::td, HTML::TagNames::tfoot, HTML::TagNames::th, HTML::TagNames::thead, HTML::TagNames::tr, HTML::TagNames::body, HTML::TagNames::html)) {
+ PARSE_ERROR();
+ break;
+ }
+ }
+
+ m_insertion_mode = InsertionMode::AfterBody;
+ process_using_the_rules_for(m_insertion_mode, token);
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::address, HTML::TagNames::article, HTML::TagNames::aside, HTML::TagNames::blockquote, HTML::TagNames::center, HTML::TagNames::details, HTML::TagNames::dialog, HTML::TagNames::dir, HTML::TagNames::div, HTML::TagNames::dl, HTML::TagNames::fieldset, HTML::TagNames::figcaption, HTML::TagNames::figure, HTML::TagNames::footer, HTML::TagNames::header, HTML::TagNames::hgroup, HTML::TagNames::main, HTML::TagNames::menu, HTML::TagNames::nav, HTML::TagNames::ol, HTML::TagNames::p, HTML::TagNames::section, HTML::TagNames::summary, HTML::TagNames::ul)) {
+ if (m_stack_of_open_elements.has_in_button_scope(HTML::TagNames::p))
+ close_a_p_element();
+ insert_html_element(token);
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::h1, HTML::TagNames::h2, HTML::TagNames::h3, HTML::TagNames::h4, HTML::TagNames::h5, HTML::TagNames::h6)) {
+ if (m_stack_of_open_elements.has_in_button_scope(HTML::TagNames::p))
+ close_a_p_element();
+ if (current_node().local_name().is_one_of(HTML::TagNames::h1, HTML::TagNames::h2, HTML::TagNames::h3, HTML::TagNames::h4, HTML::TagNames::h5, HTML::TagNames::h6)) {
+ PARSE_ERROR();
+ m_stack_of_open_elements.pop();
+ }
+ insert_html_element(token);
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::pre, HTML::TagNames::listing)) {
+ if (m_stack_of_open_elements.has_in_button_scope(HTML::TagNames::p))
+ close_a_p_element();
+
+ insert_html_element(token);
+
+ m_frameset_ok = false;
+
+ // If the next token is a U+000A LINE FEED (LF) character token,
+ // then ignore that token and move on to the next one.
+ // (Newlines at the start of pre blocks are ignored as an authoring convenience.)
+ auto next_token = m_tokenizer.next_token();
+ if (next_token.has_value() && next_token.value().is_character() && next_token.value().code_point() == '\n') {
+ // Ignore it.
+ } else {
+ process_using_the_rules_for(m_insertion_mode, next_token.value());
+ }
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::form) {
+ if (m_form_element && !m_stack_of_open_elements.contains(HTML::TagNames::template_)) {
+ PARSE_ERROR();
+ return;
+ }
+ if (m_stack_of_open_elements.has_in_button_scope(HTML::TagNames::p))
+ close_a_p_element();
+ auto element = insert_html_element(token);
+ if (!m_stack_of_open_elements.contains(HTML::TagNames::template_))
+ m_form_element = downcast<HTMLFormElement>(*element);
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::li) {
+ m_frameset_ok = false;
+
+ for (ssize_t i = m_stack_of_open_elements.elements().size() - 1; i >= 0; --i) {
+ RefPtr<DOM::Element> node = m_stack_of_open_elements.elements()[i];
+
+ if (node->local_name() == HTML::TagNames::li) {
+ generate_implied_end_tags(HTML::TagNames::li);
+ if (current_node().local_name() != HTML::TagNames::li) {
+ PARSE_ERROR();
+ }
+ m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::li);
+ break;
+ }
+
+ if (is_special_tag(node->local_name(), node->namespace_()) && !node->local_name().is_one_of(HTML::TagNames::address, HTML::TagNames::div, HTML::TagNames::p))
+ break;
+ }
+
+ if (m_stack_of_open_elements.has_in_button_scope(HTML::TagNames::p))
+ close_a_p_element();
+
+ insert_html_element(token);
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::dd, HTML::TagNames::dt)) {
+ m_frameset_ok = false;
+ for (ssize_t i = m_stack_of_open_elements.elements().size() - 1; i >= 0; --i) {
+ RefPtr<DOM::Element> node = m_stack_of_open_elements.elements()[i];
+ if (node->local_name() == HTML::TagNames::dd) {
+ generate_implied_end_tags(HTML::TagNames::dd);
+ if (current_node().local_name() != HTML::TagNames::dd) {
+ PARSE_ERROR();
+ }
+ m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::dd);
+ break;
+ }
+ if (node->local_name() == HTML::TagNames::dt) {
+ generate_implied_end_tags(HTML::TagNames::dt);
+ if (current_node().local_name() != HTML::TagNames::dt) {
+ PARSE_ERROR();
+ }
+ m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::dt);
+ break;
+ }
+ if (is_special_tag(node->local_name(), node->namespace_()) && !node->local_name().is_one_of(HTML::TagNames::address, HTML::TagNames::div, HTML::TagNames::p))
+ break;
+ }
+ if (m_stack_of_open_elements.has_in_button_scope(HTML::TagNames::p))
+ close_a_p_element();
+ insert_html_element(token);
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::plaintext) {
+ if (m_stack_of_open_elements.has_in_button_scope(HTML::TagNames::p))
+ close_a_p_element();
+ insert_html_element(token);
+ m_tokenizer.switch_to({}, HTMLTokenizer::State::PLAINTEXT);
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::button) {
+ if (m_stack_of_open_elements.has_in_button_scope(HTML::TagNames::button)) {
+ PARSE_ERROR();
+ generate_implied_end_tags();
+ m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::button);
+ }
+ reconstruct_the_active_formatting_elements();
+ insert_html_element(token);
+ m_frameset_ok = false;
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::address, HTML::TagNames::article, HTML::TagNames::aside, HTML::TagNames::blockquote, HTML::TagNames::button, HTML::TagNames::center, HTML::TagNames::details, HTML::TagNames::dialog, HTML::TagNames::dir, HTML::TagNames::div, HTML::TagNames::dl, HTML::TagNames::fieldset, HTML::TagNames::figcaption, HTML::TagNames::figure, HTML::TagNames::footer, HTML::TagNames::header, HTML::TagNames::hgroup, HTML::TagNames::listing, HTML::TagNames::main, HTML::TagNames::menu, HTML::TagNames::nav, HTML::TagNames::ol, HTML::TagNames::pre, HTML::TagNames::section, HTML::TagNames::summary, HTML::TagNames::ul)) {
+ if (!m_stack_of_open_elements.has_in_scope(token.tag_name())) {
+ PARSE_ERROR();
+ return;
+ }
+
+ generate_implied_end_tags();
+
+ if (current_node().local_name() != token.tag_name()) {
+ PARSE_ERROR();
+ }
+
+ m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(token.tag_name());
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name() == HTML::TagNames::form) {
+ if (!m_stack_of_open_elements.contains(HTML::TagNames::template_)) {
+ auto node = m_form_element;
+ m_form_element = nullptr;
+ if (!node || !m_stack_of_open_elements.has_in_scope(*node)) {
+ PARSE_ERROR();
+ return;
+ }
+ generate_implied_end_tags();
+ if (&current_node() != node) {
+ PARSE_ERROR();
+ }
+ m_stack_of_open_elements.elements().remove_first_matching([&](auto& entry) { return entry.ptr() == node.ptr(); });
+ } else {
+ if (!m_stack_of_open_elements.has_in_scope(HTML::TagNames::form)) {
+ PARSE_ERROR();
+ return;
+ }
+ generate_implied_end_tags();
+ if (current_node().local_name() != HTML::TagNames::form) {
+ PARSE_ERROR();
+ }
+ m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::form);
+ }
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name() == HTML::TagNames::p) {
+ if (!m_stack_of_open_elements.has_in_button_scope(HTML::TagNames::p)) {
+ PARSE_ERROR();
+ insert_html_element(HTMLToken::make_start_tag(HTML::TagNames::p));
+ }
+ close_a_p_element();
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name() == HTML::TagNames::li) {
+ if (!m_stack_of_open_elements.has_in_list_item_scope(HTML::TagNames::li)) {
+ PARSE_ERROR();
+ return;
+ }
+ generate_implied_end_tags(HTML::TagNames::li);
+ if (current_node().local_name() != HTML::TagNames::li) {
+ PARSE_ERROR();
+ dbg() << "Expected <li> current node, but had <" << current_node().local_name() << ">";
+ }
+ m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::li);
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::dd, HTML::TagNames::dt)) {
+ if (!m_stack_of_open_elements.has_in_scope(token.tag_name())) {
+ PARSE_ERROR();
+ return;
+ }
+ generate_implied_end_tags(token.tag_name());
+ if (current_node().local_name() != token.tag_name()) {
+ PARSE_ERROR();
+ }
+ m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(token.tag_name());
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::h1, HTML::TagNames::h2, HTML::TagNames::h3, HTML::TagNames::h4, HTML::TagNames::h5, HTML::TagNames::h6)) {
+ if (!m_stack_of_open_elements.has_in_scope(HTML::TagNames::h1)
+ && !m_stack_of_open_elements.has_in_scope(HTML::TagNames::h2)
+ && !m_stack_of_open_elements.has_in_scope(HTML::TagNames::h3)
+ && !m_stack_of_open_elements.has_in_scope(HTML::TagNames::h4)
+ && !m_stack_of_open_elements.has_in_scope(HTML::TagNames::h5)
+ && !m_stack_of_open_elements.has_in_scope(HTML::TagNames::h6)) {
+ PARSE_ERROR();
+ return;
+ }
+
+ generate_implied_end_tags();
+ if (current_node().local_name() != token.tag_name()) {
+ PARSE_ERROR();
+ }
+
+ for (;;) {
+ auto popped_element = m_stack_of_open_elements.pop();
+ if (popped_element->local_name().is_one_of(HTML::TagNames::h1, HTML::TagNames::h2, HTML::TagNames::h3, HTML::TagNames::h4, HTML::TagNames::h5, HTML::TagNames::h6))
+ break;
+ }
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::a) {
+ if (auto* element = m_list_of_active_formatting_elements.last_element_with_tag_name_before_marker(HTML::TagNames::a)) {
+ PARSE_ERROR();
+ if (run_the_adoption_agency_algorithm(token) == AdoptionAgencyAlgorithmOutcome::RunAnyOtherEndTagSteps)
+ goto AnyOtherEndTag;
+ m_list_of_active_formatting_elements.remove(*element);
+ m_stack_of_open_elements.elements().remove_first_matching([&](auto& entry) {
+ return entry.ptr() == element;
+ });
+ }
+ reconstruct_the_active_formatting_elements();
+ auto element = insert_html_element(token);
+ m_list_of_active_formatting_elements.add(*element);
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::b, HTML::TagNames::big, HTML::TagNames::code, HTML::TagNames::em, HTML::TagNames::font, HTML::TagNames::i, HTML::TagNames::s, HTML::TagNames::small, HTML::TagNames::strike, HTML::TagNames::strong, HTML::TagNames::tt, HTML::TagNames::u)) {
+ reconstruct_the_active_formatting_elements();
+ auto element = insert_html_element(token);
+ m_list_of_active_formatting_elements.add(*element);
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::nobr) {
+ reconstruct_the_active_formatting_elements();
+ if (m_stack_of_open_elements.has_in_scope(HTML::TagNames::nobr)) {
+ PARSE_ERROR();
+ run_the_adoption_agency_algorithm(token);
+ reconstruct_the_active_formatting_elements();
+ }
+ auto element = insert_html_element(token);
+ m_list_of_active_formatting_elements.add(*element);
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::a, HTML::TagNames::b, HTML::TagNames::big, HTML::TagNames::code, HTML::TagNames::em, HTML::TagNames::font, HTML::TagNames::i, HTML::TagNames::nobr, HTML::TagNames::s, HTML::TagNames::small, HTML::TagNames::strike, HTML::TagNames::strong, HTML::TagNames::tt, HTML::TagNames::u)) {
+ if (run_the_adoption_agency_algorithm(token) == AdoptionAgencyAlgorithmOutcome::RunAnyOtherEndTagSteps)
+ goto AnyOtherEndTag;
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::applet, HTML::TagNames::marquee, HTML::TagNames::object)) {
+ reconstruct_the_active_formatting_elements();
+ insert_html_element(token);
+ m_list_of_active_formatting_elements.add_marker();
+ m_frameset_ok = false;
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::applet, HTML::TagNames::marquee, HTML::TagNames::object)) {
+ if (!m_stack_of_open_elements.has_in_scope(token.tag_name())) {
+ PARSE_ERROR();
+ return;
+ }
+
+ generate_implied_end_tags();
+ if (current_node().local_name() != token.tag_name()) {
+ PARSE_ERROR();
+ }
+ m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(token.tag_name());
+ m_list_of_active_formatting_elements.clear_up_to_the_last_marker();
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::table) {
+ if (!document().in_quirks_mode()) {
+ if (m_stack_of_open_elements.has_in_button_scope(HTML::TagNames::p))
+ close_a_p_element();
+ }
+ insert_html_element(token);
+ m_frameset_ok = false;
+ m_insertion_mode = InsertionMode::InTable;
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name() == HTML::TagNames::br) {
+ token.drop_attributes();
+ goto BRStartTag;
+ }
+
+ if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::area, HTML::TagNames::br, HTML::TagNames::embed, HTML::TagNames::img, HTML::TagNames::keygen, HTML::TagNames::wbr)) {
+ BRStartTag:
+ reconstruct_the_active_formatting_elements();
+ insert_html_element(token);
+ m_stack_of_open_elements.pop();
+ token.acknowledge_self_closing_flag_if_set();
+ m_frameset_ok = false;
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::input) {
+ reconstruct_the_active_formatting_elements();
+ insert_html_element(token);
+ m_stack_of_open_elements.pop();
+ token.acknowledge_self_closing_flag_if_set();
+ auto type_attribute = token.attribute(HTML::AttributeNames::type);
+ if (type_attribute.is_null() || !type_attribute.equals_ignoring_case("hidden")) {
+ m_frameset_ok = false;
+ }
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::param, HTML::TagNames::source, HTML::TagNames::track)) {
+ insert_html_element(token);
+ m_stack_of_open_elements.pop();
+ token.acknowledge_self_closing_flag_if_set();
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::hr) {
+ if (m_stack_of_open_elements.has_in_button_scope(HTML::TagNames::p))
+ close_a_p_element();
+ insert_html_element(token);
+ m_stack_of_open_elements.pop();
+ token.acknowledge_self_closing_flag_if_set();
+ m_frameset_ok = false;
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::image) {
+ // Parse error. Change the token's tag name to HTML::TagNames::img and reprocess it. (Don't ask.)
+ PARSE_ERROR();
+ token.m_tag.tag_name.clear();
+ token.m_tag.tag_name.append(HTML::TagNames::img);
+ process_using_the_rules_for(m_insertion_mode, token);
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::textarea) {
+ insert_html_element(token);
+
+ m_tokenizer.switch_to({}, HTMLTokenizer::State::RCDATA);
+
+ // If the next token is a U+000A LINE FEED (LF) character token,
+ // then ignore that token and move on to the next one.
+ // (Newlines at the start of pre blocks are ignored as an authoring convenience.)
+ auto next_token = m_tokenizer.next_token();
+
+ m_original_insertion_mode = m_insertion_mode;
+ m_frameset_ok = false;
+ m_insertion_mode = InsertionMode::Text;
+
+ if (next_token.has_value() && next_token.value().is_character() && next_token.value().code_point() == '\n') {
+ // Ignore it.
+ } else {
+ process_using_the_rules_for(m_insertion_mode, next_token.value());
+ }
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::xmp) {
+ if (m_stack_of_open_elements.has_in_button_scope(HTML::TagNames::p)) {
+ close_a_p_element();
+ }
+ reconstruct_the_active_formatting_elements();
+ m_frameset_ok = false;
+ parse_generic_raw_text_element(token);
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::iframe) {
+ m_frameset_ok = false;
+ parse_generic_raw_text_element(token);
+ return;
+ }
+
+ if (token.is_start_tag() && ((token.tag_name() == HTML::TagNames::noembed) || (token.tag_name() == HTML::TagNames::noscript && m_scripting_enabled))) {
+ parse_generic_raw_text_element(token);
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::select) {
+ reconstruct_the_active_formatting_elements();
+ insert_html_element(token);
+ m_frameset_ok = false;
+ switch (m_insertion_mode) {
+ case InsertionMode::InTable:
+ case InsertionMode::InCaption:
+ case InsertionMode::InTableBody:
+ case InsertionMode::InRow:
+ case InsertionMode::InCell:
+ m_insertion_mode = InsertionMode::InSelectInTable;
+ break;
+ default:
+ m_insertion_mode = InsertionMode::InSelect;
+ break;
+ }
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::optgroup, HTML::TagNames::option)) {
+ if (current_node().local_name() == HTML::TagNames::option)
+ m_stack_of_open_elements.pop();
+ reconstruct_the_active_formatting_elements();
+ insert_html_element(token);
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::rb, HTML::TagNames::rtc)) {
+ if (m_stack_of_open_elements.has_in_scope(HTML::TagNames::ruby))
+ generate_implied_end_tags();
+
+ if (current_node().local_name() != HTML::TagNames::ruby)
+ PARSE_ERROR();
+
+ insert_html_element(token);
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::rp, HTML::TagNames::rt)) {
+ if (m_stack_of_open_elements.has_in_scope(HTML::TagNames::ruby))
+ generate_implied_end_tags(HTML::TagNames::rtc);
+
+ if (current_node().local_name() != HTML::TagNames::rtc || current_node().local_name() != HTML::TagNames::ruby)
+ PARSE_ERROR();
+
+ insert_html_element(token);
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::math) {
+ reconstruct_the_active_formatting_elements();
+ adjust_mathml_attributes(token);
+ adjust_foreign_attributes(token);
+
+ insert_foreign_element(token, Namespace::MathML);
+
+ if (token.is_self_closing()) {
+ m_stack_of_open_elements.pop();
+ token.acknowledge_self_closing_flag_if_set();
+ }
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::svg) {
+ reconstruct_the_active_formatting_elements();
+ adjust_svg_attributes(token);
+ adjust_foreign_attributes(token);
+
+ insert_foreign_element(token, Namespace::SVG);
+
+ if (token.is_self_closing()) {
+ m_stack_of_open_elements.pop();
+ token.acknowledge_self_closing_flag_if_set();
+ }
+ return;
+ }
+
+ if ((token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::caption, HTML::TagNames::col, HTML::TagNames::colgroup, HTML::TagNames::frame, HTML::TagNames::head, HTML::TagNames::tbody, HTML::TagNames::td, HTML::TagNames::tfoot, HTML::TagNames::th, HTML::TagNames::thead, HTML::TagNames::tr))) {
+ PARSE_ERROR();
+ return;
+ }
+
+ // Any other start tag
+ if (token.is_start_tag()) {
+ reconstruct_the_active_formatting_elements();
+ insert_html_element(token);
+ return;
+ }
+
+ if (token.is_end_tag()) {
+ AnyOtherEndTag:
+ RefPtr<DOM::Element> node;
+ for (ssize_t i = m_stack_of_open_elements.elements().size() - 1; i >= 0; --i) {
+ node = m_stack_of_open_elements.elements()[i];
+ if (node->local_name() == token.tag_name()) {
+ generate_implied_end_tags(token.tag_name());
+ if (node != current_node()) {
+ PARSE_ERROR();
+ }
+ while (&current_node() != node) {
+ m_stack_of_open_elements.pop();
+ }
+ m_stack_of_open_elements.pop();
+ break;
+ }
+ if (is_special_tag(node->local_name(), node->namespace_())) {
+ PARSE_ERROR();
+ return;
+ }
+ }
+ return;
+ }
+}
+
+void HTMLDocumentParser::adjust_mathml_attributes(HTMLToken& token)
+{
+ token.adjust_attribute_name("definitionurl", "definitionURL");
+}
+
+void HTMLDocumentParser::adjust_svg_tag_names(HTMLToken& token)
+{
+ token.adjust_tag_name("altglyph", "altGlyph");
+ token.adjust_tag_name("altglyphdef", "altGlyphDef");
+ token.adjust_tag_name("altglyphitem", "altGlyphItem");
+ token.adjust_tag_name("animatecolor", "animateColor");
+ token.adjust_tag_name("animatemotion", "animateMotion");
+ token.adjust_tag_name("animatetransform", "animateTransform");
+ token.adjust_tag_name("clippath", "clipPath");
+ token.adjust_tag_name("feblend", "feBlend");
+ token.adjust_tag_name("fecolormatrix", "feColorMatrix");
+ token.adjust_tag_name("fecomponenttransfer", "feComponentTransfer");
+ token.adjust_tag_name("fecomposite", "feComposite");
+ token.adjust_tag_name("feconvolvematrix", "feConvolveMatrix");
+ token.adjust_tag_name("fediffuselighting", "feDiffuseLighting");
+ token.adjust_tag_name("fedisplacementmap", "feDisplacementMap");
+ token.adjust_tag_name("fedistantlight", "feDistantLight");
+ token.adjust_tag_name("fedropshadow", "feDropShadow");
+ token.adjust_tag_name("feflood", "feFlood");
+ token.adjust_tag_name("fefunca", "feFuncA");
+ token.adjust_tag_name("fefuncb", "feFuncB");
+ token.adjust_tag_name("fefuncg", "feFuncG");
+ token.adjust_tag_name("fefuncr", "feFuncR");
+ token.adjust_tag_name("fegaussianblur", "feGaussianBlur");
+ token.adjust_tag_name("feimage", "feImage");
+ token.adjust_tag_name("femerge", "feMerge");
+ token.adjust_tag_name("femergenode", "feMergeNode");
+ token.adjust_tag_name("femorphology", "feMorphology");
+ token.adjust_tag_name("feoffset", "feOffset");
+ token.adjust_tag_name("fepointlight", "fePointLight");
+ token.adjust_tag_name("fespecularlighting", "feSpecularLighting");
+ token.adjust_tag_name("fespotlight", "feSpotlight");
+ token.adjust_tag_name("glyphref", "glyphRef");
+ token.adjust_tag_name("lineargradient", "linearGradient");
+ token.adjust_tag_name("radialgradient", "radialGradient");
+ token.adjust_tag_name("textpath", "textPath");
+}
+
+void HTMLDocumentParser::adjust_svg_attributes(HTMLToken& token)
+{
+ token.adjust_attribute_name("attributename", "attributeName");
+ token.adjust_attribute_name("attributetype", "attributeType");
+ token.adjust_attribute_name("basefrequency", "baseFrequency");
+ token.adjust_attribute_name("baseprofile", "baseProfile");
+ token.adjust_attribute_name("calcmode", "calcMode");
+ token.adjust_attribute_name("clippathunits", "clipPathUnits");
+ token.adjust_attribute_name("diffuseconstant", "diffuseConstant");
+ token.adjust_attribute_name("edgemode", "edgeMode");
+ token.adjust_attribute_name("filterunits", "filterUnits");
+ token.adjust_attribute_name("glyphref", "glyphRef");
+ token.adjust_attribute_name("gradienttransform", "gradientTransform");
+ token.adjust_attribute_name("gradientunits", "gradientUnits");
+ token.adjust_attribute_name("kernelmatrix", "kernelMatrix");
+ token.adjust_attribute_name("kernelunitlength", "kernelUnitLength");
+ token.adjust_attribute_name("keypoints", "keyPoints");
+ token.adjust_attribute_name("keysplines", "keySplines");
+ token.adjust_attribute_name("keytimes", "keyTimes");
+ token.adjust_attribute_name("lengthadjust", "lengthAdjust");
+ token.adjust_attribute_name("limitingconeangle", "limitingConeAngle");
+ token.adjust_attribute_name("markerheight", "markerHeight");
+ token.adjust_attribute_name("markerunits", "markerUnits");
+ token.adjust_attribute_name("markerwidth", "markerWidth");
+ token.adjust_attribute_name("maskcontentunits", "maskContentUnits");
+ token.adjust_attribute_name("maskunits", "maskUnits");
+ token.adjust_attribute_name("numoctaves", "numOctaves");
+ token.adjust_attribute_name("pathlength", "pathLength");
+ token.adjust_attribute_name("patterncontentunits", "patternContentUnits");
+ token.adjust_attribute_name("patterntransform", "patternTransform");
+ token.adjust_attribute_name("patternunits", "patternUnits");
+ token.adjust_attribute_name("pointsatx", "pointsAtX");
+ token.adjust_attribute_name("pointsaty", "pointsAtY");
+ token.adjust_attribute_name("pointsatz", "pointsAtZ");
+ token.adjust_attribute_name("preservealpha", "preserveAlpha");
+ token.adjust_attribute_name("preserveaspectratio", "preserveAspectRatio");
+ token.adjust_attribute_name("primitiveunits", "primitiveUnits");
+ token.adjust_attribute_name("refx", "refX");
+ token.adjust_attribute_name("refy", "refY");
+ token.adjust_attribute_name("repeatcount", "repeatCount");
+ token.adjust_attribute_name("repeatdur", "repeatDur");
+ token.adjust_attribute_name("requiredextensions", "requiredExtensions");
+ token.adjust_attribute_name("requiredfeatures", "requiredFeatures");
+ token.adjust_attribute_name("specularconstant", "specularConstant");
+ token.adjust_attribute_name("specularexponent", "specularExponent");
+ token.adjust_attribute_name("spreadmethod", "spreadMethod");
+ token.adjust_attribute_name("startoffset", "startOffset");
+ token.adjust_attribute_name("stddeviation", "stdDeviation");
+ token.adjust_attribute_name("stitchtiles", "stitchTiles");
+ token.adjust_attribute_name("surfacescale", "surfaceScale");
+ token.adjust_attribute_name("systemlanguage", "systemLanguage");
+ token.adjust_attribute_name("tablevalues", "tableValues");
+ token.adjust_attribute_name("targetx", "targetX");
+ token.adjust_attribute_name("targety", "targetY");
+ token.adjust_attribute_name("textlength", "textLength");
+ token.adjust_attribute_name("viewbox", "viewBox");
+ token.adjust_attribute_name("viewtarget", "viewTarget");
+ token.adjust_attribute_name("xchannelselector", "xChannelSelector");
+ token.adjust_attribute_name("ychannelselector", "yChannelSelector");
+ token.adjust_attribute_name("zoomandpan", "zoomAndPan");
+}
+
+void HTMLDocumentParser::adjust_foreign_attributes(HTMLToken& token)
+{
+ token.adjust_foreign_attribute("xlink:actuate", "xlink", "actuate", Namespace::XLink);
+ token.adjust_foreign_attribute("xlink:arcrole", "xlink", "arcrole", Namespace::XLink);
+ token.adjust_foreign_attribute("xlink:href", "xlink", "href", Namespace::XLink);
+ token.adjust_foreign_attribute("xlink:role", "xlink", "role", Namespace::XLink);
+ token.adjust_foreign_attribute("xlink:show", "xlink", "show", Namespace::XLink);
+ token.adjust_foreign_attribute("xlink:title", "xlink", "title", Namespace::XLink);
+ token.adjust_foreign_attribute("xlink:type", "xlink", "type", Namespace::XLink);
+
+ token.adjust_foreign_attribute("xml:lang", "xml", "lang", Namespace::XML);
+ token.adjust_foreign_attribute("xml:space", "xml", "space", Namespace::XML);
+
+ token.adjust_foreign_attribute("xmlns", "", "xmlns", Namespace::XMLNS);
+ token.adjust_foreign_attribute("xmlns:xlink", "xmlns", "xlink", Namespace::XMLNS);
+}
+
+void HTMLDocumentParser::increment_script_nesting_level()
+{
+ ++m_script_nesting_level;
+}
+
+void HTMLDocumentParser::decrement_script_nesting_level()
+{
+ ASSERT(m_script_nesting_level);
+ --m_script_nesting_level;
+}
+
+void HTMLDocumentParser::handle_text(HTMLToken& token)
+{
+ if (token.is_character()) {
+ insert_character(token.code_point());
+ return;
+ }
+ if (token.is_end_of_file()) {
+ PARSE_ERROR();
+ if (current_node().local_name() == HTML::TagNames::script)
+ downcast<HTMLScriptElement>(current_node()).set_already_started({}, true);
+ m_stack_of_open_elements.pop();
+ m_insertion_mode = m_original_insertion_mode;
+ process_using_the_rules_for(m_insertion_mode, token);
+ return;
+ }
+ if (token.is_end_tag() && token.tag_name() == HTML::TagNames::script) {
+ // Make sure the <script> element has up-to-date text content before preparing the script.
+ flush_character_insertions();
+
+ NonnullRefPtr<HTMLScriptElement> script = downcast<HTMLScriptElement>(current_node());
+ m_stack_of_open_elements.pop();
+ m_insertion_mode = m_original_insertion_mode;
+ // FIXME: Handle tokenizer insertion point stuff here.
+ increment_script_nesting_level();
+ script->prepare_script({});
+ decrement_script_nesting_level();
+ if (script_nesting_level() == 0)
+ m_parser_pause_flag = false;
+ // FIXME: Handle tokenizer insertion point stuff here too.
+
+ while (document().pending_parsing_blocking_script()) {
+ if (script_nesting_level() != 0) {
+ m_parser_pause_flag = true;
+ // FIXME: Abort the processing of any nested invocations of the tokenizer,
+ // yielding control back to the caller. (Tokenization will resume when
+ // the caller returns to the "outer" tree construction stage.)
+ TODO();
+ } else {
+ auto the_script = document().take_pending_parsing_blocking_script({});
+ m_tokenizer.set_blocked(true);
+
+ // FIXME: If the parser's Document has a style sheet that is blocking scripts
+ // or the script's "ready to be parser-executed" flag is not set:
+ // spin the event loop until the parser's Document has no style sheet
+ // that is blocking scripts and the script's "ready to be parser-executed"
+ // flag is set.
+
+ if (the_script->failed_to_load())
+ return;
+
+ ASSERT(the_script->is_ready_to_be_parser_executed());
+
+ if (m_aborted)
+ return;
+
+ m_tokenizer.set_blocked(false);
+
+ // FIXME: Handle tokenizer insertion point stuff here too.
+
+ ASSERT(script_nesting_level() == 0);
+ increment_script_nesting_level();
+
+ the_script->execute_script();
+
+ decrement_script_nesting_level();
+ ASSERT(script_nesting_level() == 0);
+ m_parser_pause_flag = false;
+
+ // FIXME: Handle tokenizer insertion point stuff here too.
+ }
+ }
+ return;
+ }
+
+ if (token.is_end_tag()) {
+ m_stack_of_open_elements.pop();
+ m_insertion_mode = m_original_insertion_mode;
+ return;
+ }
+ TODO();
+}
+
+void HTMLDocumentParser::clear_the_stack_back_to_a_table_context()
+{
+ while (!current_node().local_name().is_one_of(HTML::TagNames::table, HTML::TagNames::template_, HTML::TagNames::html))
+ m_stack_of_open_elements.pop();
+
+ if (current_node().local_name() == HTML::TagNames::html)
+ ASSERT(m_parsing_fragment);
+}
+
+void HTMLDocumentParser::clear_the_stack_back_to_a_table_row_context()
+{
+ while (!current_node().local_name().is_one_of(HTML::TagNames::tr, HTML::TagNames::template_, HTML::TagNames::html))
+ m_stack_of_open_elements.pop();
+
+ if (current_node().local_name() == HTML::TagNames::html)
+ ASSERT(m_parsing_fragment);
+}
+
+void HTMLDocumentParser::clear_the_stack_back_to_a_table_body_context()
+{
+ while (!current_node().local_name().is_one_of(HTML::TagNames::tbody, HTML::TagNames::tfoot, HTML::TagNames::thead, HTML::TagNames::template_, HTML::TagNames::html))
+ m_stack_of_open_elements.pop();
+
+ if (current_node().local_name() == HTML::TagNames::html)
+ ASSERT(m_parsing_fragment);
+}
+
+void HTMLDocumentParser::handle_in_row(HTMLToken& token)
+{
+ if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::th, HTML::TagNames::td)) {
+ clear_the_stack_back_to_a_table_row_context();
+ insert_html_element(token);
+ m_insertion_mode = InsertionMode::InCell;
+ m_list_of_active_formatting_elements.add_marker();
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name() == HTML::TagNames::tr) {
+ if (!m_stack_of_open_elements.has_in_table_scope(HTML::TagNames::tr)) {
+ PARSE_ERROR();
+ return;
+ }
+ clear_the_stack_back_to_a_table_row_context();
+ m_stack_of_open_elements.pop();
+ m_insertion_mode = InsertionMode::InTableBody;
+ return;
+ }
+
+ if ((token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::caption, HTML::TagNames::col, HTML::TagNames::colgroup, HTML::TagNames::tbody, HTML::TagNames::tfoot, HTML::TagNames::thead, HTML::TagNames::tr))
+ || (token.is_end_tag() && token.tag_name() == HTML::TagNames::table)) {
+ if (!m_stack_of_open_elements.has_in_table_scope(HTML::TagNames::tr)) {
+ PARSE_ERROR();
+ return;
+ }
+ clear_the_stack_back_to_a_table_row_context();
+ m_stack_of_open_elements.pop();
+ m_insertion_mode = InsertionMode::InTableBody;
+ process_using_the_rules_for(m_insertion_mode, token);
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::tbody, HTML::TagNames::tfoot, HTML::TagNames::thead)) {
+ if (!m_stack_of_open_elements.has_in_table_scope(token.tag_name())) {
+ PARSE_ERROR();
+ return;
+ }
+ if (!m_stack_of_open_elements.has_in_table_scope(HTML::TagNames::tr)) {
+ return;
+ }
+ clear_the_stack_back_to_a_table_row_context();
+ m_stack_of_open_elements.pop();
+ m_insertion_mode = InsertionMode::InTableBody;
+ process_using_the_rules_for(m_insertion_mode, token);
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::body, HTML::TagNames::caption, HTML::TagNames::col, HTML::TagNames::colgroup, HTML::TagNames::html, HTML::TagNames::td, HTML::TagNames::th)) {
+ PARSE_ERROR();
+ return;
+ }
+
+ process_using_the_rules_for(InsertionMode::InTable, token);
+}
+
+void HTMLDocumentParser::close_the_cell()
+{
+ generate_implied_end_tags();
+ if (!current_node().local_name().is_one_of(HTML::TagNames::td, HTML::TagNames::th)) {
+ PARSE_ERROR();
+ }
+ while (!current_node().local_name().is_one_of(HTML::TagNames::td, HTML::TagNames::th))
+ m_stack_of_open_elements.pop();
+ m_stack_of_open_elements.pop();
+ m_list_of_active_formatting_elements.clear_up_to_the_last_marker();
+ m_insertion_mode = InsertionMode::InRow;
+}
+
+void HTMLDocumentParser::handle_in_cell(HTMLToken& token)
+{
+ if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::td, HTML::TagNames::th)) {
+ if (!m_stack_of_open_elements.has_in_table_scope(token.tag_name())) {
+ PARSE_ERROR();
+ return;
+ }
+ generate_implied_end_tags();
+
+ if (current_node().local_name() != token.tag_name()) {
+ PARSE_ERROR();
+ }
+
+ m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(token.tag_name());
+
+ m_list_of_active_formatting_elements.clear_up_to_the_last_marker();
+
+ m_insertion_mode = InsertionMode::InRow;
+ return;
+ }
+ if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::caption, HTML::TagNames::col, HTML::TagNames::colgroup, HTML::TagNames::tbody, HTML::TagNames::td, HTML::TagNames::tfoot, HTML::TagNames::th, HTML::TagNames::thead, HTML::TagNames::tr)) {
+ if (!m_stack_of_open_elements.has_in_table_scope(HTML::TagNames::td) && !m_stack_of_open_elements.has_in_table_scope(HTML::TagNames::th)) {
+ ASSERT(m_parsing_fragment);
+ PARSE_ERROR();
+ return;
+ }
+ close_the_cell();
+ process_using_the_rules_for(m_insertion_mode, token);
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::body, HTML::TagNames::caption, HTML::TagNames::col, HTML::TagNames::colgroup, HTML::TagNames::html)) {
+ PARSE_ERROR();
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::table, HTML::TagNames::tbody, HTML::TagNames::tfoot, HTML::TagNames::thead, HTML::TagNames::tr)) {
+ if (!m_stack_of_open_elements.has_in_table_scope(token.tag_name())) {
+ PARSE_ERROR();
+ return;
+ }
+ close_the_cell();
+ // Reprocess the token.
+ process_using_the_rules_for(m_insertion_mode, token);
+ return;
+ }
+
+ process_using_the_rules_for(InsertionMode::InBody, token);
+}
+
+void HTMLDocumentParser::handle_in_table_text(HTMLToken& token)
+{
+ if (token.is_character()) {
+ if (token.code_point() == 0) {
+ PARSE_ERROR();
+ return;
+ }
+
+ m_pending_table_character_tokens.append(token);
+ return;
+ }
+
+ for (auto& pending_token : m_pending_table_character_tokens) {
+ ASSERT(pending_token.is_character());
+ if (!pending_token.is_parser_whitespace()) {
+ // If any of the tokens in the pending table character tokens list
+ // are character tokens that are not ASCII whitespace, then this is a parse error:
+ // reprocess the character tokens in the pending table character tokens list using
+ // the rules given in the "anything else" entry in the "in table" insertion mode.
+ PARSE_ERROR();
+ m_foster_parenting = true;
+ process_using_the_rules_for(InsertionMode::InBody, token);
+ m_foster_parenting = false;
+ return;
+ }
+ }
+
+ for (auto& pending_token : m_pending_table_character_tokens) {
+ insert_character(pending_token.code_point());
+ }
+
+ m_insertion_mode = m_original_insertion_mode;
+ process_using_the_rules_for(m_insertion_mode, token);
+}
+
+void HTMLDocumentParser::handle_in_table_body(HTMLToken& token)
+{
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::tr) {
+ clear_the_stack_back_to_a_table_body_context();
+ insert_html_element(token);
+ m_insertion_mode = InsertionMode::InRow;
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::th, HTML::TagNames::td)) {
+ PARSE_ERROR();
+ clear_the_stack_back_to_a_table_body_context();
+ insert_html_element(HTMLToken::make_start_tag(HTML::TagNames::tr));
+ m_insertion_mode = InsertionMode::InRow;
+ process_using_the_rules_for(m_insertion_mode, token);
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::tbody, HTML::TagNames::tfoot, HTML::TagNames::thead)) {
+ if (!m_stack_of_open_elements.has_in_table_scope(token.tag_name())) {
+ PARSE_ERROR();
+ return;
+ }
+ clear_the_stack_back_to_a_table_body_context();
+ m_stack_of_open_elements.pop();
+ m_insertion_mode = InsertionMode::InTable;
+ return;
+ }
+
+ if ((token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::caption, HTML::TagNames::col, HTML::TagNames::colgroup, HTML::TagNames::tbody, HTML::TagNames::tfoot, HTML::TagNames::thead))
+ || (token.is_end_tag() && token.tag_name() == HTML::TagNames::table)) {
+
+ if (!m_stack_of_open_elements.has_in_table_scope(HTML::TagNames::tbody)
+ && !m_stack_of_open_elements.has_in_table_scope(HTML::TagNames::thead)
+ && !m_stack_of_open_elements.has_in_table_scope(HTML::TagNames::tfoot)) {
+ PARSE_ERROR();
+ return;
+ }
+
+ clear_the_stack_back_to_a_table_body_context();
+ m_stack_of_open_elements.pop();
+ m_insertion_mode = InsertionMode::InTable;
+ process_using_the_rules_for(InsertionMode::InTable, token);
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::body, HTML::TagNames::caption, HTML::TagNames::col, HTML::TagNames::colgroup, HTML::TagNames::html, HTML::TagNames::td, HTML::TagNames::th, HTML::TagNames::tr)) {
+ PARSE_ERROR();
+ return;
+ }
+
+ process_using_the_rules_for(InsertionMode::InTable, token);
+}
+
+void HTMLDocumentParser::handle_in_table(HTMLToken& token)
+{
+ if (token.is_character() && current_node().local_name().is_one_of(HTML::TagNames::table, HTML::TagNames::tbody, HTML::TagNames::tfoot, HTML::TagNames::thead, HTML::TagNames::tr)) {
+ m_pending_table_character_tokens.clear();
+ m_original_insertion_mode = m_insertion_mode;
+ m_insertion_mode = InsertionMode::InTableText;
+ process_using_the_rules_for(InsertionMode::InTableText, token);
+ return;
+ }
+ if (token.is_comment()) {
+ insert_comment(token);
+ return;
+ }
+ if (token.is_doctype()) {
+ PARSE_ERROR();
+ return;
+ }
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::caption) {
+ clear_the_stack_back_to_a_table_context();
+ m_list_of_active_formatting_elements.add_marker();
+ insert_html_element(token);
+ m_insertion_mode = InsertionMode::InCaption;
+ return;
+ }
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::colgroup) {
+ clear_the_stack_back_to_a_table_context();
+ insert_html_element(token);
+ m_insertion_mode = InsertionMode::InColumnGroup;
+ return;
+ }
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::col) {
+ clear_the_stack_back_to_a_table_context();
+ insert_html_element(HTMLToken::make_start_tag(HTML::TagNames::colgroup));
+ m_insertion_mode = InsertionMode::InColumnGroup;
+ process_using_the_rules_for(m_insertion_mode, token);
+ return;
+ }
+ if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::tbody, HTML::TagNames::tfoot, HTML::TagNames::thead)) {
+ clear_the_stack_back_to_a_table_context();
+ insert_html_element(token);
+ m_insertion_mode = InsertionMode::InTableBody;
+ return;
+ }
+ if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::td, HTML::TagNames::th, HTML::TagNames::tr)) {
+ clear_the_stack_back_to_a_table_context();
+ insert_html_element(HTMLToken::make_start_tag(HTML::TagNames::tbody));
+ m_insertion_mode = InsertionMode::InTableBody;
+ process_using_the_rules_for(m_insertion_mode, token);
+ return;
+ }
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::table) {
+ PARSE_ERROR();
+ if (!m_stack_of_open_elements.has_in_table_scope(HTML::TagNames::table))
+ return;
+
+ m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::table);
+
+ reset_the_insertion_mode_appropriately();
+ process_using_the_rules_for(m_insertion_mode, token);
+ return;
+ }
+ if (token.is_end_tag() && token.tag_name() == HTML::TagNames::table) {
+ if (!m_stack_of_open_elements.has_in_table_scope(HTML::TagNames::table)) {
+ PARSE_ERROR();
+ return;
+ }
+
+ m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::table);
+
+ reset_the_insertion_mode_appropriately();
+ return;
+ }
+ if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::body, HTML::TagNames::caption, HTML::TagNames::col, HTML::TagNames::colgroup, HTML::TagNames::html, HTML::TagNames::tbody, HTML::TagNames::td, HTML::TagNames::tfoot, HTML::TagNames::th, HTML::TagNames::thead, HTML::TagNames::tr)) {
+ PARSE_ERROR();
+ return;
+ }
+ if ((token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::style, HTML::TagNames::script, HTML::TagNames::template_))
+ || (token.is_end_tag() && token.tag_name() == HTML::TagNames::template_)) {
+ process_using_the_rules_for(InsertionMode::InHead, token);
+ return;
+ }
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::input) {
+ auto type_attribute = token.attribute(HTML::AttributeNames::type);
+ if (type_attribute.is_null() || !type_attribute.equals_ignoring_case("hidden")) {
+ goto AnythingElse;
+ }
+
+ PARSE_ERROR();
+ insert_html_element(token);
+
+ // FIXME: Is this the correct interpretation of "Pop that input element off the stack of open elements."?
+ // Because this wording is the first time it's seen in the spec.
+ // Other times it's worded as: "Immediately pop the current node off the stack of open elements."
+ m_stack_of_open_elements.pop();
+ token.acknowledge_self_closing_flag_if_set();
+ return;
+ }
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::form) {
+ PARSE_ERROR();
+ if (m_form_element || m_stack_of_open_elements.contains(HTML::TagNames::template_)) {
+ return;
+ }
+
+ m_form_element = downcast<HTMLFormElement>(*insert_html_element(token));
+
+ // FIXME: See previous FIXME, as this is the same situation but for form.
+ m_stack_of_open_elements.pop();
+ return;
+ }
+ if (token.is_end_of_file()) {
+ process_using_the_rules_for(InsertionMode::InBody, token);
+ return;
+ }
+
+AnythingElse:
+ PARSE_ERROR();
+ m_foster_parenting = true;
+ process_using_the_rules_for(InsertionMode::InBody, token);
+ m_foster_parenting = false;
+}
+
+void HTMLDocumentParser::handle_in_select_in_table(HTMLToken& token)
+{
+ if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::caption, HTML::TagNames::table, HTML::TagNames::tbody, HTML::TagNames::tfoot, HTML::TagNames::thead, HTML::TagNames::tr, HTML::TagNames::td, HTML::TagNames::th)) {
+ PARSE_ERROR();
+ m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::select);
+ reset_the_insertion_mode_appropriately();
+ process_using_the_rules_for(m_insertion_mode, token);
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::caption, HTML::TagNames::table, HTML::TagNames::tbody, HTML::TagNames::tfoot, HTML::TagNames::thead, HTML::TagNames::tr, HTML::TagNames::td, HTML::TagNames::th)) {
+ PARSE_ERROR();
+
+ if (!m_stack_of_open_elements.has_in_table_scope(token.tag_name()))
+ return;
+
+ m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::select);
+ reset_the_insertion_mode_appropriately();
+ process_using_the_rules_for(m_insertion_mode, token);
+ return;
+ }
+
+ process_using_the_rules_for(InsertionMode::InSelect, token);
+}
+
+void HTMLDocumentParser::handle_in_select(HTMLToken& token)
+{
+ if (token.is_character()) {
+ if (token.code_point() == 0) {
+ PARSE_ERROR();
+ return;
+ }
+ insert_character(token.code_point());
+ return;
+ }
+
+ if (token.is_comment()) {
+ insert_comment(token);
+ return;
+ }
+
+ if (token.is_doctype()) {
+ PARSE_ERROR();
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::html) {
+ process_using_the_rules_for(InsertionMode::InBody, token);
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::option) {
+ if (current_node().local_name() == HTML::TagNames::option) {
+ m_stack_of_open_elements.pop();
+ }
+ insert_html_element(token);
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::optgroup) {
+ if (current_node().local_name() == HTML::TagNames::option) {
+ m_stack_of_open_elements.pop();
+ }
+ if (current_node().local_name() == HTML::TagNames::optgroup) {
+ m_stack_of_open_elements.pop();
+ }
+ insert_html_element(token);
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name() == HTML::TagNames::optgroup) {
+ if (current_node().local_name() == HTML::TagNames::option && node_before_current_node().local_name() == HTML::TagNames::optgroup)
+ m_stack_of_open_elements.pop();
+
+ if (current_node().local_name() == HTML::TagNames::optgroup) {
+ m_stack_of_open_elements.pop();
+ } else {
+ PARSE_ERROR();
+ return;
+ }
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name() == HTML::TagNames::option) {
+ if (current_node().local_name() == HTML::TagNames::option) {
+ m_stack_of_open_elements.pop();
+ } else {
+ PARSE_ERROR();
+ return;
+ }
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name() == HTML::TagNames::select) {
+ if (!m_stack_of_open_elements.has_in_select_scope(HTML::TagNames::select)) {
+ ASSERT(m_parsing_fragment);
+ PARSE_ERROR();
+ return;
+ }
+ m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::select);
+ reset_the_insertion_mode_appropriately();
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::select) {
+ PARSE_ERROR();
+
+ if (!m_stack_of_open_elements.has_in_select_scope(HTML::TagNames::select)) {
+ ASSERT(m_parsing_fragment);
+ return;
+ }
+
+ m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::select);
+ reset_the_insertion_mode_appropriately();
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::input, HTML::TagNames::keygen, HTML::TagNames::textarea)) {
+ PARSE_ERROR();
+
+ if (!m_stack_of_open_elements.has_in_select_scope(HTML::TagNames::select)) {
+ ASSERT(m_parsing_fragment);
+ return;
+ }
+
+ m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::select);
+ reset_the_insertion_mode_appropriately();
+ process_using_the_rules_for(m_insertion_mode, token);
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::script, HTML::TagNames::template_)) {
+ process_using_the_rules_for(InsertionMode::InHead, token);
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name() == HTML::TagNames::template_) {
+ process_using_the_rules_for(InsertionMode::InHead, token);
+ return;
+ }
+
+ if (token.is_end_of_file()) {
+ process_using_the_rules_for(InsertionMode::InBody, token);
+ return;
+ }
+
+ PARSE_ERROR();
+}
+
+void HTMLDocumentParser::handle_in_caption(HTMLToken& token)
+{
+ if (token.is_end_tag() && token.tag_name() == HTML::TagNames::caption) {
+ if (!m_stack_of_open_elements.has_in_table_scope(HTML::TagNames::caption)) {
+ ASSERT(m_parsing_fragment);
+ PARSE_ERROR();
+ return;
+ }
+
+ generate_implied_end_tags();
+
+ if (current_node().local_name() != HTML::TagNames::caption)
+ PARSE_ERROR();
+
+ m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::caption);
+ m_list_of_active_formatting_elements.clear_up_to_the_last_marker();
+
+ m_insertion_mode = InsertionMode::InTable;
+ return;
+ }
+
+ if ((token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::caption, HTML::TagNames::col, HTML::TagNames::colgroup, HTML::TagNames::tbody, HTML::TagNames::td, HTML::TagNames::tfoot, HTML::TagNames::th, HTML::TagNames::thead, HTML::TagNames::tr))
+ || (token.is_end_tag() && token.tag_name() == HTML::TagNames::table)) {
+ if (!m_stack_of_open_elements.has_in_table_scope(HTML::TagNames::caption)) {
+ ASSERT(m_parsing_fragment);
+ PARSE_ERROR();
+ return;
+ }
+
+ generate_implied_end_tags();
+
+ if (current_node().local_name() != HTML::TagNames::caption)
+ PARSE_ERROR();
+
+ m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::caption);
+ m_list_of_active_formatting_elements.clear_up_to_the_last_marker();
+
+ m_insertion_mode = InsertionMode::InTable;
+ process_using_the_rules_for(m_insertion_mode, token);
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::body, HTML::TagNames::col, HTML::TagNames::colgroup, HTML::TagNames::html, HTML::TagNames::tbody, HTML::TagNames::td, HTML::TagNames::tfoot, HTML::TagNames::th, HTML::TagNames::thead, HTML::TagNames::tr)) {
+ PARSE_ERROR();
+ return;
+ }
+
+ process_using_the_rules_for(InsertionMode::InBody, token);
+}
+
+void HTMLDocumentParser::handle_in_column_group(HTMLToken& token)
+{
+ if (token.is_character() && token.is_parser_whitespace()) {
+ insert_character(token.code_point());
+ return;
+ }
+
+ if (token.is_comment()) {
+ insert_comment(token);
+ return;
+ }
+
+ if (token.is_doctype()) {
+ PARSE_ERROR();
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::html) {
+ process_using_the_rules_for(InsertionMode::InBody, token);
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::col) {
+ insert_html_element(token);
+ m_stack_of_open_elements.pop();
+ token.acknowledge_self_closing_flag_if_set();
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name() == HTML::TagNames::colgroup) {
+ if (current_node().local_name() != HTML::TagNames::colgroup) {
+ PARSE_ERROR();
+ return;
+ }
+
+ m_stack_of_open_elements.pop();
+ m_insertion_mode = InsertionMode::InTable;
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name() == HTML::TagNames::col) {
+ PARSE_ERROR();
+ return;
+ }
+
+ if ((token.is_start_tag() || token.is_end_tag()) && token.tag_name() == HTML::TagNames::template_) {
+ process_using_the_rules_for(InsertionMode::InHead, token);
+ return;
+ }
+
+ if (token.is_end_of_file()) {
+ process_using_the_rules_for(InsertionMode::InBody, token);
+ return;
+ }
+
+ if (current_node().local_name() != HTML::TagNames::colgroup) {
+ PARSE_ERROR();
+ return;
+ }
+
+ m_stack_of_open_elements.pop();
+ m_insertion_mode = InsertionMode::InTable;
+ process_using_the_rules_for(m_insertion_mode, token);
+}
+
+void HTMLDocumentParser::handle_in_template(HTMLToken& token)
+{
+ if (token.is_character() || token.is_comment() || token.is_doctype()) {
+ process_using_the_rules_for(InsertionMode::InBody, token);
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::base, HTML::TagNames::basefont, HTML::TagNames::bgsound, HTML::TagNames::link, HTML::TagNames::meta, HTML::TagNames::noframes, HTML::TagNames::script, HTML::TagNames::style, HTML::TagNames::template_, HTML::TagNames::title)) {
+ process_using_the_rules_for(InsertionMode::InHead, token);
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name() == HTML::TagNames::template_) {
+ process_using_the_rules_for(InsertionMode::InHead, token);
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::caption, HTML::TagNames::colgroup, HTML::TagNames::tbody, HTML::TagNames::tfoot, HTML::TagNames::thead)) {
+ m_stack_of_template_insertion_modes.take_last();
+ m_stack_of_template_insertion_modes.append(InsertionMode::InTable);
+ m_insertion_mode = InsertionMode::InTable;
+ process_using_the_rules_for(m_insertion_mode, token);
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::col) {
+ m_stack_of_template_insertion_modes.take_last();
+ m_stack_of_template_insertion_modes.append(InsertionMode::InColumnGroup);
+ m_insertion_mode = InsertionMode::InColumnGroup;
+ process_using_the_rules_for(m_insertion_mode, token);
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::tr) {
+ m_stack_of_template_insertion_modes.take_last();
+ m_stack_of_template_insertion_modes.append(InsertionMode::InTableBody);
+ m_insertion_mode = InsertionMode::InTableBody;
+ process_using_the_rules_for(m_insertion_mode, token);
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::td, HTML::TagNames::th)) {
+ m_stack_of_template_insertion_modes.take_last();
+ m_stack_of_template_insertion_modes.append(InsertionMode::InRow);
+ m_insertion_mode = InsertionMode::InRow;
+ process_using_the_rules_for(m_insertion_mode, token);
+ return;
+ }
+
+ if (token.is_start_tag()) {
+ m_stack_of_template_insertion_modes.take_last();
+ m_stack_of_template_insertion_modes.append(InsertionMode::InBody);
+ m_insertion_mode = InsertionMode::InBody;
+ process_using_the_rules_for(m_insertion_mode, token);
+ return;
+ }
+
+ if (token.is_end_tag()) {
+ PARSE_ERROR();
+ return;
+ }
+
+ if (token.is_end_of_file()) {
+ if (!m_stack_of_open_elements.contains(HTML::TagNames::template_)) {
+ ASSERT(m_parsing_fragment);
+ stop_parsing();
+ return;
+ }
+
+ PARSE_ERROR();
+ m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::template_);
+ m_list_of_active_formatting_elements.clear_up_to_the_last_marker();
+ m_stack_of_template_insertion_modes.take_last();
+ reset_the_insertion_mode_appropriately();
+ process_using_the_rules_for(m_insertion_mode, token);
+ }
+}
+
+void HTMLDocumentParser::handle_in_frameset(HTMLToken& token)
+{
+ if (token.is_character() && token.is_parser_whitespace()) {
+ insert_character(token.code_point());
+ return;
+ }
+
+ if (token.is_comment()) {
+ insert_comment(token);
+ return;
+ }
+
+ if (token.is_doctype()) {
+ PARSE_ERROR();
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::html) {
+ process_using_the_rules_for(InsertionMode::InBody, token);
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::frameset) {
+ insert_html_element(token);
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name() == HTML::TagNames::frameset) {
+ // FIXME: If the current node is the root html element, then this is a parse error; ignore the token. (fragment case)
+
+ m_stack_of_open_elements.pop();
+
+ if (!m_parsing_fragment && current_node().local_name() != HTML::TagNames::frameset) {
+ m_insertion_mode = InsertionMode::AfterFrameset;
+ }
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::frame) {
+ insert_html_element(token);
+ m_stack_of_open_elements.pop();
+ token.acknowledge_self_closing_flag_if_set();
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::noframes) {
+ process_using_the_rules_for(InsertionMode::InHead, token);
+ return;
+ }
+
+ if (token.is_end_of_file()) {
+ //FIXME: If the current node is not the root html element, then this is a parse error.
+
+ stop_parsing();
+ return;
+ }
+
+ PARSE_ERROR();
+}
+
+void HTMLDocumentParser::handle_after_frameset(HTMLToken& token)
+{
+ if (token.is_character() && token.is_parser_whitespace()) {
+ insert_character(token.code_point());
+ return;
+ }
+
+ if (token.is_comment()) {
+ insert_comment(token);
+ return;
+ }
+
+ if (token.is_doctype()) {
+ PARSE_ERROR();
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::html) {
+ process_using_the_rules_for(InsertionMode::InBody, token);
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name() == HTML::TagNames::html) {
+ m_insertion_mode = InsertionMode::AfterAfterFrameset;
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::noframes) {
+ process_using_the_rules_for(InsertionMode::InHead, token);
+ return;
+ }
+
+ if (token.is_end_of_file()) {
+ stop_parsing();
+ return;
+ }
+
+ PARSE_ERROR();
+}
+
+void HTMLDocumentParser::handle_after_after_frameset(HTMLToken& token)
+{
+ if (token.is_comment()) {
+ auto comment = adopt(*new DOM::Comment(document(), token.m_comment_or_character.data.to_string()));
+ document().append_child(move(comment));
+ return;
+ }
+
+ if (token.is_doctype() || token.is_parser_whitespace() || (token.is_start_tag() && token.tag_name() == HTML::TagNames::html)) {
+ process_using_the_rules_for(InsertionMode::InBody, token);
+ return;
+ }
+
+ if (token.is_end_of_file()) {
+ stop_parsing();
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == HTML::TagNames::noframes) {
+ process_using_the_rules_for(InsertionMode::InHead, token);
+ return;
+ }
+
+ PARSE_ERROR();
+}
+
+void HTMLDocumentParser::process_using_the_rules_for_foreign_content(HTMLToken& token)
+{
+ if (token.is_character()) {
+ if (token.code_point() == 0) {
+ PARSE_ERROR();
+ insert_character(0xFFFD);
+ return;
+ }
+ if (token.is_parser_whitespace()) {
+ insert_character(token.code_point());
+ return;
+ }
+ insert_character(token.code_point());
+ m_frameset_ok = false;
+ return;
+ }
+
+ if (token.is_comment()) {
+ insert_comment(token);
+ return;
+ }
+
+ if (token.is_doctype()) {
+ PARSE_ERROR();
+ return;
+ }
+
+ if ((token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::b, HTML::TagNames::big, HTML::TagNames::blockquote, HTML::TagNames::body, HTML::TagNames::br, HTML::TagNames::center, HTML::TagNames::code, HTML::TagNames::dd, HTML::TagNames::div, HTML::TagNames::dl, HTML::TagNames::dt, HTML::TagNames::em, HTML::TagNames::embed, HTML::TagNames::h1, HTML::TagNames::h2, HTML::TagNames::h3, HTML::TagNames::h4, HTML::TagNames::h5, HTML::TagNames::h6, HTML::TagNames::head, HTML::TagNames::hr, HTML::TagNames::i, HTML::TagNames::img, HTML::TagNames::li, HTML::TagNames::listing, HTML::TagNames::menu, HTML::TagNames::meta, HTML::TagNames::nobr, HTML::TagNames::ol, HTML::TagNames::p, HTML::TagNames::pre, HTML::TagNames::ruby, HTML::TagNames::s, HTML::TagNames::small, HTML::TagNames::span, HTML::TagNames::strong, HTML::TagNames::strike, HTML::TagNames::sub, HTML::TagNames::sup, HTML::TagNames::table, HTML::TagNames::tt, HTML::TagNames::u, HTML::TagNames::ul, HTML::TagNames::var))
+ || (token.is_start_tag() && token.tag_name() == HTML::TagNames::font && (token.has_attribute(HTML::AttributeNames::color) || token.has_attribute(HTML::AttributeNames::face) || token.has_attribute(HTML::AttributeNames::size)))) {
+ PARSE_ERROR();
+ if (m_parsing_fragment) {
+ goto AnyOtherStartTag;
+ }
+
+ TODO();
+ }
+
+ if (token.is_start_tag()) {
+ AnyOtherStartTag:
+ if (adjusted_current_node().namespace_() == Namespace::MathML) {
+ adjust_mathml_attributes(token);
+ } else if (adjusted_current_node().namespace_() == Namespace::SVG) {
+ adjust_svg_tag_names(token);
+ adjust_svg_attributes(token);
+ }
+
+ adjust_foreign_attributes(token);
+ insert_foreign_element(token, adjusted_current_node().namespace_());
+
+ if (token.is_self_closing()) {
+ if (token.tag_name() == SVG::TagNames::script && current_node().namespace_() == Namespace::SVG) {
+ token.acknowledge_self_closing_flag_if_set();
+ goto ScriptEndTag;
+ }
+
+ m_stack_of_open_elements.pop();
+ token.acknowledge_self_closing_flag_if_set();
+ }
+
+ return;
+ }
+
+ if (token.is_end_tag() && current_node().namespace_() == Namespace::SVG && current_node().tag_name() == SVG::TagNames::script) {
+ ScriptEndTag:
+ m_stack_of_open_elements.pop();
+ TODO();
+ }
+
+ if (token.is_end_tag()) {
+ RefPtr<DOM::Element> node = current_node();
+ // FIXME: Not sure if this is the correct to_lowercase, as the specification says "to ASCII lowercase"
+ if (node->tag_name().to_lowercase() != token.tag_name())
+ PARSE_ERROR();
+ for (ssize_t i = m_stack_of_open_elements.elements().size() - 1; i >= 0; --i) {
+ if (node == m_stack_of_open_elements.first()) {
+ ASSERT(m_parsing_fragment);
+ return;
+ }
+ // FIXME: See the above FIXME
+ if (node->tag_name().to_lowercase() == token.tag_name()) {
+ while (current_node() != node)
+ m_stack_of_open_elements.pop();
+ m_stack_of_open_elements.pop();
+ return;
+ }
+
+ node = m_stack_of_open_elements.elements().at(i - 1);
+
+ if (node->namespace_() != Namespace::HTML)
+ continue;
+
+ process_using_the_rules_for(m_insertion_mode, token);
+ return;
+ }
+ }
+
+ ASSERT_NOT_REACHED();
+}
+
+void HTMLDocumentParser::reset_the_insertion_mode_appropriately()
+{
+ for (ssize_t i = m_stack_of_open_elements.elements().size() - 1; i >= 0; --i) {
+ bool last = i == 0;
+ // NOTE: When parsing fragments, we substitute the context element for the root of the stack of open elements.
+ RefPtr<DOM::Element> node;
+ if (last && m_parsing_fragment) {
+ node = m_context_element;
+ } else {
+ node = m_stack_of_open_elements.elements().at(i);
+ }
+
+ if (node->local_name() == HTML::TagNames::select) {
+ TODO();
+ }
+
+ if (!last && node->local_name().is_one_of(HTML::TagNames::td, HTML::TagNames::th)) {
+ m_insertion_mode = InsertionMode::InCell;
+ return;
+ }
+
+ if (node->local_name() == HTML::TagNames::tr) {
+ m_insertion_mode = InsertionMode::InRow;
+ return;
+ }
+
+ if (node->local_name().is_one_of(HTML::TagNames::tbody, HTML::TagNames::thead, HTML::TagNames::tfoot)) {
+ m_insertion_mode = InsertionMode::InTableBody;
+ return;
+ }
+
+ if (node->local_name() == HTML::TagNames::caption) {
+ m_insertion_mode = InsertionMode::InCaption;
+ return;
+ }
+
+ if (node->local_name() == HTML::TagNames::colgroup) {
+ m_insertion_mode = InsertionMode::InColumnGroup;
+ return;
+ }
+
+ if (node->local_name() == HTML::TagNames::table) {
+ m_insertion_mode = InsertionMode::InTable;
+ return;
+ }
+
+ if (node->local_name() == HTML::TagNames::template_) {
+ m_insertion_mode = m_stack_of_template_insertion_modes.last();
+ return;
+ }
+
+ if (!last && node->local_name() == HTML::TagNames::head) {
+ m_insertion_mode = InsertionMode::InHead;
+ return;
+ }
+
+ if (node->local_name() == HTML::TagNames::body) {
+ m_insertion_mode = InsertionMode::InBody;
+ return;
+ }
+
+ if (node->local_name() == HTML::TagNames::frameset) {
+ ASSERT(m_parsing_fragment);
+ m_insertion_mode = InsertionMode::InFrameset;
+ return;
+ }
+
+ if (node->local_name() == HTML::TagNames::html) {
+ if (!m_head_element) {
+ ASSERT(m_parsing_fragment);
+ m_insertion_mode = InsertionMode::BeforeHead;
+ return;
+ }
+
+ m_insertion_mode = InsertionMode::AfterHead;
+ return;
+ }
+ }
+
+ ASSERT(m_parsing_fragment);
+ m_insertion_mode = InsertionMode::InBody;
+}
+
+const char* HTMLDocumentParser::insertion_mode_name() const
+{
+ switch (m_insertion_mode) {
+#define __ENUMERATE_INSERTION_MODE(mode) \
+ case InsertionMode::mode: \
+ return #mode;
+ ENUMERATE_INSERTION_MODES
+#undef __ENUMERATE_INSERTION_MODE
+ }
+ ASSERT_NOT_REACHED();
+}
+
+DOM::Document& HTMLDocumentParser::document()
+{
+ return *m_document;
+}
+
+NonnullRefPtrVector<DOM::Node> HTMLDocumentParser::parse_html_fragment(DOM::Element& context_element, const StringView& markup)
+{
+ auto temp_document = DOM::Document::create();
+ HTMLDocumentParser parser(*temp_document, markup, "utf-8");
+ parser.m_context_element = context_element;
+ parser.m_parsing_fragment = true;
+ parser.document().set_quirks_mode(context_element.document().mode());
+
+ if (context_element.local_name().is_one_of(HTML::TagNames::title, HTML::TagNames::textarea)) {
+ parser.m_tokenizer.switch_to({}, HTMLTokenizer::State::RCDATA);
+ } else if (context_element.local_name().is_one_of(HTML::TagNames::style, HTML::TagNames::xmp, HTML::TagNames::iframe, HTML::TagNames::noembed, HTML::TagNames::noframes)) {
+ parser.m_tokenizer.switch_to({}, HTMLTokenizer::State::RAWTEXT);
+ } else if (context_element.local_name().is_one_of(HTML::TagNames::script)) {
+ parser.m_tokenizer.switch_to({}, HTMLTokenizer::State::ScriptData);
+ } else if (context_element.local_name().is_one_of(HTML::TagNames::noscript)) {
+ if (context_element.document().is_scripting_enabled())
+ parser.m_tokenizer.switch_to({}, HTMLTokenizer::State::RAWTEXT);
+ } else if (context_element.local_name().is_one_of(HTML::TagNames::plaintext)) {
+ parser.m_tokenizer.switch_to({}, HTMLTokenizer::State::PLAINTEXT);
+ }
+
+ auto root = create_element(context_element.document(), HTML::TagNames::html, Namespace::HTML);
+ parser.document().append_child(root);
+ parser.m_stack_of_open_elements.push(root);
+
+ if (context_element.local_name() == HTML::TagNames::template_) {
+ parser.m_stack_of_template_insertion_modes.append(InsertionMode::InTemplate);
+ }
+
+ // FIXME: Create a start tag token whose name is the local name of context and whose attributes are the attributes of context.
+
+ parser.reset_the_insertion_mode_appropriately();
+
+ for (auto* form_candidate = &context_element; form_candidate; form_candidate = form_candidate->parent_element()) {
+ if (is<HTMLFormElement>(*form_candidate)) {
+ parser.m_form_element = downcast<HTMLFormElement>(*form_candidate);
+ break;
+ }
+ }
+
+ parser.run(context_element.document().url());
+
+ NonnullRefPtrVector<DOM::Node> children;
+ while (RefPtr<DOM::Node> child = root->first_child()) {
+ root->remove_child(*child);
+ context_element.document().adopt_node(*child);
+ children.append(*child);
+ }
+ return children;
+}
+}
diff --git a/Userland/Libraries/LibWeb/HTML/Parser/HTMLDocumentParser.h b/Userland/Libraries/LibWeb/HTML/Parser/HTMLDocumentParser.h
new file mode 100644
index 0000000000..4abbb74b72
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/Parser/HTMLDocumentParser.h
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/NonnullRefPtrVector.h>
+#include <LibWeb/DOM/Node.h>
+#include <LibWeb/HTML/Parser/HTMLTokenizer.h>
+#include <LibWeb/HTML/Parser/ListOfActiveFormattingElements.h>
+#include <LibWeb/HTML/Parser/StackOfOpenElements.h>
+
+namespace Web::HTML {
+
+#define ENUMERATE_INSERTION_MODES \
+ __ENUMERATE_INSERTION_MODE(Initial) \
+ __ENUMERATE_INSERTION_MODE(BeforeHTML) \
+ __ENUMERATE_INSERTION_MODE(BeforeHead) \
+ __ENUMERATE_INSERTION_MODE(InHead) \
+ __ENUMERATE_INSERTION_MODE(InHeadNoscript) \
+ __ENUMERATE_INSERTION_MODE(AfterHead) \
+ __ENUMERATE_INSERTION_MODE(InBody) \
+ __ENUMERATE_INSERTION_MODE(Text) \
+ __ENUMERATE_INSERTION_MODE(InTable) \
+ __ENUMERATE_INSERTION_MODE(InTableText) \
+ __ENUMERATE_INSERTION_MODE(InCaption) \
+ __ENUMERATE_INSERTION_MODE(InColumnGroup) \
+ __ENUMERATE_INSERTION_MODE(InTableBody) \
+ __ENUMERATE_INSERTION_MODE(InRow) \
+ __ENUMERATE_INSERTION_MODE(InCell) \
+ __ENUMERATE_INSERTION_MODE(InSelect) \
+ __ENUMERATE_INSERTION_MODE(InSelectInTable) \
+ __ENUMERATE_INSERTION_MODE(InTemplate) \
+ __ENUMERATE_INSERTION_MODE(AfterBody) \
+ __ENUMERATE_INSERTION_MODE(InFrameset) \
+ __ENUMERATE_INSERTION_MODE(AfterFrameset) \
+ __ENUMERATE_INSERTION_MODE(AfterAfterBody) \
+ __ENUMERATE_INSERTION_MODE(AfterAfterFrameset)
+
+RefPtr<DOM::Document> parse_html_document(const StringView&, const URL&, const String& encoding);
+
+class HTMLDocumentParser {
+public:
+ HTMLDocumentParser(DOM::Document&, const StringView& input, const String& encoding);
+ ~HTMLDocumentParser();
+
+ void run(const URL&);
+
+ DOM::Document& document();
+
+ static NonnullRefPtrVector<DOM::Node> parse_html_fragment(DOM::Element& context_element, const StringView&);
+
+ enum class InsertionMode {
+#define __ENUMERATE_INSERTION_MODE(mode) mode,
+ ENUMERATE_INSERTION_MODES
+#undef __ENUMERATE_INSERTION_MODE
+ };
+
+ InsertionMode insertion_mode() const { return m_insertion_mode; }
+
+ static bool is_special_tag(const FlyString& tag_name, const FlyString& namespace_);
+
+private:
+ const char* insertion_mode_name() const;
+
+ DOM::QuirksMode which_quirks_mode(const HTMLToken&) const;
+
+ void handle_initial(HTMLToken&);
+ void handle_before_html(HTMLToken&);
+ void handle_before_head(HTMLToken&);
+ void handle_in_head(HTMLToken&);
+ void handle_in_head_noscript(HTMLToken&);
+ void handle_after_head(HTMLToken&);
+ void handle_in_body(HTMLToken&);
+ void handle_after_body(HTMLToken&);
+ void handle_after_after_body(HTMLToken&);
+ void handle_text(HTMLToken&);
+ void handle_in_table(HTMLToken&);
+ void handle_in_table_body(HTMLToken&);
+ void handle_in_row(HTMLToken&);
+ void handle_in_cell(HTMLToken&);
+ void handle_in_table_text(HTMLToken&);
+ void handle_in_select_in_table(HTMLToken&);
+ void handle_in_select(HTMLToken&);
+ void handle_in_caption(HTMLToken&);
+ void handle_in_column_group(HTMLToken&);
+ void handle_in_template(HTMLToken&);
+ void handle_in_frameset(HTMLToken&);
+ void handle_after_frameset(HTMLToken&);
+ void handle_after_after_frameset(HTMLToken&);
+
+ void stop_parsing() { m_stop_parsing = true; }
+
+ void generate_implied_end_tags(const FlyString& exception = {});
+ void generate_all_implied_end_tags_thoroughly();
+ bool stack_of_open_elements_has_element_with_tag_name_in_scope(const FlyString& tag_name);
+ NonnullRefPtr<DOM::Element> create_element_for(const HTMLToken&, const FlyString& namespace_);
+
+ struct AdjustedInsertionLocation {
+ RefPtr<DOM::Node> parent;
+ RefPtr<DOM::Node> insert_before_sibling;
+ };
+
+ AdjustedInsertionLocation find_appropriate_place_for_inserting_node();
+
+ DOM::Text* find_character_insertion_node();
+ void flush_character_insertions();
+ RefPtr<DOM::Element> insert_foreign_element(const HTMLToken&, const FlyString&);
+ RefPtr<DOM::Element> insert_html_element(const HTMLToken&);
+ DOM::Element& current_node();
+ DOM::Element& adjusted_current_node();
+ DOM::Element& node_before_current_node();
+ void insert_character(u32 data);
+ void insert_comment(HTMLToken&);
+ void reconstruct_the_active_formatting_elements();
+ void close_a_p_element();
+ void process_using_the_rules_for(InsertionMode, HTMLToken&);
+ void process_using_the_rules_for_foreign_content(HTMLToken&);
+ void parse_generic_raw_text_element(HTMLToken&);
+ void increment_script_nesting_level();
+ void decrement_script_nesting_level();
+ size_t script_nesting_level() const { return m_script_nesting_level; }
+ void reset_the_insertion_mode_appropriately();
+
+ void adjust_mathml_attributes(HTMLToken&);
+ void adjust_svg_tag_names(HTMLToken&);
+ void adjust_svg_attributes(HTMLToken&);
+ void adjust_foreign_attributes(HTMLToken&);
+
+ enum AdoptionAgencyAlgorithmOutcome {
+ DoNothing,
+ RunAnyOtherEndTagSteps,
+ };
+
+ AdoptionAgencyAlgorithmOutcome run_the_adoption_agency_algorithm(HTMLToken&);
+ void clear_the_stack_back_to_a_table_context();
+ void clear_the_stack_back_to_a_table_body_context();
+ void clear_the_stack_back_to_a_table_row_context();
+ void close_the_cell();
+
+ InsertionMode m_insertion_mode { InsertionMode::Initial };
+ InsertionMode m_original_insertion_mode { InsertionMode::Initial };
+
+ StackOfOpenElements m_stack_of_open_elements;
+ Vector<InsertionMode> m_stack_of_template_insertion_modes;
+ ListOfActiveFormattingElements m_list_of_active_formatting_elements;
+
+ HTMLTokenizer m_tokenizer;
+
+ bool m_foster_parenting { false };
+ bool m_frameset_ok { true };
+ bool m_parsing_fragment { false };
+ bool m_scripting_enabled { true };
+ bool m_invoked_via_document_write { false };
+ bool m_aborted { false };
+ bool m_parser_pause_flag { false };
+ bool m_stop_parsing { false };
+ size_t m_script_nesting_level { 0 };
+
+ NonnullRefPtr<DOM::Document> m_document;
+ RefPtr<HTMLHeadElement> m_head_element;
+ RefPtr<HTMLFormElement> m_form_element;
+ RefPtr<DOM::Element> m_context_element;
+
+ Vector<HTMLToken> m_pending_table_character_tokens;
+
+ RefPtr<DOM::Text> m_character_insertion_node;
+ StringBuilder m_character_insertion_builder;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/Parser/HTMLToken.cpp b/Userland/Libraries/LibWeb/HTML/Parser/HTMLToken.cpp
new file mode 100644
index 0000000000..53a2357be5
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/Parser/HTMLToken.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/Parser/HTMLToken.h>
+
+namespace Web::HTML {
+
+String HTMLToken::to_string() const
+{
+ StringBuilder builder;
+
+ switch (type()) {
+ case HTMLToken::Type::DOCTYPE:
+ builder.append("DOCTYPE");
+ builder.append(" { name: '");
+ builder.append(m_doctype.name.to_string());
+ builder.append("' }");
+ break;
+ case HTMLToken::Type::StartTag:
+ builder.append("StartTag");
+ break;
+ case HTMLToken::Type::EndTag:
+ builder.append("EndTag");
+ break;
+ case HTMLToken::Type::Comment:
+ builder.append("Comment");
+ break;
+ case HTMLToken::Type::Character:
+ builder.append("Character");
+ break;
+ case HTMLToken::Type::EndOfFile:
+ builder.append("EndOfFile");
+ break;
+ case HTMLToken::Type::Invalid:
+ ASSERT_NOT_REACHED();
+ }
+
+ if (type() == HTMLToken::Type::StartTag || type() == HTMLToken::Type::EndTag) {
+ builder.append(" { name: '");
+ builder.append(m_tag.tag_name.to_string());
+ builder.append("', { ");
+ for (auto& attribute : m_tag.attributes) {
+ builder.append(attribute.local_name_builder.to_string());
+ builder.append("=\"");
+ builder.append(attribute.value_builder.to_string());
+ builder.append("\" ");
+ }
+ builder.append("} }");
+ }
+
+ if (type() == HTMLToken::Type::Comment || type() == HTMLToken::Type::Character) {
+ builder.append(" { data: '");
+ builder.append(m_comment_or_character.data.to_string());
+ builder.append("' }");
+ }
+
+ return builder.to_string();
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/Parser/HTMLToken.h b/Userland/Libraries/LibWeb/HTML/Parser/HTMLToken.h
new file mode 100644
index 0000000000..c2246229c6
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/Parser/HTMLToken.h
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/FlyString.h>
+#include <AK/String.h>
+#include <AK/StringBuilder.h>
+#include <AK/Types.h>
+#include <AK/Utf8View.h>
+#include <AK/Vector.h>
+
+namespace Web::HTML {
+
+class HTMLToken {
+ friend class HTMLDocumentParser;
+ friend class HTMLTokenizer;
+
+public:
+ enum class Type {
+ Invalid,
+ DOCTYPE,
+ StartTag,
+ EndTag,
+ Comment,
+ Character,
+ EndOfFile,
+ };
+
+ static HTMLToken make_character(u32 code_point)
+ {
+ HTMLToken token;
+ token.m_type = Type::Character;
+ token.m_comment_or_character.data.append(code_point);
+ return token;
+ }
+
+ static HTMLToken make_start_tag(const FlyString& tag_name)
+ {
+ HTMLToken token;
+ token.m_type = Type::StartTag;
+ token.m_tag.tag_name.append(tag_name);
+ return token;
+ }
+
+ bool is_doctype() const { return m_type == Type::DOCTYPE; }
+ bool is_start_tag() const { return m_type == Type::StartTag; }
+ bool is_end_tag() const { return m_type == Type::EndTag; }
+ bool is_comment() const { return m_type == Type::Comment; }
+ bool is_character() const { return m_type == Type::Character; }
+ bool is_end_of_file() const { return m_type == Type::EndOfFile; }
+
+ u32 code_point() const
+ {
+ ASSERT(is_character());
+ Utf8View view(m_comment_or_character.data.string_view());
+ ASSERT(view.length() == 1);
+ return *view.begin();
+ }
+
+ bool is_parser_whitespace() const
+ {
+ // NOTE: The parser considers '\r' to be whitespace, while the tokenizer does not.
+ if (!is_character())
+ return false;
+ switch (code_point()) {
+ case '\t':
+ case '\n':
+ case '\f':
+ case '\r':
+ case ' ':
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ String tag_name() const
+ {
+ ASSERT(is_start_tag() || is_end_tag());
+ return m_tag.tag_name.to_string();
+ }
+
+ bool is_self_closing() const
+ {
+ ASSERT(is_start_tag() || is_end_tag());
+ return m_tag.self_closing;
+ }
+
+ bool has_acknowledged_self_closing_flag() const
+ {
+ ASSERT(is_self_closing());
+ return m_tag.self_closing_acknowledged;
+ }
+
+ void acknowledge_self_closing_flag_if_set()
+ {
+ if (is_self_closing())
+ m_tag.self_closing_acknowledged = true;
+ }
+
+ StringView attribute(const FlyString& attribute_name)
+ {
+ ASSERT(is_start_tag() || is_end_tag());
+ for (auto& attribute : m_tag.attributes) {
+ if (attribute_name == attribute.local_name_builder.string_view())
+ return attribute.value_builder.string_view();
+ }
+ return {};
+ }
+
+ bool has_attribute(const FlyString& attribute_name)
+ {
+ return !attribute(attribute_name).is_null();
+ }
+
+ void adjust_tag_name(const FlyString& old_name, const FlyString& new_name)
+ {
+ ASSERT(is_start_tag() || is_end_tag());
+ if (old_name == m_tag.tag_name.string_view()) {
+ m_tag.tag_name.clear();
+ m_tag.tag_name.append(new_name);
+ }
+ }
+
+ void adjust_attribute_name(const FlyString& old_name, const FlyString& new_name)
+ {
+ ASSERT(is_start_tag() || is_end_tag());
+ for (auto& attribute : m_tag.attributes) {
+ if (old_name == attribute.local_name_builder.string_view()) {
+ attribute.local_name_builder.clear();
+ attribute.local_name_builder.append(new_name);
+ }
+ }
+ }
+
+ void adjust_foreign_attribute(const FlyString& old_name, const FlyString& prefix, const FlyString& local_name, const FlyString& namespace_)
+ {
+ ASSERT(is_start_tag() || is_end_tag());
+ for (auto& attribute : m_tag.attributes) {
+ if (old_name == attribute.local_name_builder.string_view()) {
+ attribute.prefix_builder.clear();
+ attribute.prefix_builder.append(prefix);
+
+ attribute.local_name_builder.clear();
+ attribute.local_name_builder.append(local_name);
+
+ attribute.namespace_builder.clear();
+ attribute.namespace_builder.append(namespace_);
+ }
+ }
+ }
+
+ void drop_attributes()
+ {
+ ASSERT(is_start_tag() || is_end_tag());
+ m_tag.attributes.clear();
+ }
+
+ Type type() const { return m_type; }
+
+ String to_string() const;
+
+private:
+ struct AttributeBuilder {
+ StringBuilder prefix_builder;
+ StringBuilder local_name_builder;
+ StringBuilder namespace_builder;
+ StringBuilder value_builder;
+ };
+
+ Type m_type { Type::Invalid };
+
+ // Type::DOCTYPE
+ struct {
+ // NOTE: "Missing" is a distinct state from the empty string.
+
+ StringBuilder name;
+ bool missing_name { true };
+ StringBuilder public_identifier;
+ bool missing_public_identifier { true };
+ StringBuilder system_identifier;
+ bool missing_system_identifier { true };
+ bool force_quirks { false };
+ } m_doctype;
+
+ // Type::StartTag
+ // Type::EndTag
+ struct {
+ StringBuilder tag_name;
+ bool self_closing { false };
+ bool self_closing_acknowledged { false };
+ Vector<AttributeBuilder> attributes;
+ } m_tag;
+
+ // Type::Comment
+ // Type::Character
+ struct {
+ StringBuilder data;
+ } m_comment_or_character;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/Parser/HTMLTokenizer.cpp b/Userland/Libraries/LibWeb/HTML/Parser/HTMLTokenizer.cpp
new file mode 100644
index 0000000000..1bf1dab3c3
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/Parser/HTMLTokenizer.cpp
@@ -0,0 +1,2663 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibTextCodec/Decoder.h>
+#include <LibWeb/HTML/Parser/Entities.h>
+#include <LibWeb/HTML/Parser/HTMLToken.h>
+#include <LibWeb/HTML/Parser/HTMLTokenizer.h>
+#include <ctype.h>
+#include <string.h>
+
+namespace Web::HTML {
+
+#pragma GCC diagnostic ignored "-Wunused-label"
+
+//#define TOKENIZER_TRACE
+
+#ifdef TOKENIZER_TRACE
+# define PARSE_ERROR() \
+ do { \
+ dbg() << "Parse error (tokenization)" << __PRETTY_FUNCTION__ << " @ " << __LINE__; \
+ } while (0)
+#else
+# define PARSE_ERROR()
+#endif
+
+#define CONSUME_NEXT_INPUT_CHARACTER \
+ current_input_character = next_code_point();
+
+#define SWITCH_TO(new_state) \
+ do { \
+ will_switch_to(State::new_state); \
+ m_state = State::new_state; \
+ CONSUME_NEXT_INPUT_CHARACTER; \
+ goto new_state; \
+ } while (0)
+
+#define RECONSUME_IN(new_state) \
+ do { \
+ will_reconsume_in(State::new_state); \
+ m_state = State::new_state; \
+ goto new_state; \
+ } while (0)
+
+#define SWITCH_TO_RETURN_STATE \
+ do { \
+ will_switch_to(m_return_state); \
+ m_state = m_return_state; \
+ goto _StartOfFunction; \
+ } while (0)
+
+#define RECONSUME_IN_RETURN_STATE \
+ do { \
+ will_reconsume_in(m_return_state); \
+ m_state = m_return_state; \
+ if (current_input_character.has_value()) \
+ m_utf8_iterator = m_prev_utf8_iterator; \
+ goto _StartOfFunction; \
+ } while (0)
+
+#define SWITCH_TO_AND_EMIT_CURRENT_TOKEN(new_state) \
+ do { \
+ will_switch_to(State::new_state); \
+ m_state = State::new_state; \
+ will_emit(m_current_token); \
+ m_queued_tokens.enqueue(m_current_token); \
+ return m_queued_tokens.dequeue(); \
+ } while (0)
+
+#define EMIT_CHARACTER_AND_RECONSUME_IN(code_point, new_state) \
+ do { \
+ m_queued_tokens.enqueue(HTMLToken::make_character(code_point)); \
+ will_reconsume_in(State::new_state); \
+ m_state = State::new_state; \
+ goto new_state; \
+ } while (0)
+
+#define FLUSH_CODEPOINTS_CONSUMED_AS_A_CHARACTER_REFERENCE \
+ do { \
+ for (auto code_point : m_temporary_buffer) { \
+ if (consumed_as_part_of_an_attribute()) { \
+ m_current_token.m_tag.attributes.last().value_builder.append_code_point(code_point); \
+ } else { \
+ create_new_token(HTMLToken::Type::Character); \
+ m_current_token.m_comment_or_character.data.append_code_point(code_point); \
+ m_queued_tokens.enqueue(m_current_token); \
+ } \
+ } \
+ } while (0)
+
+#define DONT_CONSUME_NEXT_INPUT_CHARACTER \
+ do { \
+ m_utf8_iterator = m_prev_utf8_iterator; \
+ } while (0)
+
+#define ON(code_point) \
+ if (current_input_character.has_value() && current_input_character.value() == code_point)
+
+#define ON_EOF \
+ if (!current_input_character.has_value())
+
+#define ON_ASCII_ALPHA \
+ if (current_input_character.has_value() && isalpha(current_input_character.value()))
+
+#define ON_ASCII_ALPHANUMERIC \
+ if (current_input_character.has_value() && isalnum(current_input_character.value()))
+
+#define ON_ASCII_UPPER_ALPHA \
+ if (current_input_character.has_value() && current_input_character.value() >= 'A' && current_input_character.value() <= 'Z')
+
+#define ON_ASCII_LOWER_ALPHA \
+ if (current_input_character.has_value() && current_input_character.value() >= 'a' && current_input_character.value() <= 'z')
+
+#define ON_ASCII_DIGIT \
+ if (current_input_character.has_value() && isdigit(current_input_character.value()))
+
+#define ON_ASCII_HEX_DIGIT \
+ if (current_input_character.has_value() && isxdigit(current_input_character.value()))
+
+#define ON_WHITESPACE \
+ if (current_input_character.has_value() && strchr("\t\n\f ", current_input_character.value()))
+
+#define ANYTHING_ELSE if (1)
+
+#define EMIT_EOF \
+ do { \
+ if (m_has_emitted_eof) \
+ return {}; \
+ m_has_emitted_eof = true; \
+ create_new_token(HTMLToken::Type::EndOfFile); \
+ will_emit(m_current_token); \
+ m_queued_tokens.enqueue(m_current_token); \
+ return m_queued_tokens.dequeue(); \
+ } while (0)
+
+#define EMIT_CURRENT_TOKEN \
+ do { \
+ will_emit(m_current_token); \
+ m_queued_tokens.enqueue(m_current_token); \
+ return m_queued_tokens.dequeue(); \
+ } while (0)
+
+#define EMIT_CHARACTER(code_point) \
+ do { \
+ create_new_token(HTMLToken::Type::Character); \
+ m_current_token.m_comment_or_character.data.append_code_point(code_point); \
+ m_queued_tokens.enqueue(m_current_token); \
+ return m_queued_tokens.dequeue(); \
+ } while (0)
+
+#define EMIT_CURRENT_CHARACTER \
+ EMIT_CHARACTER(current_input_character.value());
+
+#define SWITCH_TO_AND_EMIT_CHARACTER(code_point, new_state) \
+ do { \
+ will_switch_to(State::new_state); \
+ m_state = State::new_state; \
+ EMIT_CHARACTER(code_point); \
+ } while (0)
+
+#define SWITCH_TO_AND_EMIT_CURRENT_CHARACTER(new_state) \
+ SWITCH_TO_AND_EMIT_CHARACTER(current_input_character.value(), new_state)
+
+#define BEGIN_STATE(state) \
+ state: \
+ case State::state: { \
+ { \
+ {
+
+#define END_STATE \
+ ASSERT_NOT_REACHED(); \
+ break; \
+ } \
+ } \
+ }
+
+static inline bool is_surrogate(u32 code_point)
+{
+ return (code_point & 0xfffff800) == 0xd800;
+}
+
+static inline bool is_noncharacter(u32 code_point)
+{
+ return code_point >= 0xfdd0 && (code_point <= 0xfdef || (code_point & 0xfffe) == 0xfffe) && code_point <= 0x10ffff;
+}
+
+static inline bool is_c0_control(u32 code_point)
+{
+ return code_point <= 0x1f;
+}
+
+static inline bool is_control(u32 code_point)
+{
+ return is_c0_control(code_point) || (code_point >= 0x7f && code_point <= 0x9f);
+}
+
+Optional<u32> HTMLTokenizer::next_code_point()
+{
+ if (m_utf8_iterator == m_utf8_view.end())
+ return {};
+ m_prev_utf8_iterator = m_utf8_iterator;
+ ++m_utf8_iterator;
+#ifdef TOKENIZER_TRACE
+ dbg() << "(Tokenizer) Next code_point: " << (char)*m_prev_utf8_iterator;
+#endif
+ return *m_prev_utf8_iterator;
+}
+
+Optional<u32> HTMLTokenizer::peek_code_point(size_t offset) const
+{
+ auto it = m_utf8_iterator;
+ for (size_t i = 0; i < offset && it != m_utf8_view.end(); ++i)
+ ++it;
+ if (it == m_utf8_view.end())
+ return {};
+ return *it;
+}
+
+Optional<HTMLToken> HTMLTokenizer::next_token()
+{
+_StartOfFunction:
+ if (!m_queued_tokens.is_empty())
+ return m_queued_tokens.dequeue();
+
+ for (;;) {
+ auto current_input_character = next_code_point();
+ switch (m_state) {
+ BEGIN_STATE(Data)
+ {
+ ON('&')
+ {
+ m_return_state = State::Data;
+ SWITCH_TO(CharacterReference);
+ }
+ ON('<')
+ {
+ SWITCH_TO(TagOpen);
+ }
+ ON(0)
+ {
+ PARSE_ERROR();
+ EMIT_CURRENT_CHARACTER;
+ }
+ ON_EOF
+ {
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ EMIT_CURRENT_CHARACTER;
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(TagOpen)
+ {
+ ON('!')
+ {
+ SWITCH_TO(MarkupDeclarationOpen);
+ }
+ ON('/')
+ {
+ SWITCH_TO(EndTagOpen);
+ }
+ ON_ASCII_ALPHA
+ {
+ create_new_token(HTMLToken::Type::StartTag);
+ RECONSUME_IN(TagName);
+ }
+ ON('?')
+ {
+ PARSE_ERROR();
+ create_new_token(HTMLToken::Type::Comment);
+ RECONSUME_IN(BogusComment);
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ m_queued_tokens.enqueue(HTMLToken::make_character('<'));
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ PARSE_ERROR();
+ EMIT_CHARACTER_AND_RECONSUME_IN('<', Data);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(TagName)
+ {
+ ON_WHITESPACE
+ {
+ SWITCH_TO(BeforeAttributeName);
+ }
+ ON('/')
+ {
+ SWITCH_TO(SelfClosingStartTag);
+ }
+ ON('>')
+ {
+ SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
+ }
+ ON_ASCII_UPPER_ALPHA
+ {
+ m_current_token.m_tag.tag_name.append(tolower(current_input_character.value()));
+ continue;
+ }
+ ON(0)
+ {
+ PARSE_ERROR();
+ m_current_token.m_tag.tag_name.append_code_point(0xFFFD);
+ continue;
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ m_current_token.m_tag.tag_name.append_code_point(current_input_character.value());
+ continue;
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(EndTagOpen)
+ {
+ ON_ASCII_ALPHA
+ {
+ create_new_token(HTMLToken::Type::EndTag);
+ RECONSUME_IN(TagName);
+ }
+ ON('>')
+ {
+ PARSE_ERROR();
+ SWITCH_TO(Data);
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ m_queued_tokens.enqueue(HTMLToken::make_character('<'));
+ m_queued_tokens.enqueue(HTMLToken::make_character('/'));
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ PARSE_ERROR();
+ create_new_token(HTMLToken::Type::Comment);
+ RECONSUME_IN(BogusComment);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(MarkupDeclarationOpen)
+ {
+ DONT_CONSUME_NEXT_INPUT_CHARACTER;
+ if (consume_next_if_match("--")) {
+ create_new_token(HTMLToken::Type::Comment);
+ SWITCH_TO(CommentStart);
+ }
+ if (consume_next_if_match("DOCTYPE", CaseSensitivity::CaseInsensitive)) {
+ SWITCH_TO(DOCTYPE);
+ }
+ if (consume_next_if_match("[CDATA[")) {
+ TODO();
+ }
+ ANYTHING_ELSE
+ {
+ PARSE_ERROR();
+ create_new_token(HTMLToken::Type::Comment);
+ SWITCH_TO(BogusComment);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(BogusComment)
+ {
+ ON('>')
+ {
+ SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
+ }
+ ON_EOF
+ {
+ m_queued_tokens.enqueue(m_current_token);
+ EMIT_EOF;
+ }
+ ON(0)
+ {
+ PARSE_ERROR();
+ m_current_token.m_comment_or_character.data.append_code_point(0xFFFD);
+ continue;
+ }
+ ANYTHING_ELSE
+ {
+ m_current_token.m_comment_or_character.data.append_code_point(current_input_character.value());
+ continue;
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(DOCTYPE)
+ {
+ ON_WHITESPACE
+ {
+ SWITCH_TO(BeforeDOCTYPEName);
+ }
+ ON('>')
+ {
+ RECONSUME_IN(BeforeDOCTYPEName);
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ create_new_token(HTMLToken::Type::DOCTYPE);
+ m_current_token.m_doctype.force_quirks = true;
+ m_queued_tokens.enqueue(m_current_token);
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ PARSE_ERROR();
+ RECONSUME_IN(BeforeDOCTYPEName);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(BeforeDOCTYPEName)
+ {
+ ON_WHITESPACE
+ {
+ continue;
+ }
+ ON_ASCII_UPPER_ALPHA
+ {
+ create_new_token(HTMLToken::Type::DOCTYPE);
+ m_current_token.m_doctype.name.append(tolower(current_input_character.value()));
+ m_current_token.m_doctype.missing_name = false;
+ SWITCH_TO(DOCTYPEName);
+ }
+ ON(0)
+ {
+ PARSE_ERROR();
+ create_new_token(HTMLToken::Type::DOCTYPE);
+ m_current_token.m_doctype.name.append_code_point(0xFFFD);
+ m_current_token.m_doctype.missing_name = false;
+ SWITCH_TO(DOCTYPEName);
+ }
+ ON('>')
+ {
+ PARSE_ERROR();
+ create_new_token(HTMLToken::Type::DOCTYPE);
+ m_current_token.m_doctype.force_quirks = true;
+ SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ create_new_token(HTMLToken::Type::DOCTYPE);
+ m_current_token.m_doctype.force_quirks = true;
+ m_queued_tokens.enqueue(m_current_token);
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ create_new_token(HTMLToken::Type::DOCTYPE);
+ m_current_token.m_doctype.name.append_code_point(current_input_character.value());
+ m_current_token.m_doctype.missing_name = false;
+ SWITCH_TO(DOCTYPEName);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(DOCTYPEName)
+ {
+ ON_WHITESPACE
+ {
+ SWITCH_TO(AfterDOCTYPEName);
+ }
+ ON('>')
+ {
+ SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
+ }
+ ON_ASCII_UPPER_ALPHA
+ {
+ m_current_token.m_doctype.name.append(tolower(current_input_character.value()));
+ continue;
+ }
+ ON(0)
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.name.append_code_point(0xFFFD);
+ continue;
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.force_quirks = true;
+ m_queued_tokens.enqueue(m_current_token);
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ m_current_token.m_doctype.name.append_code_point(current_input_character.value());
+ continue;
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(AfterDOCTYPEName)
+ {
+ ON_WHITESPACE
+ {
+ continue;
+ }
+ ON('>')
+ {
+ SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.force_quirks = true;
+ m_queued_tokens.enqueue(m_current_token);
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ if (toupper(current_input_character.value()) == 'P' && consume_next_if_match("UBLIC", CaseSensitivity::CaseInsensitive)) {
+ SWITCH_TO(AfterDOCTYPEPublicKeyword);
+ }
+ if (toupper(current_input_character.value()) == 'S' && consume_next_if_match("YSTEM", CaseSensitivity::CaseInsensitive)) {
+ SWITCH_TO(AfterDOCTYPESystemKeyword);
+ }
+ PARSE_ERROR();
+ m_current_token.m_doctype.force_quirks = true;
+ RECONSUME_IN(BogusDOCTYPE);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(AfterDOCTYPEPublicKeyword)
+ {
+ ON_WHITESPACE
+ {
+ SWITCH_TO(BeforeDOCTYPEPublicIdentifier);
+ }
+ ON('"')
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.public_identifier.clear();
+ m_current_token.m_doctype.missing_public_identifier = false;
+ SWITCH_TO(DOCTYPEPublicIdentifierDoubleQuoted);
+ }
+ ON('\'')
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.public_identifier.clear();
+ m_current_token.m_doctype.missing_public_identifier = false;
+ SWITCH_TO(DOCTYPEPublicIdentifierSingleQuoted);
+ }
+ ON('>')
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.force_quirks = true;
+ SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.force_quirks = true;
+ m_queued_tokens.enqueue(m_current_token);
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.force_quirks = true;
+ RECONSUME_IN(BogusDOCTYPE);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(AfterDOCTYPESystemKeyword)
+ {
+ ON_WHITESPACE
+ {
+ SWITCH_TO(BeforeDOCTYPESystemIdentifier);
+ }
+ ON('"')
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.system_identifier.clear();
+ m_current_token.m_doctype.missing_system_identifier = false;
+ SWITCH_TO(DOCTYPESystemIdentifierDoubleQuoted);
+ }
+ ON('\'')
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.system_identifier.clear();
+ m_current_token.m_doctype.missing_system_identifier = false;
+ SWITCH_TO(DOCTYPESystemIdentifierSingleQuoted);
+ }
+ ON('>')
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.force_quirks = true;
+ SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.force_quirks = true;
+ m_queued_tokens.enqueue(m_current_token);
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.force_quirks = true;
+ RECONSUME_IN(BogusDOCTYPE);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(BeforeDOCTYPEPublicIdentifier)
+ {
+ ON_WHITESPACE
+ {
+ continue;
+ }
+ ON('"')
+ {
+ m_current_token.m_doctype.public_identifier.clear();
+ m_current_token.m_doctype.missing_public_identifier = false;
+ SWITCH_TO(DOCTYPEPublicIdentifierDoubleQuoted);
+ }
+ ON('\'')
+ {
+ m_current_token.m_doctype.public_identifier.clear();
+ m_current_token.m_doctype.missing_public_identifier = false;
+ SWITCH_TO(DOCTYPEPublicIdentifierSingleQuoted);
+ }
+ ON('>')
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.force_quirks = true;
+ SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.force_quirks = true;
+ m_queued_tokens.enqueue(m_current_token);
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.force_quirks = true;
+ RECONSUME_IN(BogusDOCTYPE);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(BeforeDOCTYPESystemIdentifier)
+ {
+ ON_WHITESPACE
+ {
+ continue;
+ }
+ ON('"')
+ {
+ m_current_token.m_doctype.system_identifier.clear();
+ m_current_token.m_doctype.missing_system_identifier = false;
+ SWITCH_TO(DOCTYPESystemIdentifierDoubleQuoted);
+ }
+ ON('\'')
+ {
+ m_current_token.m_doctype.system_identifier.clear();
+ m_current_token.m_doctype.missing_system_identifier = false;
+ SWITCH_TO(DOCTYPESystemIdentifierSingleQuoted);
+ }
+ ON('>')
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.force_quirks = true;
+ SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.force_quirks = true;
+ m_queued_tokens.enqueue(m_current_token);
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.force_quirks = true;
+ RECONSUME_IN(BogusDOCTYPE);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(DOCTYPEPublicIdentifierDoubleQuoted)
+ {
+ ON('"')
+ {
+ SWITCH_TO(AfterDOCTYPEPublicIdentifier);
+ }
+ ON(0)
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.public_identifier.append_code_point(0xFFFD);
+ continue;
+ }
+ ON('>')
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.force_quirks = true;
+ SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.force_quirks = true;
+ m_queued_tokens.enqueue(m_current_token);
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ m_current_token.m_doctype.public_identifier.append_code_point(current_input_character.value());
+ continue;
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(DOCTYPEPublicIdentifierSingleQuoted)
+ {
+ ON('\'')
+ {
+ SWITCH_TO(AfterDOCTYPEPublicIdentifier);
+ }
+ ON(0)
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.public_identifier.append_code_point(0xFFFD);
+ continue;
+ }
+ ON('>')
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.force_quirks = true;
+ SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.force_quirks = true;
+ m_queued_tokens.enqueue(m_current_token);
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ m_current_token.m_doctype.public_identifier.append_code_point(current_input_character.value());
+ continue;
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(DOCTYPESystemIdentifierDoubleQuoted)
+ {
+ ON('"')
+ {
+ SWITCH_TO(AfterDOCTYPESystemIdentifier);
+ }
+ ON(0)
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.system_identifier.append_code_point(0xFFFD);
+ continue;
+ }
+ ON('>')
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.force_quirks = true;
+ SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.force_quirks = true;
+ m_queued_tokens.enqueue(m_current_token);
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ m_current_token.m_doctype.system_identifier.append_code_point(current_input_character.value());
+ continue;
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(DOCTYPESystemIdentifierSingleQuoted)
+ {
+ ON('\'')
+ {
+ SWITCH_TO(AfterDOCTYPESystemIdentifier);
+ }
+ ON(0)
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.system_identifier.append_code_point(0xFFFD);
+ continue;
+ }
+ ON('>')
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.force_quirks = true;
+ SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.force_quirks = true;
+ m_queued_tokens.enqueue(m_current_token);
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ m_current_token.m_doctype.system_identifier.append_code_point(current_input_character.value());
+ continue;
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(AfterDOCTYPEPublicIdentifier)
+ {
+ ON_WHITESPACE
+ {
+ SWITCH_TO(BetweenDOCTYPEPublicAndSystemIdentifiers);
+ }
+ ON('>')
+ {
+ SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
+ }
+ ON('"')
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.system_identifier.clear();
+ m_current_token.m_doctype.missing_system_identifier = false;
+ SWITCH_TO(DOCTYPESystemIdentifierDoubleQuoted);
+ }
+ ON('\'')
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.system_identifier.clear();
+ m_current_token.m_doctype.missing_system_identifier = false;
+ SWITCH_TO(DOCTYPESystemIdentifierSingleQuoted);
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.force_quirks = true;
+ m_queued_tokens.enqueue(m_current_token);
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.force_quirks = true;
+ RECONSUME_IN(BogusDOCTYPE);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(BetweenDOCTYPEPublicAndSystemIdentifiers)
+ {
+ ON_WHITESPACE
+ {
+ continue;
+ }
+ ON('>')
+ {
+ SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
+ }
+ ON('"')
+ {
+ m_current_token.m_doctype.system_identifier.clear();
+ m_current_token.m_doctype.missing_system_identifier = false;
+ SWITCH_TO(DOCTYPESystemIdentifierDoubleQuoted);
+ }
+ ON('\'')
+ {
+ m_current_token.m_doctype.system_identifier.clear();
+ m_current_token.m_doctype.missing_system_identifier = false;
+ SWITCH_TO(DOCTYPESystemIdentifierSingleQuoted);
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.force_quirks = true;
+ m_queued_tokens.enqueue(m_current_token);
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.force_quirks = true;
+ RECONSUME_IN(BogusDOCTYPE);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(AfterDOCTYPESystemIdentifier)
+ {
+ ON_WHITESPACE
+ {
+ continue;
+ }
+ ON('>')
+ {
+ SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ m_current_token.m_doctype.force_quirks = true;
+ m_queued_tokens.enqueue(m_current_token);
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ PARSE_ERROR();
+ RECONSUME_IN(BogusDOCTYPE);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(BogusDOCTYPE)
+ {
+ ON('>')
+ {
+ SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
+ }
+ ON(0)
+ {
+ PARSE_ERROR();
+ continue;
+ }
+ ON_EOF
+ {
+ m_queued_tokens.enqueue(m_current_token);
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ continue;
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(BeforeAttributeName)
+ {
+ ON_WHITESPACE
+ {
+ continue;
+ }
+ ON('/')
+ {
+ RECONSUME_IN(AfterAttributeName);
+ }
+ ON('>')
+ {
+ RECONSUME_IN(AfterAttributeName);
+ }
+ ON_EOF
+ {
+ RECONSUME_IN(AfterAttributeName);
+ }
+ ON('=')
+ {
+ PARSE_ERROR();
+ auto new_attribute = HTMLToken::AttributeBuilder();
+ new_attribute.local_name_builder.append_code_point(current_input_character.value());
+ m_current_token.m_tag.attributes.append(new_attribute);
+ SWITCH_TO(AttributeName);
+ }
+ ANYTHING_ELSE
+ {
+ m_current_token.m_tag.attributes.append(HTMLToken::AttributeBuilder());
+ RECONSUME_IN(AttributeName);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(SelfClosingStartTag)
+ {
+ ON('>')
+ {
+ m_current_token.m_tag.self_closing = true;
+ SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ PARSE_ERROR();
+ RECONSUME_IN(BeforeAttributeName);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(AttributeName)
+ {
+ ON_WHITESPACE
+ {
+ RECONSUME_IN(AfterAttributeName);
+ }
+ ON('/')
+ {
+ RECONSUME_IN(AfterAttributeName);
+ }
+ ON('>')
+ {
+ RECONSUME_IN(AfterAttributeName);
+ }
+ ON_EOF
+ {
+ RECONSUME_IN(AfterAttributeName);
+ }
+ ON('=')
+ {
+ SWITCH_TO(BeforeAttributeValue);
+ }
+ ON_ASCII_UPPER_ALPHA
+ {
+ m_current_token.m_tag.attributes.last().local_name_builder.append_code_point(tolower(current_input_character.value()));
+ continue;
+ }
+ ON(0)
+ {
+ PARSE_ERROR();
+ m_current_token.m_tag.attributes.last().local_name_builder.append_code_point(0xFFFD);
+ continue;
+ }
+ ON('"')
+ {
+ PARSE_ERROR();
+ goto AnythingElseAttributeName;
+ }
+ ON('\'')
+ {
+ PARSE_ERROR();
+ goto AnythingElseAttributeName;
+ }
+ ON('<')
+ {
+ PARSE_ERROR();
+ goto AnythingElseAttributeName;
+ }
+ ANYTHING_ELSE
+ {
+ AnythingElseAttributeName:
+ m_current_token.m_tag.attributes.last().local_name_builder.append_code_point(current_input_character.value());
+ continue;
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(AfterAttributeName)
+ {
+ ON_WHITESPACE
+ {
+ continue;
+ }
+ ON('/')
+ {
+ SWITCH_TO(SelfClosingStartTag);
+ }
+ ON('=')
+ {
+ SWITCH_TO(BeforeAttributeValue);
+ }
+ ON('>')
+ {
+ SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ m_current_token.m_tag.attributes.append(HTMLToken::AttributeBuilder());
+ RECONSUME_IN(AttributeName);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(BeforeAttributeValue)
+ {
+ ON_WHITESPACE
+ {
+ continue;
+ }
+ ON('"')
+ {
+ SWITCH_TO(AttributeValueDoubleQuoted);
+ }
+ ON('\'')
+ {
+ SWITCH_TO(AttributeValueSingleQuoted);
+ }
+ ON('>')
+ {
+ PARSE_ERROR();
+ SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
+ }
+ ANYTHING_ELSE
+ {
+ RECONSUME_IN(AttributeValueUnquoted);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(AttributeValueDoubleQuoted)
+ {
+ ON('"')
+ {
+ SWITCH_TO(AfterAttributeValueQuoted);
+ }
+ ON('&')
+ {
+ m_return_state = State::AttributeValueDoubleQuoted;
+ SWITCH_TO(CharacterReference);
+ }
+ ON(0)
+ {
+ PARSE_ERROR();
+ m_current_token.m_tag.attributes.last().value_builder.append_code_point(0xFFFD);
+ continue;
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ m_current_token.m_tag.attributes.last().value_builder.append_code_point(current_input_character.value());
+ continue;
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(AttributeValueSingleQuoted)
+ {
+ ON('\'')
+ {
+ SWITCH_TO(AfterAttributeValueQuoted);
+ }
+ ON('&')
+ {
+ m_return_state = State::AttributeValueSingleQuoted;
+ SWITCH_TO(CharacterReference);
+ }
+ ON(0)
+ {
+ PARSE_ERROR();
+ m_current_token.m_tag.attributes.last().value_builder.append_code_point(0xFFFD);
+ continue;
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ m_current_token.m_tag.attributes.last().value_builder.append_code_point(current_input_character.value());
+ continue;
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(AttributeValueUnquoted)
+ {
+ ON_WHITESPACE
+ {
+ SWITCH_TO(BeforeAttributeName);
+ }
+ ON('&')
+ {
+ m_return_state = State::AttributeValueUnquoted;
+ SWITCH_TO(CharacterReference);
+ }
+ ON('>')
+ {
+ SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
+ }
+ ON(0)
+ {
+ PARSE_ERROR();
+ m_current_token.m_tag.attributes.last().value_builder.append_code_point(0xFFFD);
+ continue;
+ }
+ ON('"')
+ {
+ PARSE_ERROR();
+ goto AnythingElseAttributeValueUnquoted;
+ }
+ ON('\'')
+ {
+ PARSE_ERROR();
+ goto AnythingElseAttributeValueUnquoted;
+ }
+ ON('<')
+ {
+ PARSE_ERROR();
+ goto AnythingElseAttributeValueUnquoted;
+ }
+ ON('=')
+ {
+ PARSE_ERROR();
+ goto AnythingElseAttributeValueUnquoted;
+ }
+ ON('`')
+ {
+ PARSE_ERROR();
+ goto AnythingElseAttributeValueUnquoted;
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ AnythingElseAttributeValueUnquoted:
+ m_current_token.m_tag.attributes.last().value_builder.append_code_point(current_input_character.value());
+ continue;
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(AfterAttributeValueQuoted)
+ {
+ ON_WHITESPACE
+ {
+ SWITCH_TO(BeforeAttributeName);
+ }
+ ON('/')
+ {
+ SWITCH_TO(SelfClosingStartTag);
+ }
+ ON('>')
+ {
+ SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ PARSE_ERROR();
+ RECONSUME_IN(BeforeAttributeName);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(CommentStart)
+ {
+ ON('-')
+ {
+ SWITCH_TO(CommentStartDash);
+ }
+ ON('>')
+ {
+ PARSE_ERROR();
+ SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
+ }
+ ANYTHING_ELSE
+ {
+ RECONSUME_IN(Comment);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(CommentStartDash)
+ {
+ ON('-')
+ {
+ SWITCH_TO(CommentEnd);
+ }
+ ON('>')
+ {
+ PARSE_ERROR();
+ SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ m_queued_tokens.enqueue(m_current_token);
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ m_current_token.m_comment_or_character.data.append('-');
+ RECONSUME_IN(Comment);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(Comment)
+ {
+ ON('<')
+ {
+ m_current_token.m_comment_or_character.data.append_code_point(current_input_character.value());
+ SWITCH_TO(CommentLessThanSign);
+ }
+ ON('-')
+ {
+ SWITCH_TO(CommentEndDash);
+ }
+ ON(0)
+ {
+ PARSE_ERROR();
+ m_current_token.m_comment_or_character.data.append_code_point(0xFFFD);
+ continue;
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ m_queued_tokens.enqueue(m_current_token);
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ m_current_token.m_comment_or_character.data.append_code_point(current_input_character.value());
+ continue;
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(CommentEnd)
+ {
+ ON('>')
+ {
+ SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
+ }
+ ON('!')
+ {
+ SWITCH_TO(CommentEndBang);
+ }
+ ON('-')
+ {
+ m_current_token.m_comment_or_character.data.append('-');
+ continue;
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ m_queued_tokens.enqueue(m_current_token);
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ m_current_token.m_comment_or_character.data.append('-');
+ RECONSUME_IN(Comment);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(CommentEndBang)
+ {
+ ON('-')
+ {
+ m_current_token.m_comment_or_character.data.append("--!");
+ SWITCH_TO(CommentEndDash);
+ }
+ ON('>')
+ {
+ PARSE_ERROR();
+ SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ m_queued_tokens.enqueue(m_current_token);
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ m_current_token.m_comment_or_character.data.append("--!");
+ RECONSUME_IN(Comment);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(CommentEndDash)
+ {
+ ON('-')
+ {
+ SWITCH_TO(CommentEnd);
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ m_queued_tokens.enqueue(m_current_token);
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ m_current_token.m_comment_or_character.data.append('-');
+ RECONSUME_IN(Comment);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(CommentLessThanSign)
+ {
+ ON('!')
+ {
+ m_current_token.m_comment_or_character.data.append_code_point(current_input_character.value());
+ SWITCH_TO(CommentLessThanSignBang);
+ }
+ ON('<')
+ {
+ m_current_token.m_comment_or_character.data.append_code_point(current_input_character.value());
+ continue;
+ }
+ ANYTHING_ELSE
+ {
+ RECONSUME_IN(Comment);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(CommentLessThanSignBang)
+ {
+ ON('-')
+ {
+ SWITCH_TO(CommentLessThanSignBangDash);
+ }
+ ANYTHING_ELSE
+ {
+ RECONSUME_IN(Comment);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(CommentLessThanSignBangDash)
+ {
+ ON('-')
+ {
+ SWITCH_TO(CommentLessThanSignBangDashDash);
+ }
+ ANYTHING_ELSE
+ {
+ RECONSUME_IN(CommentEndDash);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(CommentLessThanSignBangDashDash)
+ {
+ ON('>')
+ {
+ RECONSUME_IN(CommentEnd);
+ }
+ ON_EOF
+ {
+ RECONSUME_IN(CommentEnd);
+ }
+ ANYTHING_ELSE
+ {
+ PARSE_ERROR();
+ RECONSUME_IN(CommentEnd);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(CharacterReference)
+ {
+ m_temporary_buffer.clear();
+ m_temporary_buffer.append('&');
+
+ ON_ASCII_ALPHANUMERIC
+ {
+ RECONSUME_IN(NamedCharacterReference);
+ }
+ ON('#')
+ {
+ m_temporary_buffer.append(current_input_character.value());
+ SWITCH_TO(NumericCharacterReference);
+ }
+ ANYTHING_ELSE
+ {
+ FLUSH_CODEPOINTS_CONSUMED_AS_A_CHARACTER_REFERENCE;
+ RECONSUME_IN_RETURN_STATE;
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(NamedCharacterReference)
+ {
+ size_t byte_offset = m_utf8_view.byte_offset_of(m_prev_utf8_iterator);
+
+ auto match = HTML::code_points_from_entity(m_decoded_input.substring_view(byte_offset, m_decoded_input.length() - byte_offset - 1));
+
+ if (match.has_value()) {
+ for (size_t i = 0; i < match.value().entity.length() - 1; ++i) {
+ m_prev_utf8_iterator = m_utf8_iterator;
+ ++m_utf8_iterator;
+ }
+ for (auto ch : match.value().entity)
+ m_temporary_buffer.append(ch);
+
+ if (consumed_as_part_of_an_attribute() && !match.value().entity.ends_with(';')) {
+ auto next_code_point = peek_code_point(0);
+ if (next_code_point.has_value() && (next_code_point.value() == '=' || isalnum(next_code_point.value()))) {
+ FLUSH_CODEPOINTS_CONSUMED_AS_A_CHARACTER_REFERENCE;
+ SWITCH_TO_RETURN_STATE;
+ }
+ }
+
+ if (!match.value().entity.ends_with(';')) {
+ PARSE_ERROR();
+ }
+
+ m_temporary_buffer.clear();
+ m_temporary_buffer.append(match.value().code_points);
+
+ FLUSH_CODEPOINTS_CONSUMED_AS_A_CHARACTER_REFERENCE;
+ SWITCH_TO_RETURN_STATE;
+ } else {
+ FLUSH_CODEPOINTS_CONSUMED_AS_A_CHARACTER_REFERENCE;
+ // FIXME: This should be SWITCH_TO, but we always lose the first character on this path, so just reconsume it.
+ // I can't wrap my head around how to do it as the spec says.
+ RECONSUME_IN(AmbiguousAmpersand);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(AmbiguousAmpersand)
+ {
+ ON_ASCII_ALPHANUMERIC
+ {
+ if (consumed_as_part_of_an_attribute()) {
+ m_current_token.m_tag.attributes.last().value_builder.append_code_point(current_input_character.value());
+ continue;
+ } else {
+ EMIT_CURRENT_CHARACTER;
+ }
+ }
+ ON(';')
+ {
+ PARSE_ERROR();
+ RECONSUME_IN_RETURN_STATE;
+ }
+ ANYTHING_ELSE
+ {
+ RECONSUME_IN_RETURN_STATE;
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(NumericCharacterReference)
+ {
+ m_character_reference_code = 0;
+
+ ON('X')
+ {
+ m_temporary_buffer.append(current_input_character.value());
+ SWITCH_TO(HexadecimalCharacterReferenceStart);
+ }
+ ON('x')
+ {
+ m_temporary_buffer.append(current_input_character.value());
+ SWITCH_TO(HexadecimalCharacterReferenceStart);
+ }
+ ANYTHING_ELSE
+ {
+ RECONSUME_IN(DecimalCharacterReferenceStart);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(HexadecimalCharacterReferenceStart)
+ {
+ ON_ASCII_HEX_DIGIT
+ {
+ RECONSUME_IN(HexadecimalCharacterReference);
+ }
+ ANYTHING_ELSE
+ {
+ PARSE_ERROR();
+ FLUSH_CODEPOINTS_CONSUMED_AS_A_CHARACTER_REFERENCE;
+ RECONSUME_IN_RETURN_STATE;
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(DecimalCharacterReferenceStart)
+ {
+ ON_ASCII_DIGIT
+ {
+ RECONSUME_IN(DecimalCharacterReference);
+ }
+ ANYTHING_ELSE
+ {
+ PARSE_ERROR();
+ FLUSH_CODEPOINTS_CONSUMED_AS_A_CHARACTER_REFERENCE;
+ RECONSUME_IN_RETURN_STATE;
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(HexadecimalCharacterReference)
+ {
+ ON_ASCII_DIGIT
+ {
+ m_character_reference_code *= 16;
+ m_character_reference_code += current_input_character.value() - 0x30;
+ continue;
+ }
+ ON_ASCII_UPPER_ALPHA
+ {
+ m_character_reference_code *= 16;
+ m_character_reference_code += current_input_character.value() - 0x37;
+ continue;
+ }
+ ON_ASCII_LOWER_ALPHA
+ {
+ m_character_reference_code *= 16;
+ m_character_reference_code += current_input_character.value() - 0x57;
+ continue;
+ }
+ ON(';')
+ {
+ SWITCH_TO(NumericCharacterReferenceEnd);
+ }
+ ANYTHING_ELSE
+ {
+ PARSE_ERROR();
+ RECONSUME_IN(NumericCharacterReferenceEnd);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(DecimalCharacterReference)
+ {
+ ON_ASCII_DIGIT
+ {
+ m_character_reference_code *= 10;
+ m_character_reference_code += current_input_character.value() - 0x30;
+ continue;
+ }
+ ON(';')
+ {
+ SWITCH_TO(NumericCharacterReferenceEnd);
+ }
+ ANYTHING_ELSE
+ {
+ PARSE_ERROR();
+ RECONSUME_IN(NumericCharacterReferenceEnd);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(NumericCharacterReferenceEnd)
+ {
+ DONT_CONSUME_NEXT_INPUT_CHARACTER;
+
+ if (m_character_reference_code == 0) {
+ PARSE_ERROR();
+ m_character_reference_code = 0xFFFD;
+ }
+ if (m_character_reference_code > 0x10ffff) {
+ PARSE_ERROR();
+ m_character_reference_code = 0xFFFD;
+ }
+ if (is_surrogate(m_character_reference_code)) {
+ PARSE_ERROR();
+ m_character_reference_code = 0xFFFD;
+ }
+ if (is_noncharacter(m_character_reference_code)) {
+ PARSE_ERROR();
+ }
+ if (m_character_reference_code == 0xd || (is_control(m_character_reference_code) && !isspace(m_character_reference_code))) {
+ PARSE_ERROR();
+ constexpr struct {
+ u32 number;
+ u32 code_point;
+ } conversion_table[] = {
+ { 0x80, 0x20AC },
+ { 0x82, 0x201A },
+ { 0x83, 0x0192 },
+ { 0x84, 0x201E },
+ { 0x85, 0x2026 },
+ { 0x86, 0x2020 },
+ { 0x87, 0x2021 },
+ { 0x88, 0x02C6 },
+ { 0x89, 0x2030 },
+ { 0x8A, 0x0160 },
+ { 0x8B, 0x2039 },
+ { 0x8C, 0x0152 },
+ { 0x8E, 0x017D },
+ { 0x91, 0x2018 },
+ { 0x92, 0x2019 },
+ { 0x93, 0x201C },
+ { 0x94, 0x201D },
+ { 0x95, 0x2022 },
+ { 0x96, 0x2013 },
+ { 0x97, 0x2014 },
+ { 0x98, 0x02DC },
+ { 0x99, 0x2122 },
+ { 0x9A, 0x0161 },
+ { 0x9B, 0x203A },
+ { 0x9C, 0x0153 },
+ { 0x9E, 0x017E },
+ { 0x9F, 0x0178 },
+ };
+ for (auto& entry : conversion_table) {
+ if (m_character_reference_code == entry.number) {
+ m_character_reference_code = entry.code_point;
+ break;
+ }
+ }
+ }
+
+ m_temporary_buffer.clear();
+ m_temporary_buffer.append(m_character_reference_code);
+ FLUSH_CODEPOINTS_CONSUMED_AS_A_CHARACTER_REFERENCE;
+ SWITCH_TO_RETURN_STATE;
+ }
+ END_STATE
+
+ BEGIN_STATE(RCDATA)
+ {
+ ON('&')
+ {
+ m_return_state = State::RCDATA;
+ SWITCH_TO(CharacterReference);
+ }
+ ON('<')
+ {
+ SWITCH_TO(RCDATALessThanSign);
+ }
+ ON(0)
+ {
+ PARSE_ERROR();
+ EMIT_CHARACTER(0xFFFD);
+ }
+ ON_EOF
+ {
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ EMIT_CURRENT_CHARACTER;
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(RCDATALessThanSign)
+ {
+ ON('/')
+ {
+ m_temporary_buffer.clear();
+ SWITCH_TO(RCDATAEndTagOpen);
+ }
+ ANYTHING_ELSE
+ {
+ EMIT_CHARACTER_AND_RECONSUME_IN('<', RCDATA);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(RCDATAEndTagOpen)
+ {
+ ON_ASCII_ALPHA
+ {
+ create_new_token(HTMLToken::Type::EndTag);
+ RECONSUME_IN(RCDATAEndTagName);
+ }
+ ANYTHING_ELSE
+ {
+ m_queued_tokens.enqueue(HTMLToken::make_character('<'));
+ m_queued_tokens.enqueue(HTMLToken::make_character('/'));
+ RECONSUME_IN(RCDATA);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(RCDATAEndTagName)
+ {
+ ON_WHITESPACE
+ {
+ if (!current_end_tag_token_is_appropriate()) {
+ m_queued_tokens.enqueue(HTMLToken::make_character('<'));
+ m_queued_tokens.enqueue(HTMLToken::make_character('/'));
+ for (auto code_point : m_temporary_buffer)
+ m_queued_tokens.enqueue(HTMLToken::make_character(code_point));
+ RECONSUME_IN(RCDATA);
+ }
+ SWITCH_TO(BeforeAttributeName);
+ }
+ ON('/')
+ {
+ if (!current_end_tag_token_is_appropriate()) {
+ m_queued_tokens.enqueue(HTMLToken::make_character('<'));
+ m_queued_tokens.enqueue(HTMLToken::make_character('/'));
+ for (auto code_point : m_temporary_buffer)
+ m_queued_tokens.enqueue(HTMLToken::make_character(code_point));
+ RECONSUME_IN(RCDATA);
+ }
+ SWITCH_TO(SelfClosingStartTag);
+ }
+ ON('>')
+ {
+ if (!current_end_tag_token_is_appropriate()) {
+ m_queued_tokens.enqueue(HTMLToken::make_character('<'));
+ m_queued_tokens.enqueue(HTMLToken::make_character('/'));
+ for (auto code_point : m_temporary_buffer)
+ m_queued_tokens.enqueue(HTMLToken::make_character(code_point));
+ RECONSUME_IN(RCDATA);
+ }
+ SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
+ }
+ ON_ASCII_UPPER_ALPHA
+ {
+ m_current_token.m_tag.tag_name.append(tolower(current_input_character.value()));
+ m_temporary_buffer.append(current_input_character.value());
+ continue;
+ }
+ ON_ASCII_LOWER_ALPHA
+ {
+ m_current_token.m_tag.tag_name.append_code_point(current_input_character.value());
+ m_temporary_buffer.append(current_input_character.value());
+ continue;
+ }
+ ANYTHING_ELSE
+ {
+ m_queued_tokens.enqueue(HTMLToken::make_character('<'));
+ m_queued_tokens.enqueue(HTMLToken::make_character('/'));
+ for (auto code_point : m_temporary_buffer)
+ m_queued_tokens.enqueue(HTMLToken::make_character(code_point));
+ RECONSUME_IN(RCDATA);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(RAWTEXT)
+ {
+ ON('<')
+ {
+ SWITCH_TO(RAWTEXTLessThanSign);
+ }
+ ON(0)
+ {
+ PARSE_ERROR();
+ EMIT_CHARACTER(0xFFFD);
+ }
+ ON_EOF
+ {
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ EMIT_CURRENT_CHARACTER;
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(RAWTEXTLessThanSign)
+ {
+ ON('/')
+ {
+ m_temporary_buffer.clear();
+ SWITCH_TO(RAWTEXTEndTagOpen);
+ }
+ ANYTHING_ELSE
+ {
+ EMIT_CHARACTER_AND_RECONSUME_IN('<', RAWTEXT);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(RAWTEXTEndTagOpen)
+ {
+ ON_ASCII_ALPHA
+ {
+ create_new_token(HTMLToken::Type::EndTag);
+ RECONSUME_IN(RAWTEXTEndTagName);
+ }
+ ANYTHING_ELSE
+ {
+ m_queued_tokens.enqueue(HTMLToken::make_character('<'));
+ m_queued_tokens.enqueue(HTMLToken::make_character('/'));
+ RECONSUME_IN(RAWTEXT);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(RAWTEXTEndTagName)
+ {
+ ON_WHITESPACE
+ {
+ if (!current_end_tag_token_is_appropriate()) {
+ m_queued_tokens.enqueue(HTMLToken::make_character('<'));
+ m_queued_tokens.enqueue(HTMLToken::make_character('/'));
+ for (auto code_point : m_temporary_buffer)
+ m_queued_tokens.enqueue(HTMLToken::make_character(code_point));
+ RECONSUME_IN(RAWTEXT);
+ }
+ SWITCH_TO(BeforeAttributeName);
+ }
+ ON('/')
+ {
+ if (!current_end_tag_token_is_appropriate()) {
+ m_queued_tokens.enqueue(HTMLToken::make_character('<'));
+ m_queued_tokens.enqueue(HTMLToken::make_character('/'));
+ for (auto code_point : m_temporary_buffer)
+ m_queued_tokens.enqueue(HTMLToken::make_character(code_point));
+ RECONSUME_IN(RAWTEXT);
+ }
+ SWITCH_TO(SelfClosingStartTag);
+ }
+ ON('>')
+ {
+ if (!current_end_tag_token_is_appropriate()) {
+ m_queued_tokens.enqueue(HTMLToken::make_character('<'));
+ m_queued_tokens.enqueue(HTMLToken::make_character('/'));
+ for (auto code_point : m_temporary_buffer)
+ m_queued_tokens.enqueue(HTMLToken::make_character(code_point));
+ RECONSUME_IN(RAWTEXT);
+ }
+ SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
+ }
+ ON_ASCII_UPPER_ALPHA
+ {
+ m_current_token.m_tag.tag_name.append(tolower(current_input_character.value()));
+ m_temporary_buffer.append(current_input_character.value());
+ continue;
+ }
+ ON_ASCII_LOWER_ALPHA
+ {
+ m_current_token.m_tag.tag_name.append(current_input_character.value());
+ m_temporary_buffer.append(current_input_character.value());
+ continue;
+ }
+ ANYTHING_ELSE
+ {
+ m_queued_tokens.enqueue(HTMLToken::make_character('<'));
+ m_queued_tokens.enqueue(HTMLToken::make_character('/'));
+ for (auto code_point : m_temporary_buffer)
+ m_queued_tokens.enqueue(HTMLToken::make_character(code_point));
+ RECONSUME_IN(RAWTEXT);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(ScriptData)
+ {
+ ON('<')
+ {
+ SWITCH_TO(ScriptDataLessThanSign);
+ }
+ ON(0)
+ {
+ PARSE_ERROR();
+ EMIT_CHARACTER(0xFFFD);
+ }
+ ON_EOF
+ {
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ EMIT_CURRENT_CHARACTER;
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(PLAINTEXT)
+ {
+ ON(0)
+ {
+ PARSE_ERROR();
+ EMIT_CHARACTER(0xFFFD);
+ }
+ ON_EOF
+ {
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ EMIT_CURRENT_CHARACTER;
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(ScriptDataLessThanSign)
+ {
+ ON('/')
+ {
+ m_temporary_buffer.clear();
+ SWITCH_TO(ScriptDataEndTagOpen);
+ }
+ ON('!')
+ {
+ m_queued_tokens.enqueue(HTMLToken::make_character('<'));
+ m_queued_tokens.enqueue(HTMLToken::make_character('!'));
+ SWITCH_TO(ScriptDataEscapeStart);
+ }
+ ANYTHING_ELSE
+ {
+ EMIT_CHARACTER_AND_RECONSUME_IN('<', ScriptData);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(ScriptDataEscapeStart)
+ {
+ ON('-')
+ {
+ SWITCH_TO_AND_EMIT_CHARACTER('-', ScriptDataEscapeStartDash);
+ }
+ ANYTHING_ELSE
+ {
+ RECONSUME_IN(ScriptData);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(ScriptDataEscapeStartDash)
+ {
+ ON('-')
+ {
+ SWITCH_TO_AND_EMIT_CHARACTER('-', ScriptDataEscapedDashDash);
+ }
+ ANYTHING_ELSE
+ {
+ RECONSUME_IN(ScriptData);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(ScriptDataEscapedDashDash)
+ {
+ ON('-')
+ {
+ EMIT_CHARACTER('-');
+ }
+ ON('<')
+ {
+ SWITCH_TO(ScriptDataEscapedLessThanSign);
+ }
+ ON('>')
+ {
+ SWITCH_TO_AND_EMIT_CHARACTER('>', ScriptData);
+ }
+ ON(0)
+ {
+ PARSE_ERROR();
+ SWITCH_TO_AND_EMIT_CHARACTER(0xFFFD, ScriptDataEscaped);
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ SWITCH_TO_AND_EMIT_CURRENT_CHARACTER(ScriptDataEscaped);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(ScriptDataEscapedLessThanSign)
+ {
+ ON('/')
+ {
+ m_temporary_buffer.clear();
+ SWITCH_TO(ScriptDataEscapedEndTagOpen);
+ }
+ ON_ASCII_ALPHA
+ {
+ m_temporary_buffer.clear();
+ EMIT_CHARACTER_AND_RECONSUME_IN('<', ScriptDataDoubleEscapeStart);
+ }
+ ANYTHING_ELSE
+ {
+ EMIT_CHARACTER_AND_RECONSUME_IN('<', ScriptDataEscaped);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(ScriptDataEscapedEndTagOpen)
+ {
+ ON_ASCII_ALPHA
+ {
+ create_new_token(HTMLToken::Type::EndTag);
+ RECONSUME_IN(ScriptDataEscapedEndTagName);
+ }
+ ANYTHING_ELSE
+ {
+ m_queued_tokens.enqueue(HTMLToken::make_character('<'));
+ m_queued_tokens.enqueue(HTMLToken::make_character('/'));
+ RECONSUME_IN(ScriptDataEscaped);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(ScriptDataEscapedEndTagName)
+ {
+ ON_WHITESPACE
+ {
+ if (current_end_tag_token_is_appropriate())
+ SWITCH_TO(BeforeAttributeName);
+
+ m_queued_tokens.enqueue(HTMLToken::make_character('<'));
+ m_queued_tokens.enqueue(HTMLToken::make_character('/'));
+ for (auto code_point : m_temporary_buffer) {
+ m_queued_tokens.enqueue(HTMLToken::make_character(code_point));
+ }
+ RECONSUME_IN(ScriptDataEscaped);
+ }
+ ON('/')
+ {
+ if (current_end_tag_token_is_appropriate())
+ SWITCH_TO(SelfClosingStartTag);
+
+ m_queued_tokens.enqueue(HTMLToken::make_character('<'));
+ m_queued_tokens.enqueue(HTMLToken::make_character('/'));
+ for (auto code_point : m_temporary_buffer) {
+ m_queued_tokens.enqueue(HTMLToken::make_character(code_point));
+ }
+ RECONSUME_IN(ScriptDataEscaped);
+ }
+ ON('>')
+ {
+ if (current_end_tag_token_is_appropriate())
+ SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
+
+ m_queued_tokens.enqueue(HTMLToken::make_character('<'));
+ m_queued_tokens.enqueue(HTMLToken::make_character('/'));
+ for (auto code_point : m_temporary_buffer) {
+ m_queued_tokens.enqueue(HTMLToken::make_character(code_point));
+ }
+ RECONSUME_IN(ScriptDataEscaped);
+ }
+ ON_ASCII_UPPER_ALPHA
+ {
+ m_current_token.m_tag.tag_name.append(tolower(current_input_character.value()));
+ m_temporary_buffer.append(current_input_character.value());
+ continue;
+ }
+ ON_ASCII_LOWER_ALPHA
+ {
+ m_current_token.m_tag.tag_name.append(current_input_character.value());
+ m_temporary_buffer.append(current_input_character.value());
+ continue;
+ }
+ ANYTHING_ELSE
+ {
+ m_queued_tokens.enqueue(HTMLToken::make_character('<'));
+ m_queued_tokens.enqueue(HTMLToken::make_character('/'));
+ for (auto code_point : m_temporary_buffer) {
+ m_queued_tokens.enqueue(HTMLToken::make_character(code_point));
+ }
+ RECONSUME_IN(ScriptDataEscaped);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(ScriptDataDoubleEscapeStart)
+ {
+ auto temporary_buffer_equal_to_script = [this]() -> bool {
+ if (m_temporary_buffer.size() != 6)
+ return false;
+
+ // FIXME: Is there a better way of doing this?
+ return m_temporary_buffer[0] == 's' && m_temporary_buffer[1] == 'c' && m_temporary_buffer[2] == 'r' && m_temporary_buffer[3] == 'i' && m_temporary_buffer[4] == 'p' && m_temporary_buffer[5] == 't';
+ };
+ ON_WHITESPACE
+ {
+ if (temporary_buffer_equal_to_script())
+ SWITCH_TO_AND_EMIT_CURRENT_CHARACTER(ScriptDataDoubleEscaped);
+ else
+ SWITCH_TO_AND_EMIT_CURRENT_CHARACTER(ScriptDataEscaped);
+ }
+ ON('/')
+ {
+ if (temporary_buffer_equal_to_script())
+ SWITCH_TO_AND_EMIT_CURRENT_CHARACTER(ScriptDataDoubleEscaped);
+ else
+ SWITCH_TO_AND_EMIT_CURRENT_CHARACTER(ScriptDataEscaped);
+ }
+ ON('>')
+ {
+ if (temporary_buffer_equal_to_script())
+ SWITCH_TO_AND_EMIT_CURRENT_CHARACTER(ScriptDataDoubleEscaped);
+ else
+ SWITCH_TO_AND_EMIT_CURRENT_CHARACTER(ScriptDataEscaped);
+ }
+ ON_ASCII_UPPER_ALPHA
+ {
+ m_temporary_buffer.append(tolower(current_input_character.value()));
+ EMIT_CURRENT_CHARACTER;
+ }
+ ON_ASCII_LOWER_ALPHA
+ {
+ m_temporary_buffer.append(current_input_character.value());
+ EMIT_CURRENT_CHARACTER;
+ }
+ ANYTHING_ELSE
+ {
+ RECONSUME_IN(ScriptDataEscaped);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(ScriptDataDoubleEscaped)
+ {
+ ON('-')
+ {
+ SWITCH_TO_AND_EMIT_CHARACTER('-', ScriptDataDoubleEscapedDash);
+ }
+ ON('<')
+ {
+ SWITCH_TO_AND_EMIT_CHARACTER('<', ScriptDataDoubleEscapedLessThanSign);
+ }
+ ON(0)
+ {
+ PARSE_ERROR();
+ EMIT_CHARACTER(0xFFFD);
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ EMIT_CURRENT_CHARACTER;
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(ScriptDataDoubleEscapedDash)
+ {
+ ON('-')
+ {
+ SWITCH_TO_AND_EMIT_CHARACTER('-', ScriptDataDoubleEscapedDashDash);
+ }
+ ON('<')
+ {
+ SWITCH_TO_AND_EMIT_CHARACTER('<', ScriptDataDoubleEscapedLessThanSign);
+ }
+ ON(0)
+ {
+ PARSE_ERROR();
+ SWITCH_TO_AND_EMIT_CHARACTER(0xFFFD, ScriptDataDoubleEscaped);
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ SWITCH_TO_AND_EMIT_CURRENT_CHARACTER(ScriptDataDoubleEscaped);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(ScriptDataDoubleEscapedDashDash)
+ {
+ ON('-')
+ {
+ EMIT_CHARACTER('-');
+ }
+ ON('<')
+ {
+ SWITCH_TO_AND_EMIT_CHARACTER('<', ScriptDataDoubleEscapedLessThanSign);
+ }
+ ON('>')
+ {
+ SWITCH_TO_AND_EMIT_CHARACTER('>', ScriptData);
+ }
+ ON(0)
+ {
+ PARSE_ERROR();
+ SWITCH_TO_AND_EMIT_CHARACTER(0xFFFD, ScriptDataDoubleEscaped);
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ SWITCH_TO_AND_EMIT_CURRENT_CHARACTER(ScriptDataDoubleEscaped);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(ScriptDataDoubleEscapedLessThanSign)
+ {
+ ON('/')
+ {
+ m_temporary_buffer.clear();
+ SWITCH_TO_AND_EMIT_CHARACTER('/', ScriptDataDoubleEscapeEnd);
+ }
+ ANYTHING_ELSE
+ {
+ RECONSUME_IN(ScriptDataDoubleEscaped);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(ScriptDataDoubleEscapeEnd)
+ {
+ auto temporary_buffer_equal_to_script = [this]() -> bool {
+ if (m_temporary_buffer.size() != 6)
+ return false;
+
+ // FIXME: Is there a better way of doing this?
+ return m_temporary_buffer[0] == 's' && m_temporary_buffer[1] == 'c' && m_temporary_buffer[2] == 'r' && m_temporary_buffer[3] == 'i' && m_temporary_buffer[4] == 'p' && m_temporary_buffer[5] == 't';
+ };
+ ON_WHITESPACE
+ {
+ if (temporary_buffer_equal_to_script())
+ SWITCH_TO_AND_EMIT_CURRENT_CHARACTER(ScriptDataEscaped);
+ else
+ SWITCH_TO_AND_EMIT_CURRENT_CHARACTER(ScriptDataDoubleEscaped);
+ }
+ ON('/')
+ {
+ if (temporary_buffer_equal_to_script())
+ SWITCH_TO_AND_EMIT_CURRENT_CHARACTER(ScriptDataEscaped);
+ else
+ SWITCH_TO_AND_EMIT_CURRENT_CHARACTER(ScriptDataDoubleEscaped);
+ }
+ ON('>')
+ {
+ if (temporary_buffer_equal_to_script())
+ SWITCH_TO_AND_EMIT_CURRENT_CHARACTER(ScriptDataEscaped);
+ else
+ SWITCH_TO_AND_EMIT_CURRENT_CHARACTER(ScriptDataDoubleEscaped);
+ }
+ ON_ASCII_UPPER_ALPHA
+ {
+ m_temporary_buffer.append(tolower(current_input_character.value()));
+ EMIT_CURRENT_CHARACTER;
+ }
+ ON_ASCII_LOWER_ALPHA
+ {
+ m_temporary_buffer.append(current_input_character.value());
+ EMIT_CURRENT_CHARACTER;
+ }
+ ANYTHING_ELSE
+ {
+ RECONSUME_IN(ScriptDataDoubleEscaped);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(ScriptDataEscapedDash)
+ {
+ ON('-')
+ {
+ SWITCH_TO_AND_EMIT_CHARACTER('-', ScriptDataEscapedDashDash);
+ }
+ ON('<')
+ {
+ SWITCH_TO(ScriptDataEscapedLessThanSign);
+ }
+ ON(0)
+ {
+ PARSE_ERROR();
+ SWITCH_TO_AND_EMIT_CHARACTER(0xFFFD, ScriptDataEscaped);
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ SWITCH_TO_AND_EMIT_CURRENT_CHARACTER(ScriptDataEscaped);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(ScriptDataEscaped)
+ {
+ ON('-')
+ {
+ SWITCH_TO_AND_EMIT_CHARACTER('-', ScriptDataEscapedDash);
+ }
+ ON('<')
+ {
+ SWITCH_TO(ScriptDataEscapedLessThanSign);
+ }
+ ON(0)
+ {
+ PARSE_ERROR();
+ EMIT_CHARACTER(0xFFFD);
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ EMIT_CURRENT_CHARACTER;
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(ScriptDataEndTagOpen)
+ {
+ ON_ASCII_ALPHA
+ {
+ create_new_token(HTMLToken::Type::EndTag);
+ RECONSUME_IN(ScriptDataEndTagName);
+ }
+ ANYTHING_ELSE
+ {
+ m_queued_tokens.enqueue(HTMLToken::make_character('<'));
+ m_queued_tokens.enqueue(HTMLToken::make_character('/'));
+ RECONSUME_IN(ScriptData);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(ScriptDataEndTagName)
+ {
+ ON_WHITESPACE
+ {
+ if (current_end_tag_token_is_appropriate())
+ SWITCH_TO(BeforeAttributeName);
+ m_queued_tokens.enqueue(HTMLToken::make_character('<'));
+ m_queued_tokens.enqueue(HTMLToken::make_character('/'));
+ for (auto code_point : m_temporary_buffer)
+ m_queued_tokens.enqueue(HTMLToken::make_character(code_point));
+ RECONSUME_IN(ScriptData);
+ }
+ ON('/')
+ {
+ if (current_end_tag_token_is_appropriate())
+ SWITCH_TO(SelfClosingStartTag);
+ m_queued_tokens.enqueue(HTMLToken::make_character('<'));
+ m_queued_tokens.enqueue(HTMLToken::make_character('/'));
+ for (auto code_point : m_temporary_buffer)
+ m_queued_tokens.enqueue(HTMLToken::make_character(code_point));
+ RECONSUME_IN(ScriptData);
+ }
+ ON('>')
+ {
+ if (current_end_tag_token_is_appropriate())
+ SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
+ m_queued_tokens.enqueue(HTMLToken::make_character('<'));
+ m_queued_tokens.enqueue(HTMLToken::make_character('/'));
+ for (auto code_point : m_temporary_buffer)
+ m_queued_tokens.enqueue(HTMLToken::make_character(code_point));
+ RECONSUME_IN(ScriptData);
+ }
+ ON_ASCII_UPPER_ALPHA
+ {
+ m_current_token.m_tag.tag_name.append(tolower(current_input_character.value()));
+ m_temporary_buffer.append(current_input_character.value());
+ continue;
+ }
+ ON_ASCII_LOWER_ALPHA
+ {
+ m_current_token.m_tag.tag_name.append(current_input_character.value());
+ m_temporary_buffer.append(current_input_character.value());
+ continue;
+ }
+ ANYTHING_ELSE
+ {
+ m_queued_tokens.enqueue(HTMLToken::make_character('<'));
+ m_queued_tokens.enqueue(HTMLToken::make_character('/'));
+ for (auto code_point : m_temporary_buffer)
+ m_queued_tokens.enqueue(HTMLToken::make_character(code_point));
+ RECONSUME_IN(ScriptData);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(CDATASection)
+ {
+ ON(']')
+ {
+ SWITCH_TO(CDATASectionBracket);
+ }
+ ON_EOF
+ {
+ PARSE_ERROR();
+ EMIT_EOF;
+ }
+ ANYTHING_ELSE
+ {
+ EMIT_CURRENT_CHARACTER;
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(CDATASectionBracket)
+ {
+ ON(']')
+ {
+ SWITCH_TO(CDATASectionEnd);
+ }
+ ANYTHING_ELSE
+ {
+ EMIT_CHARACTER_AND_RECONSUME_IN(']', CDATASection);
+ }
+ }
+ END_STATE
+
+ BEGIN_STATE(CDATASectionEnd)
+ {
+ ON(']')
+ {
+ EMIT_CHARACTER(']');
+ }
+ ON('>')
+ {
+ SWITCH_TO(Data);
+ }
+ ANYTHING_ELSE
+ {
+ m_queued_tokens.enqueue(HTMLToken::make_character(']'));
+ m_queued_tokens.enqueue(HTMLToken::make_character(']'));
+ RECONSUME_IN(CDATASection);
+ }
+ }
+ END_STATE
+
+ default:
+ TODO();
+ }
+ }
+}
+
+bool HTMLTokenizer::consume_next_if_match(const StringView& string, CaseSensitivity case_sensitivity)
+{
+ for (size_t i = 0; i < string.length(); ++i) {
+ auto code_point = peek_code_point(i);
+ if (!code_point.has_value())
+ return false;
+ // FIXME: This should be more Unicode-aware.
+ if (case_sensitivity == CaseSensitivity::CaseInsensitive) {
+ if (code_point.value() < 0x80) {
+ if (tolower(code_point.value()) != tolower(string[i]))
+ return false;
+ continue;
+ }
+ }
+ if (code_point.value() != (u32)string[i])
+ return false;
+ }
+ for (size_t i = 0; i < string.length(); ++i) {
+ m_prev_utf8_iterator = m_utf8_iterator;
+ ++m_utf8_iterator;
+ }
+ return true;
+}
+
+void HTMLTokenizer::create_new_token(HTMLToken::Type type)
+{
+ m_current_token = {};
+ m_current_token.m_type = type;
+}
+
+HTMLTokenizer::HTMLTokenizer(const StringView& input, const String& encoding)
+{
+ auto* decoder = TextCodec::decoder_for(encoding);
+ ASSERT(decoder);
+ m_decoded_input = decoder->to_utf8(input);
+ m_utf8_view = Utf8View(m_decoded_input);
+ m_utf8_iterator = m_utf8_view.begin();
+}
+
+void HTMLTokenizer::will_switch_to([[maybe_unused]] State new_state)
+{
+#ifdef TOKENIZER_TRACE
+ dbg() << "[" << state_name(m_state) << "] Switch to " << state_name(new_state);
+#endif
+}
+
+void HTMLTokenizer::will_reconsume_in([[maybe_unused]] State new_state)
+{
+#ifdef TOKENIZER_TRACE
+ dbg() << "[" << state_name(m_state) << "] Reconsume in " << state_name(new_state);
+#endif
+}
+
+void HTMLTokenizer::switch_to(Badge<HTMLDocumentParser>, State new_state)
+{
+#ifdef TOKENIZER_TRACE
+ dbg() << "[" << state_name(m_state) << "] Parser switches tokenizer state to " << state_name(new_state);
+#endif
+ m_state = new_state;
+}
+
+void HTMLTokenizer::will_emit(HTMLToken& token)
+{
+ if (token.is_start_tag())
+ m_last_emitted_start_tag = token;
+}
+
+bool HTMLTokenizer::current_end_tag_token_is_appropriate() const
+{
+ ASSERT(m_current_token.is_end_tag());
+ if (!m_last_emitted_start_tag.is_start_tag())
+ return false;
+ return m_current_token.tag_name() == m_last_emitted_start_tag.tag_name();
+}
+
+bool HTMLTokenizer::consumed_as_part_of_an_attribute() const
+{
+ return m_return_state == State::AttributeValueUnquoted || m_return_state == State::AttributeValueSingleQuoted || m_return_state == State::AttributeValueDoubleQuoted;
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/Parser/HTMLTokenizer.h b/Userland/Libraries/LibWeb/HTML/Parser/HTMLTokenizer.h
new file mode 100644
index 0000000000..787bc12b46
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/Parser/HTMLTokenizer.h
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/Queue.h>
+#include <AK/StringView.h>
+#include <AK/Types.h>
+#include <AK/Utf8View.h>
+#include <LibWeb/Forward.h>
+#include <LibWeb/HTML/Parser/HTMLToken.h>
+
+namespace Web::HTML {
+
+#define ENUMERATE_TOKENIZER_STATES \
+ __ENUMERATE_TOKENIZER_STATE(Data) \
+ __ENUMERATE_TOKENIZER_STATE(RCDATA) \
+ __ENUMERATE_TOKENIZER_STATE(RAWTEXT) \
+ __ENUMERATE_TOKENIZER_STATE(ScriptData) \
+ __ENUMERATE_TOKENIZER_STATE(PLAINTEXT) \
+ __ENUMERATE_TOKENIZER_STATE(TagOpen) \
+ __ENUMERATE_TOKENIZER_STATE(EndTagOpen) \
+ __ENUMERATE_TOKENIZER_STATE(TagName) \
+ __ENUMERATE_TOKENIZER_STATE(RCDATALessThanSign) \
+ __ENUMERATE_TOKENIZER_STATE(RCDATAEndTagOpen) \
+ __ENUMERATE_TOKENIZER_STATE(RCDATAEndTagName) \
+ __ENUMERATE_TOKENIZER_STATE(RAWTEXTLessThanSign) \
+ __ENUMERATE_TOKENIZER_STATE(RAWTEXTEndTagOpen) \
+ __ENUMERATE_TOKENIZER_STATE(RAWTEXTEndTagName) \
+ __ENUMERATE_TOKENIZER_STATE(ScriptDataLessThanSign) \
+ __ENUMERATE_TOKENIZER_STATE(ScriptDataEndTagOpen) \
+ __ENUMERATE_TOKENIZER_STATE(ScriptDataEndTagName) \
+ __ENUMERATE_TOKENIZER_STATE(ScriptDataEscapeStart) \
+ __ENUMERATE_TOKENIZER_STATE(ScriptDataEscapeStartDash) \
+ __ENUMERATE_TOKENIZER_STATE(ScriptDataEscaped) \
+ __ENUMERATE_TOKENIZER_STATE(ScriptDataEscapedDash) \
+ __ENUMERATE_TOKENIZER_STATE(ScriptDataEscapedDashDash) \
+ __ENUMERATE_TOKENIZER_STATE(ScriptDataEscapedLessThanSign) \
+ __ENUMERATE_TOKENIZER_STATE(ScriptDataEscapedEndTagOpen) \
+ __ENUMERATE_TOKENIZER_STATE(ScriptDataEscapedEndTagName) \
+ __ENUMERATE_TOKENIZER_STATE(ScriptDataDoubleEscapeStart) \
+ __ENUMERATE_TOKENIZER_STATE(ScriptDataDoubleEscaped) \
+ __ENUMERATE_TOKENIZER_STATE(ScriptDataDoubleEscapedDash) \
+ __ENUMERATE_TOKENIZER_STATE(ScriptDataDoubleEscapedDashDash) \
+ __ENUMERATE_TOKENIZER_STATE(ScriptDataDoubleEscapedLessThanSign) \
+ __ENUMERATE_TOKENIZER_STATE(ScriptDataDoubleEscapeEnd) \
+ __ENUMERATE_TOKENIZER_STATE(BeforeAttributeName) \
+ __ENUMERATE_TOKENIZER_STATE(AttributeName) \
+ __ENUMERATE_TOKENIZER_STATE(AfterAttributeName) \
+ __ENUMERATE_TOKENIZER_STATE(BeforeAttributeValue) \
+ __ENUMERATE_TOKENIZER_STATE(AttributeValueDoubleQuoted) \
+ __ENUMERATE_TOKENIZER_STATE(AttributeValueSingleQuoted) \
+ __ENUMERATE_TOKENIZER_STATE(AttributeValueUnquoted) \
+ __ENUMERATE_TOKENIZER_STATE(AfterAttributeValueQuoted) \
+ __ENUMERATE_TOKENIZER_STATE(SelfClosingStartTag) \
+ __ENUMERATE_TOKENIZER_STATE(BogusComment) \
+ __ENUMERATE_TOKENIZER_STATE(MarkupDeclarationOpen) \
+ __ENUMERATE_TOKENIZER_STATE(CommentStart) \
+ __ENUMERATE_TOKENIZER_STATE(CommentStartDash) \
+ __ENUMERATE_TOKENIZER_STATE(Comment) \
+ __ENUMERATE_TOKENIZER_STATE(CommentLessThanSign) \
+ __ENUMERATE_TOKENIZER_STATE(CommentLessThanSignBang) \
+ __ENUMERATE_TOKENIZER_STATE(CommentLessThanSignBangDash) \
+ __ENUMERATE_TOKENIZER_STATE(CommentLessThanSignBangDashDash) \
+ __ENUMERATE_TOKENIZER_STATE(CommentEndDash) \
+ __ENUMERATE_TOKENIZER_STATE(CommentEnd) \
+ __ENUMERATE_TOKENIZER_STATE(CommentEndBang) \
+ __ENUMERATE_TOKENIZER_STATE(DOCTYPE) \
+ __ENUMERATE_TOKENIZER_STATE(BeforeDOCTYPEName) \
+ __ENUMERATE_TOKENIZER_STATE(DOCTYPEName) \
+ __ENUMERATE_TOKENIZER_STATE(AfterDOCTYPEName) \
+ __ENUMERATE_TOKENIZER_STATE(AfterDOCTYPEPublicKeyword) \
+ __ENUMERATE_TOKENIZER_STATE(BeforeDOCTYPEPublicIdentifier) \
+ __ENUMERATE_TOKENIZER_STATE(DOCTYPEPublicIdentifierDoubleQuoted) \
+ __ENUMERATE_TOKENIZER_STATE(DOCTYPEPublicIdentifierSingleQuoted) \
+ __ENUMERATE_TOKENIZER_STATE(AfterDOCTYPEPublicIdentifier) \
+ __ENUMERATE_TOKENIZER_STATE(BetweenDOCTYPEPublicAndSystemIdentifiers) \
+ __ENUMERATE_TOKENIZER_STATE(AfterDOCTYPESystemKeyword) \
+ __ENUMERATE_TOKENIZER_STATE(BeforeDOCTYPESystemIdentifier) \
+ __ENUMERATE_TOKENIZER_STATE(DOCTYPESystemIdentifierDoubleQuoted) \
+ __ENUMERATE_TOKENIZER_STATE(DOCTYPESystemIdentifierSingleQuoted) \
+ __ENUMERATE_TOKENIZER_STATE(AfterDOCTYPESystemIdentifier) \
+ __ENUMERATE_TOKENIZER_STATE(BogusDOCTYPE) \
+ __ENUMERATE_TOKENIZER_STATE(CDATASection) \
+ __ENUMERATE_TOKENIZER_STATE(CDATASectionBracket) \
+ __ENUMERATE_TOKENIZER_STATE(CDATASectionEnd) \
+ __ENUMERATE_TOKENIZER_STATE(CharacterReference) \
+ __ENUMERATE_TOKENIZER_STATE(NamedCharacterReference) \
+ __ENUMERATE_TOKENIZER_STATE(AmbiguousAmpersand) \
+ __ENUMERATE_TOKENIZER_STATE(NumericCharacterReference) \
+ __ENUMERATE_TOKENIZER_STATE(HexadecimalCharacterReferenceStart) \
+ __ENUMERATE_TOKENIZER_STATE(DecimalCharacterReferenceStart) \
+ __ENUMERATE_TOKENIZER_STATE(HexadecimalCharacterReference) \
+ __ENUMERATE_TOKENIZER_STATE(DecimalCharacterReference) \
+ __ENUMERATE_TOKENIZER_STATE(NumericCharacterReferenceEnd)
+
+class HTMLTokenizer {
+public:
+ explicit HTMLTokenizer(const StringView& input, const String& encoding);
+
+ enum class State {
+#define __ENUMERATE_TOKENIZER_STATE(state) state,
+ ENUMERATE_TOKENIZER_STATES
+#undef __ENUMERATE_TOKENIZER_STATE
+ };
+
+ Optional<HTMLToken> next_token();
+
+ void switch_to(Badge<HTMLDocumentParser>, State new_state);
+
+ void set_blocked(bool b) { m_blocked = b; }
+ bool is_blocked() const { return m_blocked; }
+
+ String source() const { return m_decoded_input; }
+
+private:
+ Optional<u32> next_code_point();
+ Optional<u32> peek_code_point(size_t offset) const;
+ bool consume_next_if_match(const StringView&, CaseSensitivity = CaseSensitivity::CaseSensitive);
+ void create_new_token(HTMLToken::Type);
+ bool current_end_tag_token_is_appropriate() const;
+
+ static const char* state_name(State state)
+ {
+ switch (state) {
+#define __ENUMERATE_TOKENIZER_STATE(state) \
+ case State::state: \
+ return #state;
+ ENUMERATE_TOKENIZER_STATES
+#undef __ENUMERATE_TOKENIZER_STATE
+ };
+ ASSERT_NOT_REACHED();
+ }
+
+ void will_emit(HTMLToken&);
+ void will_switch_to(State);
+ void will_reconsume_in(State);
+
+ bool consumed_as_part_of_an_attribute() const;
+
+ State m_state { State::Data };
+ State m_return_state { State::Data };
+
+ Vector<u32> m_temporary_buffer;
+
+ String m_decoded_input;
+
+ StringView m_input;
+
+ Utf8View m_utf8_view;
+ AK::Utf8CodepointIterator m_utf8_iterator;
+ AK::Utf8CodepointIterator m_prev_utf8_iterator;
+
+ HTMLToken m_current_token;
+
+ HTMLToken m_last_emitted_start_tag;
+
+ bool m_has_emitted_eof { false };
+
+ Queue<HTMLToken> m_queued_tokens;
+
+ u32 m_character_reference_code { 0 };
+
+ bool m_blocked { false };
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/Parser/ListOfActiveFormattingElements.cpp b/Userland/Libraries/LibWeb/HTML/Parser/ListOfActiveFormattingElements.cpp
new file mode 100644
index 0000000000..bad6140632
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/Parser/ListOfActiveFormattingElements.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/DOM/Element.h>
+#include <LibWeb/HTML/Parser/ListOfActiveFormattingElements.h>
+
+namespace Web::HTML {
+
+ListOfActiveFormattingElements::~ListOfActiveFormattingElements()
+{
+}
+
+void ListOfActiveFormattingElements::add(DOM::Element& element)
+{
+ // FIXME: Implement the Noah's Ark clause https://html.spec.whatwg.org/multipage/parsing.html#push-onto-the-list-of-active-formatting-elements
+ m_entries.append({ element });
+}
+
+void ListOfActiveFormattingElements::add_marker()
+{
+ m_entries.append({ nullptr });
+}
+
+bool ListOfActiveFormattingElements::contains(const DOM::Element& element) const
+{
+ for (auto& entry : m_entries) {
+ if (entry.element == &element)
+ return true;
+ }
+ return false;
+}
+
+DOM::Element* ListOfActiveFormattingElements::last_element_with_tag_name_before_marker(const FlyString& tag_name)
+{
+ for (ssize_t i = m_entries.size() - 1; i >= 0; --i) {
+ auto& entry = m_entries[i];
+ if (entry.is_marker())
+ return nullptr;
+ if (entry.element->local_name() == tag_name)
+ return entry.element;
+ }
+ return nullptr;
+}
+
+void ListOfActiveFormattingElements::remove(DOM::Element& element)
+{
+ m_entries.remove_first_matching([&](auto& entry) {
+ return entry.element == &element;
+ });
+}
+
+void ListOfActiveFormattingElements::clear_up_to_the_last_marker()
+{
+ while (!m_entries.is_empty()) {
+ auto entry = m_entries.take_last();
+ if (entry.is_marker())
+ break;
+ }
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/Parser/ListOfActiveFormattingElements.h b/Userland/Libraries/LibWeb/HTML/Parser/ListOfActiveFormattingElements.h
new file mode 100644
index 0000000000..65064be7e5
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/Parser/ListOfActiveFormattingElements.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/NonnullRefPtrVector.h>
+#include <LibWeb/DOM/Element.h>
+#include <LibWeb/Forward.h>
+
+namespace Web::HTML {
+
+class ListOfActiveFormattingElements {
+public:
+ ListOfActiveFormattingElements() { }
+ ~ListOfActiveFormattingElements();
+
+ struct Entry {
+ bool is_marker() const { return !element; }
+
+ RefPtr<DOM::Element> element;
+ };
+
+ bool is_empty() const { return m_entries.is_empty(); }
+ bool contains(const DOM::Element&) const;
+
+ void add(DOM::Element& element);
+ void add_marker();
+
+ void remove(DOM::Element&);
+
+ const Vector<Entry>& entries() const { return m_entries; }
+ Vector<Entry>& entries() { return m_entries; }
+
+ DOM::Element* last_element_with_tag_name_before_marker(const FlyString& tag_name);
+
+ void clear_up_to_the_last_marker();
+
+private:
+ Vector<Entry> m_entries;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/Parser/StackOfOpenElements.cpp b/Userland/Libraries/LibWeb/HTML/Parser/StackOfOpenElements.cpp
new file mode 100644
index 0000000000..2406711bff
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/Parser/StackOfOpenElements.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/DOM/Element.h>
+#include <LibWeb/HTML/Parser/HTMLDocumentParser.h>
+#include <LibWeb/HTML/Parser/StackOfOpenElements.h>
+
+namespace Web::HTML {
+
+static Vector<FlyString> s_base_list { "applet", "caption", "html", "table", "td", "th", "marquee", "object", "template" };
+
+StackOfOpenElements::~StackOfOpenElements()
+{
+}
+
+bool StackOfOpenElements::has_in_scope_impl(const FlyString& tag_name, const Vector<FlyString>& list) const
+{
+ for (ssize_t i = m_elements.size() - 1; i >= 0; --i) {
+ auto& node = m_elements.at(i);
+ if (node.local_name() == tag_name)
+ return true;
+ if (list.contains_slow(node.local_name()))
+ return false;
+ }
+ ASSERT_NOT_REACHED();
+}
+
+bool StackOfOpenElements::has_in_scope(const FlyString& tag_name) const
+{
+ return has_in_scope_impl(tag_name, s_base_list);
+}
+
+bool StackOfOpenElements::has_in_scope_impl(const DOM::Element& target_node, const Vector<FlyString>& list) const
+{
+ for (ssize_t i = m_elements.size() - 1; i >= 0; --i) {
+ auto& node = m_elements.at(i);
+ if (&node == &target_node)
+ return true;
+ if (list.contains_slow(node.local_name()))
+ return false;
+ }
+ ASSERT_NOT_REACHED();
+}
+
+bool StackOfOpenElements::has_in_scope(const DOM::Element& target_node) const
+{
+ return has_in_scope_impl(target_node, s_base_list);
+}
+
+bool StackOfOpenElements::has_in_button_scope(const FlyString& tag_name) const
+{
+ auto list = s_base_list;
+ list.append("button");
+ return has_in_scope_impl(tag_name, list);
+}
+
+bool StackOfOpenElements::has_in_table_scope(const FlyString& tag_name) const
+{
+ return has_in_scope_impl(tag_name, { "html", "table", "template" });
+}
+
+bool StackOfOpenElements::has_in_list_item_scope(const FlyString& tag_name) const
+{
+ auto list = s_base_list;
+ list.append("ol");
+ list.append("ul");
+ return has_in_scope_impl(tag_name, list);
+}
+
+bool StackOfOpenElements::has_in_select_scope(const FlyString& tag_name) const
+{
+ return has_in_scope_impl(tag_name, { "option", "optgroup" });
+}
+
+bool StackOfOpenElements::contains(const DOM::Element& element) const
+{
+ for (auto& element_on_stack : m_elements) {
+ if (&element == &element_on_stack)
+ return true;
+ }
+ return false;
+}
+
+bool StackOfOpenElements::contains(const FlyString& tag_name) const
+{
+ for (auto& element_on_stack : m_elements) {
+ if (element_on_stack.local_name() == tag_name)
+ return true;
+ }
+ return false;
+}
+
+void StackOfOpenElements::pop_until_an_element_with_tag_name_has_been_popped(const FlyString& tag_name)
+{
+ while (m_elements.last().local_name() != tag_name)
+ pop();
+ pop();
+}
+
+DOM::Element* StackOfOpenElements::topmost_special_node_below(const DOM::Element& formatting_element)
+{
+ DOM::Element* found_element = nullptr;
+ for (ssize_t i = m_elements.size() - 1; i >= 0; --i) {
+ auto& element = m_elements[i];
+ if (&element == &formatting_element)
+ break;
+ if (HTMLDocumentParser::is_special_tag(element.local_name(), element.namespace_()))
+ found_element = &element;
+ }
+ return found_element;
+}
+
+StackOfOpenElements::LastElementResult StackOfOpenElements::last_element_with_tag_name(const FlyString& tag_name)
+{
+ for (ssize_t i = m_elements.size() - 1; i >= 0; --i) {
+ auto& element = m_elements[i];
+ if (element.local_name() == tag_name)
+ return { &element, i };
+ }
+ return { nullptr, -1 };
+}
+
+DOM::Element* StackOfOpenElements::element_before(const DOM::Element& target)
+{
+ bool found_target = false;
+ for (ssize_t i = m_elements.size() - 1; i >= 0; --i) {
+ auto& element = m_elements[i];
+ if (&element == &target) {
+ found_target = true;
+ } else if (found_target)
+ return &element;
+ }
+ return nullptr;
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/Parser/StackOfOpenElements.h b/Userland/Libraries/LibWeb/HTML/Parser/StackOfOpenElements.h
new file mode 100644
index 0000000000..2a62bbf53e
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/Parser/StackOfOpenElements.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/NonnullRefPtrVector.h>
+#include <LibWeb/DOM/Element.h>
+#include <LibWeb/Forward.h>
+
+namespace Web::HTML {
+
+class StackOfOpenElements {
+public:
+ StackOfOpenElements() { }
+ ~StackOfOpenElements();
+
+ DOM::Element& first() { return m_elements.first(); }
+ DOM::Element& last() { return m_elements.last(); }
+
+ bool is_empty() const { return m_elements.is_empty(); }
+ void push(NonnullRefPtr<DOM::Element> element) { m_elements.append(move(element)); }
+ NonnullRefPtr<DOM::Element> pop() { return m_elements.take_last(); }
+
+ const DOM::Element& current_node() const { return m_elements.last(); }
+ DOM::Element& current_node() { return m_elements.last(); }
+
+ bool has_in_scope(const FlyString& tag_name) const;
+ bool has_in_button_scope(const FlyString& tag_name) const;
+ bool has_in_table_scope(const FlyString& tag_name) const;
+ bool has_in_list_item_scope(const FlyString& tag_name) const;
+ bool has_in_select_scope(const FlyString& tag_name) const;
+
+ bool has_in_scope(const DOM::Element&) const;
+
+ bool contains(const DOM::Element&) const;
+ bool contains(const FlyString& tag_name) const;
+
+ const NonnullRefPtrVector<DOM::Element>& elements() const { return m_elements; }
+ NonnullRefPtrVector<DOM::Element>& elements() { return m_elements; }
+
+ void pop_until_an_element_with_tag_name_has_been_popped(const FlyString&);
+
+ DOM::Element* topmost_special_node_below(const DOM::Element&);
+
+ struct LastElementResult {
+ DOM::Element* element;
+ ssize_t index;
+ };
+ LastElementResult last_element_with_tag_name(const FlyString&);
+ DOM::Element* element_before(const DOM::Element&);
+
+private:
+ bool has_in_scope_impl(const FlyString& tag_name, const Vector<FlyString>&) const;
+ bool has_in_scope_impl(const DOM::Element& target_node, const Vector<FlyString>&) const;
+
+ NonnullRefPtrVector<DOM::Element> m_elements;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/SubmitEvent.h b/Userland/Libraries/LibWeb/HTML/SubmitEvent.h
new file mode 100644
index 0000000000..06c4b6fb0b
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/SubmitEvent.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020, the SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/DOM/Event.h>
+
+namespace Web::HTML {
+
+class SubmitEvent final : public DOM::Event {
+public:
+ using WrapperType = Bindings::SubmitEventWrapper;
+
+ static NonnullRefPtr<SubmitEvent> create(const FlyString& event_name, RefPtr<HTMLElement> submitter)
+ {
+ return adopt(*new SubmitEvent(event_name, submitter));
+ }
+
+ const RefPtr<HTMLElement> submitter() const { return m_submitter; }
+
+private:
+ SubmitEvent(const FlyString& event_name, RefPtr<HTMLElement> submitter)
+ : DOM::Event(event_name)
+ , m_submitter(submitter)
+ {
+ }
+
+ RefPtr<HTMLElement> m_submitter;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/SubmitEvent.idl b/Userland/Libraries/LibWeb/HTML/SubmitEvent.idl
new file mode 100644
index 0000000000..c816ae663f
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/SubmitEvent.idl
@@ -0,0 +1,5 @@
+interface SubmitEvent : Event {
+
+ readonly attribute HTMLElement? submitter;
+
+};
diff --git a/Userland/Libraries/LibWeb/HTML/TagNames.cpp b/Userland/Libraries/LibWeb/HTML/TagNames.cpp
new file mode 100644
index 0000000000..1ab76b7376
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/TagNames.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/TagNames.h>
+
+namespace Web::HTML::TagNames {
+
+#define __ENUMERATE_HTML_TAG(name) FlyString name;
+ENUMERATE_HTML_TAGS
+#undef __ENUMERATE_HTML_TAG
+
+[[gnu::constructor]] static void initialize()
+{
+ static bool s_initialized = false;
+ if (s_initialized)
+ return;
+
+#define __ENUMERATE_HTML_TAG(name) \
+ name = #name;
+ ENUMERATE_HTML_TAGS
+#undef __ENUMERATE_HTML_TAG
+
+ template_ = "template";
+
+ s_initialized = true;
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HTML/TagNames.h b/Userland/Libraries/LibWeb/HTML/TagNames.h
new file mode 100644
index 0000000000..9d7f924e2a
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HTML/TagNames.h
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/FlyString.h>
+
+namespace Web::HTML::TagNames {
+
+#define ENUMERATE_HTML_TAGS \
+ __ENUMERATE_HTML_TAG(a) \
+ __ENUMERATE_HTML_TAG(abbr) \
+ __ENUMERATE_HTML_TAG(acronym) \
+ __ENUMERATE_HTML_TAG(address) \
+ __ENUMERATE_HTML_TAG(applet) \
+ __ENUMERATE_HTML_TAG(area) \
+ __ENUMERATE_HTML_TAG(article) \
+ __ENUMERATE_HTML_TAG(aside) \
+ __ENUMERATE_HTML_TAG(audio) \
+ __ENUMERATE_HTML_TAG(b) \
+ __ENUMERATE_HTML_TAG(base) \
+ __ENUMERATE_HTML_TAG(basefont) \
+ __ENUMERATE_HTML_TAG(bdi) \
+ __ENUMERATE_HTML_TAG(bdo) \
+ __ENUMERATE_HTML_TAG(bgsound) \
+ __ENUMERATE_HTML_TAG(big) \
+ __ENUMERATE_HTML_TAG(blink) \
+ __ENUMERATE_HTML_TAG(blockquote) \
+ __ENUMERATE_HTML_TAG(body) \
+ __ENUMERATE_HTML_TAG(br) \
+ __ENUMERATE_HTML_TAG(button) \
+ __ENUMERATE_HTML_TAG(canvas) \
+ __ENUMERATE_HTML_TAG(caption) \
+ __ENUMERATE_HTML_TAG(center) \
+ __ENUMERATE_HTML_TAG(cite) \
+ __ENUMERATE_HTML_TAG(code) \
+ __ENUMERATE_HTML_TAG(col) \
+ __ENUMERATE_HTML_TAG(colgroup) \
+ __ENUMERATE_HTML_TAG(data) \
+ __ENUMERATE_HTML_TAG(datalist) \
+ __ENUMERATE_HTML_TAG(dd) \
+ __ENUMERATE_HTML_TAG(del) \
+ __ENUMERATE_HTML_TAG(details) \
+ __ENUMERATE_HTML_TAG(dfn) \
+ __ENUMERATE_HTML_TAG(dialog) \
+ __ENUMERATE_HTML_TAG(dir) \
+ __ENUMERATE_HTML_TAG(div) \
+ __ENUMERATE_HTML_TAG(dl) \
+ __ENUMERATE_HTML_TAG(dt) \
+ __ENUMERATE_HTML_TAG(em) \
+ __ENUMERATE_HTML_TAG(embed) \
+ __ENUMERATE_HTML_TAG(fieldset) \
+ __ENUMERATE_HTML_TAG(figcaption) \
+ __ENUMERATE_HTML_TAG(figure) \
+ __ENUMERATE_HTML_TAG(font) \
+ __ENUMERATE_HTML_TAG(footer) \
+ __ENUMERATE_HTML_TAG(form) \
+ __ENUMERATE_HTML_TAG(frame) \
+ __ENUMERATE_HTML_TAG(frameset) \
+ __ENUMERATE_HTML_TAG(h1) \
+ __ENUMERATE_HTML_TAG(h2) \
+ __ENUMERATE_HTML_TAG(h3) \
+ __ENUMERATE_HTML_TAG(h4) \
+ __ENUMERATE_HTML_TAG(h5) \
+ __ENUMERATE_HTML_TAG(h6) \
+ __ENUMERATE_HTML_TAG(head) \
+ __ENUMERATE_HTML_TAG(header) \
+ __ENUMERATE_HTML_TAG(hgroup) \
+ __ENUMERATE_HTML_TAG(hr) \
+ __ENUMERATE_HTML_TAG(html) \
+ __ENUMERATE_HTML_TAG(i) \
+ __ENUMERATE_HTML_TAG(iframe) \
+ __ENUMERATE_HTML_TAG(image) \
+ __ENUMERATE_HTML_TAG(img) \
+ __ENUMERATE_HTML_TAG(input) \
+ __ENUMERATE_HTML_TAG(ins) \
+ __ENUMERATE_HTML_TAG(kbd) \
+ __ENUMERATE_HTML_TAG(keygen) \
+ __ENUMERATE_HTML_TAG(label) \
+ __ENUMERATE_HTML_TAG(legend) \
+ __ENUMERATE_HTML_TAG(li) \
+ __ENUMERATE_HTML_TAG(link) \
+ __ENUMERATE_HTML_TAG(listing) \
+ __ENUMERATE_HTML_TAG(main) \
+ __ENUMERATE_HTML_TAG(map) \
+ __ENUMERATE_HTML_TAG(mark) \
+ __ENUMERATE_HTML_TAG(marquee) \
+ __ENUMERATE_HTML_TAG(math) \
+ __ENUMERATE_HTML_TAG(menu) \
+ __ENUMERATE_HTML_TAG(meta) \
+ __ENUMERATE_HTML_TAG(meter) \
+ __ENUMERATE_HTML_TAG(nav) \
+ __ENUMERATE_HTML_TAG(nobr) \
+ __ENUMERATE_HTML_TAG(noembed) \
+ __ENUMERATE_HTML_TAG(noframes) \
+ __ENUMERATE_HTML_TAG(noscript) \
+ __ENUMERATE_HTML_TAG(object) \
+ __ENUMERATE_HTML_TAG(ol) \
+ __ENUMERATE_HTML_TAG(optgroup) \
+ __ENUMERATE_HTML_TAG(option) \
+ __ENUMERATE_HTML_TAG(output) \
+ __ENUMERATE_HTML_TAG(p) \
+ __ENUMERATE_HTML_TAG(param) \
+ __ENUMERATE_HTML_TAG(picture) \
+ __ENUMERATE_HTML_TAG(path) \
+ __ENUMERATE_HTML_TAG(plaintext) \
+ __ENUMERATE_HTML_TAG(pre) \
+ __ENUMERATE_HTML_TAG(progress) \
+ __ENUMERATE_HTML_TAG(q) \
+ __ENUMERATE_HTML_TAG(ruby) \
+ __ENUMERATE_HTML_TAG(rb) \
+ __ENUMERATE_HTML_TAG(rp) \
+ __ENUMERATE_HTML_TAG(rt) \
+ __ENUMERATE_HTML_TAG(rtc) \
+ __ENUMERATE_HTML_TAG(s) \
+ __ENUMERATE_HTML_TAG(samp) \
+ __ENUMERATE_HTML_TAG(script) \
+ __ENUMERATE_HTML_TAG(section) \
+ __ENUMERATE_HTML_TAG(select) \
+ __ENUMERATE_HTML_TAG(slot) \
+ __ENUMERATE_HTML_TAG(small) \
+ __ENUMERATE_HTML_TAG(source) \
+ __ENUMERATE_HTML_TAG(span) \
+ __ENUMERATE_HTML_TAG(strike) \
+ __ENUMERATE_HTML_TAG(strong) \
+ __ENUMERATE_HTML_TAG(style) \
+ __ENUMERATE_HTML_TAG(sub) \
+ __ENUMERATE_HTML_TAG(sup) \
+ __ENUMERATE_HTML_TAG(summary) \
+ __ENUMERATE_HTML_TAG(svg) \
+ __ENUMERATE_HTML_TAG(table) \
+ __ENUMERATE_HTML_TAG(tbody) \
+ __ENUMERATE_HTML_TAG(td) \
+ __ENUMERATE_HTML_TAG(template_) \
+ __ENUMERATE_HTML_TAG(textarea) \
+ __ENUMERATE_HTML_TAG(tfoot) \
+ __ENUMERATE_HTML_TAG(th) \
+ __ENUMERATE_HTML_TAG(thead) \
+ __ENUMERATE_HTML_TAG(time) \
+ __ENUMERATE_HTML_TAG(title) \
+ __ENUMERATE_HTML_TAG(tr) \
+ __ENUMERATE_HTML_TAG(track) \
+ __ENUMERATE_HTML_TAG(tt) \
+ __ENUMERATE_HTML_TAG(u) \
+ __ENUMERATE_HTML_TAG(ul) \
+ __ENUMERATE_HTML_TAG(var) \
+ __ENUMERATE_HTML_TAG(video) \
+ __ENUMERATE_HTML_TAG(wbr) \
+ __ENUMERATE_HTML_TAG(xmp)
+
+#define __ENUMERATE_HTML_TAG(name) extern FlyString name;
+ENUMERATE_HTML_TAGS
+#undef __ENUMERATE_HTML_TAG
+
+}
diff --git a/Userland/Libraries/LibWeb/HighResolutionTime/Performance.cpp b/Userland/Libraries/LibWeb/HighResolutionTime/Performance.cpp
new file mode 100644
index 0000000000..cdefa63eff
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HighResolutionTime/Performance.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/Bindings/PerformanceWrapper.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/DOM/Event.h>
+#include <LibWeb/DOM/EventDispatcher.h>
+#include <LibWeb/DOM/Window.h>
+#include <LibWeb/HighResolutionTime/Performance.h>
+
+namespace Web::HighResolutionTime {
+
+Performance::Performance(DOM::Window& window)
+ : DOM::EventTarget(static_cast<Bindings::ScriptExecutionContext&>(window.document()))
+ , m_window(window)
+{
+ m_timer.start();
+}
+
+Performance::~Performance()
+{
+}
+
+double Performance::time_origin() const
+{
+ auto origin = m_timer.origin_time();
+ return (origin.tv_sec * 1000.0) + (origin.tv_usec / 1000.0);
+}
+
+void Performance::ref_event_target()
+{
+ m_window.ref();
+}
+
+void Performance::unref_event_target()
+{
+ m_window.unref();
+}
+
+bool Performance::dispatch_event(NonnullRefPtr<DOM::Event> event)
+{
+ return DOM::EventDispatcher::dispatch(*this, event);
+}
+
+Bindings::EventTargetWrapper* Performance::create_wrapper(JS::GlobalObject& global_object)
+{
+ return Bindings::wrap(global_object, *this);
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/HighResolutionTime/Performance.h b/Userland/Libraries/LibWeb/HighResolutionTime/Performance.h
new file mode 100644
index 0000000000..d5a9effbf5
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HighResolutionTime/Performance.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/StdLibExtras.h>
+#include <LibCore/ElapsedTimer.h>
+#include <LibWeb/Bindings/Wrappable.h>
+#include <LibWeb/DOM/EventTarget.h>
+
+namespace Web::HighResolutionTime {
+
+class Performance final
+ : public DOM::EventTarget
+ , public Bindings::Wrappable {
+public:
+ using WrapperType = Bindings::PerformanceWrapper;
+ using AllowOwnPtr = AK::TrueType;
+
+ explicit Performance(DOM::Window&);
+ ~Performance();
+
+ double now() const { return m_timer.elapsed(); }
+ double time_origin() const;
+
+ virtual void ref_event_target() override;
+ virtual void unref_event_target() override;
+
+ virtual bool dispatch_event(NonnullRefPtr<DOM::Event>) override;
+ virtual Bindings::EventTargetWrapper* create_wrapper(JS::GlobalObject&) override;
+
+private:
+ DOM::Window& m_window;
+ Core::ElapsedTimer m_timer;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/HighResolutionTime/Performance.idl b/Userland/Libraries/LibWeb/HighResolutionTime/Performance.idl
new file mode 100644
index 0000000000..889760a08a
--- /dev/null
+++ b/Userland/Libraries/LibWeb/HighResolutionTime/Performance.idl
@@ -0,0 +1,4 @@
+interface Performance : EventTarget {
+ double now();
+ readonly attribute double timeOrigin;
+};
diff --git a/Userland/Libraries/LibWeb/InProcessWebView.cpp b/Userland/Libraries/LibWeb/InProcessWebView.cpp
new file mode 100644
index 0000000000..84b8dfec07
--- /dev/null
+++ b/Userland/Libraries/LibWeb/InProcessWebView.cpp
@@ -0,0 +1,435 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/LexicalPath.h>
+#include <AK/URL.h>
+#include <LibCore/File.h>
+#include <LibCore/MimeData.h>
+#include <LibGUI/Action.h>
+#include <LibGUI/Application.h>
+#include <LibGUI/Clipboard.h>
+#include <LibGUI/MessageBox.h>
+#include <LibGUI/Painter.h>
+#include <LibGUI/ScrollBar.h>
+#include <LibGUI/Window.h>
+#include <LibGfx/ImageDecoder.h>
+#include <LibGfx/ShareableBitmap.h>
+#include <LibJS/Runtime/Value.h>
+#include <LibWeb/DOM/Element.h>
+#include <LibWeb/DOM/ElementFactory.h>
+#include <LibWeb/DOM/Text.h>
+#include <LibWeb/Dump.h>
+#include <LibWeb/HTML/HTMLAnchorElement.h>
+#include <LibWeb/HTML/HTMLImageElement.h>
+#include <LibWeb/HTML/Parser/HTMLDocumentParser.h>
+#include <LibWeb/InProcessWebView.h>
+#include <LibWeb/Layout/BreakNode.h>
+#include <LibWeb/Layout/InitialContainingBlockBox.h>
+#include <LibWeb/Layout/Node.h>
+#include <LibWeb/Layout/TextNode.h>
+#include <LibWeb/Loader/ResourceLoader.h>
+#include <LibWeb/Page/EventHandler.h>
+#include <LibWeb/Page/Frame.h>
+#include <LibWeb/Painting/PaintContext.h>
+#include <LibWeb/UIEvents/MouseEvent.h>
+#include <stdio.h>
+
+//#define SELECTION_DEBUG
+
+REGISTER_WIDGET(Web, InProcessWebView)
+
+namespace Web {
+
+InProcessWebView::InProcessWebView()
+ : m_page(make<Page>(*this))
+{
+ set_should_hide_unnecessary_scrollbars(true);
+ set_background_role(ColorRole::Base);
+ set_focus_policy(GUI::FocusPolicy::StrongFocus);
+
+ m_copy_action = GUI::CommonActions::make_copy_action([this](auto&) {
+ GUI::Clipboard::the().set_plain_text(selected_text());
+ });
+
+ m_select_all_action = GUI::CommonActions::make_select_all_action([this](auto&) {
+ select_all();
+ });
+}
+
+InProcessWebView::~InProcessWebView()
+{
+}
+
+void InProcessWebView::select_all()
+{
+ auto* layout_root = this->layout_root();
+ if (!layout_root)
+ return;
+
+ const Layout::Node* first_layout_node = layout_root;
+
+ for (;;) {
+ auto* next = first_layout_node->next_in_pre_order();
+ if (!next)
+ break;
+ first_layout_node = next;
+ if (is<Layout::TextNode>(*first_layout_node))
+ break;
+ }
+
+ const Layout::Node* last_layout_node = first_layout_node;
+
+ for (const Layout::Node* layout_node = first_layout_node; layout_node; layout_node = layout_node->next_in_pre_order()) {
+ if (is<Layout::TextNode>(*layout_node))
+ last_layout_node = layout_node;
+ }
+
+ ASSERT(first_layout_node);
+ ASSERT(last_layout_node);
+
+ int last_layout_node_index_in_node = 0;
+ if (is<Layout::TextNode>(*last_layout_node))
+ last_layout_node_index_in_node = downcast<Layout::TextNode>(*last_layout_node).text_for_rendering().length() - 1;
+
+ layout_root->set_selection({ { first_layout_node, 0 }, { last_layout_node, last_layout_node_index_in_node } });
+ update();
+}
+
+String InProcessWebView::selected_text() const
+{
+ return page().focused_frame().selected_text();
+}
+
+void InProcessWebView::page_did_layout()
+{
+ ASSERT(layout_root());
+ set_content_size(layout_root()->size().to_type<int>());
+}
+
+void InProcessWebView::page_did_change_title(const String& title)
+{
+ if (on_title_change)
+ on_title_change(title);
+}
+
+void InProcessWebView::page_did_set_document_in_main_frame(DOM::Document* document)
+{
+ if (on_set_document)
+ on_set_document(document);
+ layout_and_sync_size();
+ scroll_to_top();
+ update();
+}
+
+void InProcessWebView::page_did_start_loading(const URL& url)
+{
+ if (on_load_start)
+ on_load_start(url);
+}
+
+void InProcessWebView::page_did_finish_loading(const URL& url)
+{
+ if (on_load_finish)
+ on_load_finish(url);
+}
+
+void InProcessWebView::page_did_change_selection()
+{
+ update();
+}
+
+void InProcessWebView::page_did_request_cursor_change(Gfx::StandardCursor cursor)
+{
+ set_override_cursor(cursor);
+}
+
+void InProcessWebView::page_did_request_context_menu(const Gfx::IntPoint& content_position)
+{
+ if (on_context_menu_request)
+ on_context_menu_request(screen_relative_rect().location().translated(to_widget_position(content_position)));
+}
+
+void InProcessWebView::page_did_request_link_context_menu(const Gfx::IntPoint& content_position, const URL& url, [[maybe_unused]] const String& target, [[maybe_unused]] unsigned modifiers)
+{
+ if (on_link_context_menu_request)
+ on_link_context_menu_request(url, screen_relative_rect().location().translated(to_widget_position(content_position)));
+}
+
+void InProcessWebView::page_did_request_image_context_menu(const Gfx::IntPoint& content_position, const URL& url, [[maybe_unused]] const String& target, [[maybe_unused]] unsigned modifiers, const Gfx::Bitmap* bitmap)
+{
+ if (!on_image_context_menu_request)
+ return;
+ Gfx::ShareableBitmap shareable_bitmap;
+ if (bitmap)
+ shareable_bitmap = Gfx::ShareableBitmap(*bitmap);
+ on_image_context_menu_request(url, screen_relative_rect().location().translated(to_widget_position(content_position)), shareable_bitmap);
+}
+
+void InProcessWebView::page_did_click_link(const URL& url, const String& target, unsigned modifiers)
+{
+ if (on_link_click)
+ on_link_click(url, target, modifiers);
+}
+
+void InProcessWebView::page_did_middle_click_link(const URL& url, const String& target, unsigned modifiers)
+{
+ if (on_link_middle_click)
+ on_link_middle_click(url, target, modifiers);
+}
+
+void InProcessWebView::page_did_enter_tooltip_area([[maybe_unused]] const Gfx::IntPoint& content_position, const String& title)
+{
+ GUI::Application::the()->show_tooltip(title, nullptr);
+}
+
+void InProcessWebView::page_did_leave_tooltip_area()
+{
+ GUI::Application::the()->hide_tooltip();
+}
+
+void InProcessWebView::page_did_hover_link(const URL& url)
+{
+ if (on_link_hover)
+ on_link_hover(url);
+}
+
+void InProcessWebView::page_did_unhover_link()
+{
+ if (on_link_hover)
+ on_link_hover({});
+}
+
+void InProcessWebView::page_did_invalidate(const Gfx::IntRect&)
+{
+ update();
+}
+
+void InProcessWebView::page_did_change_favicon(const Gfx::Bitmap& bitmap)
+{
+ if (on_favicon_change)
+ on_favicon_change(bitmap);
+}
+
+void InProcessWebView::layout_and_sync_size()
+{
+ if (!document())
+ return;
+
+ bool had_vertical_scrollbar = vertical_scrollbar().is_visible();
+ bool had_horizontal_scrollbar = horizontal_scrollbar().is_visible();
+
+ page().main_frame().set_size(available_size());
+ set_content_size(layout_root()->size().to_type<int>());
+
+ // NOTE: If layout caused us to gain or lose scrollbars, we have to lay out again
+ // since the scrollbars now take up some of the available space.
+ if (had_vertical_scrollbar != vertical_scrollbar().is_visible() || had_horizontal_scrollbar != horizontal_scrollbar().is_visible()) {
+ page().main_frame().set_size(available_size());
+ set_content_size(layout_root()->size().to_type<int>());
+ }
+
+ page().main_frame().set_viewport_scroll_offset({ horizontal_scrollbar().value(), vertical_scrollbar().value() });
+}
+
+void InProcessWebView::resize_event(GUI::ResizeEvent& event)
+{
+ GUI::ScrollableWidget::resize_event(event);
+ layout_and_sync_size();
+}
+
+void InProcessWebView::paint_event(GUI::PaintEvent& event)
+{
+ GUI::Frame::paint_event(event);
+
+ GUI::Painter painter(*this);
+ painter.add_clip_rect(widget_inner_rect());
+ painter.add_clip_rect(event.rect());
+
+ if (!layout_root()) {
+ painter.fill_rect(event.rect(), palette().color(background_role()));
+ return;
+ }
+
+ painter.fill_rect(event.rect(), document()->background_color(palette()));
+
+ if (auto background_bitmap = document()->background_image()) {
+ painter.draw_tiled_bitmap(event.rect(), *background_bitmap);
+ }
+
+ painter.translate(frame_thickness(), frame_thickness());
+ painter.translate(-horizontal_scrollbar().value(), -vertical_scrollbar().value());
+
+ PaintContext context(painter, palette(), { horizontal_scrollbar().value(), vertical_scrollbar().value() });
+ context.set_should_show_line_box_borders(m_should_show_line_box_borders);
+ context.set_viewport_rect(viewport_rect_in_content_coordinates());
+ context.set_has_focus(is_focused());
+ layout_root()->paint_all_phases(context);
+}
+
+void InProcessWebView::mousemove_event(GUI::MouseEvent& event)
+{
+ page().handle_mousemove(to_content_position(event.position()), event.buttons(), event.modifiers());
+ GUI::ScrollableWidget::mousemove_event(event);
+}
+
+void InProcessWebView::mousedown_event(GUI::MouseEvent& event)
+{
+ page().handle_mousedown(to_content_position(event.position()), event.button(), event.modifiers());
+ GUI::ScrollableWidget::mousedown_event(event);
+}
+
+void InProcessWebView::mouseup_event(GUI::MouseEvent& event)
+{
+ page().handle_mouseup(to_content_position(event.position()), event.button(), event.modifiers());
+ GUI::ScrollableWidget::mouseup_event(event);
+}
+
+void InProcessWebView::keydown_event(GUI::KeyEvent& event)
+{
+ bool page_accepted_event = page().handle_keydown(event.key(), event.modifiers(), event.code_point());
+
+ if (event.modifiers() == 0) {
+ switch (event.key()) {
+ case Key_Home:
+ vertical_scrollbar().set_value(0);
+ break;
+ case Key_End:
+ vertical_scrollbar().set_value(vertical_scrollbar().max());
+ break;
+ case Key_Down:
+ vertical_scrollbar().set_value(vertical_scrollbar().value() + vertical_scrollbar().step());
+ break;
+ case Key_Up:
+ vertical_scrollbar().set_value(vertical_scrollbar().value() - vertical_scrollbar().step());
+ break;
+ case Key_Left:
+ horizontal_scrollbar().set_value(horizontal_scrollbar().value() + horizontal_scrollbar().step());
+ break;
+ case Key_Right:
+ horizontal_scrollbar().set_value(horizontal_scrollbar().value() - horizontal_scrollbar().step());
+ break;
+ case Key_PageDown:
+ vertical_scrollbar().set_value(vertical_scrollbar().value() + frame_inner_rect().height());
+ break;
+ case Key_PageUp:
+ vertical_scrollbar().set_value(vertical_scrollbar().value() - frame_inner_rect().height());
+ break;
+ default:
+ if (!page_accepted_event) {
+ ScrollableWidget::keydown_event(event);
+ return;
+ }
+ break;
+ }
+ }
+
+ event.accept();
+}
+
+URL InProcessWebView::url() const
+{
+ if (!page().main_frame().document())
+ return {};
+ return page().main_frame().document()->url();
+}
+
+void InProcessWebView::reload()
+{
+ load(url());
+}
+
+void InProcessWebView::load_html(const StringView& html, const URL& url)
+{
+ page().main_frame().loader().load_html(html, url);
+}
+
+bool InProcessWebView::load(const URL& url)
+{
+ set_override_cursor(Gfx::StandardCursor::None);
+ return page().main_frame().loader().load(url, FrameLoader::Type::Navigation);
+}
+
+const Layout::InitialContainingBlockBox* InProcessWebView::layout_root() const
+{
+ return document() ? document()->layout_node() : nullptr;
+}
+
+Layout::InitialContainingBlockBox* InProcessWebView::layout_root()
+{
+ if (!document())
+ return nullptr;
+ return const_cast<Layout::InitialContainingBlockBox*>(document()->layout_node());
+}
+
+void InProcessWebView::page_did_request_scroll_into_view(const Gfx::IntRect& rect)
+{
+ scroll_into_view(rect, true, true);
+ set_override_cursor(Gfx::StandardCursor::None);
+}
+
+void InProcessWebView::load_empty_document()
+{
+ page().main_frame().set_document(nullptr);
+}
+
+DOM::Document* InProcessWebView::document()
+{
+ return page().main_frame().document();
+}
+
+const DOM::Document* InProcessWebView::document() const
+{
+ return page().main_frame().document();
+}
+
+void InProcessWebView::set_document(DOM::Document* document)
+{
+ page().main_frame().set_document(document);
+}
+
+void InProcessWebView::did_scroll()
+{
+ page().main_frame().set_viewport_scroll_offset({ horizontal_scrollbar().value(), vertical_scrollbar().value() });
+ page().main_frame().did_scroll({});
+}
+
+void InProcessWebView::drop_event(GUI::DropEvent& event)
+{
+ if (event.mime_data().has_urls()) {
+ if (on_url_drop) {
+ on_url_drop(event.mime_data().urls().first());
+ return;
+ }
+ }
+ ScrollableWidget::drop_event(event);
+}
+
+void InProcessWebView::page_did_request_alert(const String& message)
+{
+ GUI::MessageBox::show(window(), message, "Alert", GUI::MessageBox::Type::Information);
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/InProcessWebView.h b/Userland/Libraries/LibWeb/InProcessWebView.h
new file mode 100644
index 0000000000..084c3a01c4
--- /dev/null
+++ b/Userland/Libraries/LibWeb/InProcessWebView.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/URL.h>
+#include <LibGUI/ScrollableWidget.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/Page/Page.h>
+#include <LibWeb/WebViewHooks.h>
+
+namespace Web {
+
+class InProcessWebView final
+ : public GUI::ScrollableWidget
+ , public WebViewHooks
+ , public PageClient {
+ C_OBJECT(InProcessWebView);
+
+public:
+ virtual ~InProcessWebView() override;
+
+ void load_html(const StringView&, const URL&);
+ void load_empty_document();
+
+ DOM::Document* document();
+ const DOM::Document* document() const;
+
+ void set_document(DOM::Document*);
+
+ const Layout::InitialContainingBlockBox* layout_root() const;
+ Layout::InitialContainingBlockBox* layout_root();
+
+ void reload();
+ bool load(const URL&);
+
+ URL url() const;
+
+ void set_should_show_line_box_borders(bool value) { m_should_show_line_box_borders = value; }
+
+ GUI::Action& select_all_action() { return *m_select_all_action; }
+ GUI::Action& copy_action() { return *m_copy_action; }
+
+ String selected_text() const;
+ void select_all();
+
+private:
+ InProcessWebView();
+
+ Page& page() { return *m_page; }
+ const Page& page() const { return *m_page; }
+
+ virtual void resize_event(GUI::ResizeEvent&) override;
+ virtual void paint_event(GUI::PaintEvent&) override;
+ virtual void mousemove_event(GUI::MouseEvent&) override;
+ virtual void mousedown_event(GUI::MouseEvent&) override;
+ virtual void mouseup_event(GUI::MouseEvent&) override;
+ virtual void keydown_event(GUI::KeyEvent&) override;
+ virtual void drop_event(GUI::DropEvent&) override;
+
+ virtual void did_scroll() override;
+
+ // ^Web::PageClient
+ virtual Gfx::Palette palette() const override { return GUI::ScrollableWidget::palette(); }
+ virtual void page_did_change_title(const String&) override;
+ virtual void page_did_set_document_in_main_frame(DOM::Document*) override;
+ virtual void page_did_start_loading(const URL&) override;
+ virtual void page_did_finish_loading(const URL&) override;
+ virtual void page_did_change_selection() override;
+ virtual void page_did_request_cursor_change(Gfx::StandardCursor) override;
+ virtual void page_did_request_context_menu(const Gfx::IntPoint&) override;
+ virtual void page_did_request_link_context_menu(const Gfx::IntPoint&, const URL&, const String& target, unsigned modifiers) override;
+ virtual void page_did_request_image_context_menu(const Gfx::IntPoint&, const URL&, const String& target, unsigned modifiers, const Gfx::Bitmap*) override;
+ virtual void page_did_click_link(const URL&, const String& target, unsigned modifiers) override;
+ virtual void page_did_middle_click_link(const URL&, const String& target, unsigned modifiers) override;
+ virtual void page_did_enter_tooltip_area(const Gfx::IntPoint&, const String&) override;
+ virtual void page_did_leave_tooltip_area() override;
+ virtual void page_did_hover_link(const URL&) override;
+ virtual void page_did_unhover_link() override;
+ virtual void page_did_invalidate(const Gfx::IntRect&) override;
+ virtual void page_did_change_favicon(const Gfx::Bitmap&) override;
+ virtual void page_did_layout() override;
+ virtual void page_did_request_scroll_into_view(const Gfx::IntRect&) override;
+ virtual void page_did_request_alert(const String&) override;
+
+ void layout_and_sync_size();
+
+ bool m_should_show_line_box_borders { false };
+
+ NonnullOwnPtr<Page> m_page;
+
+ RefPtr<GUI::Action> m_copy_action;
+ RefPtr<GUI::Action> m_select_all_action;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/BlockBox.cpp b/Userland/Libraries/LibWeb/Layout/BlockBox.cpp
new file mode 100644
index 0000000000..aab1430afc
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/BlockBox.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibGUI/Painter.h>
+#include <LibWeb/CSS/StyleResolver.h>
+#include <LibWeb/DOM/Element.h>
+#include <LibWeb/Dump.h>
+#include <LibWeb/Layout/BlockBox.h>
+#include <LibWeb/Layout/InitialContainingBlockBox.h>
+#include <LibWeb/Layout/InlineFormattingContext.h>
+#include <LibWeb/Layout/InlineNode.h>
+#include <LibWeb/Layout/ReplacedBox.h>
+#include <LibWeb/Layout/TextNode.h>
+#include <LibWeb/Layout/WidgetBox.h>
+#include <math.h>
+
+namespace Web::Layout {
+
+BlockBox::BlockBox(DOM::Document& document, DOM::Node* node, NonnullRefPtr<CSS::StyleProperties> style)
+ : Box(document, node, move(style))
+{
+}
+
+BlockBox::BlockBox(DOM::Document& document, DOM::Node* node, CSS::ComputedValues computed_values)
+ : Box(document, node, move(computed_values))
+{
+}
+
+BlockBox::~BlockBox()
+{
+}
+
+void BlockBox::paint(PaintContext& context, PaintPhase phase)
+{
+ if (!is_visible())
+ return;
+
+ Box::paint(context, phase);
+
+ if (!children_are_inline())
+ return;
+
+ for (auto& line_box : m_line_boxes) {
+ for (auto& fragment : line_box.fragments()) {
+ if (context.should_show_line_box_borders())
+ context.painter().draw_rect(enclosing_int_rect(fragment.absolute_rect()), Color::Green);
+ fragment.paint(context, phase);
+ }
+ }
+
+ // FIXME: Merge this loop with the above somehow..
+ if (phase == PaintPhase::FocusOutline) {
+ for (auto& line_box : m_line_boxes) {
+ for (auto& fragment : line_box.fragments()) {
+ auto* node = fragment.layout_node().dom_node();
+ if (!node)
+ continue;
+ auto* parent = node->parent_element();
+ if (!parent)
+ continue;
+ if (parent->is_focused())
+ context.painter().draw_rect(enclosing_int_rect(fragment.absolute_rect()), context.palette().focus_outline());
+ }
+ }
+ }
+}
+
+HitTestResult BlockBox::hit_test(const Gfx::IntPoint& position, HitTestType type) const
+{
+ if (!children_are_inline())
+ return Box::hit_test(position, type);
+
+ HitTestResult last_good_candidate;
+ for (auto& line_box : m_line_boxes) {
+ for (auto& fragment : line_box.fragments()) {
+ if (is<Box>(fragment.layout_node()) && downcast<Box>(fragment.layout_node()).stacking_context())
+ continue;
+ if (enclosing_int_rect(fragment.absolute_rect()).contains(position)) {
+ if (is<BlockBox>(fragment.layout_node()))
+ return downcast<BlockBox>(fragment.layout_node()).hit_test(position, type);
+ return { fragment.layout_node(), fragment.text_index_at(position.x()) };
+ }
+ if (fragment.absolute_rect().top() <= position.y())
+ last_good_candidate = { fragment.layout_node(), fragment.text_index_at(position.x()) };
+ }
+ }
+
+ if (type == HitTestType::TextCursor && last_good_candidate.layout_node)
+ return last_good_candidate;
+ return { absolute_rect().contains(position.x(), position.y()) ? this : nullptr };
+}
+
+void BlockBox::split_into_lines(InlineFormattingContext& context, LayoutMode layout_mode)
+{
+ auto& containing_block = context.containing_block();
+ auto* line_box = &containing_block.ensure_last_line_box();
+
+ context.dimension_box_on_line(*this, layout_mode);
+
+ float available_width = context.available_width_at_line(containing_block.line_boxes().size() - 1);
+
+ if (layout_mode == LayoutMode::AllPossibleLineBreaks && line_box->width() > 0) {
+ line_box = &containing_block.add_line_box();
+ } else if (layout_mode == LayoutMode::Default && line_box->width() > 0 && line_box->width() + border_box_width() > available_width) {
+ line_box = &containing_block.add_line_box();
+ }
+ line_box->add_fragment(*this, 0, 0, border_box_width(), height());
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/BlockBox.h b/Userland/Libraries/LibWeb/Layout/BlockBox.h
new file mode 100644
index 0000000000..0062841454
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/BlockBox.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/Layout/Box.h>
+#include <LibWeb/Layout/LineBox.h>
+
+namespace Web::Layout {
+
+class BlockBox : public Box {
+public:
+ BlockBox(DOM::Document&, DOM::Node*, NonnullRefPtr<CSS::StyleProperties>);
+ BlockBox(DOM::Document&, DOM::Node*, CSS::ComputedValues);
+ virtual ~BlockBox() override;
+
+ virtual void paint(PaintContext&, PaintPhase) override;
+
+ virtual HitTestResult hit_test(const Gfx::IntPoint&, HitTestType) const override;
+
+ BlockBox* previous_sibling() { return downcast<BlockBox>(Node::previous_sibling()); }
+ const BlockBox* previous_sibling() const { return downcast<BlockBox>(Node::previous_sibling()); }
+ BlockBox* next_sibling() { return downcast<BlockBox>(Node::next_sibling()); }
+ const BlockBox* next_sibling() const { return downcast<BlockBox>(Node::next_sibling()); }
+
+ template<typename Callback>
+ void for_each_fragment(Callback);
+ template<typename Callback>
+ void for_each_fragment(Callback) const;
+
+ virtual void split_into_lines(InlineFormattingContext&, LayoutMode) override;
+};
+
+template<typename Callback>
+void BlockBox::for_each_fragment(Callback callback)
+{
+ for (auto& line_box : line_boxes()) {
+ for (auto& fragment : line_box.fragments()) {
+ if (callback(fragment) == IterationDecision::Break)
+ return;
+ }
+ }
+}
+
+template<typename Callback>
+void BlockBox::for_each_fragment(Callback callback) const
+{
+ for (auto& line_box : line_boxes()) {
+ for (auto& fragment : line_box.fragments()) {
+ if (callback(fragment) == IterationDecision::Break)
+ return;
+ }
+ }
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp
new file mode 100644
index 0000000000..f53ed61c3a
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp
@@ -0,0 +1,566 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/CSS/Length.h>
+#include <LibWeb/DOM/Node.h>
+#include <LibWeb/Layout/BlockBox.h>
+#include <LibWeb/Layout/BlockFormattingContext.h>
+#include <LibWeb/Layout/Box.h>
+#include <LibWeb/Layout/InitialContainingBlockBox.h>
+#include <LibWeb/Layout/InlineFormattingContext.h>
+#include <LibWeb/Layout/ListItemBox.h>
+#include <LibWeb/Layout/WidgetBox.h>
+#include <LibWeb/Page/Frame.h>
+
+namespace Web::Layout {
+
+BlockFormattingContext::BlockFormattingContext(Box& context_box, FormattingContext* parent)
+ : FormattingContext(context_box, parent)
+{
+}
+
+BlockFormattingContext::~BlockFormattingContext()
+{
+}
+
+bool BlockFormattingContext::is_initial() const
+{
+ return is<InitialContainingBlockBox>(context_box());
+}
+
+void BlockFormattingContext::run(Box& box, LayoutMode layout_mode)
+{
+ if (is_initial()) {
+ layout_initial_containing_block(layout_mode);
+ return;
+ }
+
+ // FIXME: BFC currently computes the width+height of the target box.
+ // This is necessary to be able to place absolutely positioned descendants.
+ // The same work is also done by the parent BFC for each of its blocks..
+
+ if (layout_mode == LayoutMode::Default)
+ compute_width(box);
+
+ if (box.children_are_inline()) {
+ layout_inline_children(box, layout_mode);
+ } else {
+ layout_block_level_children(box, layout_mode);
+ }
+
+ if (layout_mode == LayoutMode::Default) {
+ compute_height(box);
+
+ box.for_each_child_of_type<Box>([&](auto& child_box) {
+ if (child_box.is_absolutely_positioned()) {
+ layout_absolutely_positioned_element(child_box);
+ }
+ return IterationDecision::Continue;
+ });
+ }
+}
+
+void BlockFormattingContext::compute_width(Box& box)
+{
+ if (box.is_absolutely_positioned()) {
+ compute_width_for_absolutely_positioned_element(box);
+ return;
+ }
+
+ if (is<ReplacedBox>(box)) {
+ // FIXME: This should not be done *by* ReplacedBox
+ auto& replaced = downcast<ReplacedBox>(box);
+ replaced.prepare_for_replaced_layout();
+ compute_width_for_block_level_replaced_element_in_normal_flow(replaced);
+ return;
+ }
+
+ if (box.is_floating()) {
+ compute_width_for_floating_box(box);
+ return;
+ }
+
+ auto& computed_values = box.computed_values();
+ float width_of_containing_block = box.width_of_logical_containing_block();
+
+ auto zero_value = CSS::Length::make_px(0);
+
+ auto margin_left = CSS::Length::make_auto();
+ auto margin_right = CSS::Length::make_auto();
+ const auto padding_left = computed_values.padding().left.resolved_or_zero(box, width_of_containing_block);
+ const auto padding_right = computed_values.padding().right.resolved_or_zero(box, width_of_containing_block);
+
+ auto try_compute_width = [&](const auto& a_width) {
+ CSS::Length width = a_width;
+ margin_left = computed_values.margin().left.resolved_or_zero(box, width_of_containing_block);
+ margin_right = computed_values.margin().right.resolved_or_zero(box, width_of_containing_block);
+
+ float total_px = computed_values.border_left().width + computed_values.border_right().width;
+ for (auto& value : { margin_left, padding_left, width, padding_right, margin_right }) {
+ total_px += value.to_px(box);
+ }
+
+ if (!box.is_inline()) {
+ // 10.3.3 Block-level, non-replaced elements in normal flow
+ // If 'width' is not 'auto' and 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' (plus any of 'margin-left' or 'margin-right' that are not 'auto') is larger than the width of the containing block, then any 'auto' values for 'margin-left' or 'margin-right' are, for the following rules, treated as zero.
+ if (width.is_auto() && total_px > width_of_containing_block) {
+ if (margin_left.is_auto())
+ margin_left = zero_value;
+ if (margin_right.is_auto())
+ margin_right = zero_value;
+ }
+
+ // 10.3.3 cont'd.
+ auto underflow_px = width_of_containing_block - total_px;
+
+ if (width.is_auto()) {
+ if (margin_left.is_auto())
+ margin_left = zero_value;
+ if (margin_right.is_auto())
+ margin_right = zero_value;
+ if (underflow_px >= 0) {
+ width = CSS::Length(underflow_px, CSS::Length::Type::Px);
+ } else {
+ width = zero_value;
+ margin_right = CSS::Length(margin_right.to_px(box) + underflow_px, CSS::Length::Type::Px);
+ }
+ } else {
+ if (!margin_left.is_auto() && !margin_right.is_auto()) {
+ margin_right = CSS::Length(margin_right.to_px(box) + underflow_px, CSS::Length::Type::Px);
+ } else if (!margin_left.is_auto() && margin_right.is_auto()) {
+ margin_right = CSS::Length(underflow_px, CSS::Length::Type::Px);
+ } else if (margin_left.is_auto() && !margin_right.is_auto()) {
+ margin_left = CSS::Length(underflow_px, CSS::Length::Type::Px);
+ } else { // margin_left.is_auto() && margin_right.is_auto()
+ auto half_of_the_underflow = CSS::Length(underflow_px / 2, CSS::Length::Type::Px);
+ margin_left = half_of_the_underflow;
+ margin_right = half_of_the_underflow;
+ }
+ }
+ } else if (box.is_inline_block()) {
+
+ // 10.3.9 'Inline-block', non-replaced elements in normal flow
+
+ // A computed value of 'auto' for 'margin-left' or 'margin-right' becomes a used value of '0'.
+ if (margin_left.is_auto())
+ margin_left = zero_value;
+ if (margin_right.is_auto())
+ margin_right = zero_value;
+
+ // If 'width' is 'auto', the used value is the shrink-to-fit width as for floating elements.
+ if (width.is_auto()) {
+
+ // Find the available width: in this case, this is the width of the containing
+ // block minus the used values of 'margin-left', 'border-left-width', 'padding-left',
+ // 'padding-right', 'border-right-width', 'margin-right', and the widths of any relevant scroll bars.
+ float available_width = width_of_containing_block
+ - margin_left.to_px(box) - computed_values.border_left().width - padding_left.to_px(box)
+ - padding_right.to_px(box) - computed_values.border_right().width - margin_right.to_px(box);
+
+ auto result = calculate_shrink_to_fit_widths(box);
+
+ // Then the shrink-to-fit width is: min(max(preferred minimum width, available width), preferred width).
+ width = CSS::Length(min(max(result.preferred_minimum_width, available_width), result.preferred_width), CSS::Length::Type::Px);
+ }
+ }
+
+ return width;
+ };
+
+ auto specified_width = computed_values.width().resolved_or_auto(box, width_of_containing_block);
+
+ // 1. The tentative used width is calculated (without 'min-width' and 'max-width')
+ auto used_width = try_compute_width(specified_width);
+
+ // 2. The tentative used width is greater than 'max-width', the rules above are applied again,
+ // but this time using the computed value of 'max-width' as the computed value for 'width'.
+ auto specified_max_width = computed_values.max_width().resolved_or_auto(box, width_of_containing_block);
+ if (!specified_max_width.is_auto()) {
+ if (used_width.to_px(box) > specified_max_width.to_px(box)) {
+ used_width = try_compute_width(specified_max_width);
+ }
+ }
+
+ // 3. If the resulting width is smaller than 'min-width', the rules above are applied again,
+ // but this time using the value of 'min-width' as the computed value for 'width'.
+ auto specified_min_width = computed_values.min_width().resolved_or_auto(box, width_of_containing_block);
+ if (!specified_min_width.is_auto()) {
+ if (used_width.to_px(box) < specified_min_width.to_px(box)) {
+ used_width = try_compute_width(specified_min_width);
+ }
+ }
+
+ box.set_width(used_width.to_px(box));
+ box.box_model().margin.left = margin_left.to_px(box);
+ box.box_model().margin.right = margin_right.to_px(box);
+ box.box_model().border.left = computed_values.border_left().width;
+ box.box_model().border.right = computed_values.border_right().width;
+ box.box_model().padding.left = padding_left.to_px(box);
+ box.box_model().padding.right = padding_right.to_px(box);
+}
+
+void BlockFormattingContext::compute_width_for_floating_box(Box& box)
+{
+ // 10.3.5 Floating, non-replaced elements
+ auto& computed_values = box.computed_values();
+ float width_of_containing_block = box.width_of_logical_containing_block();
+ auto zero_value = CSS::Length::make_px(0);
+
+ auto margin_left = CSS::Length::make_auto();
+ auto margin_right = CSS::Length::make_auto();
+ const auto padding_left = computed_values.padding().left.resolved_or_zero(box, width_of_containing_block);
+ const auto padding_right = computed_values.padding().right.resolved_or_zero(box, width_of_containing_block);
+
+ // If 'margin-left', or 'margin-right' are computed as 'auto', their used value is '0'.
+ if (margin_left.is_auto())
+ margin_left = zero_value;
+ if (margin_right.is_auto())
+ margin_right = zero_value;
+
+ auto width = computed_values.width().resolved_or_auto(box, width_of_containing_block);
+
+ // If 'width' is computed as 'auto', the used value is the "shrink-to-fit" width.
+ if (width.is_auto()) {
+
+ // Find the available width: in this case, this is the width of the containing
+ // block minus the used values of 'margin-left', 'border-left-width', 'padding-left',
+ // 'padding-right', 'border-right-width', 'margin-right', and the widths of any relevant scroll bars.
+ float available_width = width_of_containing_block
+ - margin_left.to_px(box) - computed_values.border_left().width - padding_left.to_px(box)
+ - padding_right.to_px(box) - computed_values.border_right().width - margin_right.to_px(box);
+
+ auto result = calculate_shrink_to_fit_widths(box);
+
+ // Then the shrink-to-fit width is: min(max(preferred minimum width, available width), preferred width).
+ width = CSS::Length(min(max(result.preferred_minimum_width, available_width), result.preferred_width), CSS::Length::Type::Px);
+ }
+
+ float final_width = width.resolved_or_zero(box, width_of_containing_block).to_px(box);
+ box.set_width(final_width);
+}
+
+void BlockFormattingContext::compute_width_for_block_level_replaced_element_in_normal_flow(ReplacedBox& box)
+{
+ box.set_width(compute_width_for_replaced_element(box));
+}
+
+void BlockFormattingContext::compute_height_for_block_level_replaced_element_in_normal_flow(ReplacedBox& box)
+{
+ box.set_height(compute_height_for_replaced_element(box));
+}
+
+void BlockFormattingContext::compute_height(Box& box)
+{
+ if (is<ReplacedBox>(box)) {
+ compute_height_for_block_level_replaced_element_in_normal_flow(downcast<ReplacedBox>(box));
+ return;
+ }
+
+ auto& computed_values = box.computed_values();
+ auto& containing_block = *box.containing_block();
+
+ CSS::Length specified_height;
+
+ if (computed_values.height().is_percentage() && !containing_block.computed_values().height().is_absolute()) {
+ specified_height = CSS::Length::make_auto();
+ } else {
+ specified_height = computed_values.height().resolved_or_auto(box, containing_block.height());
+ }
+
+ auto specified_max_height = computed_values.max_height().resolved_or_auto(box, containing_block.height());
+
+ box.box_model().margin.top = computed_values.margin().top.resolved_or_zero(box, containing_block.width()).to_px(box);
+ box.box_model().margin.bottom = computed_values.margin().bottom.resolved_or_zero(box, containing_block.width()).to_px(box);
+ box.box_model().border.top = computed_values.border_top().width;
+ box.box_model().border.bottom = computed_values.border_bottom().width;
+ box.box_model().padding.top = computed_values.padding().top.resolved_or_zero(box, containing_block.width()).to_px(box);
+ box.box_model().padding.bottom = computed_values.padding().bottom.resolved_or_zero(box, containing_block.width()).to_px(box);
+
+ if (!specified_height.is_auto()) {
+ float used_height = specified_height.to_px(box);
+ if (!specified_max_height.is_auto())
+ used_height = min(used_height, specified_max_height.to_px(box));
+ box.set_height(used_height);
+ }
+}
+
+void BlockFormattingContext::layout_inline_children(Box& box, LayoutMode layout_mode)
+{
+ InlineFormattingContext context(box, this);
+ context.run(box, layout_mode);
+}
+
+void BlockFormattingContext::layout_block_level_children(Box& box, LayoutMode layout_mode)
+{
+ float content_height = 0;
+ float content_width = 0;
+
+ box.for_each_child_of_type<Box>([&](auto& child_box) {
+ if (child_box.is_absolutely_positioned())
+ return IterationDecision::Continue;
+
+ if (child_box.is_floating()) {
+ layout_floating_child(child_box, box);
+ return IterationDecision::Continue;
+ }
+
+ compute_width(child_box);
+ layout_inside(child_box, layout_mode);
+ compute_height(child_box);
+
+ if (is<ReplacedBox>(child_box))
+ place_block_level_replaced_element_in_normal_flow(child_box, box);
+ else if (is<BlockBox>(child_box))
+ place_block_level_non_replaced_element_in_normal_flow(child_box, box);
+
+ // FIXME: This should be factored differently. It's uncool that we mutate the tree *during* layout!
+ // Instead, we should generate the marker box during the tree build.
+ if (is<ListItemBox>(child_box))
+ downcast<ListItemBox>(child_box).layout_marker();
+
+ content_height = max(content_height, child_box.effective_offset().y() + child_box.height() + child_box.box_model().margin_box().bottom);
+ content_width = max(content_width, downcast<Box>(child_box).width());
+ return IterationDecision::Continue;
+ });
+
+ if (layout_mode != LayoutMode::Default) {
+ if (box.computed_values().width().is_undefined() || box.computed_values().width().is_auto())
+ box.set_width(content_width);
+ }
+
+ // FIXME: It's not right to always shrink-wrap the box to the content here.
+ box.set_height(content_height);
+}
+
+void BlockFormattingContext::place_block_level_replaced_element_in_normal_flow(Box& child_box, Box& containing_block)
+{
+ ASSERT(!containing_block.is_absolutely_positioned());
+ auto& replaced_element_box_model = child_box.box_model();
+
+ replaced_element_box_model.margin.top = child_box.computed_values().margin().top.resolved_or_zero(containing_block, containing_block.width()).to_px(child_box);
+ replaced_element_box_model.margin.bottom = child_box.computed_values().margin().bottom.resolved_or_zero(containing_block, containing_block.width()).to_px(child_box);
+ replaced_element_box_model.border.top = child_box.computed_values().border_top().width;
+ replaced_element_box_model.border.bottom = child_box.computed_values().border_bottom().width;
+ replaced_element_box_model.padding.top = child_box.computed_values().padding().top.resolved_or_zero(containing_block, containing_block.width()).to_px(child_box);
+ replaced_element_box_model.padding.bottom = child_box.computed_values().padding().bottom.resolved_or_zero(containing_block, containing_block.width()).to_px(child_box);
+
+ float x = replaced_element_box_model.margin.left
+ + replaced_element_box_model.border.left
+ + replaced_element_box_model.padding.left
+ + replaced_element_box_model.offset.left;
+
+ float y = replaced_element_box_model.margin_box().top + containing_block.box_model().offset.top;
+
+ child_box.set_offset(x, y);
+}
+
+void BlockFormattingContext::place_block_level_non_replaced_element_in_normal_flow(Box& child_box, Box& containing_block)
+{
+ auto& box_model = child_box.box_model();
+ auto& computed_values = child_box.computed_values();
+
+ box_model.margin.top = computed_values.margin().top.resolved_or_zero(containing_block, containing_block.width()).to_px(child_box);
+ box_model.margin.bottom = computed_values.margin().bottom.resolved_or_zero(containing_block, containing_block.width()).to_px(child_box);
+ box_model.border.top = computed_values.border_top().width;
+ box_model.border.bottom = computed_values.border_bottom().width;
+ box_model.padding.top = computed_values.padding().top.resolved_or_zero(containing_block, containing_block.width()).to_px(child_box);
+ box_model.padding.bottom = computed_values.padding().bottom.resolved_or_zero(containing_block, containing_block.width()).to_px(child_box);
+
+ float x = box_model.margin.left
+ + box_model.border.left
+ + box_model.padding.left
+ + box_model.offset.left;
+
+ if (containing_block.computed_values().text_align() == CSS::TextAlign::LibwebCenter) {
+ x = (containing_block.width() / 2) - child_box.width() / 2;
+ }
+
+ float y = box_model.margin_box().top
+ + box_model.offset.top;
+
+ // NOTE: Empty (0-height) preceding siblings have their margins collapsed with *their* preceding sibling, etc.
+ float collapsed_bottom_margin_of_preceding_siblings = 0;
+
+ auto* relevant_sibling = child_box.previous_sibling_of_type<Layout::BlockBox>();
+ while (relevant_sibling != nullptr) {
+ if (!relevant_sibling->is_absolutely_positioned() && !relevant_sibling->is_floating()) {
+ collapsed_bottom_margin_of_preceding_siblings = max(collapsed_bottom_margin_of_preceding_siblings, relevant_sibling->box_model().margin.bottom);
+ if (relevant_sibling->border_box_height() > 0)
+ break;
+ }
+ relevant_sibling = relevant_sibling->previous_sibling();
+ }
+
+ if (relevant_sibling) {
+ y += relevant_sibling->effective_offset().y()
+ + relevant_sibling->height()
+ + relevant_sibling->box_model().border_box().bottom;
+
+ // Collapse top margin with bottom margin of preceding siblings if needed
+ float my_margin_top = box_model.margin.top;
+
+ if (my_margin_top < 0 || collapsed_bottom_margin_of_preceding_siblings < 0) {
+ // Negative margins present.
+ float largest_negative_margin = -min(my_margin_top, collapsed_bottom_margin_of_preceding_siblings);
+ float largest_positive_margin = (my_margin_top < 0 && collapsed_bottom_margin_of_preceding_siblings < 0) ? 0 : max(my_margin_top, collapsed_bottom_margin_of_preceding_siblings);
+ float final_margin = largest_positive_margin - largest_negative_margin;
+ y += final_margin - my_margin_top;
+ } else if (collapsed_bottom_margin_of_preceding_siblings > my_margin_top) {
+ // Sibling's margin is larger than mine, adjust so we use sibling's.
+ y += collapsed_bottom_margin_of_preceding_siblings - my_margin_top;
+ }
+ }
+
+ if (child_box.computed_values().clear() == CSS::Clear::Left || child_box.computed_values().clear() == CSS::Clear::Both) {
+ if (!m_left_floating_boxes.is_empty()) {
+ float clearance_y = 0;
+ for (auto* floating_box : m_left_floating_boxes) {
+ clearance_y = max(clearance_y, floating_box->effective_offset().y() + floating_box->box_model().margin_box().bottom);
+ }
+ y = max(y, clearance_y);
+ m_left_floating_boxes.clear();
+ }
+ }
+
+ if (child_box.computed_values().clear() == CSS::Clear::Right || child_box.computed_values().clear() == CSS::Clear::Both) {
+ if (!m_right_floating_boxes.is_empty()) {
+ float clearance_y = 0;
+ for (auto* floating_box : m_right_floating_boxes) {
+ clearance_y = max(clearance_y, floating_box->effective_offset().y() + floating_box->box_model().margin_box().bottom);
+ }
+ y = max(y, clearance_y);
+ m_right_floating_boxes.clear();
+ }
+ }
+
+ child_box.set_offset(x, y);
+}
+
+void BlockFormattingContext::layout_initial_containing_block(LayoutMode layout_mode)
+{
+ auto viewport_rect = context_box().frame().viewport_rect();
+
+ auto& icb = downcast<Layout::InitialContainingBlockBox>(context_box());
+ icb.build_stacking_context_tree();
+
+ icb.set_width(viewport_rect.width());
+
+ layout_block_level_children(context_box(), layout_mode);
+
+ ASSERT(!icb.children_are_inline());
+
+ // FIXME: The ICB should have the height of the viewport.
+ // Instead of auto-sizing the ICB, we should spill into overflow.
+ float lowest_bottom = 0;
+ icb.for_each_child_of_type<Box>([&](auto& child) {
+ lowest_bottom = max(lowest_bottom, child.absolute_rect().bottom());
+ });
+
+ // FIXME: This is a hack and should be managed by an overflow mechanism.
+ icb.set_height(max(static_cast<float>(viewport_rect.height()), lowest_bottom));
+
+ // FIXME: This is a total hack. Make sure any GUI::Widgets are moved into place after layout.
+ // We should stop embedding GUI::Widgets entirely, since that won't work out-of-process.
+ icb.for_each_in_subtree_of_type<Layout::WidgetBox>([&](auto& widget) {
+ widget.update_widget();
+ return IterationDecision::Continue;
+ });
+}
+
+static Gfx::FloatRect rect_in_coordinate_space(const Box& box, const Box& context_box)
+{
+ Gfx::FloatRect rect { box.effective_offset(), box.size() };
+ for (auto* ancestor = box.parent(); ancestor; ancestor = ancestor->parent()) {
+ if (is<Box>(*ancestor)) {
+ auto offset = downcast<Box>(*ancestor).effective_offset();
+ rect.move_by(offset);
+ }
+ if (ancestor == &context_box)
+ break;
+ }
+ return rect;
+}
+
+void BlockFormattingContext::layout_floating_child(Box& box, Box& containing_block)
+{
+ ASSERT(box.is_floating());
+
+ compute_width(box);
+ layout_inside(box, LayoutMode::Default);
+ compute_height(box);
+
+ // First we place the box normally (to get the right y coordinate.)
+ place_block_level_non_replaced_element_in_normal_flow(box, containing_block);
+
+ // Then we float it to the left or right.
+ float x = box.effective_offset().x();
+
+ auto box_in_context_rect = rect_in_coordinate_space(box, context_box());
+ float y_in_context_box = box_in_context_rect.y();
+
+ // Next, float to the left and/or right
+ if (box.computed_values().float_() == CSS::Float::Left) {
+ if (!m_left_floating_boxes.is_empty()) {
+ auto& previous_floating_box = *m_left_floating_boxes.last();
+ auto previous_rect = rect_in_coordinate_space(previous_floating_box, context_box());
+ if (previous_rect.contains_vertically(y_in_context_box)) {
+ // This box touches another already floating box. Stack to the right.
+ x = previous_floating_box.effective_offset().x() + previous_floating_box.width();
+ } else {
+ // This box does not touch another floating box, go all the way to the left.
+ x = 0;
+ // Also, forget all previous left-floating boxes while we're here since they're no longer relevant.
+ m_left_floating_boxes.clear();
+ }
+ } else {
+ // This is the first left-floating box. Go all the way to the left.
+ x = 0;
+ }
+ m_left_floating_boxes.append(&box);
+ } else if (box.computed_values().float_() == CSS::Float::Right) {
+ if (!m_right_floating_boxes.is_empty()) {
+ auto& previous_floating_box = *m_right_floating_boxes.last();
+ auto previous_rect = rect_in_coordinate_space(previous_floating_box, context_box());
+ if (previous_rect.contains_vertically(y_in_context_box)) {
+ // This box touches another already floating box. Stack to the left.
+ x = previous_floating_box.effective_offset().x() - box.width();
+ } else {
+ // This box does not touch another floating box, go all the way to the right.
+ x = containing_block.width() - box.width();
+ // Also, forget all previous right-floating boxes while we're here since they're no longer relevant.
+ m_right_floating_boxes.clear();
+ }
+ } else {
+ // This is the first right-floating box. Go all the way to the right.
+ x = containing_block.width() - box.width();
+ }
+ m_right_floating_boxes.append(&box);
+ }
+
+ box.set_offset(x, box.effective_offset().y());
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h
new file mode 100644
index 0000000000..efaa9ab0b7
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/Vector.h>
+#include <LibWeb/Forward.h>
+#include <LibWeb/Layout/FormattingContext.h>
+
+namespace Web::Layout {
+
+class BlockFormattingContext : public FormattingContext {
+public:
+ explicit BlockFormattingContext(Box&, FormattingContext* parent);
+ ~BlockFormattingContext();
+
+ virtual void run(Box&, LayoutMode) override;
+
+ bool is_initial() const;
+
+ const Vector<Box*>& left_floating_boxes() const { return m_left_floating_boxes; }
+ const Vector<Box*>& right_floating_boxes() const { return m_right_floating_boxes; }
+
+protected:
+ void compute_width(Box&);
+ void compute_height(Box&);
+
+private:
+ virtual bool is_block_formatting_context() const final { return true; }
+
+ void compute_width_for_floating_box(Box&);
+
+ void compute_width_for_block_level_replaced_element_in_normal_flow(ReplacedBox&);
+ void compute_height_for_block_level_replaced_element_in_normal_flow(ReplacedBox&);
+
+ void layout_initial_containing_block(LayoutMode);
+
+ void layout_block_level_children(Box&, LayoutMode);
+ void layout_inline_children(Box&, LayoutMode);
+
+ void place_block_level_replaced_element_in_normal_flow(Box& child, Box& container);
+ void place_block_level_non_replaced_element_in_normal_flow(Box& child, Box& container);
+
+ void layout_floating_child(Box&, Box& containing_block);
+
+ Vector<Box*> m_left_floating_boxes;
+ Vector<Box*> m_right_floating_boxes;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/Box.cpp b/Userland/Libraries/LibWeb/Layout/Box.cpp
new file mode 100644
index 0000000000..9c468252a8
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/Box.cpp
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibGUI/Painter.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/HTML/HTMLBodyElement.h>
+#include <LibWeb/Layout/BlockBox.h>
+#include <LibWeb/Layout/Box.h>
+#include <LibWeb/Page/Frame.h>
+#include <LibWeb/Painting/BorderPainting.h>
+
+namespace Web::Layout {
+
+void Box::paint(PaintContext& context, PaintPhase phase)
+{
+ if (!is_visible())
+ return;
+
+ Gfx::PainterStateSaver saver(context.painter());
+ if (is_fixed_position())
+ context.painter().translate(context.scroll_offset());
+
+ Gfx::FloatRect padded_rect;
+ padded_rect.set_x(absolute_x() - box_model().padding.left);
+ padded_rect.set_width(width() + box_model().padding.left + box_model().padding.right);
+ padded_rect.set_y(absolute_y() - box_model().padding.top);
+ padded_rect.set_height(height() + box_model().padding.top + box_model().padding.bottom);
+
+ if (phase == PaintPhase::Background && !is_body()) {
+ context.painter().fill_rect(enclosing_int_rect(padded_rect), computed_values().background_color());
+
+ if (background_image() && background_image()->bitmap())
+ context.painter().draw_tiled_bitmap(enclosing_int_rect(padded_rect), *background_image()->bitmap());
+ }
+
+ if (phase == PaintPhase::Border) {
+ Gfx::FloatRect bordered_rect;
+ bordered_rect.set_x(padded_rect.x() - box_model().border.left);
+ bordered_rect.set_width(padded_rect.width() + box_model().border.left + box_model().border.right);
+ bordered_rect.set_y(padded_rect.y() - box_model().border.top);
+ bordered_rect.set_height(padded_rect.height() + box_model().border.top + box_model().border.bottom);
+
+ Painting::paint_border(context, Painting::BorderEdge::Left, bordered_rect, computed_values());
+ Painting::paint_border(context, Painting::BorderEdge::Right, bordered_rect, computed_values());
+ Painting::paint_border(context, Painting::BorderEdge::Top, bordered_rect, computed_values());
+ Painting::paint_border(context, Painting::BorderEdge::Bottom, bordered_rect, computed_values());
+ }
+
+ Layout::NodeWithStyleAndBoxModelMetrics::paint(context, phase);
+
+ if (phase == PaintPhase::Overlay && dom_node() && document().inspected_node() == dom_node()) {
+ auto content_rect = absolute_rect();
+
+ auto margin_box = box_model().margin_box();
+ Gfx::FloatRect margin_rect;
+ margin_rect.set_x(absolute_x() - margin_box.left);
+ margin_rect.set_width(width() + margin_box.left + margin_box.right);
+ margin_rect.set_y(absolute_y() - margin_box.top);
+ margin_rect.set_height(height() + margin_box.top + margin_box.bottom);
+
+ context.painter().draw_rect(enclosing_int_rect(margin_rect), Color::Yellow);
+ context.painter().draw_rect(enclosing_int_rect(padded_rect), Color::Cyan);
+ context.painter().draw_rect(enclosing_int_rect(content_rect), Color::Magenta);
+ }
+
+ if (phase == PaintPhase::FocusOutline && dom_node() && dom_node()->is_element() && downcast<DOM::Element>(*dom_node()).is_focused()) {
+ context.painter().draw_rect(enclosing_int_rect(absolute_rect()), context.palette().focus_outline());
+ }
+}
+
+HitTestResult Box::hit_test(const Gfx::IntPoint& position, HitTestType type) const
+{
+ // FIXME: It would be nice if we could confidently skip over hit testing
+ // parts of the layout tree, but currently we can't just check
+ // m_rect.contains() since inline text rects can't be trusted..
+ HitTestResult result { absolute_rect().contains(position.x(), position.y()) ? this : nullptr };
+ for_each_child_in_paint_order([&](auto& child) {
+ auto child_result = child.hit_test(position, type);
+ if (child_result.layout_node)
+ result = child_result;
+ });
+ return result;
+}
+
+void Box::set_needs_display()
+{
+ if (!is_inline()) {
+ frame().set_needs_display(enclosing_int_rect(absolute_rect()));
+ return;
+ }
+
+ Node::set_needs_display();
+}
+
+bool Box::is_body() const
+{
+ return dom_node() && dom_node() == document().body();
+}
+
+void Box::set_offset(const Gfx::FloatPoint& offset)
+{
+ if (m_offset == offset)
+ return;
+ m_offset = offset;
+ did_set_rect();
+}
+
+void Box::set_size(const Gfx::FloatSize& size)
+{
+ if (m_size == size)
+ return;
+ m_size = size;
+ did_set_rect();
+}
+
+Gfx::FloatPoint Box::effective_offset() const
+{
+ if (m_containing_line_box_fragment)
+ return m_containing_line_box_fragment->offset();
+ return m_offset;
+}
+
+const Gfx::FloatRect Box::absolute_rect() const
+{
+ Gfx::FloatRect rect { effective_offset(), size() };
+ for (auto* block = containing_block(); block; block = block->containing_block()) {
+ rect.move_by(block->effective_offset());
+ }
+ return rect;
+}
+
+void Box::set_containing_line_box_fragment(LineBoxFragment& fragment)
+{
+ m_containing_line_box_fragment = fragment.make_weak_ptr();
+}
+
+StackingContext* Box::enclosing_stacking_context()
+{
+ for (auto* ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
+ if (!is<Box>(ancestor))
+ continue;
+ auto& ancestor_box = downcast<Box>(*ancestor);
+ if (!ancestor_box.establishes_stacking_context())
+ continue;
+ ASSERT(ancestor_box.stacking_context());
+ return ancestor_box.stacking_context();
+ }
+ // We should always reach the Layout::InitialContainingBlockBox stacking context.
+ ASSERT_NOT_REACHED();
+}
+
+bool Box::establishes_stacking_context() const
+{
+ if (!has_style())
+ return false;
+ if (dom_node() == document().root())
+ return true;
+ auto position = computed_values().position();
+ auto z_index = computed_values().z_index();
+ if (position == CSS::Position::Absolute || position == CSS::Position::Relative) {
+ if (z_index.has_value())
+ return true;
+ }
+ if (position == CSS::Position::Fixed || position == CSS::Position::Sticky)
+ return true;
+ return false;
+}
+
+LineBox& Box::ensure_last_line_box()
+{
+ if (m_line_boxes.is_empty())
+ return add_line_box();
+ return m_line_boxes.last();
+}
+
+LineBox& Box::add_line_box()
+{
+ m_line_boxes.append(LineBox());
+ return m_line_boxes.last();
+}
+
+float Box::width_of_logical_containing_block() const
+{
+ auto* containing_block = this->containing_block();
+ ASSERT(containing_block);
+ return containing_block->width();
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/Box.h b/Userland/Libraries/LibWeb/Layout/Box.h
new file mode 100644
index 0000000000..7b05b381e3
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/Box.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/OwnPtr.h>
+#include <LibGfx/Rect.h>
+#include <LibWeb/Layout/LineBox.h>
+#include <LibWeb/Layout/Node.h>
+#include <LibWeb/Painting/StackingContext.h>
+
+namespace Web::Layout {
+
+class Box : public NodeWithStyleAndBoxModelMetrics {
+public:
+ const Gfx::FloatRect absolute_rect() const;
+
+ Gfx::FloatPoint effective_offset() const;
+
+ void set_offset(const Gfx::FloatPoint& offset);
+ void set_offset(float x, float y) { set_offset({ x, y }); }
+
+ const Gfx::FloatSize& size() const { return m_size; }
+ void set_size(const Gfx::FloatSize&);
+ void set_size(float width, float height) { set_size({ width, height }); }
+
+ void set_width(float width) { set_size(width, height()); }
+ void set_height(float height) { set_size(width(), height); }
+ float width() const { return m_size.width(); }
+ float height() const { return m_size.height(); }
+
+ float border_box_width() const
+ {
+ auto border_box = box_model().border_box();
+ return width() + border_box.left + border_box.right;
+ }
+
+ float border_box_height() const
+ {
+ auto border_box = box_model().border_box();
+ return height() + border_box.top + border_box.bottom;
+ }
+
+ Gfx::FloatRect content_box_as_relative_rect() const
+ {
+ return { m_offset, m_size };
+ }
+
+ Gfx::FloatRect margin_box_as_relative_rect() const
+ {
+ auto rect = content_box_as_relative_rect();
+ auto margin_box = box_model().margin_box();
+ rect.set_x(rect.x() - margin_box.left);
+ rect.set_width(rect.width() + margin_box.left + margin_box.right);
+ rect.set_y(rect.y() - margin_box.top);
+ rect.set_height(rect.height() + margin_box.top + margin_box.bottom);
+ return rect;
+ }
+
+ float absolute_x() const { return absolute_rect().x(); }
+ float absolute_y() const { return absolute_rect().y(); }
+ Gfx::FloatPoint absolute_position() const { return absolute_rect().location(); }
+
+ virtual HitTestResult hit_test(const Gfx::IntPoint&, HitTestType) const override;
+ virtual void set_needs_display() override;
+
+ bool is_body() const;
+
+ void set_containing_line_box_fragment(LineBoxFragment&);
+
+ bool establishes_stacking_context() const;
+ StackingContext* stacking_context() { return m_stacking_context; }
+ const StackingContext* stacking_context() const { return m_stacking_context; }
+ void set_stacking_context(NonnullOwnPtr<StackingContext> context) { m_stacking_context = move(context); }
+ StackingContext* enclosing_stacking_context();
+
+ virtual void paint(PaintContext&, PaintPhase) override;
+
+ Vector<LineBox>& line_boxes() { return m_line_boxes; }
+ const Vector<LineBox>& line_boxes() const { return m_line_boxes; }
+
+ LineBox& ensure_last_line_box();
+ LineBox& add_line_box();
+
+ virtual float width_of_logical_containing_block() const;
+
+protected:
+ Box(DOM::Document& document, DOM::Node* node, NonnullRefPtr<CSS::StyleProperties> style)
+ : NodeWithStyleAndBoxModelMetrics(document, node, move(style))
+ {
+ }
+
+ Box(DOM::Document& document, DOM::Node* node, CSS::ComputedValues computed_values)
+ : NodeWithStyleAndBoxModelMetrics(document, node, move(computed_values))
+ {
+ }
+
+ virtual void did_set_rect() { }
+
+ Vector<LineBox> m_line_boxes;
+
+private:
+ virtual bool is_box() const final { return true; }
+
+ Gfx::FloatPoint m_offset;
+ Gfx::FloatSize m_size;
+
+ // Some boxes hang off of line box fragments. (inline-block, inline-table, replaced, etc)
+ WeakPtr<LineBoxFragment> m_containing_line_box_fragment;
+
+ OwnPtr<StackingContext> m_stacking_context;
+};
+
+}
+
+namespace AK {
+template<>
+inline bool is<Web::Layout::Box>(const Web::Layout::Node& input)
+{
+ return input.is_box();
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/BoxModelMetrics.cpp b/Userland/Libraries/LibWeb/Layout/BoxModelMetrics.cpp
new file mode 100644
index 0000000000..d18e5ef531
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/BoxModelMetrics.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/Layout/BoxModelMetrics.h>
+
+namespace Web::Layout {
+
+PixelBox BoxModelMetrics::margin_box() const
+{
+ return {
+ margin.top + border.top + padding.top,
+ margin.right + border.right + padding.right,
+ margin.bottom + border.bottom + padding.bottom,
+ margin.left + border.left + padding.left,
+ };
+}
+
+PixelBox BoxModelMetrics::padding_box() const
+{
+ return {
+ padding.top,
+ padding.right,
+ padding.bottom,
+ padding.left,
+ };
+}
+
+PixelBox BoxModelMetrics::border_box() const
+{
+ return {
+ border.top + padding.top,
+ border.right + padding.right,
+ border.bottom + padding.bottom,
+ border.left + padding.left,
+ };
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/BoxModelMetrics.h b/Userland/Libraries/LibWeb/Layout/BoxModelMetrics.h
new file mode 100644
index 0000000000..4b36062b7b
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/BoxModelMetrics.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibGfx/Size.h>
+
+namespace Web::Layout {
+
+struct PixelBox {
+ float top { 0 };
+ float right { 0 };
+ float bottom { 0 };
+ float left { 0 };
+};
+
+struct BoxModelMetrics {
+public:
+ PixelBox margin;
+ PixelBox padding;
+ PixelBox border;
+ PixelBox offset;
+
+ PixelBox margin_box() const;
+ PixelBox padding_box() const;
+ PixelBox border_box() const;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/BreakNode.cpp b/Userland/Libraries/LibWeb/Layout/BreakNode.cpp
new file mode 100644
index 0000000000..9951ae4fe0
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/BreakNode.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/Layout/BlockBox.h>
+#include <LibWeb/Layout/BreakNode.h>
+#include <LibWeb/Layout/InlineFormattingContext.h>
+
+namespace Web::Layout {
+
+BreakNode::BreakNode(DOM::Document& document, HTML::HTMLBRElement& element)
+ : Layout::NodeWithStyleAndBoxModelMetrics(document, &element, CSS::StyleProperties::create())
+{
+ set_inline(true);
+}
+
+BreakNode::~BreakNode()
+{
+}
+
+void BreakNode::split_into_lines(InlineFormattingContext& context, LayoutMode)
+{
+ context.containing_block().add_line_box();
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/BreakNode.h b/Userland/Libraries/LibWeb/Layout/BreakNode.h
new file mode 100644
index 0000000000..58ebbe3322
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/BreakNode.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLBRElement.h>
+#include <LibWeb/Layout/Node.h>
+
+namespace Web::Layout {
+
+class BreakNode final : public NodeWithStyleAndBoxModelMetrics {
+public:
+ BreakNode(DOM::Document&, HTML::HTMLBRElement&);
+ virtual ~BreakNode() override;
+
+ const HTML::HTMLBRElement& dom_node() const { return downcast<HTML::HTMLBRElement>(*Node::dom_node()); }
+
+private:
+ virtual void split_into_lines(InlineFormattingContext&, LayoutMode) override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/ButtonBox.cpp b/Userland/Libraries/LibWeb/Layout/ButtonBox.cpp
new file mode 100644
index 0000000000..c0b3706e28
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/ButtonBox.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibGUI/Event.h>
+#include <LibGUI/Painter.h>
+#include <LibGfx/Font.h>
+#include <LibGfx/StylePainter.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/Layout/ButtonBox.h>
+#include <LibWeb/Page/Frame.h>
+
+namespace Web::Layout {
+
+ButtonBox::ButtonBox(DOM::Document& document, HTML::HTMLInputElement& element, NonnullRefPtr<CSS::StyleProperties> style)
+ : ReplacedBox(document, element, move(style))
+{
+}
+
+ButtonBox::~ButtonBox()
+{
+}
+
+void ButtonBox::prepare_for_replaced_layout()
+{
+ set_intrinsic_width(font().width(dom_node().value()) + 20);
+ set_has_intrinsic_width(true);
+
+ set_intrinsic_height(20);
+ set_has_intrinsic_height(true);
+}
+
+void ButtonBox::paint(PaintContext& context, PaintPhase phase)
+{
+ if (!is_visible())
+ return;
+
+ ReplacedBox::paint(context, phase);
+
+ if (phase == PaintPhase::Foreground) {
+ bool hovered = document().hovered_node() == &dom_node();
+ Gfx::StylePainter::paint_button(context.painter(), enclosing_int_rect(absolute_rect()), context.palette(), Gfx::ButtonStyle::Normal, m_being_pressed, hovered, dom_node().checked(), dom_node().enabled());
+
+ auto text_rect = enclosing_int_rect(absolute_rect());
+ if (m_being_pressed)
+ text_rect.move_by(1, 1);
+ context.painter().draw_text(text_rect, dom_node().value(), font(), Gfx::TextAlignment::Center, context.palette().button_text());
+ }
+}
+
+void ButtonBox::handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned)
+{
+ if (button != GUI::MouseButton::Left || !dom_node().enabled())
+ return;
+
+ m_being_pressed = true;
+ set_needs_display();
+
+ m_tracking_mouse = true;
+ frame().event_handler().set_mouse_event_tracking_layout_node(this);
+}
+
+void ButtonBox::handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint& position, unsigned button, unsigned)
+{
+ if (!m_tracking_mouse || button != GUI::MouseButton::Left || !dom_node().enabled())
+ return;
+
+ // NOTE: Handling the click may run arbitrary JS, which could disappear this node.
+ NonnullRefPtr protected_this = *this;
+ NonnullRefPtr protected_frame = frame();
+
+ bool is_inside = enclosing_int_rect(absolute_rect()).contains(position);
+ if (is_inside)
+ dom_node().did_click_button({});
+
+ m_being_pressed = false;
+ m_tracking_mouse = false;
+
+ protected_frame->event_handler().set_mouse_event_tracking_layout_node(nullptr);
+}
+
+void ButtonBox::handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint& position, unsigned, unsigned)
+{
+ if (!m_tracking_mouse || !dom_node().enabled())
+ return;
+
+ bool is_inside = enclosing_int_rect(absolute_rect()).contains(position);
+ if (m_being_pressed == is_inside)
+ return;
+
+ m_being_pressed = is_inside;
+ set_needs_display();
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/ButtonBox.h b/Userland/Libraries/LibWeb/Layout/ButtonBox.h
new file mode 100644
index 0000000000..f26c7ed8d8
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/ButtonBox.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLInputElement.h>
+#include <LibWeb/Layout/ReplacedBox.h>
+
+namespace Web::Layout {
+
+class ButtonBox : public ReplacedBox {
+public:
+ ButtonBox(DOM::Document&, HTML::HTMLInputElement&, NonnullRefPtr<CSS::StyleProperties>);
+ virtual ~ButtonBox() override;
+
+ virtual void prepare_for_replaced_layout() override;
+ virtual void paint(PaintContext&, PaintPhase) override;
+
+ const HTML::HTMLInputElement& dom_node() const { return static_cast<const HTML::HTMLInputElement&>(ReplacedBox::dom_node()); }
+ HTML::HTMLInputElement& dom_node() { return static_cast<HTML::HTMLInputElement&>(ReplacedBox::dom_node()); }
+
+private:
+ virtual bool wants_mouse_events() const override { return true; }
+ virtual void handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers) override;
+ virtual void handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers) override;
+ virtual void handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint&, unsigned buttons, unsigned modifiers) override;
+
+ bool m_being_pressed { false };
+ bool m_tracking_mouse { false };
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/CanvasBox.cpp b/Userland/Libraries/LibWeb/Layout/CanvasBox.cpp
new file mode 100644
index 0000000000..c1eb048ace
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/CanvasBox.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibGUI/Painter.h>
+#include <LibGfx/Font.h>
+#include <LibGfx/StylePainter.h>
+#include <LibWeb/Layout/CanvasBox.h>
+
+namespace Web::Layout {
+
+CanvasBox::CanvasBox(DOM::Document& document, HTML::HTMLCanvasElement& element, NonnullRefPtr<CSS::StyleProperties> style)
+ : ReplacedBox(document, element, move(style))
+{
+}
+
+CanvasBox::~CanvasBox()
+{
+}
+
+void CanvasBox::prepare_for_replaced_layout()
+{
+ set_has_intrinsic_width(true);
+ set_has_intrinsic_height(true);
+ set_intrinsic_width(dom_node().width());
+ set_intrinsic_height(dom_node().height());
+}
+
+void CanvasBox::paint(PaintContext& context, PaintPhase phase)
+{
+ if (!is_visible())
+ return;
+
+ ReplacedBox::paint(context, phase);
+
+ if (phase == PaintPhase::Foreground) {
+ // FIXME: This should be done at a different level. Also rect() does not include padding etc!
+ if (!context.viewport_rect().intersects(enclosing_int_rect(absolute_rect())))
+ return;
+
+ if (dom_node().bitmap())
+ context.painter().draw_scaled_bitmap(enclosing_int_rect(absolute_rect()), *dom_node().bitmap(), dom_node().bitmap()->rect());
+ }
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/CanvasBox.h b/Userland/Libraries/LibWeb/Layout/CanvasBox.h
new file mode 100644
index 0000000000..150e28ae21
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/CanvasBox.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLCanvasElement.h>
+#include <LibWeb/Layout/ReplacedBox.h>
+
+namespace Web::Layout {
+
+class CanvasBox : public ReplacedBox {
+public:
+ CanvasBox(DOM::Document&, HTML::HTMLCanvasElement&, NonnullRefPtr<CSS::StyleProperties>);
+ virtual ~CanvasBox() override;
+
+ virtual void prepare_for_replaced_layout() override;
+ virtual void paint(PaintContext&, PaintPhase) override;
+
+ const HTML::HTMLCanvasElement& dom_node() const { return static_cast<const HTML::HTMLCanvasElement&>(ReplacedBox::dom_node()); }
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/CheckBox.cpp b/Userland/Libraries/LibWeb/Layout/CheckBox.cpp
new file mode 100644
index 0000000000..a259e4f2d5
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/CheckBox.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibGUI/Event.h>
+#include <LibGUI/Painter.h>
+#include <LibGfx/Font.h>
+#include <LibGfx/StylePainter.h>
+#include <LibWeb/Layout/CheckBox.h>
+#include <LibWeb/Page/Frame.h>
+
+namespace Web::Layout {
+
+CheckBox::CheckBox(DOM::Document& document, HTML::HTMLInputElement& element, NonnullRefPtr<CSS::StyleProperties> style)
+ : ReplacedBox(document, element, move(style))
+{
+ set_has_intrinsic_width(true);
+ set_has_intrinsic_height(true);
+ set_intrinsic_width(13);
+ set_intrinsic_height(13);
+}
+
+CheckBox::~CheckBox()
+{
+}
+
+void CheckBox::paint(PaintContext& context, PaintPhase phase)
+{
+ if (!is_visible())
+ return;
+
+ ReplacedBox::paint(context, phase);
+
+ if (phase == PaintPhase::Foreground) {
+ Gfx::StylePainter::paint_check_box(context.painter(), enclosing_int_rect(absolute_rect()), context.palette(), dom_node().enabled(), dom_node().checked(), m_being_pressed);
+ }
+}
+
+void CheckBox::handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned)
+{
+ if (button != GUI::MouseButton::Left || !dom_node().enabled())
+ return;
+
+ m_being_pressed = true;
+ set_needs_display();
+
+ m_tracking_mouse = true;
+ frame().event_handler().set_mouse_event_tracking_layout_node(this);
+}
+
+void CheckBox::handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint& position, unsigned button, unsigned)
+{
+ if (!m_tracking_mouse || button != GUI::MouseButton::Left || !dom_node().enabled())
+ return;
+
+ // NOTE: Changing the checked state of the DOM node may run arbitrary JS, which could disappear this node.
+ NonnullRefPtr protect = *this;
+
+ bool is_inside = enclosing_int_rect(absolute_rect()).contains(position);
+ if (is_inside)
+ dom_node().set_checked(!dom_node().checked());
+
+ m_being_pressed = false;
+ m_tracking_mouse = false;
+ frame().event_handler().set_mouse_event_tracking_layout_node(nullptr);
+}
+
+void CheckBox::handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint& position, unsigned, unsigned)
+{
+ if (!m_tracking_mouse || !dom_node().enabled())
+ return;
+
+ bool is_inside = enclosing_int_rect(absolute_rect()).contains(position);
+ if (m_being_pressed == is_inside)
+ return;
+
+ m_being_pressed = is_inside;
+ set_needs_display();
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/CheckBox.h b/Userland/Libraries/LibWeb/Layout/CheckBox.h
new file mode 100644
index 0000000000..47809c07f9
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/CheckBox.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLInputElement.h>
+#include <LibWeb/Layout/ReplacedBox.h>
+
+namespace Web::Layout {
+
+class CheckBox : public ReplacedBox {
+public:
+ CheckBox(DOM::Document&, HTML::HTMLInputElement&, NonnullRefPtr<CSS::StyleProperties>);
+ virtual ~CheckBox() override;
+
+ virtual void paint(PaintContext&, PaintPhase) override;
+
+ const HTML::HTMLInputElement& dom_node() const { return static_cast<const HTML::HTMLInputElement&>(ReplacedBox::dom_node()); }
+ HTML::HTMLInputElement& dom_node() { return static_cast<HTML::HTMLInputElement&>(ReplacedBox::dom_node()); }
+
+private:
+ virtual bool wants_mouse_events() const override { return true; }
+ virtual void handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers) override;
+ virtual void handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers) override;
+ virtual void handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint&, unsigned buttons, unsigned modifiers) override;
+
+ bool m_being_pressed { false };
+ bool m_tracking_mouse { false };
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp
new file mode 100644
index 0000000000..e0a802e002
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp
@@ -0,0 +1,548 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/Dump.h>
+#include <LibWeb/Layout/BlockBox.h>
+#include <LibWeb/Layout/BlockFormattingContext.h>
+#include <LibWeb/Layout/Box.h>
+#include <LibWeb/Layout/FormattingContext.h>
+#include <LibWeb/Layout/InlineFormattingContext.h>
+#include <LibWeb/Layout/ReplacedBox.h>
+#include <LibWeb/Layout/TableBox.h>
+#include <LibWeb/Layout/TableCellBox.h>
+#include <LibWeb/Layout/TableFormattingContext.h>
+#include <LibWeb/Layout/TableRowBox.h>
+
+namespace Web::Layout {
+
+FormattingContext::FormattingContext(Box& context_box, FormattingContext* parent)
+ : m_parent(parent)
+ , m_context_box(&context_box)
+{
+}
+
+FormattingContext::~FormattingContext()
+{
+}
+
+bool FormattingContext::creates_block_formatting_context(const Box& box)
+{
+ if (box.is_root_element())
+ return true;
+ if (box.is_floating())
+ return true;
+ if (box.is_absolutely_positioned())
+ return true;
+ if (box.is_inline_block())
+ return true;
+ if (is<TableCellBox>(box))
+ return true;
+ // FIXME: table-caption
+ // FIXME: anonymous table cells
+ // FIXME: Block elements where overflow has a value other than visible and clip.
+ // FIXME: display: flow-root
+ // FIXME: Elements with contain: layout, content, or paint.
+ // FIXME: flex
+ // FIXME: grid
+ // FIXME: multicol
+ // FIXME: column-span: all
+ return false;
+}
+
+void FormattingContext::layout_inside(Box& box, LayoutMode layout_mode)
+{
+ if (creates_block_formatting_context(box)) {
+ BlockFormattingContext context(box, this);
+ context.run(box, layout_mode);
+ return;
+ }
+ if (is<TableBox>(box)) {
+ TableFormattingContext context(box, this);
+ context.run(box, layout_mode);
+ } else if (box.children_are_inline()) {
+ InlineFormattingContext context(box, this);
+ context.run(box, layout_mode);
+ } else {
+ // FIXME: This needs refactoring!
+ ASSERT(is_block_formatting_context());
+ run(box, layout_mode);
+ }
+}
+
+static float greatest_child_width(const Box& box)
+{
+ float max_width = 0;
+ if (box.children_are_inline()) {
+ for (auto& child : box.line_boxes()) {
+ max_width = max(max_width, child.width());
+ }
+ } else {
+ box.for_each_child_of_type<Box>([&](auto& child) {
+ max_width = max(max_width, child.border_box_width());
+ });
+ }
+ return max_width;
+}
+
+FormattingContext::ShrinkToFitResult FormattingContext::calculate_shrink_to_fit_widths(Box& box)
+{
+ // Calculate the preferred width by formatting the content without breaking lines
+ // other than where explicit line breaks occur.
+ layout_inside(box, LayoutMode::OnlyRequiredLineBreaks);
+ float preferred_width = greatest_child_width(box);
+
+ // Also calculate the preferred minimum width, e.g., by trying all possible line breaks.
+ // CSS 2.2 does not define the exact algorithm.
+
+ layout_inside(box, LayoutMode::AllPossibleLineBreaks);
+ float preferred_minimum_width = greatest_child_width(box);
+
+ return { preferred_width, preferred_minimum_width };
+}
+
+static Gfx::FloatSize solve_replaced_size_constraint(float w, float h, const ReplacedBox& box)
+{
+ // 10.4 Minimum and maximum widths: 'min-width' and 'max-width'
+
+ auto& containing_block = *box.containing_block();
+ auto specified_min_width = box.computed_values().min_width().resolved_or_zero(box, containing_block.width()).to_px(box);
+ auto specified_max_width = box.computed_values().max_width().resolved(CSS::Length::make_px(w), box, containing_block.width()).to_px(box);
+ auto specified_min_height = box.computed_values().min_height().resolved_or_auto(box, containing_block.height()).to_px(box);
+ auto specified_max_height = box.computed_values().max_height().resolved(CSS::Length::make_px(h), box, containing_block.height()).to_px(box);
+
+ auto min_width = min(specified_min_width, specified_max_width);
+ auto max_width = max(specified_min_width, specified_max_width);
+ auto min_height = min(specified_min_height, specified_max_height);
+ auto max_height = max(specified_min_height, specified_max_height);
+
+ if (w > max_width)
+ return { w, max(max_width * h / w, min_height) };
+ if (w < min_width)
+ return { max_width, min(min_width * h / w, max_height) };
+ if (h > max_height)
+ return { max(max_height * w / h, min_width), max_height };
+ if (h < min_height)
+ return { min(min_height * w / h, max_width), min_height };
+ if ((w > max_width && h > max_height) && (max_width / w < max_height / h))
+ return { max_width, max(min_height, max_width * h / w) };
+ if ((w > max_width && h > max_height) && (max_width / w > max_height / h))
+ return { max(min_width, max_height * w / h), max_height };
+ if ((w < min_width && h < min_height) && (min_width / w < min_height / h))
+ return { min(max_width, min_height * w / h), min_height };
+ if ((w < min_width && h < min_height) && (min_width / w > min_height / h))
+ return { min_width, min(max_height, min_width * h / w) };
+ if (w < min_width && h > max_height)
+ return { min_width, max_height };
+ if (w > max_width && h < min_height)
+ return { max_width, min_height };
+ return { w, h };
+}
+
+float FormattingContext::tentative_width_for_replaced_element(const ReplacedBox& box, const CSS::Length& width)
+{
+ auto& containing_block = *box.containing_block();
+ auto specified_height = box.computed_values().height().resolved_or_auto(box, containing_block.height());
+
+ float used_width = width.to_px(box);
+
+ // If 'height' and 'width' both have computed values of 'auto' and the element also has an intrinsic width,
+ // then that intrinsic width is the used value of 'width'.
+ if (specified_height.is_auto() && width.is_auto() && box.has_intrinsic_width()) {
+ used_width = box.intrinsic_width();
+ }
+
+ // If 'height' and 'width' both have computed values of 'auto' and the element has no intrinsic width,
+ // but does have an intrinsic height and intrinsic ratio;
+ // or if 'width' has a computed value of 'auto',
+ // 'height' has some other computed value, and the element does have an intrinsic ratio; then the used value of 'width' is:
+ //
+ // (used height) * (intrinsic ratio)
+ else if ((specified_height.is_auto() && width.is_auto() && !box.has_intrinsic_width() && box.has_intrinsic_height() && box.has_intrinsic_ratio()) || (width.is_auto() && box.has_intrinsic_ratio())) {
+ used_width = compute_height_for_replaced_element(box) * box.intrinsic_ratio();
+ }
+
+ else if (width.is_auto() && box.has_intrinsic_width()) {
+ used_width = box.intrinsic_width();
+ }
+
+ else if (width.is_auto()) {
+ used_width = 300;
+ }
+
+ return used_width;
+}
+
+void FormattingContext::compute_width_for_absolutely_positioned_element(Box& box)
+{
+ if (is<ReplacedBox>(box))
+ compute_width_for_absolutely_positioned_replaced_element(downcast<ReplacedBox>(box));
+ else
+ compute_width_for_absolutely_positioned_non_replaced_element(box);
+}
+
+void FormattingContext::compute_height_for_absolutely_positioned_element(Box& box)
+{
+ if (is<ReplacedBox>(box))
+ compute_height_for_absolutely_positioned_replaced_element(downcast<ReplacedBox>(box));
+ else
+ compute_height_for_absolutely_positioned_non_replaced_element(box);
+}
+
+float FormattingContext::compute_width_for_replaced_element(const ReplacedBox& box)
+{
+ // 10.3.4 Block-level, replaced elements in normal flow...
+ // 10.3.2 Inline, replaced elements
+
+ auto zero_value = CSS::Length::make_px(0);
+ auto& containing_block = *box.containing_block();
+
+ auto margin_left = box.computed_values().margin().left.resolved_or_zero(box, containing_block.width());
+ auto margin_right = box.computed_values().margin().right.resolved_or_zero(box, containing_block.width());
+
+ // A computed value of 'auto' for 'margin-left' or 'margin-right' becomes a used value of '0'.
+ if (margin_left.is_auto())
+ margin_left = zero_value;
+ if (margin_right.is_auto())
+ margin_right = zero_value;
+
+ auto specified_width = box.computed_values().width().resolved_or_auto(box, containing_block.width());
+
+ // 1. The tentative used width is calculated (without 'min-width' and 'max-width')
+ auto used_width = tentative_width_for_replaced_element(box, specified_width);
+
+ // 2. The tentative used width is greater than 'max-width', the rules above are applied again,
+ // but this time using the computed value of 'max-width' as the computed value for 'width'.
+ auto specified_max_width = box.computed_values().max_width().resolved_or_auto(box, containing_block.width());
+ if (!specified_max_width.is_auto()) {
+ if (used_width > specified_max_width.to_px(box)) {
+ used_width = tentative_width_for_replaced_element(box, specified_max_width);
+ }
+ }
+
+ // 3. If the resulting width is smaller than 'min-width', the rules above are applied again,
+ // but this time using the value of 'min-width' as the computed value for 'width'.
+ auto specified_min_width = box.computed_values().min_width().resolved_or_auto(box, containing_block.width());
+ if (!specified_min_width.is_auto()) {
+ if (used_width < specified_min_width.to_px(box)) {
+ used_width = tentative_width_for_replaced_element(box, specified_min_width);
+ }
+ }
+
+ return used_width;
+}
+
+float FormattingContext::tentative_height_for_replaced_element(const ReplacedBox& box, const CSS::Length& height)
+{
+ auto& containing_block = *box.containing_block();
+ auto specified_width = box.computed_values().width().resolved_or_auto(box, containing_block.width());
+
+ float used_height = height.to_px(box);
+
+ // If 'height' and 'width' both have computed values of 'auto' and the element also has
+ // an intrinsic height, then that intrinsic height is the used value of 'height'.
+ if (specified_width.is_auto() && height.is_auto() && box.has_intrinsic_height())
+ used_height = box.intrinsic_height();
+ else if (height.is_auto() && box.has_intrinsic_ratio())
+ used_height = compute_width_for_replaced_element(box) / box.intrinsic_ratio();
+ else if (height.is_auto() && box.has_intrinsic_height())
+ used_height = box.intrinsic_height();
+ else if (height.is_auto())
+ used_height = 150;
+
+ return used_height;
+}
+
+float FormattingContext::compute_height_for_replaced_element(const ReplacedBox& box)
+{
+ // 10.6.2 Inline replaced elements, block-level replaced elements in normal flow,
+ // 'inline-block' replaced elements in normal flow and floating replaced elements
+
+ auto& containing_block = *box.containing_block();
+ auto specified_width = box.computed_values().width().resolved_or_auto(box, containing_block.width());
+ auto specified_height = box.computed_values().height().resolved_or_auto(box, containing_block.height());
+
+ float used_height = tentative_height_for_replaced_element(box, specified_height);
+
+ if (specified_width.is_auto() && specified_height.is_auto() && box.has_intrinsic_ratio()) {
+ float w = tentative_width_for_replaced_element(box, specified_width);
+ float h = used_height;
+ used_height = solve_replaced_size_constraint(w, h, box).height();
+ }
+
+ return used_height;
+}
+
+void FormattingContext::compute_width_for_absolutely_positioned_non_replaced_element(Box& box)
+{
+ auto& containing_block = *box.containing_block();
+ auto& computed_values = box.computed_values();
+ auto zero_value = CSS::Length::make_px(0);
+
+ auto margin_left = CSS::Length::make_auto();
+ auto margin_right = CSS::Length::make_auto();
+ const auto border_left = computed_values.border_left().width;
+ const auto border_right = computed_values.border_right().width;
+ const auto padding_left = computed_values.padding().left.resolved_or_zero(box, containing_block.width());
+ const auto padding_right = computed_values.padding().right.resolved_or_zero(box, containing_block.width());
+
+ auto try_compute_width = [&](const auto& a_width) {
+ margin_left = computed_values.margin().left.resolved_or_zero(box, containing_block.width());
+ margin_right = computed_values.margin().right.resolved_or_zero(box, containing_block.width());
+
+ auto left = computed_values.offset().left.resolved_or_auto(box, containing_block.width());
+ auto right = computed_values.offset().right.resolved_or_auto(box, containing_block.width());
+ auto width = a_width;
+
+ auto solve_for_left = [&] {
+ return CSS::Length(containing_block.width() - margin_left.to_px(box) - border_left - padding_left.to_px(box) - width.to_px(box) - padding_right.to_px(box) - border_right - margin_right.to_px(box) - right.to_px(box), CSS::Length::Type::Px);
+ };
+
+ auto solve_for_width = [&] {
+ return CSS::Length(containing_block.width() - left.to_px(box) - margin_left.to_px(box) - border_left - padding_left.to_px(box) - padding_right.to_px(box) - border_right - margin_right.to_px(box) - right.to_px(box), CSS::Length::Type::Px);
+ };
+
+ auto solve_for_right = [&] {
+ return CSS::Length(containing_block.width() - left.to_px(box) - margin_left.to_px(box) - border_left - padding_left.to_px(box) - width.to_px(box) - padding_right.to_px(box) - border_right - margin_right.to_px(box), CSS::Length::Type::Px);
+ };
+
+ // If all three of 'left', 'width', and 'right' are 'auto':
+ if (left.is_auto() && width.is_auto() && right.is_auto()) {
+ // First set any 'auto' values for 'margin-left' and 'margin-right' to 0.
+ if (margin_left.is_auto())
+ margin_left = CSS::Length::make_px(0);
+ if (margin_right.is_auto())
+ margin_right = CSS::Length::make_px(0);
+ // Then, if the 'direction' property of the element establishing the static-position containing block
+ // is 'ltr' set 'left' to the static position and apply rule number three below;
+ // otherwise, set 'right' to the static position and apply rule number one below.
+ // FIXME: This is very hackish.
+ left = CSS::Length::make_px(0);
+ goto Rule3;
+ }
+
+ if (!left.is_auto() && !width.is_auto() && !right.is_auto()) {
+ // FIXME: This should be solved in a more complicated way.
+ return width;
+ }
+
+ if (margin_left.is_auto())
+ margin_left = CSS::Length::make_px(0);
+ if (margin_right.is_auto())
+ margin_right = CSS::Length::make_px(0);
+
+ // 1. 'left' and 'width' are 'auto' and 'right' is not 'auto',
+ // then the width is shrink-to-fit. Then solve for 'left'
+ if (left.is_auto() && width.is_auto() && !right.is_auto()) {
+ auto result = calculate_shrink_to_fit_widths(box);
+ solve_for_left();
+ auto available_width = solve_for_width();
+ width = CSS::Length(min(max(result.preferred_minimum_width, available_width.to_px(box)), result.preferred_width), CSS::Length::Type::Px);
+ }
+
+ // 2. 'left' and 'right' are 'auto' and 'width' is not 'auto',
+ // then if the 'direction' property of the element establishing
+ // the static-position containing block is 'ltr' set 'left'
+ // to the static position, otherwise set 'right' to the static position.
+ // Then solve for 'left' (if 'direction is 'rtl') or 'right' (if 'direction' is 'ltr').
+ else if (left.is_auto() && right.is_auto() && !width.is_auto()) {
+ // FIXME: Check direction
+ // FIXME: Use the static-position containing block
+ left = zero_value;
+ right = solve_for_right();
+ }
+
+ // 3. 'width' and 'right' are 'auto' and 'left' is not 'auto',
+ // then the width is shrink-to-fit. Then solve for 'right'
+ else if (width.is_auto() && right.is_auto() && !left.is_auto()) {
+ Rule3:
+ auto result = calculate_shrink_to_fit_widths(box);
+ auto available_width = solve_for_width();
+ width = CSS::Length(min(max(result.preferred_minimum_width, available_width.to_px(box)), result.preferred_width), CSS::Length::Type::Px);
+ right = solve_for_right();
+ }
+
+ // 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve for 'left'
+ else if (left.is_auto() && !width.is_auto() && !right.is_auto()) {
+ left = solve_for_left();
+ }
+
+ // 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve for 'width'
+ else if (width.is_auto() && !left.is_auto() && !right.is_auto()) {
+ width = solve_for_width();
+ }
+
+ // 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve for 'right'
+ else if (right.is_auto() && !left.is_auto() && !width.is_auto()) {
+ right = solve_for_right();
+ }
+
+ return width;
+ };
+
+ auto specified_width = computed_values.width().resolved_or_auto(box, containing_block.width());
+
+ // 1. The tentative used width is calculated (without 'min-width' and 'max-width')
+ auto used_width = try_compute_width(specified_width);
+
+ // 2. The tentative used width is greater than 'max-width', the rules above are applied again,
+ // but this time using the computed value of 'max-width' as the computed value for 'width'.
+ auto specified_max_width = computed_values.max_width().resolved_or_auto(box, containing_block.width());
+ if (!specified_max_width.is_auto()) {
+ if (used_width.to_px(box) > specified_max_width.to_px(box)) {
+ used_width = try_compute_width(specified_max_width);
+ }
+ }
+
+ // 3. If the resulting width is smaller than 'min-width', the rules above are applied again,
+ // but this time using the value of 'min-width' as the computed value for 'width'.
+ auto specified_min_width = computed_values.min_width().resolved_or_auto(box, containing_block.width());
+ if (!specified_min_width.is_auto()) {
+ if (used_width.to_px(box) < specified_min_width.to_px(box)) {
+ used_width = try_compute_width(specified_min_width);
+ }
+ }
+
+ box.set_width(used_width.to_px(box));
+
+ box.box_model().margin.left = margin_left.to_px(box);
+ box.box_model().margin.right = margin_right.to_px(box);
+ box.box_model().border.left = border_left;
+ box.box_model().border.right = border_right;
+ box.box_model().padding.left = padding_left.to_px(box);
+ box.box_model().padding.right = padding_right.to_px(box);
+}
+
+void FormattingContext::compute_width_for_absolutely_positioned_replaced_element(ReplacedBox& box)
+{
+ // 10.3.8 Absolutely positioned, replaced elements
+ // The used value of 'width' is determined as for inline replaced elements.
+ box.prepare_for_replaced_layout();
+ box.set_width(compute_width_for_replaced_element(box));
+}
+
+void FormattingContext::compute_height_for_absolutely_positioned_non_replaced_element(Box& box)
+{
+ auto& computed_values = box.computed_values();
+ auto& containing_block = *box.containing_block();
+
+ CSS::Length specified_height;
+
+ if (computed_values.height().is_percentage() && !containing_block.computed_values().height().is_absolute()) {
+ specified_height = CSS::Length::make_auto();
+ } else {
+ specified_height = computed_values.height().resolved_or_auto(box, containing_block.height());
+ }
+
+ auto specified_max_height = computed_values.max_height().resolved_or_auto(box, containing_block.height());
+
+ box.box_model().margin.top = computed_values.margin().top.resolved_or_zero(box, containing_block.width()).to_px(box);
+ box.box_model().margin.bottom = computed_values.margin().bottom.resolved_or_zero(box, containing_block.width()).to_px(box);
+ box.box_model().border.top = computed_values.border_top().width;
+ box.box_model().border.bottom = computed_values.border_bottom().width;
+ box.box_model().padding.top = computed_values.padding().top.resolved_or_zero(box, containing_block.width()).to_px(box);
+ box.box_model().padding.bottom = computed_values.padding().bottom.resolved_or_zero(box, containing_block.width()).to_px(box);
+
+ if (!specified_height.is_auto()) {
+ float used_height = specified_height.to_px(box);
+ if (!specified_max_height.is_auto())
+ used_height = min(used_height, specified_max_height.to_px(box));
+ box.set_height(used_height);
+ }
+}
+
+void FormattingContext::layout_absolutely_positioned_element(Box& box)
+{
+ auto& containing_block = context_box();
+ auto& box_model = box.box_model();
+
+ auto specified_width = box.computed_values().width().resolved_or_auto(box, containing_block.width());
+
+ compute_width_for_absolutely_positioned_element(box);
+ layout_inside(box, LayoutMode::Default);
+ compute_height_for_absolutely_positioned_element(box);
+
+ box_model.margin.left = box.computed_values().margin().left.resolved_or_auto(box, containing_block.width()).to_px(box);
+ box_model.margin.top = box.computed_values().margin().top.resolved_or_auto(box, containing_block.height()).to_px(box);
+ box_model.margin.right = box.computed_values().margin().right.resolved_or_auto(box, containing_block.width()).to_px(box);
+ box_model.margin.bottom = box.computed_values().margin().bottom.resolved_or_auto(box, containing_block.height()).to_px(box);
+
+ box_model.border.left = box.computed_values().border_left().width;
+ box_model.border.right = box.computed_values().border_right().width;
+ box_model.border.top = box.computed_values().border_top().width;
+ box_model.border.bottom = box.computed_values().border_bottom().width;
+
+ box_model.offset.left = box.computed_values().offset().left.resolved_or_auto(box, containing_block.width()).to_px(box);
+ box_model.offset.top = box.computed_values().offset().top.resolved_or_auto(box, containing_block.height()).to_px(box);
+ box_model.offset.right = box.computed_values().offset().right.resolved_or_auto(box, containing_block.width()).to_px(box);
+ box_model.offset.bottom = box.computed_values().offset().bottom.resolved_or_auto(box, containing_block.height()).to_px(box);
+
+ if (box.computed_values().offset().left.is_auto() && specified_width.is_auto() && box.computed_values().offset().right.is_auto()) {
+ if (box.computed_values().margin().left.is_auto())
+ box_model.margin.left = 0;
+ if (box.computed_values().margin().right.is_auto())
+ box_model.margin.right = 0;
+ }
+
+ Gfx::FloatPoint used_offset;
+
+ if (!box.computed_values().offset().left.is_auto()) {
+ float x_offset = box_model.offset.left
+ + box_model.border_box().left;
+ used_offset.set_x(x_offset + box_model.margin.left);
+ } else if (!box.computed_values().offset().right.is_auto()) {
+ float x_offset = 0
+ - box_model.offset.right
+ - box_model.border_box().right;
+ used_offset.set_x(containing_block.width() + x_offset - box.width() - box_model.margin.right);
+ } else {
+ float x_offset = box_model.margin_box().left;
+ used_offset.set_x(x_offset);
+ }
+
+ if (!box.computed_values().offset().top.is_auto()) {
+ float y_offset = box_model.offset.top
+ + box_model.border_box().top;
+ used_offset.set_y(y_offset + box_model.margin.top);
+ } else if (!box.computed_values().offset().bottom.is_auto()) {
+ float y_offset = 0
+ - box_model.offset.bottom
+ - box_model.border_box().bottom;
+ used_offset.set_y(containing_block.height() + y_offset - box.height() - box_model.margin.bottom);
+ } else {
+ float y_offset = box_model.margin_box().top;
+ used_offset.set_y(y_offset);
+ }
+
+ box.set_offset(used_offset);
+}
+
+void FormattingContext::compute_height_for_absolutely_positioned_replaced_element(ReplacedBox& box)
+{
+ // FIXME: Implement this.
+ return compute_height_for_absolutely_positioned_non_replaced_element(box);
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/FormattingContext.h b/Userland/Libraries/LibWeb/Layout/FormattingContext.h
new file mode 100644
index 0000000000..aad024a04e
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/FormattingContext.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/Forward.h>
+
+namespace Web::Layout {
+
+class FormattingContext {
+public:
+ virtual void run(Box&, LayoutMode) = 0;
+
+ Box& context_box() { return *m_context_box; }
+ const Box& context_box() const { return *m_context_box; }
+
+ FormattingContext* parent() { return m_parent; }
+ const FormattingContext* parent() const { return m_parent; }
+
+ virtual bool is_block_formatting_context() const { return false; }
+
+ static bool creates_block_formatting_context(const Box&);
+
+ static float compute_width_for_replaced_element(const ReplacedBox&);
+ static float compute_height_for_replaced_element(const ReplacedBox&);
+
+protected:
+ FormattingContext(Box&, FormattingContext* parent = nullptr);
+ virtual ~FormattingContext();
+
+ void layout_inside(Box&, LayoutMode);
+
+ struct ShrinkToFitResult {
+ float preferred_width { 0 };
+ float preferred_minimum_width { 0 };
+ };
+
+ static float tentative_width_for_replaced_element(const ReplacedBox&, const CSS::Length& width);
+ static float tentative_height_for_replaced_element(const ReplacedBox&, const CSS::Length& width);
+
+ ShrinkToFitResult calculate_shrink_to_fit_widths(Box&);
+
+ void layout_absolutely_positioned_element(Box&);
+ void compute_width_for_absolutely_positioned_element(Box&);
+ void compute_width_for_absolutely_positioned_non_replaced_element(Box&);
+ void compute_width_for_absolutely_positioned_replaced_element(ReplacedBox&);
+ void compute_height_for_absolutely_positioned_element(Box&);
+ void compute_height_for_absolutely_positioned_non_replaced_element(Box&);
+ void compute_height_for_absolutely_positioned_replaced_element(ReplacedBox&);
+
+ FormattingContext* m_parent { nullptr };
+ Box* m_context_box { nullptr };
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/FrameBox.cpp b/Userland/Libraries/LibWeb/Layout/FrameBox.cpp
new file mode 100644
index 0000000000..79ad0e1815
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/FrameBox.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibGUI/Painter.h>
+#include <LibGUI/ScrollBar.h>
+#include <LibGUI/Widget.h>
+#include <LibGfx/Font.h>
+#include <LibGfx/StylePainter.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/InProcessWebView.h>
+#include <LibWeb/Layout/FrameBox.h>
+#include <LibWeb/Layout/InitialContainingBlockBox.h>
+#include <LibWeb/Page/Frame.h>
+
+//#define DEBUG_HIGHLIGHT_FOCUSED_FRAME
+
+namespace Web::Layout {
+
+FrameBox::FrameBox(DOM::Document& document, DOM::Element& element, NonnullRefPtr<CSS::StyleProperties> style)
+ : ReplacedBox(document, element, move(style))
+{
+}
+
+FrameBox::~FrameBox()
+{
+}
+
+void FrameBox::prepare_for_replaced_layout()
+{
+ ASSERT(dom_node().content_frame());
+
+ set_has_intrinsic_width(true);
+ set_has_intrinsic_height(true);
+ // FIXME: Do proper error checking, etc.
+ set_intrinsic_width(dom_node().attribute(HTML::AttributeNames::width).to_int().value_or(300));
+ set_intrinsic_height(dom_node().attribute(HTML::AttributeNames::height).to_int().value_or(150));
+}
+
+void FrameBox::paint(PaintContext& context, PaintPhase phase)
+{
+ ReplacedBox::paint(context, phase);
+
+ if (phase == PaintPhase::Foreground) {
+ auto* hosted_document = dom_node().content_document();
+ if (!hosted_document)
+ return;
+ auto* hosted_layout_tree = hosted_document->layout_node();
+ if (!hosted_layout_tree)
+ return;
+
+ context.painter().save();
+ auto old_viewport_rect = context.viewport_rect();
+
+ context.painter().add_clip_rect(enclosing_int_rect(absolute_rect()));
+ context.painter().translate(absolute_x(), absolute_y());
+
+ context.set_viewport_rect({ {}, dom_node().content_frame()->size() });
+ const_cast<Layout::InitialContainingBlockBox*>(hosted_layout_tree)->paint_all_phases(context);
+
+ context.set_viewport_rect(old_viewport_rect);
+ context.painter().restore();
+
+#ifdef DEBUG_HIGHLIGHT_FOCUSED_FRAME
+ if (dom_node().content_frame()->is_focused_frame()) {
+ context.painter().draw_rect(absolute_rect().to<int>(), Color::Cyan);
+ }
+#endif
+ }
+}
+
+void FrameBox::did_set_rect()
+{
+ ReplacedBox::did_set_rect();
+
+ ASSERT(dom_node().content_frame());
+ dom_node().content_frame()->set_size(size().to_type<int>());
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/FrameBox.h b/Userland/Libraries/LibWeb/Layout/FrameBox.h
new file mode 100644
index 0000000000..93a5216f3f
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/FrameBox.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLIFrameElement.h>
+#include <LibWeb/Layout/ReplacedBox.h>
+
+namespace Web::Layout {
+
+class FrameBox final : public ReplacedBox {
+public:
+ FrameBox(DOM::Document&, DOM::Element&, NonnullRefPtr<CSS::StyleProperties>);
+ virtual ~FrameBox() override;
+
+ virtual void paint(PaintContext&, PaintPhase) override;
+ virtual void prepare_for_replaced_layout() override;
+
+ const HTML::HTMLIFrameElement& dom_node() const { return downcast<HTML::HTMLIFrameElement>(ReplacedBox::dom_node()); }
+ HTML::HTMLIFrameElement& dom_node() { return downcast<HTML::HTMLIFrameElement>(ReplacedBox::dom_node()); }
+
+private:
+ virtual void did_set_rect() override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/ImageBox.cpp b/Userland/Libraries/LibWeb/Layout/ImageBox.cpp
new file mode 100644
index 0000000000..a00c551c7a
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/ImageBox.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibGUI/Painter.h>
+#include <LibGfx/Font.h>
+#include <LibGfx/FontDatabase.h>
+#include <LibGfx/ImageDecoder.h>
+#include <LibGfx/StylePainter.h>
+#include <LibWeb/Layout/ImageBox.h>
+
+namespace Web::Layout {
+
+ImageBox::ImageBox(DOM::Document& document, DOM::Element& element, NonnullRefPtr<CSS::StyleProperties> style, const ImageLoader& image_loader)
+ : ReplacedBox(document, element, move(style))
+ , m_image_loader(image_loader)
+{
+}
+
+ImageBox::~ImageBox()
+{
+}
+
+int ImageBox::preferred_width() const
+{
+ return dom_node().attribute(HTML::AttributeNames::width).to_int().value_or(m_image_loader.width());
+}
+
+int ImageBox::preferred_height() const
+{
+ return dom_node().attribute(HTML::AttributeNames::height).to_int().value_or(m_image_loader.height());
+}
+
+void ImageBox::prepare_for_replaced_layout()
+{
+ if (!m_image_loader.has_loaded_or_failed()) {
+ set_has_intrinsic_width(true);
+ set_has_intrinsic_height(true);
+ set_intrinsic_width(0);
+ set_intrinsic_height(0);
+ } else {
+ if (m_image_loader.width()) {
+ set_has_intrinsic_width(true);
+ set_intrinsic_width(m_image_loader.width());
+ }
+ if (m_image_loader.height()) {
+ set_has_intrinsic_height(true);
+ set_intrinsic_height(m_image_loader.height());
+ }
+
+ if (m_image_loader.width() && m_image_loader.height()) {
+ set_has_intrinsic_ratio(true);
+ set_intrinsic_ratio((float)m_image_loader.width() / (float)m_image_loader.height());
+ } else {
+ set_has_intrinsic_ratio(false);
+ }
+ }
+
+ if (renders_as_alt_text()) {
+ auto& image_element = downcast<HTML::HTMLImageElement>(dom_node());
+ auto& font = Gfx::FontDatabase::default_font();
+ auto alt = image_element.alt();
+ if (alt.is_empty())
+ alt = image_element.src();
+ set_width(font.width(alt) + 16);
+ set_height(font.glyph_height() + 16);
+ }
+
+ if (!has_intrinsic_width() && !has_intrinsic_height()) {
+ set_width(16);
+ set_height(16);
+ }
+}
+
+void ImageBox::paint(PaintContext& context, PaintPhase phase)
+{
+ if (!is_visible())
+ return;
+
+ // FIXME: This should be done at a different level. Also rect() does not include padding etc!
+ if (!context.viewport_rect().intersects(enclosing_int_rect(absolute_rect())))
+ return;
+
+ ReplacedBox::paint(context, phase);
+
+ if (phase == PaintPhase::Foreground) {
+ if (renders_as_alt_text()) {
+ auto& image_element = downcast<HTML::HTMLImageElement>(dom_node());
+ context.painter().set_font(Gfx::FontDatabase::default_font());
+ Gfx::StylePainter::paint_frame(context.painter(), enclosing_int_rect(absolute_rect()), context.palette(), Gfx::FrameShape::Container, Gfx::FrameShadow::Sunken, 2);
+ auto alt = image_element.alt();
+ if (alt.is_empty())
+ alt = image_element.src();
+ context.painter().draw_text(enclosing_int_rect(absolute_rect()), alt, Gfx::TextAlignment::Center, computed_values().color(), Gfx::TextElision::Right);
+ } else if (auto bitmap = m_image_loader.bitmap()) {
+ context.painter().draw_scaled_bitmap(enclosing_int_rect(absolute_rect()), *bitmap, bitmap->rect());
+ }
+ }
+}
+
+bool ImageBox::renders_as_alt_text() const
+{
+ if (is<HTML::HTMLImageElement>(dom_node()))
+ return !m_image_loader.has_image();
+ return false;
+}
+
+void ImageBox::set_visible_in_viewport(Badge<Layout::InitialContainingBlockBox>, bool visible_in_viewport)
+{
+ m_image_loader.set_visible_in_viewport(visible_in_viewport);
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/ImageBox.h b/Userland/Libraries/LibWeb/Layout/ImageBox.h
new file mode 100644
index 0000000000..a404ca1ca0
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/ImageBox.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/HTML/HTMLImageElement.h>
+#include <LibWeb/Layout/ReplacedBox.h>
+
+namespace Web::Layout {
+
+class ImageBox : public ReplacedBox {
+public:
+ ImageBox(DOM::Document&, DOM::Element&, NonnullRefPtr<CSS::StyleProperties>, const ImageLoader&);
+ virtual ~ImageBox() override;
+
+ virtual void prepare_for_replaced_layout() override;
+ virtual void paint(PaintContext&, PaintPhase) override;
+
+ const DOM::Element& dom_node() const { return static_cast<const DOM::Element&>(ReplacedBox::dom_node()); }
+
+ bool renders_as_alt_text() const;
+
+ void set_visible_in_viewport(Badge<InitialContainingBlockBox>, bool);
+
+private:
+ int preferred_width() const;
+ int preferred_height() const;
+
+ const ImageLoader& m_image_loader;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/InitialContainingBlockBox.cpp b/Userland/Libraries/LibWeb/Layout/InitialContainingBlockBox.cpp
new file mode 100644
index 0000000000..320f0f770c
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/InitialContainingBlockBox.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/Dump.h>
+#include <LibWeb/Layout/ImageBox.h>
+#include <LibWeb/Layout/InitialContainingBlockBox.h>
+#include <LibWeb/Layout/WidgetBox.h>
+#include <LibWeb/Page/Frame.h>
+#include <LibWeb/Painting/StackingContext.h>
+
+namespace Web::Layout {
+
+InitialContainingBlockBox::InitialContainingBlockBox(DOM::Document& document, NonnullRefPtr<CSS::StyleProperties> style)
+ : BlockBox(document, &document, move(style))
+{
+}
+
+InitialContainingBlockBox::~InitialContainingBlockBox()
+{
+}
+
+void InitialContainingBlockBox::build_stacking_context_tree()
+{
+ if (stacking_context())
+ return;
+
+ set_stacking_context(make<StackingContext>(*this, nullptr));
+
+ for_each_in_subtree_of_type<Box>([&](Box& box) {
+ if (&box == this)
+ return IterationDecision::Continue;
+ if (!box.establishes_stacking_context()) {
+ ASSERT(!box.stacking_context());
+ return IterationDecision::Continue;
+ }
+ auto* parent_context = box.enclosing_stacking_context();
+ ASSERT(parent_context);
+ box.set_stacking_context(make<StackingContext>(box, parent_context));
+ return IterationDecision::Continue;
+ });
+}
+
+void InitialContainingBlockBox::did_set_viewport_rect(Badge<Frame>, const Gfx::IntRect& a_viewport_rect)
+{
+ Gfx::FloatRect viewport_rect(a_viewport_rect.x(), a_viewport_rect.y(), a_viewport_rect.width(), a_viewport_rect.height());
+ for_each_in_subtree_of_type<ImageBox>([&](auto& layout_image) {
+ const_cast<ImageBox&>(layout_image).set_visible_in_viewport({}, viewport_rect.intersects(layout_image.absolute_rect()));
+ return IterationDecision::Continue;
+ });
+}
+
+void InitialContainingBlockBox::paint_all_phases(PaintContext& context)
+{
+ paint(context, PaintPhase::Background);
+ paint(context, PaintPhase::Border);
+ paint(context, PaintPhase::Foreground);
+ if (context.has_focus())
+ paint(context, PaintPhase::FocusOutline);
+ paint(context, PaintPhase::Overlay);
+}
+
+void InitialContainingBlockBox::paint(PaintContext& context, PaintPhase phase)
+{
+ stacking_context()->paint(context, phase);
+}
+
+HitTestResult InitialContainingBlockBox::hit_test(const Gfx::IntPoint& position, HitTestType type) const
+{
+ return stacking_context()->hit_test(position, type);
+}
+
+void InitialContainingBlockBox::recompute_selection_states()
+{
+ SelectionState state = SelectionState::None;
+
+ auto selection = this->selection().normalized();
+
+ for_each_in_subtree([&](auto& layout_node) {
+ if (!selection.is_valid()) {
+ // Everything gets SelectionState::None.
+ } else if (&layout_node == selection.start().layout_node && &layout_node == selection.end().layout_node) {
+ state = SelectionState::StartAndEnd;
+ } else if (&layout_node == selection.start().layout_node) {
+ state = SelectionState::Start;
+ } else if (&layout_node == selection.end().layout_node) {
+ state = SelectionState::End;
+ } else {
+ if (state == SelectionState::Start)
+ state = SelectionState::Full;
+ else if (state == SelectionState::End || state == SelectionState::StartAndEnd)
+ state = SelectionState::None;
+ }
+ layout_node.set_selection_state(state);
+ return IterationDecision::Continue;
+ });
+}
+
+void InitialContainingBlockBox::set_selection(const LayoutRange& selection)
+{
+ m_selection = selection;
+ recompute_selection_states();
+}
+
+void InitialContainingBlockBox::set_selection_end(const LayoutPosition& position)
+{
+ m_selection.set_end(position);
+ recompute_selection_states();
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/InitialContainingBlockBox.h b/Userland/Libraries/LibWeb/Layout/InitialContainingBlockBox.h
new file mode 100644
index 0000000000..b7b4810364
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/InitialContainingBlockBox.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/Layout/BlockBox.h>
+
+namespace Web::Layout {
+
+class InitialContainingBlockBox final : public BlockBox {
+public:
+ explicit InitialContainingBlockBox(DOM::Document&, NonnullRefPtr<CSS::StyleProperties>);
+ virtual ~InitialContainingBlockBox() override;
+
+ const DOM::Document& dom_node() const { return static_cast<const DOM::Document&>(*Node::dom_node()); }
+
+ void paint_all_phases(PaintContext&);
+ virtual void paint(PaintContext&, PaintPhase) override;
+
+ virtual HitTestResult hit_test(const Gfx::IntPoint&, HitTestType) const override;
+
+ const LayoutRange& selection() const { return m_selection; }
+ void set_selection(const LayoutRange&);
+ void set_selection_end(const LayoutPosition&);
+
+ void did_set_viewport_rect(Badge<Frame>, const Gfx::IntRect&);
+
+ void build_stacking_context_tree();
+
+ void recompute_selection_states();
+
+private:
+ LayoutRange m_selection;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp
new file mode 100644
index 0000000000..b309e02e04
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp
@@ -0,0 +1,254 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/CSS/Length.h>
+#include <LibWeb/DOM/Node.h>
+#include <LibWeb/Dump.h>
+#include <LibWeb/Layout/BlockBox.h>
+#include <LibWeb/Layout/BlockFormattingContext.h>
+#include <LibWeb/Layout/Box.h>
+#include <LibWeb/Layout/InlineFormattingContext.h>
+#include <LibWeb/Layout/InlineNode.h>
+#include <LibWeb/Layout/ReplacedBox.h>
+
+namespace Web::Layout {
+
+InlineFormattingContext::InlineFormattingContext(Box& containing_block, FormattingContext* parent)
+ : FormattingContext(containing_block, parent)
+{
+}
+
+InlineFormattingContext::~InlineFormattingContext()
+{
+}
+
+struct AvailableSpaceForLineInfo {
+ float left { 0 };
+ float right { 0 };
+};
+
+static AvailableSpaceForLineInfo available_space_for_line(const InlineFormattingContext& context, size_t line_index)
+{
+ AvailableSpaceForLineInfo info;
+
+ // FIXME: This is a total hack guess since we don't actually know the final y position of lines here!
+ float line_height = context.containing_block().line_height();
+ float y = (line_index * line_height);
+
+ auto& bfc = static_cast<const BlockFormattingContext&>(*context.parent());
+
+ for (ssize_t i = bfc.left_floating_boxes().size() - 1; i >= 0; --i) {
+ auto& floating_box = *bfc.left_floating_boxes().at(i);
+ auto rect = floating_box.margin_box_as_relative_rect();
+ if (rect.contains_vertically(y)) {
+ info.left = rect.right() + 1;
+ break;
+ }
+ }
+
+ info.right = context.containing_block().width();
+
+ for (ssize_t i = bfc.right_floating_boxes().size() - 1; i >= 0; --i) {
+ auto& floating_box = *bfc.right_floating_boxes().at(i);
+ auto rect = floating_box.margin_box_as_relative_rect();
+ if (rect.contains_vertically(y)) {
+ info.right = rect.left() - 1;
+ break;
+ }
+ }
+
+ return info;
+}
+
+float InlineFormattingContext::available_width_at_line(size_t line_index) const
+{
+ auto info = available_space_for_line(*this, line_index);
+ return info.right - info.left;
+}
+
+void InlineFormattingContext::run(Box&, LayoutMode layout_mode)
+{
+ ASSERT(containing_block().children_are_inline());
+ containing_block().line_boxes().clear();
+ containing_block().for_each_child([&](auto& child) {
+ ASSERT(child.is_inline());
+ if (is<Box>(child) && child.is_absolutely_positioned()) {
+ layout_absolutely_positioned_element(downcast<Box>(child));
+ return;
+ }
+
+ child.split_into_lines(*this, layout_mode);
+ });
+
+ for (auto& line_box : containing_block().line_boxes()) {
+ line_box.trim_trailing_whitespace();
+ }
+
+ // If there's an empty line box at the bottom, just remove it instead of giving it height.
+ if (!containing_block().line_boxes().is_empty() && containing_block().line_boxes().last().fragments().is_empty())
+ containing_block().line_boxes().take_last();
+
+ auto text_align = containing_block().computed_values().text_align();
+ float min_line_height = containing_block().line_height();
+ float content_height = 0;
+ float max_linebox_width = 0;
+
+ for (size_t line_index = 0; line_index < containing_block().line_boxes().size(); ++line_index) {
+ auto& line_box = containing_block().line_boxes()[line_index];
+ float max_height = min_line_height;
+ for (auto& fragment : line_box.fragments()) {
+ max_height = max(max_height, fragment.height());
+ }
+
+ float x_offset = available_space_for_line(*this, line_index).left;
+
+ float excess_horizontal_space = (float)containing_block().width() - line_box.width();
+
+ switch (text_align) {
+ case CSS::TextAlign::Center:
+ case CSS::TextAlign::LibwebCenter:
+ x_offset += excess_horizontal_space / 2;
+ break;
+ case CSS::TextAlign::Right:
+ x_offset += excess_horizontal_space;
+ break;
+ case CSS::TextAlign::Left:
+ case CSS::TextAlign::Justify:
+ default:
+ break;
+ }
+
+ float excess_horizontal_space_including_whitespace = excess_horizontal_space;
+ int whitespace_count = 0;
+ if (text_align == CSS::TextAlign::Justify) {
+ for (auto& fragment : line_box.fragments()) {
+ if (fragment.is_justifiable_whitespace()) {
+ ++whitespace_count;
+ excess_horizontal_space_including_whitespace += fragment.width();
+ }
+ }
+ }
+
+ float justified_space_width = whitespace_count ? (excess_horizontal_space_including_whitespace / (float)whitespace_count) : 0;
+
+ for (size_t i = 0; i < line_box.fragments().size(); ++i) {
+ auto& fragment = line_box.fragments()[i];
+
+ if (fragment.type() == LineBoxFragment::Type::Leading || fragment.type() == LineBoxFragment::Type::Trailing) {
+ fragment.set_height(max_height);
+ } else {
+ fragment.set_height(max(min_line_height, fragment.height()));
+ }
+
+ // Vertically align everyone's bottom to the line.
+ // FIXME: Support other kinds of vertical alignment.
+ fragment.set_offset({ roundf(x_offset + fragment.offset().x()), content_height + (max_height - fragment.height()) });
+
+ if (text_align == CSS::TextAlign::Justify
+ && fragment.is_justifiable_whitespace()
+ && fragment.width() != justified_space_width) {
+ float diff = justified_space_width - fragment.width();
+ fragment.set_width(justified_space_width);
+ // Shift subsequent sibling fragments to the right to adjust for change in width.
+ for (size_t j = i + 1; j < line_box.fragments().size(); ++j) {
+ auto offset = line_box.fragments()[j].offset();
+ offset.move_by(diff, 0);
+ line_box.fragments()[j].set_offset(offset);
+ }
+ }
+ }
+
+ if (!line_box.fragments().is_empty()) {
+ float left_edge = line_box.fragments().first().offset().x();
+ float right_edge = line_box.fragments().last().offset().x() + line_box.fragments().last().width();
+ float final_line_box_width = right_edge - left_edge;
+ line_box.m_width = final_line_box_width;
+ max_linebox_width = max(max_linebox_width, final_line_box_width);
+ }
+
+ content_height += max_height;
+ }
+
+ if (layout_mode != LayoutMode::Default) {
+ containing_block().set_width(max_linebox_width);
+ }
+
+ containing_block().set_height(content_height);
+}
+
+void InlineFormattingContext::dimension_box_on_line(Box& box, LayoutMode layout_mode)
+{
+ if (is<ReplacedBox>(box)) {
+ auto& replaced = downcast<ReplacedBox>(box);
+ replaced.set_width(compute_width_for_replaced_element(replaced));
+ replaced.set_height(compute_height_for_replaced_element(replaced));
+ return;
+ }
+
+ if (box.is_inline_block()) {
+ auto& inline_block = const_cast<BlockBox&>(downcast<BlockBox>(box));
+
+ if (inline_block.computed_values().width().is_undefined_or_auto()) {
+ auto result = calculate_shrink_to_fit_widths(inline_block);
+
+ auto margin_left = inline_block.computed_values().margin().left.resolved_or_zero(inline_block, containing_block().width()).to_px(inline_block);
+ auto border_left_width = inline_block.computed_values().border_left().width;
+ auto padding_left = inline_block.computed_values().padding().left.resolved_or_zero(inline_block, containing_block().width()).to_px(inline_block);
+
+ auto margin_right = inline_block.computed_values().margin().right.resolved_or_zero(inline_block, containing_block().width()).to_px(inline_block);
+ auto border_right_width = inline_block.computed_values().border_right().width;
+ auto padding_right = inline_block.computed_values().padding().right.resolved_or_zero(inline_block, containing_block().width()).to_px(inline_block);
+
+ auto available_width = containing_block().width()
+ - margin_left
+ - border_left_width
+ - padding_left
+ - padding_right
+ - border_right_width
+ - margin_right;
+
+ auto width = min(max(result.preferred_minimum_width, available_width), result.preferred_width);
+ inline_block.set_width(width);
+ } else {
+ inline_block.set_width(inline_block.computed_values().width().resolved_or_zero(inline_block, containing_block().width()).to_px(inline_block));
+ }
+ layout_inside(inline_block, layout_mode);
+
+ if (inline_block.computed_values().height().is_undefined_or_auto()) {
+ // FIXME: (10.6.6) If 'height' is 'auto', the height depends on the element's descendants per 10.6.7.
+ } else {
+ inline_block.set_height(inline_block.computed_values().height().resolved_or_zero(inline_block, containing_block().height()).to_px(inline_block));
+ }
+ return;
+ }
+
+ // Non-replaced, non-inline-block, box on a line!?
+ // I don't think we should be here. Dump the box tree so we can take a look at it.
+ dbgln("FIXME: I've been asked to dimension a non-replaced, non-inline-block box on a line:");
+ dump_tree(box);
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.h b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.h
new file mode 100644
index 0000000000..38af898d21
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/Types.h>
+#include <LibWeb/Forward.h>
+#include <LibWeb/Layout/FormattingContext.h>
+
+namespace Web::Layout {
+
+class InlineFormattingContext final : public FormattingContext {
+public:
+ InlineFormattingContext(Box& containing_block, FormattingContext* parent);
+ ~InlineFormattingContext();
+
+ Box& containing_block() { return context_box(); }
+ const Box& containing_block() const { return context_box(); }
+
+ virtual void run(Box&, LayoutMode) override;
+
+ float available_width_at_line(size_t line_index) const;
+
+ void dimension_box_on_line(Box&, LayoutMode);
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/InlineNode.cpp b/Userland/Libraries/LibWeb/Layout/InlineNode.cpp
new file mode 100644
index 0000000000..ab3974d54f
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/InlineNode.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibGfx/Painter.h>
+#include <LibWeb/DOM/Element.h>
+#include <LibWeb/Layout/BlockBox.h>
+#include <LibWeb/Layout/InlineFormattingContext.h>
+#include <LibWeb/Layout/InlineNode.h>
+
+namespace Web::Layout {
+
+InlineNode::InlineNode(DOM::Document& document, DOM::Element& element, NonnullRefPtr<CSS::StyleProperties> style)
+ : Layout::NodeWithStyleAndBoxModelMetrics(document, &element, move(style))
+{
+ set_inline(true);
+}
+
+InlineNode::~InlineNode()
+{
+}
+
+void InlineNode::split_into_lines(InlineFormattingContext& context, LayoutMode layout_mode)
+{
+ auto& containing_block = context.context_box();
+
+ if (!computed_values().padding().left.is_undefined_or_auto()) {
+ float padding_left = computed_values().padding().left.resolved(CSS::Length::make_px(0), *this, containing_block.width()).to_px(*this);
+ containing_block.ensure_last_line_box().add_fragment(*this, 0, 0, padding_left, 0, LineBoxFragment::Type::Leading);
+ }
+
+ NodeWithStyleAndBoxModelMetrics::split_into_lines(context, layout_mode);
+
+ if (!computed_values().padding().right.is_undefined_or_auto()) {
+ float padding_right = computed_values().padding().right.resolved(CSS::Length::make_px(0), *this, containing_block.width()).to_px(*this);
+ containing_block.ensure_last_line_box().add_fragment(*this, 0, 0, padding_right, 0, LineBoxFragment::Type::Trailing);
+ }
+}
+
+void InlineNode::paint_fragment(PaintContext& context, const LineBoxFragment& fragment, PaintPhase phase) const
+{
+ auto& painter = context.painter();
+
+ if (phase == PaintPhase::Background) {
+ painter.fill_rect(enclosing_int_rect(fragment.absolute_rect()), computed_values().background_color());
+ }
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/InlineNode.h b/Userland/Libraries/LibWeb/Layout/InlineNode.h
new file mode 100644
index 0000000000..8f47b24f34
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/InlineNode.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/Layout/Box.h>
+
+namespace Web::Layout {
+
+class InlineNode : public NodeWithStyleAndBoxModelMetrics {
+public:
+ InlineNode(DOM::Document&, DOM::Element&, NonnullRefPtr<CSS::StyleProperties>);
+ virtual ~InlineNode() override;
+
+ virtual void paint_fragment(PaintContext&, const LineBoxFragment&, PaintPhase) const override;
+
+ virtual void split_into_lines(InlineFormattingContext&, LayoutMode) override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/LayoutPosition.cpp b/Userland/Libraries/LibWeb/Layout/LayoutPosition.cpp
new file mode 100644
index 0000000000..307c81f9f7
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/LayoutPosition.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/DOM/Position.h>
+#include <LibWeb/DOM/Range.h>
+#include <LibWeb/Layout/LayoutPosition.h>
+#include <LibWeb/Layout/Node.h>
+
+namespace Web::Layout {
+
+DOM::Position LayoutPosition::to_dom_position() const
+{
+ if (!layout_node)
+ return {};
+
+ // FIXME: Verify that there are no shenanigans going on.
+ return { const_cast<DOM::Node&>(*layout_node->dom_node()), (unsigned)index_in_node };
+}
+
+LayoutRange LayoutRange::normalized() const
+{
+ if (!is_valid())
+ return {};
+ if (m_start.layout_node == m_end.layout_node) {
+ if (m_start.index_in_node < m_end.index_in_node)
+ return *this;
+ return { m_end, m_start };
+ }
+ if (m_start.layout_node->is_before(*m_end.layout_node))
+ return *this;
+ return { m_end, m_start };
+}
+
+NonnullRefPtr<DOM::Range> LayoutRange::to_dom_range() const
+{
+ ASSERT(is_valid());
+
+ auto start = m_start.to_dom_position();
+ auto end = m_end.to_dom_position();
+
+ return DOM::Range::create(*start.node(), start.offset(), *end.node(), end.offset());
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/LayoutPosition.h b/Userland/Libraries/LibWeb/Layout/LayoutPosition.h
new file mode 100644
index 0000000000..840a9c388b
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/LayoutPosition.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/RefPtr.h>
+#include <LibWeb/Forward.h>
+#include <LibWeb/Layout/Node.h>
+
+namespace Web::Layout {
+
+class Node;
+
+struct LayoutPosition {
+ RefPtr<Node> layout_node;
+ int index_in_node { 0 };
+
+ DOM::Position to_dom_position() const;
+};
+
+class LayoutRange {
+public:
+ LayoutRange() { }
+ LayoutRange(const LayoutPosition& start, const LayoutPosition& end)
+ : m_start(start)
+ , m_end(end)
+ {
+ }
+
+ bool is_valid() const { return m_start.layout_node && m_end.layout_node; }
+
+ void set(const LayoutPosition& start, const LayoutPosition& end)
+ {
+ m_start = start;
+ m_end = end;
+ }
+
+ void set_start(const LayoutPosition& start) { m_start = start; }
+ void set_end(const LayoutPosition& end) { m_end = end; }
+
+ const LayoutPosition& start() const { return m_start; }
+ LayoutPosition& start() { return m_start; }
+ const LayoutPosition& end() const { return m_end; }
+ LayoutPosition& end() { return m_end; }
+
+ LayoutRange normalized() const;
+
+ NonnullRefPtr<DOM::Range> to_dom_range() const;
+
+private:
+ LayoutPosition m_start;
+ LayoutPosition m_end;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/LineBox.cpp b/Userland/Libraries/LibWeb/Layout/LineBox.cpp
new file mode 100644
index 0000000000..df08bfe9eb
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/LineBox.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/Utf8View.h>
+#include <LibWeb/Layout/Box.h>
+#include <LibWeb/Layout/LineBox.h>
+#include <LibWeb/Layout/Node.h>
+#include <LibWeb/Layout/TextNode.h>
+#include <ctype.h>
+
+namespace Web::Layout {
+
+void LineBox::add_fragment(Node& layout_node, int start, int length, float width, float height, LineBoxFragment::Type fragment_type)
+{
+ bool text_align_is_justify = layout_node.computed_values().text_align() == CSS::TextAlign::Justify;
+ if (!text_align_is_justify && !m_fragments.is_empty() && &m_fragments.last().layout_node() == &layout_node) {
+ // The fragment we're adding is from the last Layout::Node on the line.
+ // Expand the last fragment instead of adding a new one with the same Layout::Node.
+ m_fragments.last().m_length = (start - m_fragments.last().m_start) + length;
+ m_fragments.last().set_width(m_fragments.last().width() + width);
+ } else {
+ m_fragments.append(make<LineBoxFragment>(layout_node, start, length, Gfx::FloatPoint(m_width, 0.0f), Gfx::FloatSize(width, height), fragment_type));
+ }
+ m_width += width;
+
+ if (is<Box>(layout_node))
+ downcast<Box>(layout_node).set_containing_line_box_fragment(m_fragments.last());
+}
+
+void LineBox::trim_trailing_whitespace()
+{
+ while (!m_fragments.is_empty() && m_fragments.last().is_justifiable_whitespace()) {
+ auto fragment = m_fragments.take_last();
+ m_width -= fragment->width();
+ }
+
+ if (m_fragments.is_empty())
+ return;
+
+ auto last_text = m_fragments.last().text();
+ if (last_text.is_null())
+ return;
+ auto& last_fragment = m_fragments.last();
+
+ int space_width = last_fragment.layout_node().font().glyph_width(' ');
+ while (last_fragment.length() && isspace(last_text[last_fragment.length() - 1])) {
+ last_fragment.m_length -= 1;
+ last_fragment.set_width(last_fragment.width() - space_width);
+ m_width -= space_width;
+ }
+}
+
+bool LineBox::is_empty_or_ends_in_whitespace() const
+{
+ if (m_fragments.is_empty())
+ return true;
+ return m_fragments.last().ends_in_whitespace();
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/LineBox.h b/Userland/Libraries/LibWeb/Layout/LineBox.h
new file mode 100644
index 0000000000..dcf1efd081
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/LineBox.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/NonnullOwnPtrVector.h>
+#include <AK/Vector.h>
+#include <LibWeb/Layout/LineBoxFragment.h>
+
+namespace Web::Layout {
+
+class LineBox {
+public:
+ LineBox() { }
+
+ float width() const { return m_width; }
+
+ void add_fragment(Node& layout_node, int start, int length, float width, float height, LineBoxFragment::Type = LineBoxFragment::Type::Normal);
+
+ const NonnullOwnPtrVector<LineBoxFragment>& fragments() const { return m_fragments; }
+ NonnullOwnPtrVector<LineBoxFragment>& fragments() { return m_fragments; }
+
+ void trim_trailing_whitespace();
+
+ bool is_empty_or_ends_in_whitespace() const;
+
+private:
+ friend class BlockBox;
+ friend class InlineFormattingContext;
+ NonnullOwnPtrVector<LineBoxFragment> m_fragments;
+ float m_width { 0 };
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/LineBoxFragment.cpp b/Userland/Libraries/LibWeb/Layout/LineBoxFragment.cpp
new file mode 100644
index 0000000000..ba49f93d18
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/LineBoxFragment.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/Utf8View.h>
+#include <LibGUI/Painter.h>
+#include <LibWeb/Layout/InitialContainingBlockBox.h>
+#include <LibWeb/Layout/LineBoxFragment.h>
+#include <LibWeb/Layout/TextNode.h>
+#include <LibWeb/Painting/BorderPainting.h>
+#include <LibWeb/Painting/PaintContext.h>
+#include <ctype.h>
+
+namespace Web::Layout {
+
+void LineBoxFragment::paint(PaintContext& context, PaintPhase phase)
+{
+ for (auto* ancestor = layout_node().parent(); ancestor; ancestor = ancestor->parent()) {
+ if (!ancestor->is_visible())
+ return;
+ }
+
+ layout_node().paint_fragment(context, *this, phase);
+}
+
+bool LineBoxFragment::ends_in_whitespace() const
+{
+ auto text = this->text();
+ if (text.is_empty())
+ return false;
+ return isspace(text[text.length() - 1]);
+}
+
+bool LineBoxFragment::is_justifiable_whitespace() const
+{
+ return text() == " ";
+}
+
+StringView LineBoxFragment::text() const
+{
+ if (!is<TextNode>(layout_node()))
+ return {};
+ return downcast<TextNode>(layout_node()).text_for_rendering().substring_view(m_start, m_length);
+}
+
+const Gfx::FloatRect LineBoxFragment::absolute_rect() const
+{
+ Gfx::FloatRect rect { {}, size() };
+ rect.set_location(m_layout_node.containing_block()->absolute_position());
+ rect.move_by(offset());
+ return rect;
+}
+
+int LineBoxFragment::text_index_at(float x) const
+{
+ if (!is<TextNode>(layout_node()))
+ return 0;
+ auto& layout_text = downcast<TextNode>(layout_node());
+ auto& font = layout_text.font();
+ Utf8View view(text());
+
+ float relative_x = x - absolute_x();
+ float glyph_spacing = font.glyph_spacing();
+
+ if (relative_x < 0)
+ return 0;
+
+ float width_so_far = 0;
+ for (auto it = view.begin(); it != view.end(); ++it) {
+ float glyph_width = font.glyph_or_emoji_width(*it);
+ if ((width_so_far + (glyph_width + glyph_spacing) / 2) > relative_x)
+ return m_start + view.byte_offset_of(it);
+ width_so_far += glyph_width + glyph_spacing;
+ }
+ return m_start + m_length;
+}
+
+Gfx::FloatRect LineBoxFragment::selection_rect(const Gfx::Font& font) const
+{
+ if (layout_node().selection_state() == Node::SelectionState::None)
+ return {};
+
+ if (layout_node().selection_state() == Node::SelectionState::Full)
+ return absolute_rect();
+
+ auto selection = layout_node().root().selection().normalized();
+ if (!selection.is_valid())
+ return {};
+ if (!is<TextNode>(layout_node()))
+ return {};
+
+ const auto start_index = m_start;
+ const auto end_index = m_start + m_length;
+ auto text = this->text();
+
+ if (layout_node().selection_state() == Node::SelectionState::StartAndEnd) {
+ // we are in the start/end node (both the same)
+ if (start_index > selection.end().index_in_node)
+ return {};
+ if (end_index < selection.start().index_in_node)
+ return {};
+
+ if (selection.start().index_in_node == selection.end().index_in_node)
+ return {};
+
+ auto selection_start_in_this_fragment = max(0, selection.start().index_in_node - m_start);
+ auto selection_end_in_this_fragment = min(m_length, selection.end().index_in_node - m_start);
+ auto pixel_distance_to_first_selected_character = font.width(text.substring_view(0, selection_start_in_this_fragment));
+ auto pixel_width_of_selection = font.width(text.substring_view(selection_start_in_this_fragment, selection_end_in_this_fragment - selection_start_in_this_fragment)) + 1;
+
+ auto rect = absolute_rect();
+ rect.set_x(rect.x() + pixel_distance_to_first_selected_character);
+ rect.set_width(pixel_width_of_selection);
+
+ return rect;
+ }
+ if (layout_node().selection_state() == Node::SelectionState::Start) {
+ // we are in the start node
+ if (end_index < selection.start().index_in_node)
+ return {};
+
+ auto selection_start_in_this_fragment = max(0, selection.start().index_in_node - m_start);
+ auto selection_end_in_this_fragment = m_length;
+ auto pixel_distance_to_first_selected_character = font.width(text.substring_view(0, selection_start_in_this_fragment));
+ auto pixel_width_of_selection = font.width(text.substring_view(selection_start_in_this_fragment, selection_end_in_this_fragment - selection_start_in_this_fragment)) + 1;
+
+ auto rect = absolute_rect();
+ rect.set_x(rect.x() + pixel_distance_to_first_selected_character);
+ rect.set_width(pixel_width_of_selection);
+
+ return rect;
+ }
+ if (layout_node().selection_state() == Node::SelectionState::End) {
+ // we are in the end node
+ if (start_index > selection.end().index_in_node)
+ return {};
+
+ auto selection_start_in_this_fragment = 0;
+ auto selection_end_in_this_fragment = min(selection.end().index_in_node, m_length);
+ auto pixel_distance_to_first_selected_character = font.width(text.substring_view(0, selection_start_in_this_fragment));
+ auto pixel_width_of_selection = font.width(text.substring_view(selection_start_in_this_fragment, selection_end_in_this_fragment - selection_start_in_this_fragment)) + 1;
+
+ auto rect = absolute_rect();
+ rect.set_x(rect.x() + pixel_distance_to_first_selected_character);
+ rect.set_width(pixel_width_of_selection);
+
+ return rect;
+ }
+ return {};
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/LineBoxFragment.h b/Userland/Libraries/LibWeb/Layout/LineBoxFragment.h
new file mode 100644
index 0000000000..03819f0fb3
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/LineBoxFragment.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/Weakable.h>
+#include <LibGfx/Forward.h>
+#include <LibGfx/Rect.h>
+#include <LibWeb/Forward.h>
+
+namespace Web::Layout {
+
+class LineBoxFragment : public Weakable<LineBoxFragment> {
+ friend class LineBox;
+
+public:
+ enum class Type {
+ Normal,
+ Leading,
+ Trailing,
+ };
+
+ LineBoxFragment(Node& layout_node, int start, int length, const Gfx::FloatPoint& offset, const Gfx::FloatSize& size, Type type)
+ : m_layout_node(layout_node)
+ , m_start(start)
+ , m_length(length)
+ , m_offset(offset)
+ , m_size(size)
+ , m_type(type)
+ {
+ }
+
+ Node& layout_node() const { return m_layout_node; }
+ int start() const { return m_start; }
+ int length() const { return m_length; }
+ const Gfx::FloatRect absolute_rect() const;
+ Type type() const { return m_type; }
+
+ const Gfx::FloatPoint& offset() const { return m_offset; }
+ void set_offset(const Gfx::FloatPoint& offset) { m_offset = offset; }
+
+ const Gfx::FloatSize& size() const { return m_size; }
+ void set_width(float width) { m_size.set_width(width); }
+ void set_height(float height) { m_size.set_height(height); }
+ float width() const { return m_size.width(); }
+ float height() const { return m_size.height(); }
+
+ float absolute_x() const { return absolute_rect().x(); }
+
+ void paint(PaintContext&, PaintPhase);
+
+ bool ends_in_whitespace() const;
+ bool is_justifiable_whitespace() const;
+ StringView text() const;
+
+ int text_index_at(float x) const;
+
+ Gfx::FloatRect selection_rect(const Gfx::Font&) const;
+
+private:
+ Node& m_layout_node;
+ int m_start { 0 };
+ int m_length { 0 };
+ Gfx::FloatPoint m_offset;
+ Gfx::FloatSize m_size;
+ Type m_type { Type::Normal };
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/ListItemBox.cpp b/Userland/Libraries/LibWeb/Layout/ListItemBox.cpp
new file mode 100644
index 0000000000..06ed92533e
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/ListItemBox.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/Layout/ListItemBox.h>
+#include <LibWeb/Layout/ListItemMarkerBox.h>
+
+namespace Web::Layout {
+
+ListItemBox::ListItemBox(DOM::Document& document, DOM::Element& element, NonnullRefPtr<CSS::StyleProperties> style)
+ : Layout::BlockBox(document, &element, move(style))
+{
+}
+
+ListItemBox::~ListItemBox()
+{
+}
+
+void ListItemBox::layout_marker()
+{
+ if (m_marker) {
+ remove_child(*m_marker);
+ m_marker = nullptr;
+ }
+
+ if (computed_values().list_style_type() == CSS::ListStyleType::None)
+ return;
+
+ if (!m_marker) {
+ m_marker = adopt(*new ListItemMarkerBox(document()));
+ if (first_child())
+ m_marker->set_inline(first_child()->is_inline());
+ append_child(*m_marker);
+ }
+
+ m_marker->set_offset(-8, 0);
+ m_marker->set_size(4, height());
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/ListItemBox.h b/Userland/Libraries/LibWeb/Layout/ListItemBox.h
new file mode 100644
index 0000000000..39b5af84e3
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/ListItemBox.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/DOM/Element.h>
+#include <LibWeb/Layout/BlockBox.h>
+
+namespace Web::Layout {
+
+class ListItemMarkerBox;
+
+class ListItemBox final : public BlockBox {
+public:
+ ListItemBox(DOM::Document&, DOM::Element&, NonnullRefPtr<CSS::StyleProperties>);
+ virtual ~ListItemBox() override;
+
+ void layout_marker();
+
+private:
+ RefPtr<ListItemMarkerBox> m_marker;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/ListItemMarkerBox.cpp b/Userland/Libraries/LibWeb/Layout/ListItemMarkerBox.cpp
new file mode 100644
index 0000000000..2b387112e6
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/ListItemMarkerBox.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibGUI/Painter.h>
+#include <LibWeb/Layout/ListItemMarkerBox.h>
+
+namespace Web::Layout {
+
+ListItemMarkerBox::ListItemMarkerBox(DOM::Document& document)
+ : Box(document, nullptr, CSS::StyleProperties::create())
+{
+}
+
+ListItemMarkerBox::~ListItemMarkerBox()
+{
+}
+
+void ListItemMarkerBox::paint(PaintContext& context, PaintPhase phase)
+{
+ if (phase != PaintPhase::Foreground)
+ return;
+ Gfx::IntRect bullet_rect { 0, 0, 4, 4 };
+ bullet_rect.center_within(enclosing_int_rect(absolute_rect()));
+ // FIXME: It would be nicer to not have to go via the parent here to get our inherited style.
+ auto color = parent()->computed_values().color();
+ context.painter().fill_rect(bullet_rect, color);
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/ListItemMarkerBox.h b/Userland/Libraries/LibWeb/Layout/ListItemMarkerBox.h
new file mode 100644
index 0000000000..e570fd21df
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/ListItemMarkerBox.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/Layout/Box.h>
+
+namespace Web::Layout {
+
+class ListItemMarkerBox final : public Box {
+public:
+ explicit ListItemMarkerBox(DOM::Document&);
+ virtual ~ListItemMarkerBox() override;
+
+ virtual void paint(PaintContext&, PaintPhase) override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/Node.cpp b/Userland/Libraries/LibWeb/Layout/Node.cpp
new file mode 100644
index 0000000000..7af494a0fb
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/Node.cpp
@@ -0,0 +1,339 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/Demangle.h>
+#include <LibGUI/Painter.h>
+#include <LibGfx/FontDatabase.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/DOM/Element.h>
+#include <LibWeb/Dump.h>
+#include <LibWeb/HTML/HTMLHtmlElement.h>
+#include <LibWeb/Layout/BlockBox.h>
+#include <LibWeb/Layout/FormattingContext.h>
+#include <LibWeb/Layout/InitialContainingBlockBox.h>
+#include <LibWeb/Layout/Node.h>
+#include <LibWeb/Layout/ReplacedBox.h>
+#include <LibWeb/Layout/TextNode.h>
+#include <LibWeb/Page/Frame.h>
+#include <typeinfo>
+
+namespace Web::Layout {
+
+Node::Node(DOM::Document& document, DOM::Node* node)
+ : m_document(document)
+ , m_dom_node(node)
+{
+ if (m_dom_node)
+ m_dom_node->set_layout_node({}, this);
+}
+
+Node::~Node()
+{
+ if (m_dom_node && m_dom_node->layout_node() == this)
+ m_dom_node->set_layout_node({}, nullptr);
+}
+
+bool Node::can_contain_boxes_with_position_absolute() const
+{
+ return computed_values().position() != CSS::Position::Static || is<InitialContainingBlockBox>(*this);
+}
+
+const BlockBox* Node::containing_block() const
+{
+ auto nearest_block_ancestor = [this] {
+ auto* ancestor = parent();
+ while (ancestor && !is<BlockBox>(*ancestor))
+ ancestor = ancestor->parent();
+ return downcast<BlockBox>(ancestor);
+ };
+
+ if (is<TextNode>(*this))
+ return nearest_block_ancestor();
+
+ auto position = computed_values().position();
+
+ if (position == CSS::Position::Absolute) {
+ auto* ancestor = parent();
+ while (ancestor && !ancestor->can_contain_boxes_with_position_absolute())
+ ancestor = ancestor->parent();
+ while (ancestor && (!is<BlockBox>(ancestor) || ancestor->is_anonymous()))
+ ancestor = ancestor->containing_block();
+ return downcast<BlockBox>(ancestor);
+ }
+
+ if (position == CSS::Position::Fixed)
+ return &root();
+
+ return nearest_block_ancestor();
+}
+
+void Node::paint(PaintContext& context, PaintPhase phase)
+{
+ if (!is_visible())
+ return;
+
+ before_children_paint(context, phase);
+
+ for_each_child_in_paint_order([&](auto& child) {
+ child.paint(context, phase);
+ });
+
+ after_children_paint(context, phase);
+}
+
+HitTestResult Node::hit_test(const Gfx::IntPoint& position, HitTestType type) const
+{
+ HitTestResult result;
+ for_each_child_in_paint_order([&](auto& child) {
+ auto child_result = child.hit_test(position, type);
+ if (child_result.layout_node)
+ result = child_result;
+ });
+ return result;
+}
+
+const Frame& Node::frame() const
+{
+ ASSERT(document().frame());
+ return *document().frame();
+}
+
+Frame& Node::frame()
+{
+ ASSERT(document().frame());
+ return *document().frame();
+}
+
+const InitialContainingBlockBox& Node::root() const
+{
+ ASSERT(document().layout_node());
+ return *document().layout_node();
+}
+
+InitialContainingBlockBox& Node::root()
+{
+ ASSERT(document().layout_node());
+ return *document().layout_node();
+}
+
+void Node::split_into_lines(InlineFormattingContext& context, LayoutMode layout_mode)
+{
+ for_each_child([&](auto& child) {
+ child.split_into_lines(context, layout_mode);
+ });
+}
+
+void Node::set_needs_display()
+{
+ if (auto* block = containing_block()) {
+ block->for_each_fragment([&](auto& fragment) {
+ if (&fragment.layout_node() == this || is_ancestor_of(fragment.layout_node())) {
+ frame().set_needs_display(enclosing_int_rect(fragment.absolute_rect()));
+ }
+ return IterationDecision::Continue;
+ });
+ }
+}
+
+Gfx::FloatPoint Node::box_type_agnostic_position() const
+{
+ if (is<Box>(*this))
+ return downcast<Box>(*this).absolute_position();
+ ASSERT(is_inline());
+ Gfx::FloatPoint position;
+ if (auto* block = containing_block()) {
+ block->for_each_fragment([&](auto& fragment) {
+ if (&fragment.layout_node() == this || is_ancestor_of(fragment.layout_node())) {
+ position = fragment.absolute_rect().location();
+ return IterationDecision::Break;
+ }
+ return IterationDecision::Continue;
+ });
+ }
+ return position;
+}
+
+bool Node::is_floating() const
+{
+ if (!has_style())
+ return false;
+ return computed_values().float_() != CSS::Float::None;
+}
+
+bool Node::is_positioned() const
+{
+ return has_style() && computed_values().position() != CSS::Position::Static;
+}
+
+bool Node::is_absolutely_positioned() const
+{
+ if (!has_style())
+ return false;
+ auto position = computed_values().position();
+ return position == CSS::Position::Absolute || position == CSS::Position::Fixed;
+}
+
+bool Node::is_fixed_position() const
+{
+ if (!has_style())
+ return false;
+ auto position = computed_values().position();
+ return position == CSS::Position::Fixed;
+}
+
+NodeWithStyle::NodeWithStyle(DOM::Document& document, DOM::Node* node, NonnullRefPtr<CSS::StyleProperties> specified_style)
+ : Node(document, node)
+{
+ m_has_style = true;
+ apply_style(*specified_style);
+}
+
+NodeWithStyle::NodeWithStyle(DOM::Document& document, DOM::Node* node, CSS::ComputedValues computed_values)
+ : Node(document, node)
+ , m_computed_values(move(computed_values))
+{
+ m_has_style = true;
+ m_font = Gfx::FontDatabase::default_font();
+}
+
+void NodeWithStyle::apply_style(const CSS::StyleProperties& specified_style)
+{
+ auto& computed_values = static_cast<CSS::MutableComputedValues&>(m_computed_values);
+
+ m_font = specified_style.font();
+ m_line_height = specified_style.line_height(*this);
+
+ {
+ // FIXME: This doesn't work right for relative font-sizes
+ auto length = specified_style.length_or_fallback(CSS::PropertyID::FontSize, CSS::Length(10, CSS::Length::Type::Px));
+ m_font_size = length.raw_value();
+ }
+
+ auto bgimage = specified_style.property(CSS::PropertyID::BackgroundImage);
+ if (bgimage.has_value() && bgimage.value()->is_image()) {
+ m_background_image = static_ptr_cast<CSS::ImageStyleValue>(bgimage.value());
+ }
+
+ computed_values.set_display(specified_style.display());
+
+ auto position = specified_style.position();
+ if (position.has_value())
+ computed_values.set_position(position.value());
+
+ auto text_align = specified_style.text_align();
+ if (text_align.has_value())
+ computed_values.set_text_align(text_align.value());
+
+ auto white_space = specified_style.white_space();
+ if (white_space.has_value())
+ computed_values.set_white_space(white_space.value());
+
+ auto float_ = specified_style.float_();
+ if (float_.has_value())
+ computed_values.set_float(float_.value());
+
+ auto clear = specified_style.clear();
+ if (clear.has_value())
+ computed_values.set_clear(clear.value());
+
+ auto text_decoration_line = specified_style.text_decoration_line();
+ if (text_decoration_line.has_value())
+ computed_values.set_text_decoration_line(text_decoration_line.value());
+
+ auto text_transform = specified_style.text_transform();
+ if (text_transform.has_value())
+ computed_values.set_text_transform(text_transform.value());
+
+ if (auto list_style_type = specified_style.list_style_type(); list_style_type.has_value())
+ computed_values.set_list_style_type(list_style_type.value());
+
+ computed_values.set_color(specified_style.color_or_fallback(CSS::PropertyID::Color, document(), Color::Black));
+ computed_values.set_background_color(specified_style.color_or_fallback(CSS::PropertyID::BackgroundColor, document(), Color::Transparent));
+
+ computed_values.set_z_index(specified_style.z_index());
+ computed_values.set_width(specified_style.length_or_fallback(CSS::PropertyID::Width, {}));
+ computed_values.set_min_width(specified_style.length_or_fallback(CSS::PropertyID::MinWidth, {}));
+ computed_values.set_max_width(specified_style.length_or_fallback(CSS::PropertyID::MaxWidth, {}));
+ computed_values.set_height(specified_style.length_or_fallback(CSS::PropertyID::Height, {}));
+ computed_values.set_min_height(specified_style.length_or_fallback(CSS::PropertyID::MinHeight, {}));
+ computed_values.set_max_height(specified_style.length_or_fallback(CSS::PropertyID::MaxHeight, {}));
+
+ computed_values.set_offset(specified_style.length_box(CSS::PropertyID::Left, CSS::PropertyID::Top, CSS::PropertyID::Right, CSS::PropertyID::Bottom, CSS::Length::make_auto()));
+ computed_values.set_margin(specified_style.length_box(CSS::PropertyID::MarginLeft, CSS::PropertyID::MarginTop, CSS::PropertyID::MarginRight, CSS::PropertyID::MarginBottom, CSS::Length::make_px(0)));
+ computed_values.set_padding(specified_style.length_box(CSS::PropertyID::PaddingLeft, CSS::PropertyID::PaddingTop, CSS::PropertyID::PaddingRight, CSS::PropertyID::PaddingBottom, CSS::Length::make_px(0)));
+
+ auto do_border_style = [&](CSS::BorderData& border, CSS::PropertyID width_property, CSS::PropertyID color_property, CSS::PropertyID style_property) {
+ border.width = specified_style.length_or_fallback(width_property, {}).resolved_or_zero(*this, 0).to_px(*this);
+ border.color = specified_style.color_or_fallback(color_property, document(), Color::Transparent);
+ border.line_style = specified_style.line_style(style_property).value_or(CSS::LineStyle::None);
+ };
+
+ do_border_style(computed_values.border_left(), CSS::PropertyID::BorderLeftWidth, CSS::PropertyID::BorderLeftColor, CSS::PropertyID::BorderLeftStyle);
+ do_border_style(computed_values.border_top(), CSS::PropertyID::BorderTopWidth, CSS::PropertyID::BorderTopColor, CSS::PropertyID::BorderTopStyle);
+ do_border_style(computed_values.border_right(), CSS::PropertyID::BorderRightWidth, CSS::PropertyID::BorderRightColor, CSS::PropertyID::BorderRightStyle);
+ do_border_style(computed_values.border_bottom(), CSS::PropertyID::BorderBottomWidth, CSS::PropertyID::BorderBottomColor, CSS::PropertyID::BorderBottomStyle);
+}
+
+void Node::handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned, unsigned)
+{
+}
+
+void Node::handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint&, unsigned, unsigned)
+{
+}
+
+void Node::handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint&, unsigned, unsigned)
+{
+}
+
+bool Node::is_root_element() const
+{
+ if (is_anonymous())
+ return false;
+ return is<HTML::HTMLHtmlElement>(*dom_node());
+}
+
+String Node::class_name() const
+{
+ return demangle(typeid(*this).name());
+}
+
+bool Node::is_inline_block() const
+{
+ return is_inline() && is<BlockBox>(*this);
+}
+
+NonnullRefPtr<NodeWithStyle> NodeWithStyle::create_anonymous_wrapper() const
+{
+ auto wrapper = adopt(*new BlockBox(const_cast<DOM::Document&>(document()), nullptr, m_computed_values.clone_inherited_values()));
+ wrapper->m_font = m_font;
+ wrapper->m_font_size = m_font_size;
+ wrapper->m_line_height = m_line_height;
+ wrapper->m_background_image = m_background_image;
+ return wrapper;
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/Node.h b/Userland/Libraries/LibWeb/Layout/Node.h
new file mode 100644
index 0000000000..c1699924e3
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/Node.h
@@ -0,0 +1,278 @@
+/*
+ * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/NonnullRefPtr.h>
+#include <AK/TypeCasts.h>
+#include <AK/Vector.h>
+#include <LibGfx/Rect.h>
+#include <LibWeb/CSS/ComputedValues.h>
+#include <LibWeb/CSS/StyleProperties.h>
+#include <LibWeb/Forward.h>
+#include <LibWeb/Layout/BoxModelMetrics.h>
+#include <LibWeb/Layout/LayoutPosition.h>
+#include <LibWeb/Painting/PaintContext.h>
+#include <LibWeb/TreeNode.h>
+
+namespace Web::Layout {
+
+enum class LayoutMode {
+ Default,
+ AllPossibleLineBreaks,
+ OnlyRequiredLineBreaks,
+};
+
+enum class PaintPhase {
+ Background,
+ Border,
+ Foreground,
+ FocusOutline,
+ Overlay,
+};
+
+struct HitTestResult {
+ RefPtr<Node> layout_node;
+ int index_in_node { 0 };
+
+ enum InternalPosition {
+ None,
+ Before,
+ Inside,
+ After,
+ };
+ InternalPosition internal_position { None };
+};
+
+enum class HitTestType {
+ Exact, // Exact matches only
+ TextCursor, // Clicking past the right/bottom edge of text will still hit the text
+};
+
+class Node : public TreeNode<Node> {
+public:
+ virtual ~Node();
+
+ virtual HitTestResult hit_test(const Gfx::IntPoint&, HitTestType) const;
+
+ bool is_anonymous() const { return !m_dom_node; }
+ const DOM::Node* dom_node() const { return m_dom_node; }
+ DOM::Node* dom_node() { return m_dom_node; }
+
+ DOM::Document& document() { return m_document; }
+ const DOM::Document& document() const { return m_document; }
+
+ const Frame& frame() const;
+ Frame& frame();
+
+ const InitialContainingBlockBox& root() const;
+ InitialContainingBlockBox& root();
+
+ bool is_root_element() const;
+
+ String class_name() const;
+
+ bool has_style() const { return m_has_style; }
+
+ virtual bool can_have_children() const { return true; }
+
+ bool is_inline() const { return m_inline; }
+ void set_inline(bool b) { m_inline = b; }
+
+ bool is_inline_block() const;
+
+ virtual bool wants_mouse_events() const { return false; }
+
+ virtual void handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers);
+ virtual void handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers);
+ virtual void handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint&, unsigned buttons, unsigned modifiers);
+
+ virtual void before_children_paint(PaintContext&, PaintPhase) {};
+ virtual void paint(PaintContext&, PaintPhase);
+ virtual void paint_fragment(PaintContext&, const LineBoxFragment&, PaintPhase) const { }
+ virtual void after_children_paint(PaintContext&, PaintPhase) {};
+
+ virtual bool is_box() const { return false; }
+
+ bool is_floating() const;
+ bool is_positioned() const;
+ bool is_absolutely_positioned() const;
+ bool is_fixed_position() const;
+
+ const BlockBox* containing_block() const;
+ BlockBox* containing_block() { return const_cast<BlockBox*>(const_cast<const Node*>(this)->containing_block()); }
+
+ bool can_contain_boxes_with_position_absolute() const;
+
+ const Gfx::Font& font() const;
+ const CSS::ImmutableComputedValues& computed_values() const;
+
+ NodeWithStyle* parent();
+ const NodeWithStyle* parent() const;
+
+ void inserted_into(Node&) { }
+ void removed_from(Node&) { }
+ void children_changed() { }
+
+ virtual void split_into_lines(InlineFormattingContext&, LayoutMode);
+
+ bool is_visible() const { return m_visible; }
+ void set_visible(bool visible) { m_visible = visible; }
+
+ virtual void set_needs_display();
+
+ bool children_are_inline() const { return m_children_are_inline; }
+ void set_children_are_inline(bool value) { m_children_are_inline = value; }
+
+ Gfx::FloatPoint box_type_agnostic_position() const;
+
+ float font_size() const;
+
+ enum class SelectionState {
+ None, // No selection
+ Start, // Selection starts in this Node
+ End, // Selection ends in this Node
+ StartAndEnd, // Selection starts and ends in this Node
+ Full, // Selection starts before and ends after this Node
+ };
+
+ SelectionState selection_state() const { return m_selection_state; }
+ void set_selection_state(SelectionState state) { m_selection_state = state; }
+
+ template<typename Callback>
+ void for_each_child_in_paint_order(Callback callback) const
+ {
+ for_each_child([&](auto& child) {
+ if (is<Box>(child) && downcast<Box>(child).stacking_context())
+ return;
+ if (!child.is_positioned())
+ callback(child);
+ });
+ for_each_child([&](auto& child) {
+ if (is<Box>(child) && downcast<Box>(child).stacking_context())
+ return;
+ if (child.is_positioned())
+ callback(child);
+ });
+ }
+
+protected:
+ Node(DOM::Document&, DOM::Node*);
+
+private:
+ friend class NodeWithStyle;
+
+ NonnullRefPtr<DOM::Document> m_document;
+ RefPtr<DOM::Node> m_dom_node;
+
+ bool m_inline { false };
+ bool m_has_style { false };
+ bool m_visible { true };
+ bool m_children_are_inline { false };
+ SelectionState m_selection_state { SelectionState::None };
+};
+
+class NodeWithStyle : public Node {
+public:
+ virtual ~NodeWithStyle() override { }
+
+ const CSS::ImmutableComputedValues& computed_values() const { return static_cast<const CSS::ImmutableComputedValues&>(m_computed_values); }
+
+ void apply_style(const CSS::StyleProperties&);
+
+ const Gfx::Font& font() const { return *m_font; }
+ float line_height() const { return m_line_height; }
+ float font_size() const { return m_font_size; }
+ const CSS::ImageStyleValue* background_image() const { return m_background_image; }
+
+ NonnullRefPtr<NodeWithStyle> create_anonymous_wrapper() const;
+
+protected:
+ NodeWithStyle(DOM::Document&, DOM::Node*, NonnullRefPtr<CSS::StyleProperties>);
+ NodeWithStyle(DOM::Document&, DOM::Node*, CSS::ComputedValues);
+
+private:
+ CSS::ComputedValues m_computed_values;
+ RefPtr<Gfx::Font> m_font;
+ float m_line_height { 0 };
+ float m_font_size { 0 };
+ RefPtr<CSS::ImageStyleValue> m_background_image;
+
+ CSS::Position m_position;
+};
+
+class NodeWithStyleAndBoxModelMetrics : public NodeWithStyle {
+public:
+ BoxModelMetrics& box_model() { return m_box_model; }
+ const BoxModelMetrics& box_model() const { return m_box_model; }
+
+protected:
+ NodeWithStyleAndBoxModelMetrics(DOM::Document& document, DOM::Node* node, NonnullRefPtr<CSS::StyleProperties> style)
+ : NodeWithStyle(document, node, move(style))
+ {
+ }
+
+ NodeWithStyleAndBoxModelMetrics(DOM::Document& document, DOM::Node* node, CSS::ComputedValues computed_values)
+ : NodeWithStyle(document, node, move(computed_values))
+ {
+ }
+
+private:
+ BoxModelMetrics m_box_model;
+};
+
+inline const Gfx::Font& Node::font() const
+{
+ if (m_has_style)
+ return static_cast<const NodeWithStyle*>(this)->font();
+ return parent()->font();
+}
+
+inline float Node::font_size() const
+{
+ if (m_has_style)
+ return static_cast<const NodeWithStyle*>(this)->font_size();
+ return parent()->font_size();
+}
+
+inline const CSS::ImmutableComputedValues& Node::computed_values() const
+{
+ if (m_has_style)
+ return static_cast<const NodeWithStyle*>(this)->computed_values();
+ return parent()->computed_values();
+}
+
+inline const NodeWithStyle* Node::parent() const
+{
+ return static_cast<const NodeWithStyle*>(TreeNode<Node>::parent());
+}
+
+inline NodeWithStyle* Node::parent()
+{
+ return static_cast<NodeWithStyle*>(TreeNode<Node>::parent());
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/ReplacedBox.cpp b/Userland/Libraries/LibWeb/Layout/ReplacedBox.cpp
new file mode 100644
index 0000000000..2ea63bb233
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/ReplacedBox.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/DOM/Element.h>
+#include <LibWeb/Layout/BlockBox.h>
+#include <LibWeb/Layout/InlineFormattingContext.h>
+#include <LibWeb/Layout/ReplacedBox.h>
+
+namespace Web::Layout {
+
+ReplacedBox::ReplacedBox(DOM::Document& document, DOM::Element& element, NonnullRefPtr<CSS::StyleProperties> style)
+ : Box(document, &element, move(style))
+{
+ // FIXME: Allow non-inline replaced elements.
+ set_inline(true);
+}
+
+ReplacedBox::~ReplacedBox()
+{
+}
+
+void ReplacedBox::split_into_lines(InlineFormattingContext& context, LayoutMode layout_mode)
+{
+ auto& containing_block = context.containing_block();
+
+ prepare_for_replaced_layout();
+ context.dimension_box_on_line(*this, layout_mode);
+
+ auto* line_box = &containing_block.ensure_last_line_box();
+ if (line_box->width() > 0 && line_box->width() + width() > context.available_width_at_line(containing_block.line_boxes().size() - 1))
+ line_box = &containing_block.add_line_box();
+ line_box->add_fragment(*this, 0, 0, width(), height());
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/ReplacedBox.h b/Userland/Libraries/LibWeb/Layout/ReplacedBox.h
new file mode 100644
index 0000000000..f1da91a197
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/ReplacedBox.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/DOM/Element.h>
+#include <LibWeb/Layout/Box.h>
+
+namespace Web::Layout {
+
+class ReplacedBox : public Box {
+public:
+ ReplacedBox(DOM::Document&, DOM::Element&, NonnullRefPtr<CSS::StyleProperties>);
+ virtual ~ReplacedBox() override;
+
+ const DOM::Element& dom_node() const { return downcast<DOM::Element>(*Node::dom_node()); }
+ DOM::Element& dom_node() { return downcast<DOM::Element>(*Node::dom_node()); }
+
+ bool has_intrinsic_width() const { return m_has_intrinsic_width; }
+ bool has_intrinsic_height() const { return m_has_intrinsic_height; }
+ bool has_intrinsic_ratio() const { return m_has_intrinsic_ratio; }
+
+ float intrinsic_width() const { return m_intrinsic_width; }
+ float intrinsic_height() const { return m_intrinsic_height; }
+ float intrinsic_ratio() const { return m_intrinsic_ratio; }
+
+ void set_has_intrinsic_width(bool has) { m_has_intrinsic_width = has; }
+ void set_has_intrinsic_height(bool has) { m_has_intrinsic_height = has; }
+ void set_has_intrinsic_ratio(bool has) { m_has_intrinsic_ratio = has; }
+
+ void set_intrinsic_width(float width) { m_intrinsic_width = width; }
+ void set_intrinsic_height(float height) { m_intrinsic_height = height; }
+ void set_intrinsic_ratio(float ratio) { m_intrinsic_ratio = ratio; }
+
+ virtual void prepare_for_replaced_layout() { }
+
+ virtual bool can_have_children() const override { return false; }
+
+protected:
+ virtual void split_into_lines(InlineFormattingContext&, LayoutMode) override;
+
+private:
+ bool m_has_intrinsic_width { false };
+ bool m_has_intrinsic_height { false };
+ bool m_has_intrinsic_ratio { false };
+ float m_intrinsic_width { 0 };
+ float m_intrinsic_height { 0 };
+ float m_intrinsic_ratio { 0 };
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/SVGBox.cpp b/Userland/Libraries/LibWeb/Layout/SVGBox.cpp
new file mode 100644
index 0000000000..8370d4b9bd
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/SVGBox.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibGUI/Painter.h>
+#include <LibGfx/Font.h>
+#include <LibGfx/StylePainter.h>
+#include <LibWeb/Layout/SVGBox.h>
+
+namespace Web::Layout {
+
+SVGBox::SVGBox(DOM::Document& document, SVG::SVGElement& element, NonnullRefPtr<CSS::StyleProperties> style)
+ : ReplacedBox(document, element, move(style))
+{
+}
+
+void SVGBox::before_children_paint(PaintContext& context, PaintPhase phase)
+{
+ Node::before_children_paint(context, phase);
+ if (phase != PaintPhase::Foreground)
+ return;
+ context.svg_context().save();
+}
+
+void SVGBox::after_children_paint(PaintContext& context, PaintPhase phase)
+{
+ Node::after_children_paint(context, phase);
+ if (phase != PaintPhase::Foreground)
+ return;
+ context.svg_context().restore();
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/SVGBox.h b/Userland/Libraries/LibWeb/Layout/SVGBox.h
new file mode 100644
index 0000000000..dce2ee6f64
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/SVGBox.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/Layout/ReplacedBox.h>
+#include <LibWeb/SVG/SVGElement.h>
+#include <LibWeb/SVG/SVGGraphicsElement.h>
+
+namespace Web::Layout {
+
+class SVGBox : public ReplacedBox {
+public:
+ SVGBox(DOM::Document&, SVG::SVGElement&, NonnullRefPtr<CSS::StyleProperties>);
+ virtual ~SVGBox() override = default;
+
+ virtual void before_children_paint(PaintContext& context, PaintPhase phase) override;
+ virtual void after_children_paint(PaintContext& context, PaintPhase phase) override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/SVGGraphicsBox.cpp b/Userland/Libraries/LibWeb/Layout/SVGGraphicsBox.cpp
new file mode 100644
index 0000000000..be1f9541d4
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/SVGGraphicsBox.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/Layout/SVGGraphicsBox.h>
+
+namespace Web::Layout {
+
+SVGGraphicsBox::SVGGraphicsBox(DOM::Document& document, SVG::SVGGraphicsElement& element, NonnullRefPtr<CSS::StyleProperties> properties)
+ : SVGBox(document, element, properties)
+{
+}
+
+void SVGGraphicsBox::before_children_paint(PaintContext& context, PaintPhase phase)
+{
+ SVGBox::before_children_paint(context, phase);
+ if (phase != PaintPhase::Foreground)
+ return;
+
+ auto& graphics_element = downcast<SVG::SVGGraphicsElement>(dom_node());
+
+ if (graphics_element.fill_color().has_value())
+ context.svg_context().set_fill_color(graphics_element.fill_color().value());
+ if (graphics_element.stroke_color().has_value())
+ context.svg_context().set_stroke_color(graphics_element.stroke_color().value());
+ if (graphics_element.stroke_width().has_value())
+ context.svg_context().set_stroke_width(graphics_element.stroke_width().value());
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/SVGGraphicsBox.h b/Userland/Libraries/LibWeb/Layout/SVGGraphicsBox.h
new file mode 100644
index 0000000000..10dbadef07
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/SVGGraphicsBox.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/Layout/SVGBox.h>
+#include <LibWeb/SVG/SVGElement.h>
+#include <LibWeb/SVG/SVGGraphicsElement.h>
+
+namespace Web::Layout {
+
+class SVGGraphicsBox : public SVGBox {
+public:
+ SVGGraphicsBox(DOM::Document&, SVG::SVGGraphicsElement&, NonnullRefPtr<CSS::StyleProperties>);
+ virtual ~SVGGraphicsBox() override = default;
+
+ virtual void before_children_paint(PaintContext& context, PaintPhase phase) override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/SVGPathBox.cpp b/Userland/Libraries/LibWeb/Layout/SVGPathBox.cpp
new file mode 100644
index 0000000000..87879043eb
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/SVGPathBox.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibGfx/Painter.h>
+#include <LibWeb/Layout/SVGPathBox.h>
+#include <LibWeb/SVG/SVGPathElement.h>
+
+namespace Web::Layout {
+
+SVGPathBox::SVGPathBox(DOM::Document& document, SVG::SVGPathElement& element, NonnullRefPtr<CSS::StyleProperties> properties)
+ : SVGGraphicsBox(document, element, properties)
+{
+}
+
+void SVGPathBox::prepare_for_replaced_layout()
+{
+ auto& bounding_box = dom_node().get_path().bounding_box();
+ set_has_intrinsic_width(true);
+ set_has_intrinsic_height(true);
+ set_intrinsic_width(bounding_box.width());
+ set_intrinsic_height(bounding_box.height());
+
+ // FIXME: This does not belong here! Someone at a higher level should place this box.
+ set_offset(bounding_box.top_left());
+}
+
+void SVGPathBox::paint(PaintContext& context, PaintPhase phase)
+{
+ if (!is_visible())
+ return;
+
+ SVGGraphicsBox::paint(context, phase);
+
+ if (phase != PaintPhase::Foreground)
+ return;
+
+ auto& path_element = dom_node();
+ auto& path = path_element.get_path();
+
+ // We need to fill the path before applying the stroke, however the filled
+ // path must be closed, whereas the stroke path may not necessary be closed.
+ // Copy the path and close it for filling, but use the previous path for stroke
+ auto closed_path = path;
+ closed_path.close();
+
+ // Fills are computed as though all paths are closed (https://svgwg.org/svg2-draft/painting.html#FillProperties)
+ auto& painter = context.painter();
+ auto& svg_context = context.svg_context();
+
+ auto offset = (absolute_position() - effective_offset()).to_type<int>();
+
+ painter.translate(offset);
+
+ painter.fill_path(
+ closed_path,
+ path_element.fill_color().value_or(svg_context.fill_color()),
+ Gfx::Painter::WindingRule::EvenOdd);
+ painter.stroke_path(
+ path,
+ path_element.stroke_color().value_or(svg_context.stroke_color()),
+ path_element.stroke_width().value_or(svg_context.stroke_width()));
+
+ painter.translate(-offset);
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/SVGPathBox.h b/Userland/Libraries/LibWeb/Layout/SVGPathBox.h
new file mode 100644
index 0000000000..cbfe94b4ec
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/SVGPathBox.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/Layout/SVGGraphicsBox.h>
+
+namespace Web::Layout {
+
+class SVGPathBox final : public SVGGraphicsBox {
+public:
+ SVGPathBox(DOM::Document&, SVG::SVGPathElement&, NonnullRefPtr<CSS::StyleProperties>);
+ virtual ~SVGPathBox() override = default;
+
+ SVG::SVGPathElement& dom_node() { return downcast<SVG::SVGPathElement>(SVGGraphicsBox::dom_node()); }
+
+ virtual void prepare_for_replaced_layout() override;
+ virtual void paint(PaintContext& context, PaintPhase phase) override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/SVGSVGBox.cpp b/Userland/Libraries/LibWeb/Layout/SVGSVGBox.cpp
new file mode 100644
index 0000000000..af4a038dbc
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/SVGSVGBox.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/Layout/SVGSVGBox.h>
+
+namespace Web::Layout {
+
+SVGSVGBox::SVGSVGBox(DOM::Document& document, SVG::SVGSVGElement& element, NonnullRefPtr<CSS::StyleProperties> properties)
+ : SVGGraphicsBox(document, element, properties)
+{
+}
+
+void SVGSVGBox::prepare_for_replaced_layout()
+{
+ set_has_intrinsic_width(true);
+ set_has_intrinsic_height(true);
+ set_intrinsic_width(dom_node().width());
+ set_intrinsic_height(dom_node().height());
+}
+
+void SVGSVGBox::before_children_paint(PaintContext& context, PaintPhase phase)
+{
+ if (phase != PaintPhase::Foreground)
+ return;
+
+ if (!context.has_svg_context())
+ context.set_svg_context(SVGContext());
+
+ SVGGraphicsBox::before_children_paint(context, phase);
+}
+
+void SVGSVGBox::after_children_paint(PaintContext& context, PaintPhase phase)
+{
+ SVGGraphicsBox::after_children_paint(context, phase);
+ if (phase != PaintPhase::Foreground)
+ return;
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/SVGSVGBox.h b/Userland/Libraries/LibWeb/Layout/SVGSVGBox.h
new file mode 100644
index 0000000000..a95bf11f69
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/SVGSVGBox.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/Layout/SVGGraphicsBox.h>
+#include <LibWeb/SVG/SVGSVGElement.h>
+
+namespace Web::Layout {
+
+class SVGSVGBox final : public SVGGraphicsBox {
+public:
+ SVGSVGBox(DOM::Document&, SVG::SVGSVGElement&, NonnullRefPtr<CSS::StyleProperties>);
+ virtual ~SVGSVGBox() override = default;
+
+ SVG::SVGSVGElement& dom_node() { return downcast<SVG::SVGSVGElement>(SVGGraphicsBox::dom_node()); }
+
+ virtual void prepare_for_replaced_layout() override;
+
+ virtual void before_children_paint(PaintContext& context, PaintPhase phase) override;
+ virtual void after_children_paint(PaintContext& context, PaintPhase phase) override;
+
+ virtual bool can_have_children() const override { return true; }
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/TableBox.cpp b/Userland/Libraries/LibWeb/Layout/TableBox.cpp
new file mode 100644
index 0000000000..66fdfb8bd3
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/TableBox.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/DOM/Element.h>
+#include <LibWeb/Layout/TableBox.h>
+
+namespace Web::Layout {
+
+TableBox::TableBox(DOM::Document& document, DOM::Element* element, NonnullRefPtr<CSS::StyleProperties> style)
+ : Layout::BlockBox(document, element, move(style))
+{
+}
+
+TableBox::TableBox(DOM::Document& document, DOM::Element* element, CSS::ComputedValues computed_values)
+ : Layout::BlockBox(document, element, move(computed_values))
+{
+}
+
+TableBox::~TableBox()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/TableBox.h b/Userland/Libraries/LibWeb/Layout/TableBox.h
new file mode 100644
index 0000000000..cff6af9222
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/TableBox.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/Layout/BlockBox.h>
+
+namespace Web::Layout {
+
+class TableBox final : public Layout::BlockBox {
+public:
+ TableBox(DOM::Document&, DOM::Element*, NonnullRefPtr<CSS::StyleProperties>);
+ TableBox(DOM::Document&, DOM::Element*, CSS::ComputedValues);
+ virtual ~TableBox() override;
+
+ static CSS::Display static_display() { return CSS::Display::Table; }
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/TableCellBox.cpp b/Userland/Libraries/LibWeb/Layout/TableCellBox.cpp
new file mode 100644
index 0000000000..197fe59b87
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/TableCellBox.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/DOM/Element.h>
+#include <LibWeb/Layout/TableCellBox.h>
+#include <LibWeb/Layout/TableRowBox.h>
+
+namespace Web::Layout {
+
+TableCellBox::TableCellBox(DOM::Document& document, DOM::Element* element, NonnullRefPtr<CSS::StyleProperties> style)
+ : Layout::BlockBox(document, element, move(style))
+{
+}
+
+TableCellBox::TableCellBox(DOM::Document& document, DOM::Element* element, CSS::ComputedValues computed_values)
+ : Layout::BlockBox(document, element, move(computed_values))
+{
+}
+
+TableCellBox::~TableCellBox()
+{
+}
+
+size_t TableCellBox::colspan() const
+{
+ if (!dom_node())
+ return 0;
+ return downcast<DOM::Element>(*dom_node()).attribute(HTML::AttributeNames::colspan).to_uint().value_or(1);
+}
+
+float TableCellBox::width_of_logical_containing_block() const
+{
+ if (auto* row = first_ancestor_of_type<TableRowBox>())
+ return row->width();
+ return 0;
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/TableCellBox.h b/Userland/Libraries/LibWeb/Layout/TableCellBox.h
new file mode 100644
index 0000000000..091a4ae6d6
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/TableCellBox.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/Layout/BlockBox.h>
+
+namespace Web::Layout {
+
+class TableCellBox final : public BlockBox {
+public:
+ TableCellBox(DOM::Document&, DOM::Element*, NonnullRefPtr<CSS::StyleProperties>);
+ TableCellBox(DOM::Document&, DOM::Element*, CSS::ComputedValues);
+ virtual ~TableCellBox() override;
+
+ TableCellBox* next_cell() { return next_sibling_of_type<TableCellBox>(); }
+ const TableCellBox* next_cell() const { return next_sibling_of_type<TableCellBox>(); }
+
+ size_t colspan() const;
+
+ static CSS::Display static_display() { return CSS::Display::TableCell; }
+
+private:
+ virtual float width_of_logical_containing_block() const override;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/TableFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/TableFormattingContext.cpp
new file mode 100644
index 0000000000..865cef64e6
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/TableFormattingContext.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/CSS/Length.h>
+#include <LibWeb/DOM/Node.h>
+#include <LibWeb/Layout/BlockBox.h>
+#include <LibWeb/Layout/Box.h>
+#include <LibWeb/Layout/InlineFormattingContext.h>
+#include <LibWeb/Layout/TableBox.h>
+#include <LibWeb/Layout/TableCellBox.h>
+#include <LibWeb/Layout/TableFormattingContext.h>
+#include <LibWeb/Layout/TableRowBox.h>
+#include <LibWeb/Layout/TableRowGroupBox.h>
+#include <LibWeb/Page/Frame.h>
+
+namespace Web::Layout {
+
+TableFormattingContext::TableFormattingContext(Box& context_box, FormattingContext* parent)
+ : BlockFormattingContext(context_box, parent)
+{
+}
+
+TableFormattingContext::~TableFormattingContext()
+{
+}
+
+void TableFormattingContext::run(Box& box, LayoutMode)
+{
+ compute_width(box);
+
+ float total_content_height = 0;
+
+ box.for_each_child_of_type<TableRowGroupBox>([&](auto& row_group_box) {
+ compute_width(row_group_box);
+ auto column_count = row_group_box.column_count();
+ Vector<float> column_widths;
+ column_widths.resize(column_count);
+
+ row_group_box.template for_each_child_of_type<TableRowBox>([&](auto& row) {
+ calculate_column_widths(row, column_widths);
+ });
+
+ float content_height = 0;
+
+ row_group_box.template for_each_child_of_type<TableRowBox>([&](auto& row) {
+ row.set_offset(0, content_height);
+ layout_row(row, column_widths);
+ content_height += row.height();
+ });
+
+ row_group_box.set_height(content_height);
+
+ total_content_height += content_height;
+ });
+
+ // FIXME: This is a total hack, we should respect the 'height' property.
+ box.set_height(total_content_height);
+}
+
+void TableFormattingContext::calculate_column_widths(Box& row, Vector<float>& column_widths)
+{
+ size_t column_index = 0;
+ auto* table = row.first_ancestor_of_type<TableBox>();
+ bool use_auto_layout = !table || table->computed_values().width().is_undefined_or_auto();
+ row.for_each_child_of_type<TableCellBox>([&](auto& cell) {
+ compute_width(cell);
+ if (use_auto_layout) {
+ layout_inside(cell, LayoutMode::OnlyRequiredLineBreaks);
+ } else {
+ layout_inside(cell, LayoutMode::Default);
+ }
+ column_widths[column_index] = max(column_widths[column_index], cell.width());
+ column_index += cell.colspan();
+ });
+}
+
+void TableFormattingContext::layout_row(Box& row, Vector<float>& column_widths)
+{
+ size_t column_index = 0;
+ float tallest_cell_height = 0;
+ float content_width = 0;
+ auto* table = row.first_ancestor_of_type<TableBox>();
+ bool use_auto_layout = !table || table->computed_values().width().is_undefined_or_auto();
+
+ row.for_each_child_of_type<TableCellBox>([&](auto& cell) {
+ cell.set_offset(row.effective_offset().translated(content_width, 0));
+
+ // Layout the cell contents a second time, now that we know its final width.
+ if (use_auto_layout) {
+ layout_inside(cell, LayoutMode::OnlyRequiredLineBreaks);
+ } else {
+ layout_inside(cell, LayoutMode::Default);
+ }
+
+ size_t cell_colspan = cell.colspan();
+ for (size_t i = 0; i < cell_colspan; ++i)
+ content_width += column_widths[column_index++];
+ tallest_cell_height = max(tallest_cell_height, cell.height());
+ });
+
+ if (use_auto_layout) {
+ row.set_width(content_width);
+ } else {
+ row.set_width(table->width());
+ }
+
+ row.set_height(tallest_cell_height);
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/TableFormattingContext.h b/Userland/Libraries/LibWeb/Layout/TableFormattingContext.h
new file mode 100644
index 0000000000..7bb973a5d2
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/TableFormattingContext.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/Forward.h>
+#include <LibWeb/Layout/BlockFormattingContext.h>
+
+namespace Web::Layout {
+
+class TableFormattingContext final : public BlockFormattingContext {
+public:
+ explicit TableFormattingContext(Box&, FormattingContext* parent);
+ ~TableFormattingContext();
+
+ virtual void run(Box&, LayoutMode) override;
+
+private:
+ void calculate_column_widths(Box& row, Vector<float>& column_widths);
+ void layout_row(Box& row, Vector<float>& column_widths);
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/TableRowBox.cpp b/Userland/Libraries/LibWeb/Layout/TableRowBox.cpp
new file mode 100644
index 0000000000..b3033adb1c
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/TableRowBox.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/DOM/Element.h>
+#include <LibWeb/Layout/TableRowBox.h>
+
+namespace Web::Layout {
+
+TableRowBox::TableRowBox(DOM::Document& document, DOM::Element* element, NonnullRefPtr<CSS::StyleProperties> style)
+ : Box(document, element, move(style))
+{
+}
+
+TableRowBox::TableRowBox(DOM::Document& document, DOM::Element* element, CSS::ComputedValues computed_values)
+ : Box(document, element, move(computed_values))
+{
+}
+
+TableRowBox::~TableRowBox()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/TableRowBox.h b/Userland/Libraries/LibWeb/Layout/TableRowBox.h
new file mode 100644
index 0000000000..eab602c156
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/TableRowBox.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/Layout/Box.h>
+
+namespace Web::Layout {
+
+class TableRowBox final : public Box {
+public:
+ TableRowBox(DOM::Document&, DOM::Element*, NonnullRefPtr<CSS::StyleProperties>);
+ TableRowBox(DOM::Document&, DOM::Element*, CSS::ComputedValues);
+ virtual ~TableRowBox() override;
+
+ static CSS::Display static_display() { return CSS::Display::TableRow; }
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/TableRowGroupBox.cpp b/Userland/Libraries/LibWeb/Layout/TableRowGroupBox.cpp
new file mode 100644
index 0000000000..f6ced13a33
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/TableRowGroupBox.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/DOM/Element.h>
+#include <LibWeb/Layout/TableCellBox.h>
+#include <LibWeb/Layout/TableRowBox.h>
+#include <LibWeb/Layout/TableRowGroupBox.h>
+
+namespace Web::Layout {
+
+TableRowGroupBox::TableRowGroupBox(DOM::Document& document, DOM::Element& element, NonnullRefPtr<CSS::StyleProperties> style)
+ : Layout::BlockBox(document, &element, move(style))
+{
+}
+
+TableRowGroupBox::~TableRowGroupBox()
+{
+}
+
+size_t TableRowGroupBox::column_count() const
+{
+ size_t table_column_count = 0;
+ for_each_child_of_type<TableRowBox>([&](auto& row) {
+ size_t row_column_count = 0;
+ row.template for_each_child_of_type<TableCellBox>([&](auto& cell) {
+ row_column_count += cell.colspan();
+ });
+ table_column_count = max(table_column_count, row_column_count);
+ });
+ return table_column_count;
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/TableRowGroupBox.h b/Userland/Libraries/LibWeb/Layout/TableRowGroupBox.h
new file mode 100644
index 0000000000..6a9fd6956c
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/TableRowGroupBox.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/Layout/BlockBox.h>
+
+namespace Web::Layout {
+
+class TableRowGroupBox final : public BlockBox {
+public:
+ TableRowGroupBox(DOM::Document&, DOM::Element&, NonnullRefPtr<CSS::StyleProperties>);
+ virtual ~TableRowGroupBox() override;
+
+ size_t column_count() const;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/TextNode.cpp b/Userland/Libraries/LibWeb/Layout/TextNode.cpp
new file mode 100644
index 0000000000..bf027b9cb7
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/TextNode.cpp
@@ -0,0 +1,311 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/StringBuilder.h>
+#include <AK/Utf8View.h>
+#include <LibCore/DirIterator.h>
+#include <LibGUI/Painter.h>
+#include <LibGfx/Font.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/Layout/BlockBox.h>
+#include <LibWeb/Layout/InlineFormattingContext.h>
+#include <LibWeb/Layout/TextNode.h>
+#include <LibWeb/Page/Frame.h>
+#include <ctype.h>
+
+namespace Web::Layout {
+
+TextNode::TextNode(DOM::Document& document, DOM::Text& text)
+ : Node(document, &text)
+{
+ set_inline(true);
+}
+
+TextNode::~TextNode()
+{
+}
+
+static bool is_all_whitespace(const StringView& string)
+{
+ for (size_t i = 0; i < string.length(); ++i) {
+ if (!isspace(string[i]))
+ return false;
+ }
+ return true;
+}
+
+void TextNode::paint_fragment(PaintContext& context, const LineBoxFragment& fragment, PaintPhase phase) const
+{
+ auto& painter = context.painter();
+
+ if (phase == PaintPhase::Background) {
+ painter.fill_rect(enclosing_int_rect(fragment.absolute_rect()), computed_values().background_color());
+ }
+
+ if (phase == PaintPhase::Foreground) {
+ painter.set_font(font());
+
+ if (document().inspected_node() == &dom_node())
+ context.painter().draw_rect(enclosing_int_rect(fragment.absolute_rect()), Color::Magenta);
+
+ if (computed_values().text_decoration_line() == CSS::TextDecorationLine::Underline)
+ painter.draw_line(enclosing_int_rect(fragment.absolute_rect()).bottom_left().translated(0, 1), enclosing_int_rect(fragment.absolute_rect()).bottom_right().translated(0, 1), computed_values().color());
+
+ // FIXME: text-transform should be done already in layout, since uppercase glyphs may be wider than lowercase, etc.
+ auto text = m_text_for_rendering;
+ auto text_transform = computed_values().text_transform();
+ if (text_transform == CSS::TextTransform::Uppercase)
+ text = m_text_for_rendering.to_uppercase();
+ if (text_transform == CSS::TextTransform::Lowercase)
+ text = m_text_for_rendering.to_lowercase();
+
+ painter.draw_text(enclosing_int_rect(fragment.absolute_rect()), text.substring_view(fragment.start(), fragment.length()), Gfx::TextAlignment::CenterLeft, computed_values().color());
+
+ auto selection_rect = fragment.selection_rect(font());
+ if (!selection_rect.is_empty()) {
+ painter.fill_rect(enclosing_int_rect(selection_rect), context.palette().selection());
+ Gfx::PainterStateSaver saver(painter);
+ painter.add_clip_rect(enclosing_int_rect(selection_rect));
+ painter.draw_text(enclosing_int_rect(fragment.absolute_rect()), text.substring_view(fragment.start(), fragment.length()), Gfx::TextAlignment::CenterLeft, context.palette().selection_text());
+ }
+
+ paint_cursor_if_needed(context, fragment);
+ }
+}
+
+void TextNode::paint_cursor_if_needed(PaintContext& context, const LineBoxFragment& fragment) const
+{
+ if (!frame().is_focused_frame())
+ return;
+
+ if (!frame().cursor_blink_state())
+ return;
+
+ if (frame().cursor_position().node() != &dom_node())
+ return;
+
+ if (!(frame().cursor_position().offset() >= (unsigned)fragment.start() && frame().cursor_position().offset() < (unsigned)(fragment.start() + fragment.length())))
+ return;
+
+ if (!fragment.layout_node().dom_node() || !fragment.layout_node().dom_node()->is_editable())
+ return;
+
+ auto fragment_rect = fragment.absolute_rect();
+
+ float cursor_x = fragment_rect.x() + font().width(fragment.text().substring_view(0, frame().cursor_position().offset() - fragment.start()));
+ float cursor_top = fragment_rect.top();
+ float cursor_height = fragment_rect.height();
+ Gfx::IntRect cursor_rect(cursor_x, cursor_top, 1, cursor_height);
+
+ context.painter().draw_rect(cursor_rect, computed_values().color());
+}
+
+template<typename Callback>
+void TextNode::for_each_chunk(Callback callback, LayoutMode layout_mode, bool do_wrap_lines, bool do_wrap_breaks) const
+{
+ Utf8View view(m_text_for_rendering);
+ if (view.is_empty())
+ return;
+
+ auto start_of_chunk = view.begin();
+
+ auto commit_chunk = [&](auto it, bool has_breaking_newline, bool must_commit = false) {
+ if (layout_mode == LayoutMode::OnlyRequiredLineBreaks && !must_commit)
+ return;
+
+ int start = view.byte_offset_of(start_of_chunk);
+ int length = view.byte_offset_of(it) - view.byte_offset_of(start_of_chunk);
+
+ if (has_breaking_newline || length > 0) {
+ auto chunk_view = view.substring_view(start, length);
+ callback(chunk_view, start, length, has_breaking_newline, is_all_whitespace(chunk_view.as_string()));
+ }
+
+ start_of_chunk = it;
+ };
+
+ bool last_was_space = isspace(*view.begin());
+ bool last_was_newline = false;
+ for (auto it = view.begin(); it != view.end();) {
+ if (layout_mode == LayoutMode::AllPossibleLineBreaks) {
+ commit_chunk(it, false);
+ }
+ if (last_was_newline) {
+ last_was_newline = false;
+ commit_chunk(it, true);
+ }
+ if (do_wrap_breaks && *it == '\n') {
+ last_was_newline = true;
+ commit_chunk(it, false);
+ }
+ if (do_wrap_lines) {
+ bool is_space = isspace(*it);
+ if (is_space != last_was_space) {
+ last_was_space = is_space;
+ commit_chunk(it, false);
+ }
+ }
+ ++it;
+ }
+ if (last_was_newline)
+ commit_chunk(view.end(), true);
+ if (start_of_chunk != view.end())
+ commit_chunk(view.end(), false, true);
+}
+
+void TextNode::split_into_lines_by_rules(InlineFormattingContext& context, LayoutMode layout_mode, bool do_collapse, bool do_wrap_lines, bool do_wrap_breaks)
+{
+ auto& containing_block = context.containing_block();
+
+ auto& font = this->font();
+
+ auto& line_boxes = containing_block.line_boxes();
+ containing_block.ensure_last_line_box();
+ float available_width = context.available_width_at_line(line_boxes.size() - 1) - line_boxes.last().width();
+
+ // Collapse whitespace into single spaces
+ if (do_collapse) {
+ auto utf8_view = Utf8View(dom_node().data());
+ StringBuilder builder(dom_node().data().length());
+ auto it = utf8_view.begin();
+ auto skip_over_whitespace = [&] {
+ auto prev = it;
+ while (it != utf8_view.end() && isspace(*it)) {
+ prev = it;
+ ++it;
+ }
+ it = prev;
+ };
+ if (line_boxes.last().is_empty_or_ends_in_whitespace())
+ skip_over_whitespace();
+ for (; it != utf8_view.end(); ++it) {
+ if (!isspace(*it)) {
+ builder.append(utf8_view.as_string().characters_without_null_termination() + utf8_view.byte_offset_of(it), it.code_point_length_in_bytes());
+ } else {
+ builder.append(' ');
+ skip_over_whitespace();
+ }
+ }
+ m_text_for_rendering = builder.to_string();
+ } else {
+ m_text_for_rendering = dom_node().data();
+ }
+
+ // do_wrap_lines => chunks_are_words
+ // !do_wrap_lines => chunks_are_lines
+ struct Chunk {
+ Utf8View view;
+ int start { 0 };
+ int length { 0 };
+ bool is_break { false };
+ bool is_all_whitespace { false };
+ };
+ Vector<Chunk, 128> chunks;
+
+ for_each_chunk(
+ [&](const Utf8View& view, int start, int length, bool is_break, bool is_all_whitespace) {
+ chunks.append({ Utf8View(view), start, length, is_break, is_all_whitespace });
+ },
+ layout_mode, do_wrap_lines, do_wrap_breaks);
+
+ for (size_t i = 0; i < chunks.size(); ++i) {
+ auto& chunk = chunks[i];
+
+ // Collapse entire fragment into non-existence if previous fragment on line ended in whitespace.
+ if (do_collapse && line_boxes.last().is_empty_or_ends_in_whitespace() && chunk.is_all_whitespace)
+ continue;
+
+ float chunk_width;
+ if (do_wrap_lines) {
+ if (do_collapse && isspace(*chunk.view.begin()) && line_boxes.last().is_empty_or_ends_in_whitespace()) {
+ // This is a non-empty chunk that starts with collapsible whitespace.
+ // We are at either at the start of a new line, or after something that ended in whitespace,
+ // so we don't need to contribute our own whitespace to the line. Skip over it instead!
+ ++chunk.start;
+ --chunk.length;
+ chunk.view = chunk.view.substring_view(1, chunk.view.byte_length() - 1);
+ }
+
+ chunk_width = font.width(chunk.view) + font.glyph_spacing();
+
+ if (line_boxes.last().width() > 0 && chunk_width > available_width) {
+ containing_block.add_line_box();
+ available_width = context.available_width_at_line(line_boxes.size() - 1);
+
+ if (do_collapse && chunk.is_all_whitespace)
+ continue;
+ }
+ } else {
+ chunk_width = font.width(chunk.view);
+ }
+
+ line_boxes.last().add_fragment(*this, chunk.start, chunk.length, chunk_width, font.glyph_height());
+ available_width -= chunk_width;
+
+ if (do_wrap_lines) {
+ if (available_width < 0) {
+ containing_block.add_line_box();
+ available_width = context.available_width_at_line(line_boxes.size() - 1);
+ }
+ }
+
+ if (do_wrap_breaks) {
+ if (chunk.is_break) {
+ containing_block.add_line_box();
+ available_width = context.available_width_at_line(line_boxes.size() - 1);
+ }
+ }
+ }
+}
+
+void TextNode::split_into_lines(InlineFormattingContext& context, LayoutMode layout_mode)
+{
+ bool do_collapse = true;
+ bool do_wrap_lines = true;
+ bool do_wrap_breaks = false;
+
+ if (computed_values().white_space() == CSS::WhiteSpace::Nowrap) {
+ do_collapse = true;
+ do_wrap_lines = false;
+ do_wrap_breaks = false;
+ } else if (computed_values().white_space() == CSS::WhiteSpace::Pre) {
+ do_collapse = false;
+ do_wrap_lines = false;
+ do_wrap_breaks = true;
+ } else if (computed_values().white_space() == CSS::WhiteSpace::PreLine) {
+ do_collapse = true;
+ do_wrap_lines = true;
+ do_wrap_breaks = true;
+ } else if (computed_values().white_space() == CSS::WhiteSpace::PreWrap) {
+ do_collapse = false;
+ do_wrap_lines = true;
+ do_wrap_breaks = true;
+ }
+
+ split_into_lines_by_rules(context, layout_mode, do_collapse, do_wrap_lines, do_wrap_breaks);
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/TextNode.h b/Userland/Libraries/LibWeb/Layout/TextNode.h
new file mode 100644
index 0000000000..03781a22b2
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/TextNode.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/DOM/Text.h>
+#include <LibWeb/Layout/Node.h>
+
+namespace Web::Layout {
+
+class LineBoxFragment;
+
+class TextNode : public Node {
+public:
+ TextNode(DOM::Document&, DOM::Text&);
+ virtual ~TextNode() override;
+
+ const DOM::Text& dom_node() const { return static_cast<const DOM::Text&>(*Node::dom_node()); }
+
+ const String& text_for_rendering() const { return m_text_for_rendering; }
+
+ virtual void paint_fragment(PaintContext&, const LineBoxFragment&, PaintPhase) const override;
+
+ virtual void split_into_lines(InlineFormattingContext&, LayoutMode) override;
+
+private:
+ void split_into_lines_by_rules(InlineFormattingContext&, LayoutMode, bool do_collapse, bool do_wrap_lines, bool do_wrap_breaks);
+ void paint_cursor_if_needed(PaintContext&, const LineBoxFragment&) const;
+
+ template<typename Callback>
+ void for_each_chunk(Callback, LayoutMode, bool do_wrap_lines, bool do_wrap_breaks) const;
+
+ String m_text_for_rendering;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp b/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp
new file mode 100644
index 0000000000..6eeff109ea
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/DOM/Element.h>
+#include <LibWeb/DOM/ParentNode.h>
+#include <LibWeb/Dump.h>
+#include <LibWeb/Layout/InitialContainingBlockBox.h>
+#include <LibWeb/Layout/Node.h>
+#include <LibWeb/Layout/TableBox.h>
+#include <LibWeb/Layout/TableCellBox.h>
+#include <LibWeb/Layout/TableRowBox.h>
+#include <LibWeb/Layout/TextNode.h>
+#include <LibWeb/Layout/TreeBuilder.h>
+
+namespace Web::Layout {
+
+TreeBuilder::TreeBuilder()
+{
+}
+
+// The insertion_parent_for_*() functions maintain the invariant that block-level boxes must have either
+// only block-level children or only inline-level children.
+
+static Layout::Node& insertion_parent_for_inline_node(Layout::NodeWithStyle& layout_parent)
+{
+ if (layout_parent.is_inline() && !layout_parent.is_inline_block())
+ return layout_parent;
+
+ if (!layout_parent.has_children() || layout_parent.children_are_inline())
+ return layout_parent;
+
+ // Parent has block-level children, insert into an anonymous wrapper block (and create it first if needed)
+ if (!layout_parent.last_child()->is_anonymous() || !layout_parent.last_child()->children_are_inline()) {
+ layout_parent.append_child(layout_parent.create_anonymous_wrapper());
+ }
+ return *layout_parent.last_child();
+}
+
+static Layout::Node& insertion_parent_for_block_node(Layout::Node& layout_parent, Layout::Node& layout_node)
+{
+ if (!layout_parent.has_children()) {
+ // Parent block has no children, insert this block into parent.
+ return layout_parent;
+ }
+
+ if (!layout_parent.children_are_inline()) {
+ // Parent block has block-level children, insert this block into parent.
+ return layout_parent;
+ }
+
+ // Parent block has inline-level children (our siblings).
+ // First move these siblings into an anonymous wrapper block.
+ NonnullRefPtrVector<Layout::Node> children;
+ while (RefPtr<Layout::Node> child = layout_parent.first_child()) {
+ layout_parent.remove_child(*child);
+ children.append(child.release_nonnull());
+ }
+ layout_parent.append_child(adopt(*new BlockBox(layout_node.document(), nullptr, layout_parent.computed_values().clone_inherited_values())));
+ layout_parent.set_children_are_inline(false);
+ for (auto& child : children) {
+ layout_parent.last_child()->append_child(child);
+ }
+ layout_parent.last_child()->set_children_are_inline(true);
+ // Then it's safe to insert this block into parent.
+ return layout_parent;
+}
+
+void TreeBuilder::create_layout_tree(DOM::Node& dom_node)
+{
+ // If the parent doesn't have a layout node, we don't need one either.
+ if (dom_node.parent() && !dom_node.parent()->layout_node())
+ return;
+
+ auto layout_node = dom_node.create_layout_node();
+ if (!layout_node)
+ return;
+
+ if (!dom_node.parent()) {
+ m_layout_root = layout_node;
+ } else {
+ if (layout_node->is_inline()) {
+ // Inlines can be inserted into the nearest ancestor.
+ auto& insertion_point = insertion_parent_for_inline_node(*m_parent_stack.last());
+ insertion_point.append_child(*layout_node);
+ insertion_point.set_children_are_inline(true);
+ } else {
+ // Non-inlines can't be inserted into an inline parent, so find the nearest non-inline ancestor.
+ auto& nearest_non_inline_ancestor = [&]() -> Layout::Node& {
+ for (ssize_t i = m_parent_stack.size() - 1; i >= 0; --i) {
+ if (!m_parent_stack[i]->is_inline() || m_parent_stack[i]->is_inline_block())
+ return *m_parent_stack[i];
+ }
+ ASSERT_NOT_REACHED();
+ }();
+ auto& insertion_point = insertion_parent_for_block_node(nearest_non_inline_ancestor, *layout_node);
+ insertion_point.append_child(*layout_node);
+ insertion_point.set_children_are_inline(false);
+ }
+ }
+
+ if (dom_node.has_children() && layout_node->can_have_children()) {
+ push_parent(downcast<NodeWithStyle>(*layout_node));
+ downcast<DOM::ParentNode>(dom_node).for_each_child([&](auto& dom_child) {
+ create_layout_tree(dom_child);
+ });
+ pop_parent();
+ }
+}
+
+RefPtr<Node> TreeBuilder::build(DOM::Node& dom_node)
+{
+ if (dom_node.parent()) {
+ // We're building a partial layout tree, so start by building up the stack of parent layout nodes.
+ for (auto* ancestor = dom_node.parent()->layout_node(); ancestor; ancestor = ancestor->parent())
+ m_parent_stack.prepend(downcast<NodeWithStyle>(ancestor));
+ }
+
+ create_layout_tree(dom_node);
+
+ if (auto* root = dom_node.document().layout_node())
+ fixup_tables(*root);
+
+ return move(m_layout_root);
+}
+
+template<CSS::Display display, typename Callback>
+void TreeBuilder::for_each_in_tree_with_display(NodeWithStyle& root, Callback callback)
+{
+ root.for_each_in_subtree_of_type<Box>([&](auto& box) {
+ if (box.computed_values().display() == display)
+ callback(box);
+ return IterationDecision::Continue;
+ });
+}
+
+void TreeBuilder::fixup_tables(NodeWithStyle& root)
+{
+ // NOTE: Even if we only do a partial build, we always do fixup from the root.
+
+ remove_irrelevant_boxes(root);
+ generate_missing_child_wrappers(root);
+ generate_missing_parents(root);
+}
+
+void TreeBuilder::remove_irrelevant_boxes(NodeWithStyle& root)
+{
+ // The following boxes are discarded as if they were display:none:
+
+ NonnullRefPtrVector<Box> to_remove;
+
+ // Children of a table-column.
+ for_each_in_tree_with_display<CSS::Display::TableColumn>(root, [&](Box& table_column) {
+ table_column.for_each_child([&](auto& child) {
+ to_remove.append(child);
+ });
+ });
+
+ // Children of a table-column-group which are not a table-column.
+ for_each_in_tree_with_display<CSS::Display::TableColumnGroup>(root, [&](Box& table_column_group) {
+ table_column_group.for_each_child([&](auto& child) {
+ if (child.computed_values().display() != CSS::Display::TableColumn)
+ to_remove.append(child);
+ });
+ });
+
+ // FIXME:
+ // Anonymous inline boxes which contain only white space and are between two immediate siblings each of which is a table-non-root box.
+ // Anonymous inline boxes which meet all of the following criteria:
+ // - they contain only white space
+ // - they are the first and/or last child of a tabular container
+ // - whose immediate sibling, if any, is a table-non-root box
+
+ for (auto& box : to_remove)
+ box.parent()->remove_child(box);
+}
+
+static bool is_table_track(CSS::Display display)
+{
+ return display == CSS::Display::TableRow || display == CSS::Display::TableColumn;
+}
+
+static bool is_table_track_group(CSS::Display display)
+{
+ return display == CSS::Display::TableRowGroup || display == CSS::Display::TableColumnGroup;
+}
+
+static bool is_not_proper_table_child(const Node& node)
+{
+ if (!node.has_style())
+ return true;
+ auto display = node.computed_values().display();
+ return !is_table_track_group(display) && !is_table_track(display) && display != CSS::Display::TableCaption;
+}
+
+static bool is_not_table_row(const Node& node)
+{
+ if (!node.has_style())
+ return true;
+ auto display = node.computed_values().display();
+ return display != CSS::Display::TableRow;
+}
+
+static bool is_not_table_cell(const Node& node)
+{
+ if (!node.has_style())
+ return true;
+ auto display = node.computed_values().display();
+ return display != CSS::Display::TableCell;
+}
+
+template<typename Matcher, typename Callback>
+static void for_each_sequence_of_consecutive_children_matching(NodeWithStyle& parent, Matcher matcher, Callback callback)
+{
+ NonnullRefPtrVector<Node> sequence;
+ Node* next_sibling = nullptr;
+ for (auto* child = parent.first_child(); child; child = next_sibling) {
+ next_sibling = child->next_sibling();
+ if (matcher(*child)) {
+ sequence.append(*child);
+ } else {
+ if (!sequence.is_empty()) {
+ callback(sequence, next_sibling);
+ sequence.clear();
+ }
+ }
+ }
+ if (!sequence.is_empty())
+ callback(sequence, nullptr);
+}
+
+template<typename WrapperBoxType>
+static void wrap_in_anonymous(NonnullRefPtrVector<Node>& sequence, Node* nearest_sibling)
+{
+ ASSERT(!sequence.is_empty());
+ auto& parent = *sequence.first().parent();
+ auto computed_values = parent.computed_values().clone_inherited_values();
+ static_cast<CSS::MutableComputedValues&>(computed_values).set_display(WrapperBoxType::static_display());
+ auto wrapper = adopt(*new WrapperBoxType(parent.document(), nullptr, move(computed_values)));
+ for (auto& child : sequence) {
+ parent.remove_child(child);
+ wrapper->append_child(child);
+ }
+ if (nearest_sibling)
+ parent.insert_before(move(wrapper), *nearest_sibling);
+ else
+ parent.append_child(move(wrapper));
+}
+
+void TreeBuilder::generate_missing_child_wrappers(NodeWithStyle& root)
+{
+ // An anonymous table-row box must be generated around each sequence of consecutive children of a table-root box which are not proper table child boxes.
+ for_each_in_tree_with_display<CSS::Display::Table>(root, [&](auto& parent) {
+ for_each_sequence_of_consecutive_children_matching(parent, is_not_proper_table_child, [&](auto sequence, auto nearest_sibling) {
+ wrap_in_anonymous<TableRowBox>(sequence, nearest_sibling);
+ });
+ });
+
+ // An anonymous table-row box must be generated around each sequence of consecutive children of a table-row-group box which are not table-row boxes.
+ for_each_in_tree_with_display<CSS::Display::TableRowGroup>(root, [&](auto& parent) {
+ for_each_sequence_of_consecutive_children_matching(parent, is_not_table_row, [&](auto& sequence, auto nearest_sibling) {
+ wrap_in_anonymous<TableRowBox>(sequence, nearest_sibling);
+ });
+ });
+
+ // An anonymous table-cell box must be generated around each sequence of consecutive children of a table-row box which are not table-cell boxes. !Testcase
+ for_each_in_tree_with_display<CSS::Display::TableRow>(root, [&](auto& parent) {
+ for_each_sequence_of_consecutive_children_matching(parent, is_not_table_cell, [&](auto& sequence, auto nearest_sibling) {
+ wrap_in_anonymous<TableCellBox>(sequence, nearest_sibling);
+ });
+ });
+}
+
+void TreeBuilder::generate_missing_parents(NodeWithStyle&)
+{
+ // FIXME: Implement.
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/TreeBuilder.h b/Userland/Libraries/LibWeb/Layout/TreeBuilder.h
new file mode 100644
index 0000000000..03a87cc287
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/TreeBuilder.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/NonnullRefPtrVector.h>
+#include <AK/RefPtr.h>
+#include <LibWeb/Forward.h>
+
+namespace Web::Layout {
+
+class TreeBuilder {
+public:
+ TreeBuilder();
+
+ RefPtr<Layout::Node> build(DOM::Node&);
+
+private:
+ void create_layout_tree(DOM::Node&);
+
+ void push_parent(Layout::NodeWithStyle& node) { m_parent_stack.append(&node); }
+ void pop_parent() { m_parent_stack.take_last(); }
+
+ template<CSS::Display, typename Callback>
+ void for_each_in_tree_with_display(NodeWithStyle& root, Callback);
+
+ void fixup_tables(NodeWithStyle& root);
+ void remove_irrelevant_boxes(NodeWithStyle& root);
+ void generate_missing_child_wrappers(NodeWithStyle& root);
+ void generate_missing_parents(NodeWithStyle& root);
+
+ RefPtr<Layout::Node> m_layout_root;
+ Vector<Layout::NodeWithStyle*> m_parent_stack;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/WidgetBox.cpp b/Userland/Libraries/LibWeb/Layout/WidgetBox.cpp
new file mode 100644
index 0000000000..c38aa50765
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/WidgetBox.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibGUI/Painter.h>
+#include <LibGUI/ScrollBar.h>
+#include <LibGUI/Widget.h>
+#include <LibGfx/Font.h>
+#include <LibGfx/StylePainter.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/InProcessWebView.h>
+#include <LibWeb/Layout/WidgetBox.h>
+#include <LibWeb/Page/Frame.h>
+
+namespace Web::Layout {
+
+WidgetBox::WidgetBox(DOM::Document& document, DOM::Element& element, GUI::Widget& widget)
+ : ReplacedBox(document, element, CSS::StyleProperties::create())
+ , m_widget(widget)
+{
+ set_has_intrinsic_width(true);
+ set_has_intrinsic_height(true);
+ set_intrinsic_width(widget.width());
+ set_intrinsic_height(widget.height());
+}
+
+WidgetBox::~WidgetBox()
+{
+ widget().remove_from_parent();
+}
+
+void WidgetBox::did_set_rect()
+{
+ ReplacedBox::did_set_rect();
+ update_widget();
+}
+
+void WidgetBox::update_widget()
+{
+ auto adjusted_widget_position = absolute_rect().location().to_type<int>();
+ auto& page_view = static_cast<const InProcessWebView&>(frame().page()->client());
+ adjusted_widget_position.move_by(-page_view.horizontal_scrollbar().value(), -page_view.vertical_scrollbar().value());
+ widget().move_to(adjusted_widget_position);
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Layout/WidgetBox.h b/Userland/Libraries/LibWeb/Layout/WidgetBox.h
new file mode 100644
index 0000000000..8c7445a1fc
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Layout/WidgetBox.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/Layout/ReplacedBox.h>
+
+namespace Web::Layout {
+
+class WidgetBox final : public ReplacedBox {
+public:
+ WidgetBox(DOM::Document&, DOM::Element&, GUI::Widget&);
+ virtual ~WidgetBox() override;
+
+ GUI::Widget& widget() { return m_widget; }
+ const GUI::Widget& widget() const { return m_widget; }
+
+ void update_widget();
+
+private:
+ virtual void did_set_rect() override;
+
+ NonnullRefPtr<GUI::Widget> m_widget;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/LayoutTreeModel.cpp b/Userland/Libraries/LibWeb/LayoutTreeModel.cpp
new file mode 100644
index 0000000000..440b2fce52
--- /dev/null
+++ b/Userland/Libraries/LibWeb/LayoutTreeModel.cpp
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "LayoutTreeModel.h"
+#include <AK/StringBuilder.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/DOM/Element.h>
+#include <LibWeb/DOM/Text.h>
+#include <LibWeb/Layout/InitialContainingBlockBox.h>
+#include <LibWeb/Layout/TextNode.h>
+#include <ctype.h>
+#include <stdio.h>
+
+namespace Web {
+
+LayoutTreeModel::LayoutTreeModel(DOM::Document& document)
+ : m_document(document)
+{
+ m_document_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/filetype-html.png"));
+ m_element_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/inspector-object.png"));
+ m_text_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/filetype-unknown.png"));
+}
+
+LayoutTreeModel::~LayoutTreeModel()
+{
+}
+
+GUI::ModelIndex LayoutTreeModel::index(int row, int column, const GUI::ModelIndex& parent) const
+{
+ if (!parent.is_valid())
+ return create_index(row, column, m_document->layout_node());
+ auto& parent_node = *static_cast<Layout::Node*>(parent.internal_data());
+ return create_index(row, column, parent_node.child_at_index(row));
+}
+
+GUI::ModelIndex LayoutTreeModel::parent_index(const GUI::ModelIndex& index) const
+{
+ if (!index.is_valid())
+ return {};
+ auto& node = *static_cast<Layout::Node*>(index.internal_data());
+ if (!node.parent())
+ return {};
+
+ // No grandparent? Parent is the document!
+ if (!node.parent()->parent()) {
+ return create_index(0, 0, m_document->layout_node());
+ }
+
+ // Walk the grandparent's children to find the index of node's parent in its parent.
+ // (This is needed to produce the row number of the GUI::ModelIndex corresponding to node's parent.)
+ int grandparent_child_index = 0;
+ for (auto* grandparent_child = node.parent()->parent()->first_child(); grandparent_child; grandparent_child = grandparent_child->next_sibling()) {
+ if (grandparent_child == node.parent())
+ return create_index(grandparent_child_index, 0, node.parent());
+ ++grandparent_child_index;
+ }
+
+ ASSERT_NOT_REACHED();
+ return {};
+}
+
+int LayoutTreeModel::row_count(const GUI::ModelIndex& index) const
+{
+ if (!index.is_valid())
+ return 1;
+ auto& node = *static_cast<Layout::Node*>(index.internal_data());
+ return node.child_count();
+}
+
+int LayoutTreeModel::column_count(const GUI::ModelIndex&) const
+{
+ return 1;
+}
+
+static String with_whitespace_collapsed(const StringView& string)
+{
+ StringBuilder builder;
+ for (size_t i = 0; i < string.length(); ++i) {
+ if (isspace(string[i])) {
+ builder.append(' ');
+ while (i < string.length()) {
+ if (isspace(string[i])) {
+ ++i;
+ continue;
+ }
+ builder.append(string[i]);
+ break;
+ }
+ continue;
+ }
+ builder.append(string[i]);
+ }
+ return builder.to_string();
+}
+
+GUI::Variant LayoutTreeModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const
+{
+ auto& node = *static_cast<Layout::Node*>(index.internal_data());
+ if (role == GUI::ModelRole::Icon) {
+ if (is<Layout::InitialContainingBlockBox>(node))
+ return m_document_icon;
+ if (is<Layout::TextNode>(node))
+ return m_text_icon;
+ return m_element_icon;
+ }
+ if (role == GUI::ModelRole::Display) {
+ if (is<Layout::TextNode>(node))
+ return String::formatted("TextNode: {}", with_whitespace_collapsed(downcast<Layout::TextNode>(node).text_for_rendering()));
+ StringBuilder builder;
+ builder.append(node.class_name());
+ builder.append(' ');
+ if (node.is_anonymous()) {
+ builder.append("[anonymous]");
+ } else if (!node.dom_node()->is_element()) {
+ builder.append(node.dom_node()->node_name());
+ } else {
+ auto& element = downcast<DOM::Element>(*node.dom_node());
+ builder.append('<');
+ builder.append(element.local_name());
+ element.for_each_attribute([&](auto& name, auto& value) {
+ builder.append(' ');
+ builder.append(name);
+ builder.append('=');
+ builder.append('"');
+ builder.append(value);
+ builder.append('"');
+ });
+ builder.append('>');
+ }
+ return builder.to_string();
+ }
+ return {};
+}
+
+void LayoutTreeModel::update()
+{
+ did_update();
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/LayoutTreeModel.h b/Userland/Libraries/LibWeb/LayoutTreeModel.h
new file mode 100644
index 0000000000..f1deedcce1
--- /dev/null
+++ b/Userland/Libraries/LibWeb/LayoutTreeModel.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibGUI/Model.h>
+#include <LibWeb/Forward.h>
+
+namespace Web {
+
+class LayoutTreeModel final : public GUI::Model {
+public:
+ static NonnullRefPtr<LayoutTreeModel> create(DOM::Document& document)
+ {
+ return adopt(*new LayoutTreeModel(document));
+ }
+
+ virtual ~LayoutTreeModel() override;
+
+ virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override;
+ virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override;
+ virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole) const override;
+ virtual GUI::ModelIndex index(int row, int column, const GUI::ModelIndex& parent = GUI::ModelIndex()) const override;
+ virtual GUI::ModelIndex parent_index(const GUI::ModelIndex&) const override;
+ virtual void update() override;
+
+private:
+ explicit LayoutTreeModel(DOM::Document&);
+
+ NonnullRefPtr<DOM::Document> m_document;
+
+ GUI::Icon m_document_icon;
+ GUI::Icon m_element_icon;
+ GUI::Icon m_text_icon;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Loader/ContentFilter.cpp b/Userland/Libraries/LibWeb/Loader/ContentFilter.cpp
new file mode 100644
index 0000000000..b4633b5d01
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Loader/ContentFilter.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/StringBuilder.h>
+#include <LibWeb/Loader/ContentFilter.h>
+
+namespace Web {
+
+ContentFilter& ContentFilter::the()
+{
+ static ContentFilter* filter = new ContentFilter;
+ return *filter;
+}
+
+ContentFilter::ContentFilter()
+{
+}
+
+ContentFilter::~ContentFilter()
+{
+}
+
+bool ContentFilter::is_filtered(const URL& url) const
+{
+ auto url_string = url.to_string();
+
+ for (auto& pattern : m_patterns) {
+ if (url_string.matches(pattern.text, CaseSensitivity::CaseSensitive))
+ return true;
+ }
+ return false;
+}
+
+void ContentFilter::add_pattern(const String& pattern)
+{
+ StringBuilder builder;
+ if (!pattern.starts_with('*'))
+ builder.append('*');
+ builder.append(pattern);
+ if (!pattern.ends_with('*'))
+ builder.append('*');
+ m_patterns.empend(builder.to_string());
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Loader/ContentFilter.h b/Userland/Libraries/LibWeb/Loader/ContentFilter.h
new file mode 100644
index 0000000000..964c3b3fa2
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Loader/ContentFilter.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/URL.h>
+#include <AK/Vector.h>
+
+namespace Web {
+
+class ContentFilter {
+public:
+ static ContentFilter& the();
+
+ bool is_filtered(const URL&) const;
+ void add_pattern(const String&);
+
+private:
+ ContentFilter();
+ ~ContentFilter();
+
+ struct Pattern {
+ String text;
+ };
+ Vector<Pattern> m_patterns;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Loader/FrameLoader.cpp b/Userland/Libraries/LibWeb/Loader/FrameLoader.cpp
new file mode 100644
index 0000000000..172a9e1b2a
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Loader/FrameLoader.cpp
@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/LexicalPath.h>
+#include <LibGemini/Document.h>
+#include <LibGfx/ImageDecoder.h>
+#include <LibMarkdown/Document.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/DOM/ElementFactory.h>
+#include <LibWeb/DOM/Text.h>
+#include <LibWeb/HTML/HTMLIFrameElement.h>
+#include <LibWeb/HTML/Parser/HTMLDocumentParser.h>
+#include <LibWeb/Loader/FrameLoader.h>
+#include <LibWeb/Loader/ResourceLoader.h>
+#include <LibWeb/Namespace.h>
+#include <LibWeb/Page/Frame.h>
+#include <LibWeb/Page/Page.h>
+
+//#define GEMINI_DEBUG 1
+
+namespace Web {
+
+FrameLoader::FrameLoader(Frame& frame)
+ : m_frame(frame)
+{
+}
+
+FrameLoader::~FrameLoader()
+{
+}
+
+static bool build_markdown_document(DOM::Document& document, const ByteBuffer& data)
+{
+ auto markdown_document = Markdown::Document::parse(data);
+ if (!markdown_document)
+ return false;
+
+ HTML::HTMLDocumentParser parser(document, markdown_document->render_to_html(), "utf-8");
+ parser.run(document.url());
+ return true;
+}
+
+static bool build_text_document(DOM::Document& document, const ByteBuffer& data)
+{
+ auto html_element = document.create_element("html");
+ document.append_child(html_element);
+
+ auto head_element = document.create_element("head");
+ html_element->append_child(head_element);
+ auto title_element = document.create_element("title");
+ head_element->append_child(title_element);
+
+ auto title_text = document.create_text_node(document.url().basename());
+ title_element->append_child(title_text);
+
+ auto body_element = document.create_element("body");
+ html_element->append_child(body_element);
+
+ auto pre_element = document.create_element("pre");
+ body_element->append_child(pre_element);
+
+ pre_element->append_child(document.create_text_node(String::copy(data)));
+ return true;
+}
+
+static bool build_image_document(DOM::Document& document, const ByteBuffer& data)
+{
+ auto image_decoder = Gfx::ImageDecoder::create(data.data(), data.size());
+ auto bitmap = image_decoder->bitmap();
+ if (!bitmap)
+ return false;
+
+ auto html_element = document.create_element("html");
+ document.append_child(html_element);
+
+ auto head_element = document.create_element("head");
+ html_element->append_child(head_element);
+ auto title_element = document.create_element("title");
+ head_element->append_child(title_element);
+
+ auto basename = LexicalPath(document.url().path()).basename();
+ auto title_text = adopt(*new DOM::Text(document, String::formatted("{} [{}x{}]", basename, bitmap->width(), bitmap->height())));
+ title_element->append_child(title_text);
+
+ auto body_element = document.create_element("body");
+ html_element->append_child(body_element);
+
+ auto image_element = document.create_element("img");
+ image_element->set_attribute(HTML::AttributeNames::src, document.url().to_string());
+ body_element->append_child(image_element);
+
+ return true;
+}
+
+static bool build_gemini_document(DOM::Document& document, const ByteBuffer& data)
+{
+ StringView gemini_data { data };
+ auto gemini_document = Gemini::Document::parse(gemini_data, document.url());
+ String html_data = gemini_document->render_to_html();
+
+#ifdef GEMINI_DEBUG
+ dbgln("Gemini data:\n\"\"\"{}\"\"\"", gemini_data);
+ dbgln("Converted to HTML:\n\"\"\"{}\"\"\"", html_data);
+#endif
+
+ HTML::HTMLDocumentParser parser(document, html_data, "utf-8");
+ parser.run(document.url());
+ return true;
+}
+
+bool FrameLoader::parse_document(DOM::Document& document, const ByteBuffer& data)
+{
+ auto& mime_type = document.content_type();
+ if (mime_type == "text/html" || mime_type == "image/svg+xml") {
+ HTML::HTMLDocumentParser parser(document, data, document.encoding());
+ parser.run(document.url());
+ return true;
+ }
+ if (mime_type.starts_with("image/"))
+ return build_image_document(document, data);
+ if (mime_type == "text/plain")
+ return build_text_document(document, data);
+ if (mime_type == "text/markdown")
+ return build_markdown_document(document, data);
+ if (mime_type == "text/gemini")
+ return build_gemini_document(document, data);
+
+ return false;
+}
+
+bool FrameLoader::load(const LoadRequest& request, Type type)
+{
+ if (!request.is_valid()) {
+ load_error_page(request.url(), "Invalid request");
+ return false;
+ }
+
+ auto& url = request.url();
+
+ set_resource(ResourceLoader::the().load_resource(Resource::Type::Generic, request));
+
+ if (type == Type::Navigation) {
+ if (auto* page = frame().page())
+ page->client().page_did_start_loading(url);
+ }
+
+ if (type == Type::IFrame)
+ return true;
+
+ if (url.protocol() == "http" || url.protocol() == "https") {
+ URL favicon_url;
+ favicon_url.set_protocol(url.protocol());
+ favicon_url.set_host(url.host());
+ favicon_url.set_port(url.port());
+ favicon_url.set_path("/favicon.ico");
+
+ ResourceLoader::the().load(
+ favicon_url,
+ [this, favicon_url](auto data, auto&) {
+ dbg() << "Favicon downloaded, " << data.size() << " bytes from " << favicon_url;
+ auto decoder = Gfx::ImageDecoder::create(data.data(), data.size());
+ auto bitmap = decoder->bitmap();
+ if (!bitmap) {
+ dbg() << "Could not decode favicon " << favicon_url;
+ return;
+ }
+ dbg() << "Decoded favicon, " << bitmap->size();
+ if (auto* page = frame().page())
+ page->client().page_did_change_favicon(*bitmap);
+ });
+ }
+
+ return true;
+}
+
+bool FrameLoader::load(const URL& url, Type type)
+{
+ dbg() << "FrameLoader::load: " << url;
+
+ if (!url.is_valid()) {
+ load_error_page(url, "Invalid URL");
+ return false;
+ }
+
+ LoadRequest request;
+ request.set_url(url);
+
+ return load(request, type);
+}
+
+void FrameLoader::load_html(const StringView& html, const URL& url)
+{
+ auto document = DOM::Document::create(url);
+ HTML::HTMLDocumentParser parser(document, html, "utf-8");
+ parser.run(url);
+ frame().set_document(&parser.document());
+}
+
+// FIXME: Use an actual templating engine (our own one when it's built, preferably
+// with a way to check these usages at compile time)
+
+void FrameLoader::load_error_page(const URL& failed_url, const String& error)
+{
+ auto error_page_url = "file:///res/html/error.html";
+ ResourceLoader::the().load(
+ error_page_url,
+ [this, failed_url, error](auto data, auto&) {
+ ASSERT(!data.is_null());
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+ auto html = String::format(
+ String::copy(data).characters(),
+ escape_html_entities(failed_url.to_string()).characters(),
+ escape_html_entities(error).characters());
+#pragma GCC diagnostic pop
+ auto document = HTML::parse_html_document(html, failed_url, "utf-8");
+ ASSERT(document);
+ frame().set_document(document);
+ },
+ [](auto error) {
+ dbg() << "Failed to load error page: " << error;
+ ASSERT_NOT_REACHED();
+ });
+}
+
+void FrameLoader::resource_did_load()
+{
+ auto url = resource()->url();
+
+ if (!resource()->has_encoded_data()) {
+ load_error_page(url, "No data");
+ return;
+ }
+
+ // FIXME: Also check HTTP status code before redirecting
+ auto location = resource()->response_headers().get("Location");
+ if (location.has_value()) {
+ load(url.complete_url(location.value()), FrameLoader::Type::Navigation);
+ return;
+ }
+
+ dbgln("I believe this content has MIME type '{}', , encoding '{}'", resource()->mime_type(), resource()->encoding());
+
+ auto document = DOM::Document::create();
+ document->set_url(url);
+ document->set_encoding(resource()->encoding());
+ document->set_content_type(resource()->mime_type());
+
+ frame().set_document(document);
+
+ if (!parse_document(*document, resource()->encoded_data())) {
+ load_error_page(url, "Failed to parse content.");
+ return;
+ }
+
+ if (!url.fragment().is_empty())
+ frame().scroll_to_anchor(url.fragment());
+
+ if (auto* host_element = frame().host_element()) {
+ // FIXME: Perhaps in the future we'll have a better common base class for <frame> and <iframe>
+ ASSERT(is<HTML::HTMLIFrameElement>(*host_element));
+ downcast<HTML::HTMLIFrameElement>(*host_element).content_frame_did_load({});
+ }
+
+ if (auto* page = frame().page())
+ page->client().page_did_finish_loading(url);
+}
+
+void FrameLoader::resource_did_fail()
+{
+ load_error_page(resource()->url(), resource()->error());
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Loader/FrameLoader.h b/Userland/Libraries/LibWeb/Loader/FrameLoader.h
new file mode 100644
index 0000000000..f0c3d0bf56
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Loader/FrameLoader.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/Forward.h>
+#include <LibWeb/Forward.h>
+#include <LibWeb/Loader/Resource.h>
+
+namespace Web {
+
+class FrameLoader final
+ : public ResourceClient {
+public:
+ enum class Type {
+ Navigation,
+ Reload,
+ IFrame,
+ };
+
+ explicit FrameLoader(Frame&);
+ ~FrameLoader();
+
+ bool load(const URL&, Type);
+ bool load(const LoadRequest&, Type);
+
+ void load_html(const StringView&, const URL&);
+
+ Frame& frame() { return m_frame; }
+ const Frame& frame() const { return m_frame; }
+
+private:
+ // ^ResourceClient
+ virtual void resource_did_load() override;
+ virtual void resource_did_fail() override;
+
+ void load_error_page(const URL& failed_url, const String& error_message);
+ bool parse_document(DOM::Document&, const ByteBuffer& data);
+
+ Frame& m_frame;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Loader/ImageLoader.cpp b/Userland/Libraries/LibWeb/Loader/ImageLoader.cpp
new file mode 100644
index 0000000000..b56ab98abd
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Loader/ImageLoader.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibCore/Timer.h>
+#include <LibGfx/Bitmap.h>
+#include <LibGfx/ImageDecoder.h>
+#include <LibWeb/Loader/ImageLoader.h>
+#include <LibWeb/Loader/ResourceLoader.h>
+
+namespace Web {
+
+ImageLoader::ImageLoader()
+ : m_timer(Core::Timer::construct())
+{
+}
+
+void ImageLoader::load(const URL& url)
+{
+ m_loading_state = LoadingState::Loading;
+ LoadRequest request;
+ request.set_url(url);
+ set_resource(ResourceLoader::the().load_resource(Resource::Type::Image, request));
+}
+
+void ImageLoader::set_visible_in_viewport(bool visible_in_viewport) const
+{
+ if (m_visible_in_viewport == visible_in_viewport)
+ return;
+ m_visible_in_viewport = visible_in_viewport;
+
+ // FIXME: Don't update volatility every time. If we're here, we're probably scanning through
+ // the whole document, updating "is visible in viewport" flags, and this could lead
+ // to the same bitmap being marked volatile back and forth unnecessarily.
+ if (resource())
+ const_cast<ImageResource*>(resource())->update_volatility();
+}
+
+void ImageLoader::resource_did_load()
+{
+ ASSERT(resource());
+
+ if (!resource()->mime_type().starts_with("image/")) {
+ m_loading_state = LoadingState::Failed;
+ if (on_fail)
+ on_fail();
+ return;
+ }
+
+ m_loading_state = LoadingState::Loaded;
+
+#ifdef IMAGE_LOADER_DEBUG
+ if (!resource()->has_encoded_data()) {
+ dbg() << "ImageLoader: Resource did load, no encoded data. URL: " << resource()->url();
+ } else {
+ dbg() << "ImageLoader: Resource did load, has encoded data. URL: " << resource()->url();
+ }
+#endif
+
+ if (resource()->should_decode_in_process()) {
+ auto& decoder = resource()->ensure_decoder();
+
+ if (decoder.is_animated() && decoder.frame_count() > 1) {
+ const auto& first_frame = decoder.frame(0);
+ m_timer->set_interval(first_frame.duration);
+ m_timer->on_timeout = [this] { animate(); };
+ m_timer->start();
+ }
+ }
+
+ if (on_load)
+ on_load();
+}
+
+void ImageLoader::animate()
+{
+ if (!m_visible_in_viewport)
+ return;
+
+ auto& decoder = resource()->ensure_decoder();
+
+ m_current_frame_index = (m_current_frame_index + 1) % decoder.frame_count();
+ const auto& current_frame = decoder.frame(m_current_frame_index);
+
+ if (current_frame.duration != m_timer->interval()) {
+ m_timer->restart(current_frame.duration);
+ }
+
+ if (m_current_frame_index == decoder.frame_count() - 1) {
+ ++m_loops_completed;
+ if (m_loops_completed > 0 && m_loops_completed == decoder.loop_count()) {
+ m_timer->stop();
+ }
+ }
+
+ if (on_animate)
+ on_animate();
+}
+
+void ImageLoader::resource_did_fail()
+{
+ dbg() << "ImageLoader: Resource did fail. URL: " << resource()->url();
+ m_loading_state = LoadingState::Failed;
+ if (on_fail)
+ on_fail();
+}
+
+bool ImageLoader::has_image() const
+{
+ if (!resource())
+ return false;
+ if (resource()->should_decode_in_process())
+ return const_cast<ImageResource*>(resource())->ensure_decoder().bitmap();
+ return true;
+}
+
+unsigned ImageLoader::width() const
+{
+ if (!resource())
+ return 0;
+ if (resource()->should_decode_in_process())
+ return const_cast<ImageResource*>(resource())->ensure_decoder().width();
+ return bitmap() ? bitmap()->width() : 0;
+}
+
+unsigned ImageLoader::height() const
+{
+ if (!resource())
+ return 0;
+ if (resource()->should_decode_in_process())
+ return const_cast<ImageResource*>(resource())->ensure_decoder().height();
+ return bitmap() ? bitmap()->height() : 0;
+}
+
+const Gfx::Bitmap* ImageLoader::bitmap() const
+{
+ if (!resource())
+ return nullptr;
+ return resource()->bitmap(m_current_frame_index);
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Loader/ImageLoader.h b/Userland/Libraries/LibWeb/Loader/ImageLoader.h
new file mode 100644
index 0000000000..f07b6e3055
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Loader/ImageLoader.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/Function.h>
+#include <LibCore/Timer.h>
+#include <LibWeb/Loader/ImageResource.h>
+
+namespace Web {
+
+class ImageLoader : public ImageResourceClient {
+public:
+ ImageLoader();
+
+ void load(const URL&);
+
+ const Gfx::Bitmap* bitmap() const;
+
+ bool has_image() const;
+
+ bool has_loaded_or_failed() const { return m_loading_state != LoadingState::Loading; }
+
+ void set_visible_in_viewport(bool) const;
+
+ unsigned width() const;
+ unsigned height() const;
+
+ Function<void()> on_load;
+ Function<void()> on_fail;
+ Function<void()> on_animate;
+
+private:
+ // ^ImageResourceClient
+ virtual void resource_did_load() override;
+ virtual void resource_did_fail() override;
+ virtual bool is_visible_in_viewport() const override { return m_visible_in_viewport; }
+
+ void animate();
+
+ enum class LoadingState {
+ None,
+ Loading,
+ Loaded,
+ Failed,
+ };
+
+ mutable bool m_visible_in_viewport { false };
+
+ size_t m_current_frame_index { 0 };
+ size_t m_loops_completed { 0 };
+ LoadingState m_loading_state { LoadingState::Loading };
+ NonnullRefPtr<Core::Timer> m_timer;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Loader/ImageResource.cpp b/Userland/Libraries/LibWeb/Loader/ImageResource.cpp
new file mode 100644
index 0000000000..3daa284deb
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Loader/ImageResource.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/Function.h>
+#include <LibGfx/Bitmap.h>
+#include <LibGfx/ImageDecoder.h>
+#include <LibImageDecoderClient/Client.h>
+#include <LibWeb/Loader/ImageResource.h>
+
+namespace Web {
+
+ImageResource::ImageResource(const LoadRequest& request)
+ : Resource(Type::Image, request)
+{
+}
+
+ImageResource::~ImageResource()
+{
+}
+
+bool ImageResource::should_decode_in_process() const
+{
+ return mime_type() == "image/gif";
+}
+
+Gfx::ImageDecoder& ImageResource::ensure_decoder()
+{
+ if (!m_decoder)
+ m_decoder = Gfx::ImageDecoder::create(encoded_data());
+ return *m_decoder;
+}
+
+const Gfx::Bitmap* ImageResource::bitmap(size_t frame_index) const
+{
+ if (!has_encoded_data())
+ return nullptr;
+
+ if (should_decode_in_process()) {
+ if (!m_decoder)
+ return nullptr;
+ if (m_decoder->is_animated())
+ m_decoded_image = m_decoder->frame(frame_index).image;
+ else
+ m_decoded_image = m_decoder->bitmap();
+ } else if (!m_decoded_image && !m_has_attempted_decode) {
+ auto image_decoder_client = ImageDecoderClient::Client::construct();
+ m_decoded_image = image_decoder_client->decode_image(encoded_data());
+ m_has_attempted_decode = true;
+ }
+ return m_decoded_image;
+}
+
+void ImageResource::update_volatility()
+{
+ if (!m_decoder)
+ return;
+
+ bool visible_in_viewport = false;
+ for_each_client([&](auto& client) {
+ if (static_cast<const ImageResourceClient&>(client).is_visible_in_viewport())
+ visible_in_viewport = true;
+ });
+
+ if (!visible_in_viewport) {
+ m_decoder->set_volatile();
+ return;
+ }
+
+ bool still_has_decoded_image = m_decoder->set_nonvolatile();
+ if (still_has_decoded_image)
+ return;
+
+ m_decoder = nullptr;
+}
+
+ImageResourceClient::~ImageResourceClient()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Loader/ImageResource.h b/Userland/Libraries/LibWeb/Loader/ImageResource.h
new file mode 100644
index 0000000000..0e66785a70
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Loader/ImageResource.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/Loader/Resource.h>
+
+namespace Web {
+
+class ImageResource final : public Resource {
+ friend class Resource;
+
+public:
+ virtual ~ImageResource() override;
+ Gfx::ImageDecoder& ensure_decoder();
+ const Gfx::Bitmap* bitmap(size_t frame_index = 0) const;
+
+ bool should_decode_in_process() const;
+
+ void update_volatility();
+
+private:
+ explicit ImageResource(const LoadRequest&);
+ RefPtr<Gfx::ImageDecoder> m_decoder;
+ mutable RefPtr<Gfx::Bitmap> m_decoded_image;
+ mutable bool m_has_attempted_decode { false };
+};
+
+class ImageResourceClient : public ResourceClient {
+public:
+ virtual ~ImageResourceClient();
+
+ virtual bool is_visible_in_viewport() const { return false; }
+
+protected:
+ ImageResource* resource() { return static_cast<ImageResource*>(ResourceClient::resource()); }
+ const ImageResource* resource() const { return static_cast<const ImageResource*>(ResourceClient::resource()); }
+
+private:
+ virtual Resource::Type client_type() const override { return Resource::Type::Image; }
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Loader/LoadRequest.h b/Userland/Libraries/LibWeb/Loader/LoadRequest.h
new file mode 100644
index 0000000000..149ce2537c
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Loader/LoadRequest.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/ByteBuffer.h>
+#include <AK/HashMap.h>
+#include <AK/URL.h>
+#include <LibWeb/Forward.h>
+
+namespace Web {
+
+class LoadRequest {
+public:
+ LoadRequest()
+ {
+ }
+
+ bool is_valid() const { return m_url.is_valid(); }
+
+ const URL& url() const { return m_url; }
+ void set_url(const URL& url) { m_url = url; }
+
+ const String& method() const { return m_method; }
+ void set_method(const String& method) { m_method = method; }
+
+ const ByteBuffer& body() const { return m_body; }
+ void set_body(const ByteBuffer& body) { m_body = body; }
+
+ unsigned hash() const
+ {
+ // FIXME: Include headers in the hash as well
+ return pair_int_hash(pair_int_hash(m_url.to_string().hash(), m_method.hash()), string_hash((const char*)m_body.data(), m_body.size()));
+ }
+
+ bool operator==(const LoadRequest& other) const
+ {
+ if (m_headers.size() != other.m_headers.size())
+ return false;
+ for (auto& it : m_headers) {
+ auto jt = other.m_headers.find(it.key);
+ if (jt == other.m_headers.end())
+ return false;
+ if (it.value != jt->value)
+ return false;
+ }
+ return m_url == other.m_url && m_method == other.m_method && m_body == other.m_body;
+ }
+
+ void set_header(const String& name, const String& value) { m_headers.set(name, value); }
+ String header(const String& name) const { return m_headers.get(name).value_or({}); }
+
+ const HashMap<String, String>& headers() const { return m_headers; }
+
+private:
+ URL m_url;
+ String m_method { "GET" };
+ HashMap<String, String> m_headers;
+ ByteBuffer m_body;
+};
+
+}
+
+namespace AK {
+
+template<>
+struct Traits<Web::LoadRequest> : public GenericTraits<Web::LoadRequest> {
+ static unsigned hash(const Web::LoadRequest& request) { return request.hash(); }
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Loader/Resource.cpp b/Userland/Libraries/LibWeb/Loader/Resource.cpp
new file mode 100644
index 0000000000..2cb6ebc52d
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Loader/Resource.cpp
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/Function.h>
+#include <LibCore/MimeData.h>
+#include <LibWeb/HTML/HTMLImageElement.h>
+#include <LibWeb/Loader/Resource.h>
+
+namespace Web {
+
+NonnullRefPtr<Resource> Resource::create(Badge<ResourceLoader>, Type type, const LoadRequest& request)
+{
+ if (type == Type::Image)
+ return adopt(*new ImageResource(request));
+ return adopt(*new Resource(type, request));
+}
+
+Resource::Resource(Type type, const LoadRequest& request)
+ : m_request(request)
+ , m_type(type)
+{
+}
+
+Resource::~Resource()
+{
+}
+
+void Resource::for_each_client(Function<void(ResourceClient&)> callback)
+{
+ Vector<WeakPtr<ResourceClient>, 16> clients_copy;
+ clients_copy.ensure_capacity(m_clients.size());
+ for (auto* client : m_clients)
+ clients_copy.append(client->make_weak_ptr());
+ for (auto client : clients_copy) {
+ if (client)
+ callback(*client);
+ }
+}
+
+static String encoding_from_content_type(const String& content_type)
+{
+ auto offset = content_type.index_of("charset=");
+ if (offset.has_value()) {
+ auto encoding = content_type.substring(offset.value() + 8, content_type.length() - offset.value() - 8).to_lowercase();
+ if (encoding.length() >= 2 && encoding.starts_with('"') && encoding.ends_with('"'))
+ return encoding.substring(1, encoding.length() - 2);
+ if (encoding.length() >= 2 && encoding.starts_with('\'') && encoding.ends_with('\''))
+ return encoding.substring(1, encoding.length() - 2);
+ return encoding;
+ }
+
+ return "utf-8";
+}
+
+static String mime_type_from_content_type(const String& content_type)
+{
+ auto offset = content_type.index_of(";");
+ if (offset.has_value())
+ return content_type.substring(0, offset.value()).to_lowercase();
+
+ return content_type;
+}
+
+void Resource::did_load(Badge<ResourceLoader>, ReadonlyBytes data, const HashMap<String, String, CaseInsensitiveStringTraits>& headers)
+{
+ ASSERT(!m_loaded);
+ m_encoded_data = ByteBuffer::copy(data);
+ m_response_headers = headers;
+ m_loaded = true;
+
+ auto content_type = headers.get("Content-Type");
+ if (content_type.has_value()) {
+#ifdef RESOURCE_DEBUG
+ dbgln("Content-Type header: '{}'", content_type.value());
+#endif
+ m_encoding = encoding_from_content_type(content_type.value());
+ m_mime_type = mime_type_from_content_type(content_type.value());
+ } else if (url().protocol() == "data" && !url().data_mime_type().is_empty()) {
+#ifdef RESOURCE_DEBUG
+ dbg() << "This is a data URL with mime-type _" << url().data_mime_type() << "_";
+#endif
+ m_encoding = "utf-8"; // FIXME: This doesn't seem nice.
+ m_mime_type = url().data_mime_type();
+ } else {
+#ifdef RESOURCE_DEBUG
+ dbgln("No Content-Type header to go on! Guessing based on filename...");
+#endif
+ m_encoding = "utf-8"; // FIXME: This doesn't seem nice.
+ m_mime_type = Core::guess_mime_type_based_on_filename(url().path());
+ }
+
+ for_each_client([](auto& client) {
+ client.resource_did_load();
+ });
+}
+
+void Resource::did_fail(Badge<ResourceLoader>, const String& error)
+{
+ m_error = error;
+ m_failed = true;
+
+ for_each_client([](auto& client) {
+ client.resource_did_fail();
+ });
+}
+
+void Resource::register_client(Badge<ResourceClient>, ResourceClient& client)
+{
+ ASSERT(!m_clients.contains(&client));
+ m_clients.set(&client);
+}
+
+void Resource::unregister_client(Badge<ResourceClient>, ResourceClient& client)
+{
+ ASSERT(m_clients.contains(&client));
+ m_clients.remove(&client);
+}
+
+void ResourceClient::set_resource(Resource* resource)
+{
+ if (m_resource)
+ m_resource->unregister_client({}, *this);
+ m_resource = resource;
+ if (m_resource) {
+ ASSERT(resource->type() == client_type());
+
+ m_resource->register_client({}, *this);
+
+ // Make sure that reused resources also have their load callback fired.
+ if (resource->is_loaded())
+ resource_did_load();
+
+ // Make sure that reused resources also have their fail callback fired.
+ if (resource->is_failed())
+ resource_did_fail();
+ }
+}
+
+ResourceClient::~ResourceClient()
+{
+ if (m_resource)
+ m_resource->unregister_client({}, *this);
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Loader/Resource.h b/Userland/Libraries/LibWeb/Loader/Resource.h
new file mode 100644
index 0000000000..9838071c65
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Loader/Resource.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/ByteBuffer.h>
+#include <AK/HashMap.h>
+#include <AK/HashTable.h>
+#include <AK/Noncopyable.h>
+#include <AK/RefCounted.h>
+#include <AK/URL.h>
+#include <AK/WeakPtr.h>
+#include <AK/Weakable.h>
+#include <LibGfx/Forward.h>
+#include <LibWeb/Forward.h>
+#include <LibWeb/Loader/LoadRequest.h>
+
+namespace Web {
+
+class ResourceClient;
+
+class Resource : public RefCounted<Resource> {
+ AK_MAKE_NONCOPYABLE(Resource);
+ AK_MAKE_NONMOVABLE(Resource);
+
+public:
+ enum class Type {
+ Generic,
+ Image,
+ };
+
+ static NonnullRefPtr<Resource> create(Badge<ResourceLoader>, Type, const LoadRequest&);
+ virtual ~Resource();
+
+ Type type() const { return m_type; }
+
+ bool is_loaded() const { return m_loaded; }
+
+ bool is_failed() const { return m_failed; }
+ const String& error() const { return m_error; }
+
+ bool has_encoded_data() const { return !m_encoded_data.is_null(); }
+
+ const URL& url() const { return m_request.url(); }
+ const ByteBuffer& encoded_data() const { return m_encoded_data; }
+
+ const HashMap<String, String, CaseInsensitiveStringTraits>& response_headers() const { return m_response_headers; }
+
+ void register_client(Badge<ResourceClient>, ResourceClient&);
+ void unregister_client(Badge<ResourceClient>, ResourceClient&);
+
+ const String& encoding() const { return m_encoding; }
+ const String& mime_type() const { return m_mime_type; }
+
+ void for_each_client(Function<void(ResourceClient&)>);
+
+ void did_load(Badge<ResourceLoader>, ReadonlyBytes data, const HashMap<String, String, CaseInsensitiveStringTraits>& headers);
+ void did_fail(Badge<ResourceLoader>, const String& error);
+
+protected:
+ explicit Resource(Type, const LoadRequest&);
+
+private:
+ LoadRequest m_request;
+ ByteBuffer m_encoded_data;
+ Type m_type { Type::Generic };
+ bool m_loaded { false };
+ bool m_failed { false };
+ String m_error;
+ String m_encoding;
+ String m_mime_type;
+ HashMap<String, String, CaseInsensitiveStringTraits> m_response_headers;
+ HashTable<ResourceClient*> m_clients;
+};
+
+class ResourceClient : public Weakable<ResourceClient> {
+public:
+ virtual ~ResourceClient();
+
+ virtual void resource_did_load() { }
+ virtual void resource_did_fail() { }
+
+protected:
+ virtual Resource::Type client_type() const { return Resource::Type::Generic; }
+
+ Resource* resource() { return m_resource; }
+ const Resource* resource() const { return m_resource; }
+ void set_resource(Resource*);
+
+private:
+ RefPtr<Resource> m_resource;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Loader/ResourceLoader.cpp b/Userland/Libraries/LibWeb/Loader/ResourceLoader.cpp
new file mode 100644
index 0000000000..74ec1194d4
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Loader/ResourceLoader.cpp
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/Base64.h>
+#include <AK/JsonObject.h>
+#include <AK/SharedBuffer.h>
+#include <LibCore/EventLoop.h>
+#include <LibCore/File.h>
+#include <LibProtocol/Client.h>
+#include <LibProtocol/Download.h>
+#include <LibWeb/Loader/ContentFilter.h>
+#include <LibWeb/Loader/LoadRequest.h>
+#include <LibWeb/Loader/Resource.h>
+#include <LibWeb/Loader/ResourceLoader.h>
+
+//#define CACHE_DEBUG
+
+namespace Web {
+
+ResourceLoader& ResourceLoader::the()
+{
+ static ResourceLoader* s_the;
+ if (!s_the)
+ s_the = &ResourceLoader::construct().leak_ref();
+ return *s_the;
+}
+
+ResourceLoader::ResourceLoader()
+ : m_protocol_client(Protocol::Client::construct())
+ , m_user_agent("Mozilla/4.0 (SerenityOS; x86) LibWeb+LibJS (Not KHTML, nor Gecko) LibWeb")
+{
+}
+
+void ResourceLoader::load_sync(const URL& url, Function<void(ReadonlyBytes, const HashMap<String, String, CaseInsensitiveStringTraits>& response_headers)> success_callback, Function<void(const String&)> error_callback)
+{
+ Core::EventLoop loop;
+
+ load(
+ url,
+ [&](auto data, auto& response_headers) {
+ success_callback(data, response_headers);
+ loop.quit(0);
+ },
+ [&](auto& string) {
+ if (error_callback)
+ error_callback(string);
+ loop.quit(0);
+ });
+
+ loop.exec();
+}
+
+static HashMap<LoadRequest, NonnullRefPtr<Resource>> s_resource_cache;
+
+RefPtr<Resource> ResourceLoader::load_resource(Resource::Type type, const LoadRequest& request)
+{
+ if (!request.is_valid())
+ return nullptr;
+
+ auto it = s_resource_cache.find(request);
+ if (it != s_resource_cache.end()) {
+ if (it->value->type() != type) {
+ dbg() << "FIXME: Not using cached resource for " << request.url() << " since there's a type mismatch.";
+ } else {
+#ifdef CACHE_DEBUG
+ dbg() << "Reusing cached resource for: " << request.url();
+#endif
+ return it->value;
+ }
+ }
+
+ auto resource = Resource::create({}, type, request);
+
+ s_resource_cache.set(request, resource);
+
+ load(
+ request,
+ [=](auto data, auto& headers) {
+ const_cast<Resource&>(*resource).did_load({}, data, headers);
+ },
+ [=](auto& error) {
+ const_cast<Resource&>(*resource).did_fail({}, error);
+ });
+
+ return resource;
+}
+
+void ResourceLoader::load(const LoadRequest& request, Function<void(ReadonlyBytes, const HashMap<String, String, CaseInsensitiveStringTraits>& response_headers)> success_callback, Function<void(const String&)> error_callback)
+{
+ auto& url = request.url();
+
+ if (is_port_blocked(url.port())) {
+ dbg() << "ResourceLoader::load: Error: blocked port " << url.port() << " for URL: " << url;
+ return;
+ }
+
+ if (ContentFilter::the().is_filtered(url)) {
+ dbgln("\033[32;1mResourceLoader::load: URL was filtered! {}\033[0m", url);
+ error_callback("URL was filtered");
+ return;
+ }
+
+ if (url.protocol() == "about") {
+ dbg() << "Loading about: URL " << url;
+ deferred_invoke([success_callback = move(success_callback)](auto&) {
+ success_callback(String::empty().to_byte_buffer(), {});
+ });
+ return;
+ }
+
+ if (url.protocol() == "data") {
+ dbg() << "ResourceLoader loading a data URL with mime-type: '" << url.data_mime_type() << "', base64=" << url.data_payload_is_base64() << ", payload='" << url.data_payload() << "'";
+
+ ByteBuffer data;
+ if (url.data_payload_is_base64())
+ data = decode_base64(url.data_payload());
+ else
+ data = url.data_payload().to_byte_buffer();
+
+ deferred_invoke([data = move(data), success_callback = move(success_callback)](auto&) {
+ success_callback(data, {});
+ });
+ return;
+ }
+
+ if (url.protocol() == "file") {
+ auto f = Core::File::construct();
+ f->set_filename(url.path());
+ if (!f->open(Core::IODevice::OpenMode::ReadOnly)) {
+ dbg() << "ResourceLoader::load: Error: " << f->error_string();
+ if (error_callback)
+ error_callback(f->error_string());
+ return;
+ }
+
+ auto data = f->read_all();
+ deferred_invoke([data = move(data), success_callback = move(success_callback)](auto&) {
+ success_callback(data, {});
+ });
+ return;
+ }
+
+ if (url.protocol() == "http" || url.protocol() == "https" || url.protocol() == "gemini") {
+ HashMap<String, String> headers;
+ headers.set("User-Agent", m_user_agent);
+ headers.set("Accept-Encoding", "gzip");
+
+ for (auto& it : request.headers()) {
+ headers.set(it.key, it.value);
+ }
+
+ auto download = protocol_client().start_download(request.method(), url.to_string(), headers, request.body());
+ if (!download) {
+ if (error_callback)
+ error_callback("Failed to initiate load");
+ return;
+ }
+ download->on_buffered_download_finish = [this, success_callback = move(success_callback), error_callback = move(error_callback), download](bool success, auto, auto& response_headers, auto status_code, ReadonlyBytes payload) {
+ if (status_code.has_value() && status_code.value() >= 400 && status_code.value() <= 499) {
+ if (error_callback)
+ error_callback(String::formatted("HTTP error ({})", status_code.value()));
+ return;
+ }
+ --m_pending_loads;
+ if (on_load_counter_change)
+ on_load_counter_change();
+ if (!success) {
+ if (error_callback)
+ error_callback("HTTP load failed");
+ return;
+ }
+ deferred_invoke([download](auto&) {
+ // Clear circular reference of `download` captured by copy
+ const_cast<Protocol::Download&>(*download).on_buffered_download_finish = nullptr;
+ });
+ success_callback(payload, response_headers);
+ };
+ download->set_should_buffer_all_input(true);
+ download->on_certificate_requested = []() -> Protocol::Download::CertificateAndKey {
+ return {};
+ };
+ ++m_pending_loads;
+ if (on_load_counter_change)
+ on_load_counter_change();
+ return;
+ }
+
+ if (error_callback)
+ error_callback(String::formatted("Protocol not implemented: {}", url.protocol()));
+}
+
+void ResourceLoader::load(const URL& url, Function<void(ReadonlyBytes, const HashMap<String, String, CaseInsensitiveStringTraits>& response_headers)> success_callback, Function<void(const String&)> error_callback)
+{
+ LoadRequest request;
+ request.set_url(url);
+ load(request, move(success_callback), move(error_callback));
+}
+
+bool ResourceLoader::is_port_blocked(int port)
+{
+ int ports[] { 1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42,
+ 43, 53, 77, 79, 87, 95, 101, 102, 103, 104, 109, 110, 111, 113,
+ 115, 117, 119, 123, 135, 139, 143, 179, 389, 465, 512, 513, 514,
+ 515, 526, 530, 531, 532, 540, 556, 563, 587, 601, 636, 993, 995,
+ 2049, 3659, 4045, 6000, 6379, 6665, 6666, 6667, 6668, 6669, 9000 };
+ for (auto blocked_port : ports)
+ if (port == blocked_port)
+ return true;
+ return false;
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Loader/ResourceLoader.h b/Userland/Libraries/LibWeb/Loader/ResourceLoader.h
new file mode 100644
index 0000000000..5fe23adc8a
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Loader/ResourceLoader.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/Function.h>
+#include <AK/URL.h>
+#include <LibCore/Object.h>
+#include <LibWeb/Loader/Resource.h>
+
+namespace Protocol {
+class Client;
+}
+
+namespace Web {
+
+class ResourceLoader : public Core::Object {
+ C_OBJECT(ResourceLoader)
+public:
+ static ResourceLoader& the();
+
+ RefPtr<Resource> load_resource(Resource::Type, const LoadRequest&);
+
+ void load(const LoadRequest&, Function<void(ReadonlyBytes, const HashMap<String, String, CaseInsensitiveStringTraits>& response_headers)> success_callback, Function<void(const String&)> error_callback = nullptr);
+ void load(const URL&, Function<void(ReadonlyBytes, const HashMap<String, String, CaseInsensitiveStringTraits>& response_headers)> success_callback, Function<void(const String&)> error_callback = nullptr);
+ void load_sync(const URL&, Function<void(ReadonlyBytes, const HashMap<String, String, CaseInsensitiveStringTraits>& response_headers)> success_callback, Function<void(const String&)> error_callback = nullptr);
+
+ Function<void()> on_load_counter_change;
+
+ int pending_loads() const { return m_pending_loads; }
+
+ Protocol::Client& protocol_client() { return *m_protocol_client; }
+
+ const String& user_agent() const { return m_user_agent; }
+
+private:
+ ResourceLoader();
+ static bool is_port_blocked(int port);
+
+ int m_pending_loads { 0 };
+
+ RefPtr<Protocol::Client> m_protocol_client;
+ String m_user_agent;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Namespace.cpp b/Userland/Libraries/LibWeb/Namespace.cpp
new file mode 100644
index 0000000000..b0a1292804
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Namespace.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020, the SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/Namespace.h>
+
+namespace Web::Namespace {
+
+#define __ENUMERATE_NAMESPACE(name, namespace_) FlyString name;
+ENUMERATE_NAMESPACES
+#undef __ENUMERATE_NAMESPACE
+
+[[gnu::constructor]] static void initialize()
+{
+ static bool s_initialized = false;
+ if (s_initialized)
+ return;
+
+#define __ENUMERATE_NAMESPACE(name, namespace_) \
+ name = namespace_;
+ ENUMERATE_NAMESPACES
+#undef __ENUMERATE_NAMESPACE
+
+ s_initialized = true;
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Namespace.h b/Userland/Libraries/LibWeb/Namespace.h
new file mode 100644
index 0000000000..e7fe6ff0f7
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Namespace.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2020, the SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/FlyString.h>
+
+namespace Web::Namespace {
+
+#define ENUMERATE_NAMESPACES \
+ __ENUMERATE_NAMESPACE(HTML, "http://www.w3.org/1999/xhtml") \
+ __ENUMERATE_NAMESPACE(MathML, "http://www.w3.org/1998/Math/MathML") \
+ __ENUMERATE_NAMESPACE(SVG, "http://www.w3.org/2000/svg") \
+ __ENUMERATE_NAMESPACE(XLink, "http://www.w3.org/1999/xlink") \
+ __ENUMERATE_NAMESPACE(XML, "http://www.w3.org/XML/1998/namespace") \
+ __ENUMERATE_NAMESPACE(XMLNS, "http://www.w3.org/2000/xmlns/")
+
+#define __ENUMERATE_NAMESPACE(name, namespace_) extern FlyString name;
+ENUMERATE_NAMESPACES
+#undef __ENUMERATE_NAMESPACE
+
+}
diff --git a/Userland/Libraries/LibWeb/Origin.h b/Userland/Libraries/LibWeb/Origin.h
new file mode 100644
index 0000000000..5a4b061b33
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Origin.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/String.h>
+
+namespace Web {
+
+class Origin {
+public:
+ Origin() { }
+ Origin(const String& protocol, const String& host, u16 port)
+ : m_protocol(protocol)
+ , m_host(host)
+ , m_port(port)
+ {
+ }
+
+ bool is_null() const { return m_protocol.is_null() && m_host.is_null() && !m_port; }
+
+ const String& protocol() const { return m_protocol; }
+ const String& host() const { return m_host; }
+ u16 port() const { return m_port; }
+
+ bool is_same(const Origin& other) const
+ {
+ return protocol() == other.protocol()
+ && host() == other.host()
+ && port() == other.port();
+ }
+
+private:
+ String m_protocol;
+ String m_host;
+ u16 m_port { 0 };
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/OutOfProcessWebView.cpp b/Userland/Libraries/LibWeb/OutOfProcessWebView.cpp
new file mode 100644
index 0000000000..d30025d678
--- /dev/null
+++ b/Userland/Libraries/LibWeb/OutOfProcessWebView.cpp
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "OutOfProcessWebView.h"
+#include "WebContentClient.h"
+#include <AK/SharedBuffer.h>
+#include <LibGUI/MessageBox.h>
+#include <LibGUI/Painter.h>
+#include <LibGUI/ScrollBar.h>
+#include <LibGUI/Window.h>
+#include <LibGfx/Palette.h>
+#include <LibGfx/SystemTheme.h>
+
+REGISTER_WIDGET(Web, OutOfProcessWebView)
+
+namespace Web {
+
+OutOfProcessWebView::OutOfProcessWebView()
+{
+ set_should_hide_unnecessary_scrollbars(true);
+ set_focus_policy(GUI::FocusPolicy::StrongFocus);
+ m_client = WebContentClient::construct(*this);
+ client().post_message(Messages::WebContentServer::UpdateSystemTheme(Gfx::current_system_theme_buffer_id()));
+}
+
+OutOfProcessWebView::~OutOfProcessWebView()
+{
+}
+
+void OutOfProcessWebView::load(const URL& url)
+{
+ m_url = url;
+ client().post_message(Messages::WebContentServer::LoadURL(url));
+}
+
+void OutOfProcessWebView::load_html(const StringView& html, const URL& url)
+{
+ m_url = url;
+ client().post_message(Messages::WebContentServer::LoadHTML(html, url));
+}
+
+void OutOfProcessWebView::load_empty_document()
+{
+ m_url = {};
+ client().post_message(Messages::WebContentServer::LoadHTML("", {}));
+}
+
+void OutOfProcessWebView::paint_event(GUI::PaintEvent& event)
+{
+ GUI::ScrollableWidget::paint_event(event);
+
+ // If the available size is empty, we don't have a front or back bitmap to draw.
+ if (available_size().is_empty())
+ return;
+
+ GUI::Painter painter(*this);
+ painter.add_clip_rect(event.rect());
+
+ if (!m_has_usable_bitmap) {
+ painter.fill_rect(frame_inner_rect(), palette().base());
+ return;
+ }
+
+ painter.add_clip_rect(frame_inner_rect());
+ painter.translate(frame_thickness(), frame_thickness());
+
+ ASSERT(m_front_bitmap);
+ painter.blit({ 0, 0 }, *m_front_bitmap, m_front_bitmap->rect());
+}
+
+void OutOfProcessWebView::resize_event(GUI::ResizeEvent& event)
+{
+ GUI::ScrollableWidget::resize_event(event);
+
+ client().post_message(Messages::WebContentServer::SetViewportRect(Gfx::IntRect({ horizontal_scrollbar().value(), vertical_scrollbar().value() }, available_size())));
+
+ m_front_bitmap = nullptr;
+ m_back_bitmap = nullptr;
+ m_has_usable_bitmap = false;
+
+ if (available_size().is_empty())
+ return;
+
+ if (auto new_bitmap = Gfx::Bitmap::create_shareable(Gfx::BitmapFormat::RGB32, available_size())) {
+ new_bitmap->shared_buffer()->share_with(client().server_pid());
+ m_front_bitmap = move(new_bitmap);
+ }
+
+ if (auto new_bitmap = Gfx::Bitmap::create_shareable(Gfx::BitmapFormat::RGB32, available_size())) {
+ new_bitmap->shared_buffer()->share_with(client().server_pid());
+ m_back_bitmap = move(new_bitmap);
+ }
+
+ request_repaint();
+}
+
+void OutOfProcessWebView::keydown_event(GUI::KeyEvent& event)
+{
+ client().post_message(Messages::WebContentServer::KeyDown(event.key(), event.modifiers(), event.code_point()));
+}
+
+void OutOfProcessWebView::mousedown_event(GUI::MouseEvent& event)
+{
+ client().post_message(Messages::WebContentServer::MouseDown(to_content_position(event.position()), event.button(), event.buttons(), event.modifiers()));
+}
+
+void OutOfProcessWebView::mouseup_event(GUI::MouseEvent& event)
+{
+ client().post_message(Messages::WebContentServer::MouseUp(to_content_position(event.position()), event.button(), event.buttons(), event.modifiers()));
+}
+
+void OutOfProcessWebView::mousemove_event(GUI::MouseEvent& event)
+{
+ client().post_message(Messages::WebContentServer::MouseMove(to_content_position(event.position()), event.button(), event.buttons(), event.modifiers()));
+}
+
+void OutOfProcessWebView::theme_change_event(GUI::ThemeChangeEvent& event)
+{
+ GUI::ScrollableWidget::theme_change_event(event);
+ client().post_message(Messages::WebContentServer::UpdateSystemTheme(Gfx::current_system_theme_buffer_id()));
+ request_repaint();
+}
+
+void OutOfProcessWebView::notify_server_did_paint(Badge<WebContentClient>, i32 shbuf_id)
+{
+ if (m_back_bitmap->shbuf_id() == shbuf_id) {
+ m_has_usable_bitmap = true;
+ swap(m_back_bitmap, m_front_bitmap);
+ update();
+ }
+}
+
+void OutOfProcessWebView::notify_server_did_invalidate_content_rect(Badge<WebContentClient>, [[maybe_unused]] const Gfx::IntRect& content_rect)
+{
+ request_repaint();
+}
+
+void OutOfProcessWebView::notify_server_did_change_selection(Badge<WebContentClient>)
+{
+ request_repaint();
+}
+
+void OutOfProcessWebView::notify_server_did_layout(Badge<WebContentClient>, const Gfx::IntSize& content_size)
+{
+ set_content_size(content_size);
+}
+
+void OutOfProcessWebView::notify_server_did_change_title(Badge<WebContentClient>, const String& title)
+{
+ if (on_title_change)
+ on_title_change(title);
+}
+
+void OutOfProcessWebView::notify_server_did_request_scroll_into_view(Badge<WebContentClient>, const Gfx::IntRect& rect)
+{
+ scroll_into_view(rect, true, true);
+}
+
+void OutOfProcessWebView::notify_server_did_hover_link(Badge<WebContentClient>, const URL& url)
+{
+ set_override_cursor(Gfx::StandardCursor::Hand);
+ if (on_link_hover)
+ on_link_hover(url);
+}
+
+void OutOfProcessWebView::notify_server_did_unhover_link(Badge<WebContentClient>)
+{
+ set_override_cursor(Gfx::StandardCursor::None);
+ if (on_link_hover)
+ on_link_hover({});
+}
+
+void OutOfProcessWebView::notify_server_did_click_link(Badge<WebContentClient>, const URL& url, const String& target, unsigned int modifiers)
+{
+ if (on_link_click)
+ on_link_click(url, target, modifiers);
+}
+
+void OutOfProcessWebView::notify_server_did_middle_click_link(Badge<WebContentClient>, const URL& url, const String& target, unsigned int modifiers)
+{
+ if (on_link_middle_click)
+ on_link_middle_click(url, target, modifiers);
+}
+
+void OutOfProcessWebView::notify_server_did_start_loading(Badge<WebContentClient>, const URL& url)
+{
+ if (on_load_start)
+ on_load_start(url);
+}
+
+void OutOfProcessWebView::notify_server_did_finish_loading(Badge<WebContentClient>, const URL& url)
+{
+ if (on_load_finish)
+ on_load_finish(url);
+}
+
+void OutOfProcessWebView::notify_server_did_request_context_menu(Badge<WebContentClient>, const Gfx::IntPoint& content_position)
+{
+ if (on_context_menu_request)
+ on_context_menu_request(screen_relative_rect().location().translated(to_widget_position(content_position)));
+}
+
+void OutOfProcessWebView::notify_server_did_request_link_context_menu(Badge<WebContentClient>, const Gfx::IntPoint& content_position, const URL& url, const String&, unsigned)
+{
+ if (on_link_context_menu_request)
+ on_link_context_menu_request(url, screen_relative_rect().location().translated(to_widget_position(content_position)));
+}
+
+void OutOfProcessWebView::notify_server_did_request_alert(Badge<WebContentClient>, const String& message)
+{
+ GUI::MessageBox::show(window(), message, "Alert", GUI::MessageBox::Type::Information);
+}
+
+void OutOfProcessWebView::did_scroll()
+{
+ client().post_message(Messages::WebContentServer::SetViewportRect(visible_content_rect()));
+ request_repaint();
+}
+
+void OutOfProcessWebView::request_repaint()
+{
+ // If this widget was instantiated but not yet added to a window,
+ // it won't have a back bitmap yet, so we can just skip repaint requests.
+ if (!m_back_bitmap)
+ return;
+ client().post_message(Messages::WebContentServer::Paint(m_back_bitmap->rect().translated(horizontal_scrollbar().value(), vertical_scrollbar().value()), m_back_bitmap->shbuf_id()));
+}
+
+WebContentClient& OutOfProcessWebView::client()
+{
+ return *m_client;
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/OutOfProcessWebView.h b/Userland/Libraries/LibWeb/OutOfProcessWebView.h
new file mode 100644
index 0000000000..a742a01af8
--- /dev/null
+++ b/Userland/Libraries/LibWeb/OutOfProcessWebView.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/URL.h>
+#include <LibGUI/ScrollableWidget.h>
+#include <LibGUI/Widget.h>
+#include <LibWeb/WebViewHooks.h>
+
+namespace Web {
+
+class WebContentClient;
+
+class OutOfProcessWebView final
+ : public GUI::ScrollableWidget
+ , public Web::WebViewHooks {
+ C_OBJECT(OutOfProcessWebView);
+
+public:
+ virtual ~OutOfProcessWebView() override;
+
+ URL url() const { return m_url; }
+ void load(const URL&);
+
+ void load_html(const StringView&, const URL&);
+ void load_empty_document();
+
+ void notify_server_did_layout(Badge<WebContentClient>, const Gfx::IntSize& content_size);
+ void notify_server_did_paint(Badge<WebContentClient>, i32 shbuf_id);
+ void notify_server_did_invalidate_content_rect(Badge<WebContentClient>, const Gfx::IntRect&);
+ void notify_server_did_change_selection(Badge<WebContentClient>);
+ void notify_server_did_change_title(Badge<WebContentClient>, const String&);
+ void notify_server_did_request_scroll_into_view(Badge<WebContentClient>, const Gfx::IntRect&);
+ void notify_server_did_hover_link(Badge<WebContentClient>, const URL&);
+ void notify_server_did_unhover_link(Badge<WebContentClient>);
+ void notify_server_did_click_link(Badge<WebContentClient>, const URL&, const String& target, unsigned modifiers);
+ void notify_server_did_middle_click_link(Badge<WebContentClient>, const URL&, const String& target, unsigned modifiers);
+ void notify_server_did_start_loading(Badge<WebContentClient>, const URL&);
+ void notify_server_did_finish_loading(Badge<WebContentClient>, const URL&);
+ void notify_server_did_request_context_menu(Badge<WebContentClient>, const Gfx::IntPoint&);
+ void notify_server_did_request_link_context_menu(Badge<WebContentClient>, const Gfx::IntPoint&, const URL&, const String& target, unsigned modifiers);
+ void notify_server_did_request_alert(Badge<WebContentClient>, const String& message);
+
+private:
+ OutOfProcessWebView();
+
+ // ^Widget
+ virtual void paint_event(GUI::PaintEvent&) override;
+ virtual void resize_event(GUI::ResizeEvent&) override;
+ virtual void mousedown_event(GUI::MouseEvent&) override;
+ virtual void mouseup_event(GUI::MouseEvent&) override;
+ virtual void mousemove_event(GUI::MouseEvent&) override;
+ virtual void keydown_event(GUI::KeyEvent&) override;
+ virtual void theme_change_event(GUI::ThemeChangeEvent&) override;
+
+ // ^ScrollableWidget
+ virtual void did_scroll() override;
+
+ void request_repaint();
+
+ WebContentClient& client();
+
+ URL m_url;
+
+ RefPtr<WebContentClient> m_client;
+ RefPtr<Gfx::Bitmap> m_front_bitmap;
+ RefPtr<Gfx::Bitmap> m_back_bitmap;
+
+ bool m_has_usable_bitmap { false };
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Page/EditEventHandler.cpp b/Userland/Libraries/LibWeb/Page/EditEventHandler.cpp
new file mode 100644
index 0000000000..5d27efe2cf
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Page/EditEventHandler.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2020, the SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/StringBuilder.h>
+#include <LibWeb/DOM/Position.h>
+#include <LibWeb/DOM/Range.h>
+#include <LibWeb/DOM/Text.h>
+#include <LibWeb/Layout/LayoutPosition.h>
+#include <LibWeb/Page/Frame.h>
+
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/Dump.h>
+#include <LibWeb/Layout/InitialContainingBlockBox.h>
+
+#include "EditEventHandler.h"
+
+namespace Web {
+
+// This method is quite convoluted but this is necessary to make editing feel intuitive.
+void EditEventHandler::handle_delete(DOM::Range& range)
+{
+ auto* start = downcast<DOM::Text>(range.start_container());
+ auto* end = downcast<DOM::Text>(range.end_container());
+
+ if (start == end) {
+ StringBuilder builder;
+ builder.append(start->data().substring_view(0, range.start_offset()));
+ builder.append(end->data().substring_view(range.end_offset()));
+
+ start->set_data(builder.to_string());
+ } else {
+ // Remove all the nodes that are fully enclosed in the range.
+ HashTable<DOM::Node*> queued_for_deletion;
+ for (auto* node = start->next_in_pre_order(); node; node = node->next_in_pre_order()) {
+ if (node == end)
+ break;
+
+ queued_for_deletion.set(node);
+ }
+ for (auto* parent = start->parent(); parent; parent = parent->parent())
+ queued_for_deletion.remove(parent);
+ for (auto* parent = end->parent(); parent; parent = parent->parent())
+ queued_for_deletion.remove(parent);
+ for (auto* node : queued_for_deletion)
+ node->parent()->remove_child(*node);
+
+ // Join the parent nodes of start and end.
+ DOM::Node *insert_after = start, *remove_from = end, *parent_of_end = end->parent();
+ while (remove_from) {
+ auto* next_sibling = remove_from->next_sibling();
+
+ remove_from->parent()->remove_child(*remove_from);
+ insert_after->parent()->insert_before(*remove_from, *insert_after);
+
+ insert_after = remove_from;
+ remove_from = next_sibling;
+ }
+ if (!parent_of_end->has_children()) {
+ if (parent_of_end->parent())
+ parent_of_end->parent()->remove_child(*parent_of_end);
+ }
+
+ // Join the start and end nodes.
+ StringBuilder builder;
+ builder.append(start->data().substring_view(0, range.start_offset()));
+ builder.append(end->data().substring_view(range.end_offset()));
+
+ start->set_data(builder.to_string());
+ start->parent()->remove_child(*end);
+ }
+
+ // FIXME: When nodes are removed from the DOM, the associated layout nodes become stale and still
+ // remain in the layout tree. This has to be fixed, this just causes everything to be recomputed
+ // which really hurts performance.
+ m_frame.document()->force_layout();
+
+ m_frame.did_edit({});
+}
+
+void EditEventHandler::handle_insert(DOM::Position position, u32 code_point)
+{
+ if (is<DOM::Text>(*position.node())) {
+ auto& node = downcast<DOM::Text>(*position.node());
+
+ StringBuilder builder;
+ builder.append(node.data().substring_view(0, position.offset()));
+ builder.append_code_point(code_point);
+ builder.append(node.data().substring_view(position.offset()));
+ node.set_data(builder.to_string());
+
+ node.invalidate_style();
+ }
+
+ // FIXME: When nodes are removed from the DOM, the associated layout nodes become stale and still
+ // remain in the layout tree. This has to be fixed, this just causes everything to be recomputed
+ // which really hurts performance.
+ m_frame.document()->force_layout();
+
+ m_frame.did_edit({});
+}
+}
diff --git a/Userland/Libraries/LibWeb/Page/EditEventHandler.h b/Userland/Libraries/LibWeb/Page/EditEventHandler.h
new file mode 100644
index 0000000000..67edb9eff0
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Page/EditEventHandler.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020, the SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/Forward.h>
+
+namespace Web {
+
+class EditEventHandler {
+public:
+ explicit EditEventHandler(Frame& frame)
+ : m_frame(frame)
+ {
+ }
+
+ virtual ~EditEventHandler() = default;
+
+ virtual void handle_delete(DOM::Range&);
+ virtual void handle_insert(DOM::Position, u32 code_point);
+
+private:
+ Frame& m_frame;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Page/EventHandler.cpp b/Userland/Libraries/LibWeb/Page/EventHandler.cpp
new file mode 100644
index 0000000000..7a69085aa4
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Page/EventHandler.cpp
@@ -0,0 +1,414 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibGUI/Event.h>
+#include <LibGUI/Window.h>
+#include <LibJS/Runtime/Value.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/DOM/Range.h>
+#include <LibWeb/DOM/Text.h>
+#include <LibWeb/HTML/HTMLAnchorElement.h>
+#include <LibWeb/HTML/HTMLIFrameElement.h>
+#include <LibWeb/HTML/HTMLImageElement.h>
+#include <LibWeb/InProcessWebView.h>
+#include <LibWeb/Layout/InitialContainingBlockBox.h>
+#include <LibWeb/Page/EventHandler.h>
+#include <LibWeb/Page/Frame.h>
+#include <LibWeb/UIEvents/EventNames.h>
+#include <LibWeb/UIEvents/MouseEvent.h>
+
+namespace Web {
+
+static Gfx::IntPoint compute_mouse_event_offset(const Gfx::IntPoint& position, const Layout::Node& layout_node)
+{
+ auto top_left_of_layout_node = layout_node.box_type_agnostic_position();
+ return {
+ position.x() - static_cast<int>(top_left_of_layout_node.x()),
+ position.y() - static_cast<int>(top_left_of_layout_node.y())
+ };
+}
+
+EventHandler::EventHandler(Badge<Frame>, Frame& frame)
+ : m_frame(frame)
+ , m_edit_event_handler(make<EditEventHandler>(frame))
+{
+}
+
+EventHandler::~EventHandler()
+{
+}
+
+const Layout::InitialContainingBlockBox* EventHandler::layout_root() const
+{
+ if (!m_frame.document())
+ return nullptr;
+ return m_frame.document()->layout_node();
+}
+
+Layout::InitialContainingBlockBox* EventHandler::layout_root()
+{
+ if (!m_frame.document())
+ return nullptr;
+ return m_frame.document()->layout_node();
+}
+
+bool EventHandler::handle_mouseup(const Gfx::IntPoint& position, unsigned button, unsigned modifiers)
+{
+ if (!layout_root())
+ return false;
+
+ if (m_mouse_event_tracking_layout_node) {
+ m_mouse_event_tracking_layout_node->handle_mouseup({}, position, button, modifiers);
+ return true;
+ }
+
+ bool handled_event = false;
+
+ auto result = layout_root()->hit_test(position, Layout::HitTestType::Exact);
+
+ if (result.layout_node && result.layout_node->wants_mouse_events()) {
+ result.layout_node->handle_mouseup({}, position, button, modifiers);
+
+ // Things may have changed as a consequence of Layout::Node::handle_mouseup(). Hit test again.
+ if (!layout_root())
+ return true;
+ result = layout_root()->hit_test(position, Layout::HitTestType::Exact);
+ }
+
+ if (result.layout_node && result.layout_node->dom_node()) {
+ RefPtr<DOM::Node> node = result.layout_node->dom_node();
+ if (is<HTML::HTMLIFrameElement>(*node)) {
+ if (auto* subframe = downcast<HTML::HTMLIFrameElement>(*node).content_frame())
+ return subframe->event_handler().handle_mouseup(position.translated(compute_mouse_event_offset({}, *result.layout_node)), button, modifiers);
+ return false;
+ }
+ auto offset = compute_mouse_event_offset(position, *result.layout_node);
+ node->dispatch_event(UIEvents::MouseEvent::create(UIEvents::EventNames::mouseup, offset.x(), offset.y()));
+ handled_event = true;
+ }
+
+ if (button == GUI::MouseButton::Left)
+ m_in_mouse_selection = false;
+ return handled_event;
+}
+
+bool EventHandler::handle_mousedown(const Gfx::IntPoint& position, unsigned button, unsigned modifiers)
+{
+ if (!layout_root())
+ return false;
+
+ if (m_mouse_event_tracking_layout_node) {
+ m_mouse_event_tracking_layout_node->handle_mousedown({}, position, button, modifiers);
+ return true;
+ }
+
+ NonnullRefPtr document = *m_frame.document();
+ RefPtr<DOM::Node> node;
+
+ {
+ auto result = layout_root()->hit_test(position, Layout::HitTestType::Exact);
+ if (!result.layout_node)
+ return false;
+
+ node = result.layout_node->dom_node();
+ document->set_hovered_node(node);
+
+ if (result.layout_node->wants_mouse_events()) {
+ result.layout_node->handle_mousedown({}, position, button, modifiers);
+ return true;
+ }
+
+ if (!node)
+ return false;
+
+ if (is<HTML::HTMLIFrameElement>(*node)) {
+ if (auto* subframe = downcast<HTML::HTMLIFrameElement>(*node).content_frame())
+ return subframe->event_handler().handle_mousedown(position.translated(compute_mouse_event_offset({}, *result.layout_node)), button, modifiers);
+ return false;
+ }
+
+ if (auto* page = m_frame.page())
+ page->set_focused_frame({}, m_frame);
+
+ auto offset = compute_mouse_event_offset(position, *result.layout_node);
+ node->dispatch_event(UIEvents::MouseEvent::create(UIEvents::EventNames::mousedown, offset.x(), offset.y()));
+ }
+
+ // NOTE: Dispatching an event may have disturbed the world.
+ if (!layout_root() || layout_root() != node->document().layout_node())
+ return true;
+
+ if (button == GUI::MouseButton::Right && is<HTML::HTMLImageElement>(*node)) {
+ auto& image_element = downcast<HTML::HTMLImageElement>(*node);
+ auto image_url = image_element.document().complete_url(image_element.src());
+ if (auto* page = m_frame.page())
+ page->client().page_did_request_image_context_menu(m_frame.to_main_frame_position(position), image_url, "", modifiers, image_element.bitmap());
+ return true;
+ }
+
+ if (RefPtr<HTML::HTMLAnchorElement> link = node->enclosing_link_element()) {
+ auto href = link->href();
+ auto url = document->complete_url(href);
+ dbgln("Web::EventHandler: Clicking on a link to {}", url);
+ if (button == GUI::MouseButton::Left) {
+ auto href = link->href();
+ auto url = document->complete_url(href);
+ if (href.starts_with("javascript:")) {
+ document->run_javascript(href.substring_view(11, href.length() - 11));
+ } else if (href.starts_with('#')) {
+ auto anchor = href.substring_view(1, href.length() - 1);
+ m_frame.scroll_to_anchor(anchor);
+ } else {
+ if (m_frame.is_main_frame()) {
+ if (auto* page = m_frame.page())
+ page->client().page_did_click_link(url, link->target(), modifiers);
+ } else {
+ // FIXME: Handle different targets!
+ m_frame.loader().load(url, FrameLoader::Type::Navigation);
+ }
+ }
+ } else if (button == GUI::MouseButton::Right) {
+ if (auto* page = m_frame.page())
+ page->client().page_did_request_link_context_menu(m_frame.to_main_frame_position(position), url, link->target(), modifiers);
+ } else if (button == GUI::MouseButton::Middle) {
+ if (auto* page = m_frame.page())
+ page->client().page_did_middle_click_link(url, link->target(), modifiers);
+ }
+ } else {
+ if (button == GUI::MouseButton::Left) {
+ auto result = layout_root()->hit_test(position, Layout::HitTestType::TextCursor);
+ if (result.layout_node && result.layout_node->dom_node()) {
+ m_frame.set_cursor_position(DOM::Position(*node, result.index_in_node));
+ layout_root()->set_selection({ { result.layout_node, result.index_in_node }, {} });
+ m_in_mouse_selection = true;
+ }
+ } else if (button == GUI::MouseButton::Right) {
+ if (auto* page = m_frame.page())
+ page->client().page_did_request_context_menu(m_frame.to_main_frame_position(position));
+ }
+ }
+ return true;
+}
+
+bool EventHandler::handle_mousemove(const Gfx::IntPoint& position, unsigned buttons, unsigned modifiers)
+{
+ if (!layout_root())
+ return false;
+
+ if (m_mouse_event_tracking_layout_node) {
+ m_mouse_event_tracking_layout_node->handle_mousemove({}, position, buttons, modifiers);
+ return true;
+ }
+
+ auto& document = *m_frame.document();
+
+ bool hovered_node_changed = false;
+ bool is_hovering_link = false;
+ bool is_hovering_text = false;
+ auto result = layout_root()->hit_test(position, Layout::HitTestType::Exact);
+ const HTML::HTMLAnchorElement* hovered_link_element = nullptr;
+ if (result.layout_node) {
+
+ if (result.layout_node->wants_mouse_events()) {
+ document.set_hovered_node(result.layout_node->dom_node());
+ result.layout_node->handle_mousemove({}, position, buttons, modifiers);
+ // FIXME: It feels a bit aggressive to always update the cursor like this.
+ if (auto* page = m_frame.page())
+ page->client().page_did_request_cursor_change(Gfx::StandardCursor::None);
+ return true;
+ }
+
+ RefPtr<DOM::Node> node = result.layout_node->dom_node();
+
+ if (node && is<HTML::HTMLIFrameElement>(*node)) {
+ if (auto* subframe = downcast<HTML::HTMLIFrameElement>(*node).content_frame())
+ return subframe->event_handler().handle_mousemove(position.translated(compute_mouse_event_offset({}, *result.layout_node)), buttons, modifiers);
+ return false;
+ }
+
+ hovered_node_changed = node != document.hovered_node();
+ document.set_hovered_node(node);
+ if (node) {
+ if (node->is_text())
+ is_hovering_text = true;
+ hovered_link_element = node->enclosing_link_element();
+ if (hovered_link_element)
+ is_hovering_link = true;
+ auto offset = compute_mouse_event_offset(position, *result.layout_node);
+ node->dispatch_event(UIEvents::MouseEvent::create(UIEvents::EventNames::mousemove, offset.x(), offset.y()));
+ // NOTE: Dispatching an event may have disturbed the world.
+ if (!layout_root() || layout_root() != node->document().layout_node())
+ return true;
+ }
+ if (m_in_mouse_selection) {
+ auto hit = layout_root()->hit_test(position, Layout::HitTestType::TextCursor);
+ if (hit.layout_node && hit.layout_node->dom_node()) {
+ layout_root()->set_selection_end({ hit.layout_node, hit.index_in_node });
+ }
+ if (auto* page = m_frame.page())
+ page->client().page_did_change_selection();
+ }
+ }
+
+ if (auto* page = m_frame.page()) {
+ if (is_hovering_link)
+ page->client().page_did_request_cursor_change(Gfx::StandardCursor::Hand);
+ else if (is_hovering_text)
+ page->client().page_did_request_cursor_change(Gfx::StandardCursor::IBeam);
+ else
+ page->client().page_did_request_cursor_change(Gfx::StandardCursor::None);
+
+ if (hovered_node_changed) {
+ RefPtr<HTML::HTMLElement> hovered_html_element = document.hovered_node() ? document.hovered_node()->enclosing_html_element() : nullptr;
+ if (hovered_html_element && !hovered_html_element->title().is_null()) {
+ page->client().page_did_enter_tooltip_area(m_frame.to_main_frame_position(position), hovered_html_element->title());
+ } else {
+ page->client().page_did_leave_tooltip_area();
+ }
+ if (is_hovering_link)
+ page->client().page_did_hover_link(document.complete_url(hovered_link_element->href()));
+ else
+ page->client().page_did_unhover_link();
+ }
+ }
+ return true;
+}
+
+bool EventHandler::focus_next_element()
+{
+ if (!m_frame.document())
+ return false;
+ auto* element = m_frame.document()->focused_element();
+ if (!element) {
+ element = m_frame.document()->first_child_of_type<DOM::Element>();
+ if (element && element->is_focusable()) {
+ m_frame.document()->set_focused_element(element);
+ return true;
+ }
+ }
+
+ for (element = element->next_element_in_pre_order(); element && !element->is_focusable(); element = element->next_element_in_pre_order())
+ ;
+
+ m_frame.document()->set_focused_element(element);
+ return element;
+}
+
+bool EventHandler::focus_previous_element()
+{
+ // FIXME: Implement Shift-Tab cycling backwards through focusable elements!
+ return false;
+}
+
+bool EventHandler::handle_keydown(KeyCode key, unsigned modifiers, u32 code_point)
+{
+ if (key == KeyCode::Key_Tab) {
+ if (modifiers & KeyModifier::Mod_Shift)
+ return focus_previous_element();
+ else
+ return focus_next_element();
+ }
+
+ if (layout_root()->selection().is_valid()) {
+ auto range = layout_root()->selection().to_dom_range()->normalized();
+ if (range->start_container()->is_editable()) {
+ m_frame.document()->layout_node()->set_selection({});
+
+ // FIXME: This doesn't work for some reason?
+ m_frame.set_cursor_position({ *range->start_container(), range->start_offset() });
+
+ if (key == KeyCode::Key_Backspace || key == KeyCode::Key_Delete) {
+
+ m_edit_event_handler->handle_delete(range);
+ return true;
+ } else {
+
+ m_edit_event_handler->handle_delete(range);
+
+ m_edit_event_handler->handle_insert(m_frame.cursor_position(), code_point);
+ m_frame.cursor_position().set_offset(m_frame.cursor_position().offset() + 1);
+ return true;
+ }
+ }
+ }
+
+ if (m_frame.cursor_position().is_valid() && m_frame.cursor_position().node()->is_editable()) {
+ if (key == KeyCode::Key_Backspace) {
+ auto position = m_frame.cursor_position();
+
+ if (position.offset() == 0)
+ TODO();
+
+ m_frame.cursor_position().set_offset(position.offset() - 1);
+ m_edit_event_handler->handle_delete(DOM::Range::create(*position.node(), position.offset() - 1, *position.node(), position.offset()));
+
+ return true;
+ } else if (key == KeyCode::Key_Delete) {
+ auto position = m_frame.cursor_position();
+
+ if (position.offset() >= downcast<DOM::Text>(position.node())->data().length())
+ TODO();
+
+ m_edit_event_handler->handle_delete(DOM::Range::create(*position.node(), position.offset(), *position.node(), position.offset() + 1));
+
+ return true;
+ } else if (key == KeyCode::Key_Right) {
+ auto position = m_frame.cursor_position();
+
+ if (position.offset() >= downcast<DOM::Text>(position.node())->data().length())
+ TODO();
+
+ m_frame.cursor_position().set_offset(position.offset() + 1);
+
+ return true;
+ } else if (key == KeyCode::Key_Left) {
+ auto position = m_frame.cursor_position();
+
+ if (position.offset() == 0)
+ TODO();
+
+ m_frame.cursor_position().set_offset(position.offset() - 1);
+
+ return true;
+ } else {
+ m_edit_event_handler->handle_insert(m_frame.cursor_position(), code_point);
+ m_frame.cursor_position().set_offset(m_frame.cursor_position().offset() + 1);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void EventHandler::set_mouse_event_tracking_layout_node(Layout::Node* layout_node)
+{
+ if (layout_node)
+ m_mouse_event_tracking_layout_node = layout_node->make_weak_ptr();
+ else
+ m_mouse_event_tracking_layout_node = nullptr;
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Page/EventHandler.h b/Userland/Libraries/LibWeb/Page/EventHandler.h
new file mode 100644
index 0000000000..7ea12d6461
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Page/EventHandler.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/Forward.h>
+#include <AK/WeakPtr.h>
+#include <Kernel/API/KeyCode.h>
+#include <LibGUI/Forward.h>
+#include <LibGfx/Forward.h>
+#include <LibWeb/Forward.h>
+#include <LibWeb/Page/EditEventHandler.h>
+
+namespace Web {
+
+class Frame;
+
+class EventHandler {
+public:
+ explicit EventHandler(Badge<Frame>, Frame&);
+ ~EventHandler();
+
+ bool handle_mouseup(const Gfx::IntPoint&, unsigned button, unsigned modifiers);
+ bool handle_mousedown(const Gfx::IntPoint&, unsigned button, unsigned modifiers);
+ bool handle_mousemove(const Gfx::IntPoint&, unsigned buttons, unsigned modifiers);
+
+ bool handle_keydown(KeyCode, unsigned modifiers, u32 code_point);
+
+ void set_mouse_event_tracking_layout_node(Layout::Node*);
+
+ void set_edit_event_handler(NonnullOwnPtr<EditEventHandler> value) { m_edit_event_handler = move(value); }
+
+private:
+ bool focus_next_element();
+ bool focus_previous_element();
+
+ Layout::InitialContainingBlockBox* layout_root();
+ const Layout::InitialContainingBlockBox* layout_root() const;
+
+ Frame& m_frame;
+
+ bool m_in_mouse_selection { false };
+
+ WeakPtr<Layout::Node> m_mouse_event_tracking_layout_node;
+
+ NonnullOwnPtr<EditEventHandler> m_edit_event_handler;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Page/Frame.cpp b/Userland/Libraries/LibWeb/Page/Frame.cpp
new file mode 100644
index 0000000000..8866fea7ef
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Page/Frame.cpp
@@ -0,0 +1,274 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/HTML/HTMLAnchorElement.h>
+#include <LibWeb/InProcessWebView.h>
+#include <LibWeb/Layout/BreakNode.h>
+#include <LibWeb/Layout/InitialContainingBlockBox.h>
+#include <LibWeb/Layout/TextNode.h>
+#include <LibWeb/Layout/WidgetBox.h>
+#include <LibWeb/Page/Frame.h>
+
+namespace Web {
+
+Frame::Frame(DOM::Element& host_element, Frame& main_frame)
+ : m_page(*main_frame.page())
+ , m_main_frame(main_frame)
+ , m_loader(*this)
+ , m_event_handler({}, *this)
+ , m_host_element(host_element)
+{
+ setup();
+}
+
+Frame::Frame(Page& page)
+ : m_page(page)
+ , m_main_frame(*this)
+ , m_loader(*this)
+ , m_event_handler({}, *this)
+{
+ setup();
+}
+
+Frame::~Frame()
+{
+}
+
+void Frame::setup()
+{
+ m_cursor_blink_timer = Core::Timer::construct(500, [this] {
+ if (!is_focused_frame())
+ return;
+ if (m_cursor_position.node() && m_cursor_position.node()->layout_node()) {
+ m_cursor_blink_state = !m_cursor_blink_state;
+ m_cursor_position.node()->layout_node()->set_needs_display();
+ }
+ });
+}
+
+void Frame::did_edit(Badge<EditEventHandler>)
+{
+ // The user has edited the content, restart the cursor blink cycle so that
+ // the cursor doesn't disappear during rapid continuous editing.
+ m_cursor_blink_state = true;
+ m_cursor_blink_timer->restart();
+}
+
+bool Frame::is_focused_frame() const
+{
+ return m_page && &m_page->focused_frame() == this;
+}
+
+void Frame::set_document(DOM::Document* document)
+{
+ if (m_document == document)
+ return;
+
+ m_cursor_position = {};
+
+ if (m_document)
+ m_document->detach_from_frame({}, *this);
+
+ m_document = document;
+
+ if (m_document) {
+ m_document->attach_to_frame({}, *this);
+ if (m_page)
+ m_page->client().page_did_change_title(m_document->title());
+ }
+
+ if (m_page)
+ m_page->client().page_did_set_document_in_main_frame(m_document);
+}
+
+void Frame::set_size(const Gfx::IntSize& size)
+{
+ if (m_size == size)
+ return;
+ m_size = size;
+ if (m_document)
+ m_document->update_layout();
+}
+
+void Frame::set_viewport_scroll_offset(const Gfx::IntPoint& offset)
+{
+ if (m_viewport_scroll_offset == offset)
+ return;
+ m_viewport_scroll_offset = offset;
+
+ if (m_document && m_document->layout_node())
+ m_document->layout_node()->did_set_viewport_rect({}, viewport_rect());
+}
+
+void Frame::set_needs_display(const Gfx::IntRect& rect)
+{
+ if (!viewport_rect().intersects(rect))
+ return;
+
+ if (is_main_frame()) {
+ if (m_page)
+ m_page->client().page_did_invalidate(to_main_frame_rect(rect));
+ return;
+ }
+
+ if (host_element() && host_element()->layout_node())
+ host_element()->layout_node()->set_needs_display();
+}
+
+void Frame::did_scroll(Badge<InProcessWebView>)
+{
+ if (!m_document)
+ return;
+ if (!m_document->layout_node())
+ return;
+ m_document->layout_node()->for_each_in_subtree_of_type<Layout::WidgetBox>([&](auto& layout_widget) {
+ layout_widget.update_widget();
+ return IterationDecision::Continue;
+ });
+}
+
+void Frame::scroll_to_anchor(const String& fragment)
+{
+ if (!document())
+ return;
+
+ auto element = document()->get_element_by_id(fragment);
+ if (!element) {
+ auto candidates = document()->get_elements_by_name(fragment);
+ for (auto& candidate : candidates) {
+ if (is<HTML::HTMLAnchorElement>(candidate)) {
+ element = downcast<HTML::HTMLAnchorElement>(candidate);
+ break;
+ }
+ }
+ }
+
+ // FIXME: This is overly aggressive and should be something more like a "update_layout_if_needed()"
+ document()->force_layout();
+
+ if (!element || !element->layout_node())
+ return;
+
+ auto& layout_node = *element->layout_node();
+
+ Gfx::FloatRect float_rect { layout_node.box_type_agnostic_position(), { (float)viewport_rect().width(), (float)viewport_rect().height() } };
+ if (is<Layout::Box>(layout_node)) {
+ auto& layout_box = downcast<Layout::Box>(layout_node);
+ auto padding_box = layout_box.box_model().padding_box();
+ float_rect.move_by(-padding_box.left, -padding_box.top);
+ }
+
+ if (m_page)
+ m_page->client().page_did_request_scroll_into_view(enclosing_int_rect(float_rect));
+}
+
+Gfx::IntRect Frame::to_main_frame_rect(const Gfx::IntRect& a_rect)
+{
+ auto rect = a_rect;
+ rect.set_location(to_main_frame_position(a_rect.location()));
+ return rect;
+}
+
+Gfx::IntPoint Frame::to_main_frame_position(const Gfx::IntPoint& a_position)
+{
+ auto position = a_position;
+ for (auto* ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
+ if (ancestor->is_main_frame())
+ break;
+ if (!ancestor->host_element())
+ return {};
+ if (!ancestor->host_element()->layout_node())
+ return {};
+ position.move_by(ancestor->host_element()->layout_node()->box_type_agnostic_position().to_type<int>());
+ }
+ return position;
+}
+
+void Frame::set_cursor_position(const DOM::Position& position)
+{
+ if (m_cursor_position == position)
+ return;
+
+ if (m_cursor_position.node() && m_cursor_position.node()->layout_node())
+ m_cursor_position.node()->layout_node()->set_needs_display();
+
+ m_cursor_position = position;
+
+ if (m_cursor_position.node() && m_cursor_position.node()->layout_node())
+ m_cursor_position.node()->layout_node()->set_needs_display();
+
+ dbgln("Cursor position: {}", m_cursor_position);
+}
+
+String Frame::selected_text() const
+{
+ StringBuilder builder;
+ if (!m_document)
+ return {};
+ auto* layout_root = m_document->layout_node();
+ if (!layout_root)
+ return {};
+ if (!layout_root->selection().is_valid())
+ return {};
+
+ auto selection = layout_root->selection().normalized();
+
+ if (selection.start().layout_node == selection.end().layout_node) {
+ if (!is<Layout::TextNode>(*selection.start().layout_node))
+ return "";
+ return downcast<Layout::TextNode>(*selection.start().layout_node).text_for_rendering().substring(selection.start().index_in_node, selection.end().index_in_node - selection.start().index_in_node);
+ }
+
+ // Start node
+ auto layout_node = selection.start().layout_node;
+ if (is<Layout::TextNode>(*layout_node)) {
+ auto& text = downcast<Layout::TextNode>(*layout_node).text_for_rendering();
+ builder.append(text.substring(selection.start().index_in_node, text.length() - selection.start().index_in_node));
+ }
+
+ // Middle nodes
+ layout_node = layout_node->next_in_pre_order();
+ while (layout_node && layout_node != selection.end().layout_node) {
+ if (is<Layout::TextNode>(*layout_node))
+ builder.append(downcast<Layout::TextNode>(*layout_node).text_for_rendering());
+ else if (is<Layout::BreakNode>(*layout_node) || is<Layout::BlockBox>(*layout_node))
+ builder.append('\n');
+
+ layout_node = layout_node->next_in_pre_order();
+ }
+
+ // End node
+ ASSERT(layout_node == selection.end().layout_node);
+ if (is<Layout::TextNode>(*layout_node)) {
+ auto& text = downcast<Layout::TextNode>(*layout_node).text_for_rendering();
+ builder.append(text.substring(0, selection.end().index_in_node));
+ }
+
+ return builder.to_string();
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Page/Frame.h b/Userland/Libraries/LibWeb/Page/Frame.h
new file mode 100644
index 0000000000..d6ba5eea8d
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Page/Frame.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/Function.h>
+#include <AK/Noncopyable.h>
+#include <AK/RefPtr.h>
+#include <AK/WeakPtr.h>
+#include <LibCore/Timer.h>
+#include <LibGfx/Bitmap.h>
+#include <LibGfx/Rect.h>
+#include <LibGfx/Size.h>
+#include <LibWeb/DOM/Position.h>
+#include <LibWeb/Loader/FrameLoader.h>
+#include <LibWeb/Page/EventHandler.h>
+#include <LibWeb/TreeNode.h>
+
+namespace Web {
+
+class Frame : public TreeNode<Frame> {
+public:
+ static NonnullRefPtr<Frame> create_subframe(DOM::Element& host_element, Frame& main_frame) { return adopt(*new Frame(host_element, main_frame)); }
+ static NonnullRefPtr<Frame> create(Page& page) { return adopt(*new Frame(page)); }
+ ~Frame();
+
+ bool is_main_frame() const { return this == &m_main_frame; }
+ bool is_focused_frame() const;
+
+ const DOM::Document* document() const { return m_document; }
+ DOM::Document* document() { return m_document; }
+
+ void set_document(DOM::Document*);
+
+ Page* page() { return m_page; }
+ const Page* page() const { return m_page; }
+
+ const Gfx::IntSize& size() const { return m_size; }
+ void set_size(const Gfx::IntSize&);
+
+ void set_needs_display(const Gfx::IntRect&);
+
+ void set_viewport_scroll_offset(const Gfx::IntPoint&);
+ Gfx::IntRect viewport_rect() const { return { m_viewport_scroll_offset, m_size }; }
+
+ void did_scroll(Badge<InProcessWebView>);
+
+ FrameLoader& loader() { return m_loader; }
+ const FrameLoader& loader() const { return m_loader; }
+
+ EventHandler& event_handler() { return m_event_handler; }
+ const EventHandler& event_handler() const { return m_event_handler; }
+
+ void scroll_to(const Gfx::IntPoint&);
+ void scroll_to_anchor(const String&);
+
+ Frame& main_frame() { return m_main_frame; }
+ const Frame& main_frame() const { return m_main_frame; }
+
+ DOM::Element* host_element() { return m_host_element; }
+ const DOM::Element* host_element() const { return m_host_element; }
+
+ Gfx::IntPoint to_main_frame_position(const Gfx::IntPoint&);
+ Gfx::IntRect to_main_frame_rect(const Gfx::IntRect&);
+
+ DOM::Position& cursor_position() { return m_cursor_position; }
+ const DOM::Position& cursor_position() const { return m_cursor_position; }
+ void set_cursor_position(const DOM::Position&);
+
+ bool cursor_blink_state() const { return m_cursor_blink_state; }
+
+ String selected_text() const;
+
+ void did_edit(Badge<EditEventHandler>);
+
+private:
+ explicit Frame(DOM::Element& host_element, Frame& main_frame);
+ explicit Frame(Page&);
+
+ void setup();
+
+ WeakPtr<Page> m_page;
+ Frame& m_main_frame;
+
+ FrameLoader m_loader;
+ EventHandler m_event_handler;
+
+ WeakPtr<DOM::Element> m_host_element;
+ RefPtr<DOM::Document> m_document;
+ Gfx::IntSize m_size;
+ Gfx::IntPoint m_viewport_scroll_offset;
+
+ DOM::Position m_cursor_position;
+ RefPtr<Core::Timer> m_cursor_blink_timer;
+ bool m_cursor_blink_state { false };
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Page/Page.cpp b/Userland/Libraries/LibWeb/Page/Page.cpp
new file mode 100644
index 0000000000..dfc9ada662
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Page/Page.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/InProcessWebView.h>
+#include <LibWeb/Page/Frame.h>
+#include <LibWeb/Page/Page.h>
+
+namespace Web {
+
+Page::Page(PageClient& client)
+ : m_client(client)
+{
+ m_main_frame = Frame::create(*this);
+}
+
+Page::~Page()
+{
+}
+
+Frame& Page::focused_frame()
+{
+ if (m_focused_frame)
+ return *m_focused_frame;
+ return main_frame();
+}
+
+void Page::set_focused_frame(Badge<EventHandler>, Frame& frame)
+{
+ m_focused_frame = frame.make_weak_ptr();
+}
+
+void Page::load(const URL& url)
+{
+ main_frame().loader().load(url, FrameLoader::Type::Navigation);
+}
+
+void Page::load(const LoadRequest& request)
+{
+ main_frame().loader().load(request, FrameLoader::Type::Navigation);
+}
+
+void Page::load_html(const StringView& html, const URL& url)
+{
+ main_frame().loader().load_html(html, url);
+}
+
+Gfx::Palette Page::palette() const
+{
+ return m_client.palette();
+}
+
+bool Page::handle_mouseup(const Gfx::IntPoint& position, unsigned button, unsigned modifiers)
+{
+ return main_frame().event_handler().handle_mouseup(position, button, modifiers);
+}
+
+bool Page::handle_mousedown(const Gfx::IntPoint& position, unsigned button, unsigned modifiers)
+{
+ return main_frame().event_handler().handle_mousedown(position, button, modifiers);
+}
+
+bool Page::handle_mousemove(const Gfx::IntPoint& position, unsigned buttons, unsigned modifiers)
+{
+ return main_frame().event_handler().handle_mousemove(position, buttons, modifiers);
+}
+
+bool Page::handle_keydown(KeyCode key, unsigned modifiers, u32 code_point)
+{
+ return focused_frame().event_handler().handle_keydown(key, modifiers, code_point);
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Page/Page.h b/Userland/Libraries/LibWeb/Page/Page.h
new file mode 100644
index 0000000000..5d7325d283
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Page/Page.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/Noncopyable.h>
+#include <AK/OwnPtr.h>
+#include <AK/RefPtr.h>
+#include <AK/URL.h>
+#include <Kernel/API/KeyCode.h>
+#include <LibGUI/Window.h>
+#include <LibGfx/Forward.h>
+#include <LibGfx/Palette.h>
+#include <LibWeb/Forward.h>
+
+namespace Web {
+
+class PageClient;
+
+class Page : public Weakable<Page> {
+ AK_MAKE_NONCOPYABLE(Page);
+ AK_MAKE_NONMOVABLE(Page);
+
+public:
+ explicit Page(PageClient&);
+ ~Page();
+
+ PageClient& client() { return m_client; }
+ const PageClient& client() const { return m_client; }
+
+ Web::Frame& main_frame() { return *m_main_frame; }
+ const Web::Frame& main_frame() const { return *m_main_frame; }
+
+ Web::Frame& focused_frame();
+ const Web::Frame& focused_frame() const { return const_cast<Page*>(this)->focused_frame(); }
+
+ void set_focused_frame(Badge<EventHandler>, Frame&);
+
+ void load(const URL&);
+ void load(const LoadRequest&);
+
+ void load_html(const StringView&, const URL&);
+
+ bool handle_mouseup(const Gfx::IntPoint&, unsigned button, unsigned modifiers);
+ bool handle_mousedown(const Gfx::IntPoint&, unsigned button, unsigned modifiers);
+ bool handle_mousemove(const Gfx::IntPoint&, unsigned buttons, unsigned modifiers);
+
+ bool handle_keydown(KeyCode, unsigned modifiers, u32 code_point);
+
+ Gfx::Palette palette() const;
+
+private:
+ PageClient& m_client;
+
+ RefPtr<Frame> m_main_frame;
+ WeakPtr<Frame> m_focused_frame;
+};
+
+class PageClient {
+public:
+ virtual Gfx::Palette palette() const = 0;
+ virtual void page_did_set_document_in_main_frame(DOM::Document*) { }
+ virtual void page_did_change_title(const String&) { }
+ virtual void page_did_start_loading(const URL&) { }
+ virtual void page_did_finish_loading(const URL&) { }
+ virtual void page_did_change_selection() { }
+ virtual void page_did_request_cursor_change(Gfx::StandardCursor) { }
+ virtual void page_did_request_context_menu(const Gfx::IntPoint&) { }
+ virtual void page_did_request_link_context_menu(const Gfx::IntPoint&, const URL&, [[maybe_unused]] const String& target, [[maybe_unused]] unsigned modifiers) { }
+ virtual void page_did_request_image_context_menu(const Gfx::IntPoint&, const URL&, [[maybe_unused]] const String& target, [[maybe_unused]] unsigned modifiers, const Gfx::Bitmap*) { }
+ virtual void page_did_click_link(const URL&, [[maybe_unused]] const String& target, [[maybe_unused]] unsigned modifiers) { }
+ virtual void page_did_middle_click_link(const URL&, [[maybe_unused]] const String& target, [[maybe_unused]] unsigned modifiers) { }
+ virtual void page_did_enter_tooltip_area(const Gfx::IntPoint&, const String&) { }
+ virtual void page_did_leave_tooltip_area() { }
+ virtual void page_did_hover_link(const URL&) { }
+ virtual void page_did_unhover_link() { }
+ virtual void page_did_invalidate(const Gfx::IntRect&) { }
+ virtual void page_did_change_favicon(const Gfx::Bitmap&) { }
+ virtual void page_did_layout() { }
+ virtual void page_did_request_scroll_into_view(const Gfx::IntRect&) { }
+ virtual void page_did_request_alert(const String&) { }
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Painting/BorderPainting.cpp b/Userland/Libraries/LibWeb/Painting/BorderPainting.cpp
new file mode 100644
index 0000000000..3c5cd8831f
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Painting/BorderPainting.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibGfx/Painter.h>
+#include <LibWeb/Painting/BorderPainting.h>
+#include <LibWeb/Painting/PaintContext.h>
+
+namespace Web::Painting {
+
+void paint_border(PaintContext& context, BorderEdge edge, const Gfx::FloatRect& rect, const CSS::ComputedValues& style)
+{
+ const auto& border_data = [&] {
+ switch (edge) {
+ case BorderEdge::Top:
+ return style.border_top();
+ case BorderEdge::Right:
+ return style.border_right();
+ case BorderEdge::Bottom:
+ return style.border_bottom();
+ default: // BorderEdge::Left:
+ return style.border_left();
+ }
+ }();
+
+ float width = border_data.width;
+ if (width <= 0)
+ return;
+
+ auto color = border_data.color;
+ auto border_style = border_data.line_style;
+ int int_width = max((int)width, 1);
+
+ struct Points {
+ Gfx::FloatPoint p1;
+ Gfx::FloatPoint p2;
+ };
+
+ auto points_for_edge = [](BorderEdge edge, const Gfx::FloatRect& rect) -> Points {
+ switch (edge) {
+ case BorderEdge::Top:
+ return { rect.top_left(), rect.top_right() };
+ case BorderEdge::Right:
+ return { rect.top_right(), rect.bottom_right() };
+ case BorderEdge::Bottom:
+ return { rect.bottom_left(), rect.bottom_right() };
+ default: // Edge::Left
+ return { rect.top_left(), rect.bottom_left() };
+ }
+ };
+
+ auto [p1, p2] = points_for_edge(edge, rect);
+
+ if (border_style == CSS::LineStyle::Inset) {
+ auto top_left_color = Color::from_rgb(0x5a5a5a);
+ auto bottom_right_color = Color::from_rgb(0x888888);
+ color = (edge == BorderEdge::Left || edge == BorderEdge::Top) ? top_left_color : bottom_right_color;
+ } else if (border_style == CSS::LineStyle::Outset) {
+ auto top_left_color = Color::from_rgb(0x888888);
+ auto bottom_right_color = Color::from_rgb(0x5a5a5a);
+ color = (edge == BorderEdge::Left || edge == BorderEdge::Top) ? top_left_color : bottom_right_color;
+ }
+
+ auto gfx_line_style = Gfx::Painter::LineStyle::Solid;
+ if (border_style == CSS::LineStyle::Dotted)
+ gfx_line_style = Gfx::Painter::LineStyle::Dotted;
+ if (border_style == CSS::LineStyle::Dashed)
+ gfx_line_style = Gfx::Painter::LineStyle::Dashed;
+
+ if (gfx_line_style != Gfx::Painter::LineStyle::Solid) {
+ switch (edge) {
+ case BorderEdge::Top:
+ p1.move_by(int_width / 2, int_width / 2);
+ p2.move_by(-int_width / 2, int_width / 2);
+ break;
+ case BorderEdge::Right:
+ p1.move_by(-int_width / 2, int_width / 2);
+ p2.move_by(-int_width / 2, -int_width / 2);
+ break;
+ case BorderEdge::Bottom:
+ p1.move_by(int_width / 2, -int_width / 2);
+ p2.move_by(-int_width / 2, -int_width / 2);
+ break;
+ case BorderEdge::Left:
+ p1.move_by(int_width / 2, int_width / 2);
+ p2.move_by(int_width / 2, -int_width / 2);
+ break;
+ }
+ context.painter().draw_line({ (int)p1.x(), (int)p1.y() }, { (int)p2.x(), (int)p2.y() }, color, int_width, gfx_line_style);
+ return;
+ }
+
+ auto draw_line = [&](auto& p1, auto& p2) {
+ context.painter().draw_line({ (int)p1.x(), (int)p1.y() }, { (int)p2.x(), (int)p2.y() }, color, 1, gfx_line_style);
+ };
+
+ float p1_step = 0;
+ float p2_step = 0;
+
+ switch (edge) {
+ case BorderEdge::Top:
+ p1_step = style.border_left().width / (float)int_width;
+ p2_step = style.border_right().width / (float)int_width;
+ for (int i = 0; i < int_width; ++i) {
+ draw_line(p1, p2);
+ p1.move_by(p1_step, 1);
+ p2.move_by(-p2_step, 1);
+ }
+ break;
+ case BorderEdge::Right:
+ p1_step = style.border_top().width / (float)int_width;
+ p2_step = style.border_bottom().width / (float)int_width;
+ for (int i = int_width - 1; i >= 0; --i) {
+ draw_line(p1, p2);
+ p1.move_by(-1, p1_step);
+ p2.move_by(-1, -p2_step);
+ }
+ break;
+ case BorderEdge::Bottom:
+ p1_step = style.border_left().width / (float)int_width;
+ p2_step = style.border_right().width / (float)int_width;
+ for (int i = int_width - 1; i >= 0; --i) {
+ draw_line(p1, p2);
+ p1.move_by(p1_step, -1);
+ p2.move_by(-p2_step, -1);
+ }
+ break;
+ case BorderEdge::Left:
+ p1_step = style.border_top().width / (float)int_width;
+ p2_step = style.border_bottom().width / (float)int_width;
+ for (int i = 0; i < int_width; ++i) {
+ draw_line(p1, p2);
+ p1.move_by(1, p1_step);
+ p2.move_by(1, -p2_step);
+ }
+ break;
+ }
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Painting/BorderPainting.h b/Userland/Libraries/LibWeb/Painting/BorderPainting.h
new file mode 100644
index 0000000000..47633eb980
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Painting/BorderPainting.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibGfx/Forward.h>
+#include <LibWeb/CSS/ComputedValues.h>
+
+namespace Web::Painting {
+
+enum class BorderEdge {
+ Top,
+ Right,
+ Bottom,
+ Left,
+};
+void paint_border(PaintContext&, BorderEdge, const Gfx::FloatRect&, const CSS::ComputedValues&);
+
+}
diff --git a/Userland/Libraries/LibWeb/Painting/PaintContext.h b/Userland/Libraries/LibWeb/Painting/PaintContext.h
new file mode 100644
index 0000000000..f6ba50eb87
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Painting/PaintContext.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibGfx/Forward.h>
+#include <LibGfx/Palette.h>
+#include <LibGfx/Rect.h>
+#include <LibWeb/SVG/SVGContext.h>
+
+namespace Web {
+
+class PaintContext {
+public:
+ explicit PaintContext(Gfx::Painter& painter, const Palette& palette, const Gfx::IntPoint& scroll_offset)
+ : m_painter(painter)
+ , m_palette(palette)
+ , m_scroll_offset(scroll_offset)
+ {
+ }
+
+ Gfx::Painter& painter() const { return m_painter; }
+ const Palette& palette() const { return m_palette; }
+
+ bool has_svg_context() const { return m_svg_context.has_value(); }
+ SVGContext& svg_context() { return m_svg_context.value(); }
+ void set_svg_context(SVGContext context) { m_svg_context = context; }
+
+ bool should_show_line_box_borders() const { return m_should_show_line_box_borders; }
+ void set_should_show_line_box_borders(bool value) { m_should_show_line_box_borders = value; }
+
+ Gfx::IntRect viewport_rect() const { return m_viewport_rect; }
+ void set_viewport_rect(const Gfx::IntRect& rect) { m_viewport_rect = rect; }
+
+ const Gfx::IntPoint& scroll_offset() const { return m_scroll_offset; }
+
+ bool has_focus() const { return m_focus; }
+ void set_has_focus(bool focus) { m_focus = focus; }
+
+private:
+ Gfx::Painter& m_painter;
+ Palette m_palette;
+ Optional<SVGContext> m_svg_context;
+ Gfx::IntRect m_viewport_rect;
+ Gfx::IntPoint m_scroll_offset;
+ bool m_should_show_line_box_borders { false };
+ bool m_focus { false };
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Painting/StackingContext.cpp b/Userland/Libraries/LibWeb/Painting/StackingContext.cpp
new file mode 100644
index 0000000000..7524f4fff9
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Painting/StackingContext.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/QuickSort.h>
+#include <AK/StringBuilder.h>
+#include <LibWeb/DOM/Node.h>
+#include <LibWeb/Layout/Box.h>
+#include <LibWeb/Layout/InitialContainingBlockBox.h>
+#include <LibWeb/Painting/StackingContext.h>
+
+namespace Web::Layout {
+
+StackingContext::StackingContext(Box& box, StackingContext* parent)
+ : m_box(box)
+ , m_parent(parent)
+{
+ ASSERT(m_parent != this);
+ if (m_parent) {
+ m_parent->m_children.append(this);
+
+ // FIXME: Don't sort on every append..
+ quick_sort(m_parent->m_children, [](auto& a, auto& b) {
+ return a->m_box.computed_values().z_index().value_or(0) < b->m_box.computed_values().z_index().value_or(0);
+ });
+ }
+}
+
+void StackingContext::paint(PaintContext& context, PaintPhase phase)
+{
+ if (!is<InitialContainingBlockBox>(m_box)) {
+ m_box.paint(context, phase);
+ } else {
+ // NOTE: InitialContainingBlockBox::paint() merely calls StackingContext::paint()
+ // so we call its base class instead.
+ downcast<InitialContainingBlockBox>(m_box).BlockBox::paint(context, phase);
+ }
+ for (auto* child : m_children) {
+ child->paint(context, phase);
+ }
+}
+
+HitTestResult StackingContext::hit_test(const Gfx::IntPoint& position, HitTestType type) const
+{
+ HitTestResult result;
+ if (!is<InitialContainingBlockBox>(m_box)) {
+ result = m_box.hit_test(position, type);
+ } else {
+ // NOTE: InitialContainingBlockBox::hit_test() merely calls StackingContext::hit_test()
+ // so we call its base class instead.
+ result = downcast<InitialContainingBlockBox>(m_box).BlockBox::hit_test(position, type);
+ }
+
+ for (auto* child : m_children) {
+ auto result_here = child->hit_test(position, type);
+ if (result_here.layout_node)
+ result = result_here;
+ }
+ return result;
+}
+
+void StackingContext::dump(int indent) const
+{
+ StringBuilder builder;
+ for (int i = 0; i < indent; ++i)
+ builder.append(' ');
+ builder.appendff("SC for {}({}) {} [children: {}]", m_box.class_name(), m_box.dom_node() ? m_box.dom_node()->node_name().characters() : "(anonymous)", m_box.absolute_rect().to_string().characters(), m_children.size());
+ dbgln("{}", builder.string_view());
+ for (auto& child : m_children)
+ child->dump(indent + 1);
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Painting/StackingContext.h b/Userland/Libraries/LibWeb/Painting/StackingContext.h
new file mode 100644
index 0000000000..b0b212ed2d
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Painting/StackingContext.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/Vector.h>
+#include <LibWeb/Layout/Node.h>
+
+namespace Web::Layout {
+
+class StackingContext {
+public:
+ StackingContext(Box&, StackingContext* parent);
+
+ StackingContext* parent() { return m_parent; }
+ const StackingContext* parent() const { return m_parent; }
+
+ void paint(PaintContext&, PaintPhase);
+ HitTestResult hit_test(const Gfx::IntPoint&, HitTestType) const;
+
+ void dump(int indent = 0) const;
+
+private:
+ Box& m_box;
+ StackingContext* const m_parent { nullptr };
+ Vector<StackingContext*> m_children;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/QualifiedName.h b/Userland/Libraries/LibWeb/QualifiedName.h
new file mode 100644
index 0000000000..08103b507d
--- /dev/null
+++ b/Userland/Libraries/LibWeb/QualifiedName.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020, the SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/FlyString.h>
+
+namespace Web {
+
+class QualifiedName {
+public:
+ QualifiedName(const FlyString& local_name, const FlyString& prefix, const FlyString& namespace_)
+ : m_local_name(local_name)
+ , m_prefix(prefix)
+ , m_namespace(namespace_)
+ {
+ }
+
+ const FlyString& local_name() const { return m_local_name; }
+ const FlyString& prefix() const { return m_prefix; }
+ const FlyString& namespace_() const { return m_namespace; }
+
+private:
+ FlyString m_local_name;
+ FlyString m_prefix;
+ FlyString m_namespace;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/SVG/SVGContext.h b/Userland/Libraries/LibWeb/SVG/SVGContext.h
new file mode 100644
index 0000000000..ec7fe72eff
--- /dev/null
+++ b/Userland/Libraries/LibWeb/SVG/SVGContext.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+namespace Web {
+
+class SVGContext {
+public:
+ SVGContext()
+ {
+ m_states.append(State());
+ }
+
+ const Gfx::Color& fill_color() const { return state().fill_color; }
+ const Gfx::Color& stroke_color() const { return state().stroke_color; }
+ float stroke_width() const { return state().stroke_width; }
+
+ void set_fill_color(Gfx::Color color) { state().fill_color = color; }
+ void set_stroke_color(Gfx::Color color) { state().stroke_color = color; }
+ void set_stroke_width(float width) { state().stroke_width = width; }
+
+ void save() { m_states.append(m_states.last()); }
+ void restore() { m_states.take_last(); }
+
+private:
+ struct State {
+ Gfx::Color fill_color { Gfx::Color::Black };
+ Gfx::Color stroke_color { Gfx::Color::Transparent };
+ float stroke_width { 1.0 };
+ };
+
+ const State& state() const { return m_states.last(); }
+ State& state() { return m_states.last(); }
+
+ Vector<State> m_states;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/SVG/SVGElement.cpp b/Userland/Libraries/LibWeb/SVG/SVGElement.cpp
new file mode 100644
index 0000000000..828e9b0e99
--- /dev/null
+++ b/Userland/Libraries/LibWeb/SVG/SVGElement.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/SVG/SVGElement.h>
+
+namespace Web::SVG {
+
+SVGElement::SVGElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : Element(document, qualified_name)
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/SVG/SVGElement.h b/Userland/Libraries/LibWeb/SVG/SVGElement.h
new file mode 100644
index 0000000000..99041728f8
--- /dev/null
+++ b/Userland/Libraries/LibWeb/SVG/SVGElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/DOM/Element.h>
+
+namespace Web::SVG {
+
+class SVGElement : public DOM::Element {
+public:
+ using WrapperType = Bindings::SVGElementWrapper;
+
+protected:
+ SVGElement(DOM::Document&, const QualifiedName& qualified_name);
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/SVG/SVGElement.idl b/Userland/Libraries/LibWeb/SVG/SVGElement.idl
new file mode 100644
index 0000000000..1eb7f7f18c
--- /dev/null
+++ b/Userland/Libraries/LibWeb/SVG/SVGElement.idl
@@ -0,0 +1,3 @@
+interface SVGElement : Element {
+
+};
diff --git a/Userland/Libraries/LibWeb/SVG/SVGGeometryElement.cpp b/Userland/Libraries/LibWeb/SVG/SVGGeometryElement.cpp
new file mode 100644
index 0000000000..c3e8f386fd
--- /dev/null
+++ b/Userland/Libraries/LibWeb/SVG/SVGGeometryElement.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/SVG/SVGGeometryElement.h>
+
+namespace Web::SVG {
+
+SVGGeometryElement::SVGGeometryElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : SVGGraphicsElement(document, qualified_name)
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/SVG/SVGGeometryElement.h b/Userland/Libraries/LibWeb/SVG/SVGGeometryElement.h
new file mode 100644
index 0000000000..91c9e92288
--- /dev/null
+++ b/Userland/Libraries/LibWeb/SVG/SVGGeometryElement.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/SVG/SVGGraphicsElement.h>
+
+namespace Web::SVG {
+
+class SVGGeometryElement : public SVGGraphicsElement {
+public:
+ using WrapperType = Bindings::SVGGeometryElementWrapper;
+
+protected:
+ SVGGeometryElement(DOM::Document& document, const QualifiedName& qualified_name);
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/SVG/SVGGeometryElement.idl b/Userland/Libraries/LibWeb/SVG/SVGGeometryElement.idl
new file mode 100644
index 0000000000..bd64356c71
--- /dev/null
+++ b/Userland/Libraries/LibWeb/SVG/SVGGeometryElement.idl
@@ -0,0 +1,3 @@
+interface SVGGeometryElement : SVGGraphicsElement {
+
+};
diff --git a/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.cpp b/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.cpp
new file mode 100644
index 0000000000..2fca526027
--- /dev/null
+++ b/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/SVG/SVGGraphicsElement.h>
+
+namespace Web::SVG {
+
+SVGGraphicsElement::SVGGraphicsElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : SVGElement(document, qualified_name)
+{
+}
+
+void SVGGraphicsElement::parse_attribute(const FlyString& name, const String& value)
+{
+ SVGElement::parse_attribute(name, value);
+
+ if (name == "fill") {
+ m_fill_color = Gfx::Color::from_string(value).value_or(Color::Transparent);
+ } else if (name == "stroke") {
+ m_stroke_color = Gfx::Color::from_string(value).value_or(Color::Transparent);
+ } else if (name == "stroke-width") {
+ auto result = value.to_int();
+ if (result.has_value())
+ m_stroke_width = result.value();
+ }
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.h b/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.h
new file mode 100644
index 0000000000..ec8735a70b
--- /dev/null
+++ b/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibGfx/Path.h>
+#include <LibWeb/DOM/Node.h>
+#include <LibWeb/SVG/SVGElement.h>
+#include <LibWeb/SVG/TagNames.h>
+
+namespace Web::SVG {
+
+class SVGGraphicsElement : public SVGElement {
+public:
+ using WrapperType = Bindings::SVGGraphicsElementWrapper;
+
+ SVGGraphicsElement(DOM::Document&, const QualifiedName& qualified_name);
+
+ virtual void parse_attribute(const FlyString& name, const String& value) override;
+
+ const Optional<Gfx::Color>& fill_color() const { return m_fill_color; }
+ const Optional<Gfx::Color>& stroke_color() const { return m_stroke_color; }
+ const Optional<float>& stroke_width() const { return m_stroke_width; }
+
+protected:
+ Optional<Gfx::Color> m_fill_color;
+ Optional<Gfx::Color> m_stroke_color;
+ Optional<float> m_stroke_width;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.idl b/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.idl
new file mode 100644
index 0000000000..9636176dd0
--- /dev/null
+++ b/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.idl
@@ -0,0 +1,3 @@
+interface SVGGraphicsElement : SVGElement {
+
+};
diff --git a/Userland/Libraries/LibWeb/SVG/SVGPathElement.cpp b/Userland/Libraries/LibWeb/SVG/SVGPathElement.cpp
new file mode 100644
index 0000000000..5a3c01fc4c
--- /dev/null
+++ b/Userland/Libraries/LibWeb/SVG/SVGPathElement.cpp
@@ -0,0 +1,659 @@
+/*
+ * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/StringBuilder.h>
+#include <LibGfx/Painter.h>
+#include <LibGfx/Path.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/DOM/Event.h>
+#include <LibWeb/Layout/SVGPathBox.h>
+#include <LibWeb/SVG/SVGPathElement.h>
+#include <ctype.h>
+
+//#define PATH_DEBUG
+
+namespace Web::SVG {
+
+#ifdef PATH_DEBUG
+static void print_instruction(const PathInstruction& instruction)
+{
+ auto& data = instruction.data;
+
+ switch (instruction.type) {
+ case PathInstructionType::Move:
+ dbg() << "Move (absolute=" << instruction.absolute << ")";
+ for (size_t i = 0; i < data.size(); i += 2)
+ dbg() << " x=" << data[i] << ", y=" << data[i + 1];
+ break;
+ case PathInstructionType::ClosePath:
+ dbg() << "ClosePath (absolute=" << instruction.absolute << ")";
+ break;
+ case PathInstructionType::Line:
+ dbg() << "Line (absolute=" << instruction.absolute << ")";
+ for (size_t i = 0; i < data.size(); i += 2)
+ dbg() << " x=" << data[i] << ", y=" << data[i + 1];
+ break;
+ case PathInstructionType::HorizontalLine:
+ dbg() << "HorizontalLine (absolute=" << instruction.absolute << ")";
+ for (size_t i = 0; i < data.size(); ++i)
+ dbg() << " x=" << data[i];
+ break;
+ case PathInstructionType::VerticalLine:
+ dbg() << "VerticalLine (absolute=" << instruction.absolute << ")";
+ for (size_t i = 0; i < data.size(); ++i)
+ dbg() << " y=" << data[i];
+ break;
+ case PathInstructionType::Curve:
+ dbg() << "Curve (absolute=" << instruction.absolute << ")";
+ for (size_t i = 0; i < data.size(); i += 6)
+ dbg() << " (x1=" << data[i] << ", y1=" << data[i + 1] << "), (x2=" << data[i + 2] << ", y2=" << data[i + 3] << "), (x=" << data[i + 4] << ", y=" << data[i + 5] << ")";
+ break;
+ case PathInstructionType::SmoothCurve:
+ dbg() << "SmoothCurve (absolute=" << instruction.absolute << ")";
+ for (size_t i = 0; i < data.size(); i += 4)
+ dbg() << " (x2=" << data[i] << ", y2=" << data[i + 1] << "), (x=" << data[i + 2] << ", y=" << data[i + 3] << ")";
+ break;
+ case PathInstructionType::QuadraticBezierCurve:
+ dbg() << "QuadraticBezierCurve (absolute=" << instruction.absolute << ")";
+ for (size_t i = 0; i < data.size(); i += 4)
+ dbg() << " (x1=" << data[i] << ", y1=" << data[i + 1] << "), (x=" << data[i + 2] << ", y=" << data[i + 3] << ")";
+ break;
+ case PathInstructionType::SmoothQuadraticBezierCurve:
+ dbg() << "SmoothQuadraticBezierCurve (absolute=" << instruction.absolute << ")";
+ for (size_t i = 0; i < data.size(); i += 2)
+ dbg() << " x=" << data[i] << ", y=" << data[i + 1];
+ break;
+ case PathInstructionType::EllipticalArc:
+ dbg() << "EllipticalArc (absolute=" << instruction.absolute << ")";
+ for (size_t i = 0; i < data.size(); i += 7)
+ dbg() << " (rx=" << data[i] << ", ry=" << data[i + 1] << ") x-axis-rotation=" << data[i + 2] << ", large-arc-flag=" << data[i + 3] << ", sweep-flag=" << data[i + 4] << ", (x=" << data[i + 5] << ", y=" << data[i + 6] << ")";
+ break;
+ case PathInstructionType::Invalid:
+ dbgln("Invalid");
+ break;
+ }
+}
+#endif
+
+PathDataParser::PathDataParser(const String& source)
+ : m_source(source)
+{
+}
+
+Vector<PathInstruction> PathDataParser::parse()
+{
+ parse_whitespace();
+ while (!done())
+ parse_drawto();
+ if (!m_instructions.is_empty() && m_instructions[0].type != PathInstructionType::Move)
+ ASSERT_NOT_REACHED();
+ return m_instructions;
+}
+
+void PathDataParser::parse_drawto()
+{
+ if (match('M') || match('m')) {
+ parse_moveto();
+ } else if (match('Z') || match('z')) {
+ parse_closepath();
+ } else if (match('L') || match('l')) {
+ parse_lineto();
+ } else if (match('H') || match('h')) {
+ parse_horizontal_lineto();
+ } else if (match('V') || match('v')) {
+ parse_vertical_lineto();
+ } else if (match('C') || match('c')) {
+ parse_curveto();
+ } else if (match('S') || match('s')) {
+ parse_smooth_curveto();
+ } else if (match('Q') || match('q')) {
+ parse_quadratic_bezier_curveto();
+ } else if (match('T') || match('t')) {
+ parse_smooth_quadratic_bezier_curveto();
+ } else if (match('A') || match('a')) {
+ parse_elliptical_arc();
+ } else {
+ dbg() << "PathDataParser::parse_drawto failed to match: '" << ch() << "'";
+ TODO();
+ }
+}
+
+void PathDataParser::parse_moveto()
+{
+ bool absolute = consume() == 'M';
+ parse_whitespace();
+ for (auto& pair : parse_coordinate_pair_sequence())
+ m_instructions.append({ PathInstructionType::Move, absolute, pair });
+}
+
+void PathDataParser::parse_closepath()
+{
+ bool absolute = consume() == 'Z';
+ parse_whitespace();
+ m_instructions.append({ PathInstructionType::ClosePath, absolute, {} });
+}
+
+void PathDataParser::parse_lineto()
+{
+ bool absolute = consume() == 'L';
+ parse_whitespace();
+ for (auto& pair : parse_coordinate_pair_sequence())
+ m_instructions.append({ PathInstructionType::Line, absolute, pair });
+}
+
+void PathDataParser::parse_horizontal_lineto()
+{
+ bool absolute = consume() == 'H';
+ parse_whitespace();
+ m_instructions.append({ PathInstructionType::HorizontalLine, absolute, parse_coordinate_sequence() });
+}
+
+void PathDataParser::parse_vertical_lineto()
+{
+ bool absolute = consume() == 'V';
+ parse_whitespace();
+ m_instructions.append({ PathInstructionType::VerticalLine, absolute, parse_coordinate_sequence() });
+}
+
+void PathDataParser::parse_curveto()
+{
+ bool absolute = consume() == 'C';
+ parse_whitespace();
+
+ while (true) {
+ m_instructions.append({ PathInstructionType::Curve, absolute, parse_coordinate_pair_triplet() });
+ if (match_comma_whitespace())
+ parse_comma_whitespace();
+ if (!match_coordinate())
+ break;
+ }
+}
+
+void PathDataParser::parse_smooth_curveto()
+{
+ bool absolute = consume() == 'S';
+ parse_whitespace();
+
+ while (true) {
+ m_instructions.append({ PathInstructionType::SmoothCurve, absolute, parse_coordinate_pair_double() });
+ if (match_comma_whitespace())
+ parse_comma_whitespace();
+ if (!match_coordinate())
+ break;
+ }
+}
+
+void PathDataParser::parse_quadratic_bezier_curveto()
+{
+ bool absolute = consume() == 'Q';
+ parse_whitespace();
+
+ while (true) {
+ m_instructions.append({ PathInstructionType::QuadraticBezierCurve, absolute, parse_coordinate_pair_double() });
+ if (match_comma_whitespace())
+ parse_comma_whitespace();
+ if (!match_coordinate())
+ break;
+ }
+}
+
+void PathDataParser::parse_smooth_quadratic_bezier_curveto()
+{
+ bool absolute = consume() == 'T';
+ parse_whitespace();
+
+ while (true) {
+ m_instructions.append({ PathInstructionType::SmoothQuadraticBezierCurve, absolute, parse_coordinate_pair() });
+ if (match_comma_whitespace())
+ parse_comma_whitespace();
+ if (!match_coordinate())
+ break;
+ }
+}
+
+void PathDataParser::parse_elliptical_arc()
+{
+ bool absolute = consume() == 'A';
+ parse_whitespace();
+
+ while (true) {
+ m_instructions.append({ PathInstructionType::EllipticalArc, absolute, parse_elliptical_arg_argument() });
+ if (match_comma_whitespace())
+ parse_comma_whitespace();
+ if (!match_coordinate())
+ break;
+ }
+}
+
+float PathDataParser::parse_coordinate()
+{
+ return parse_sign() * parse_number();
+}
+
+Vector<float> PathDataParser::parse_coordinate_pair()
+{
+ Vector<float> coordinates;
+ coordinates.append(parse_coordinate());
+ if (match_comma_whitespace())
+ parse_comma_whitespace();
+ coordinates.append(parse_coordinate());
+ return coordinates;
+}
+
+Vector<float> PathDataParser::parse_coordinate_sequence()
+{
+ Vector<float> sequence;
+ while (true) {
+ sequence.append(parse_coordinate());
+ if (match_comma_whitespace())
+ parse_comma_whitespace();
+ if (!match_comma_whitespace() && !match_coordinate())
+ break;
+ }
+ return sequence;
+}
+
+Vector<Vector<float>> PathDataParser::parse_coordinate_pair_sequence()
+{
+ Vector<Vector<float>> sequence;
+ while (true) {
+ sequence.append(parse_coordinate_pair());
+ if (match_comma_whitespace())
+ parse_comma_whitespace();
+ if (!match_comma_whitespace() && !match_coordinate())
+ break;
+ }
+ return sequence;
+}
+
+Vector<float> PathDataParser::parse_coordinate_pair_double()
+{
+ Vector<float> coordinates;
+ coordinates.append(parse_coordinate_pair());
+ if (match_comma_whitespace())
+ parse_comma_whitespace();
+ coordinates.append(parse_coordinate_pair());
+ return coordinates;
+}
+
+Vector<float> PathDataParser::parse_coordinate_pair_triplet()
+{
+ Vector<float> coordinates;
+ coordinates.append(parse_coordinate_pair());
+ if (match_comma_whitespace())
+ parse_comma_whitespace();
+ coordinates.append(parse_coordinate_pair());
+ if (match_comma_whitespace())
+ parse_comma_whitespace();
+ coordinates.append(parse_coordinate_pair());
+ return coordinates;
+}
+
+Vector<float> PathDataParser::parse_elliptical_arg_argument()
+{
+ Vector<float> numbers;
+ numbers.append(parse_number());
+ if (match_comma_whitespace())
+ parse_comma_whitespace();
+ numbers.append(parse_number());
+ if (match_comma_whitespace())
+ parse_comma_whitespace();
+ numbers.append(parse_number());
+ parse_comma_whitespace();
+ numbers.append(parse_flag());
+ if (match_comma_whitespace())
+ parse_comma_whitespace();
+ numbers.append(parse_flag());
+ if (match_comma_whitespace())
+ parse_comma_whitespace();
+ numbers.append(parse_coordinate_pair());
+
+ return numbers;
+}
+
+void PathDataParser::parse_whitespace(bool must_match_once)
+{
+ bool matched = false;
+ while (!done() && match_whitespace()) {
+ consume();
+ matched = true;
+ }
+
+ ASSERT(!must_match_once || matched);
+}
+
+void PathDataParser::parse_comma_whitespace()
+{
+ if (match(',')) {
+ consume();
+ parse_whitespace();
+ } else {
+ parse_whitespace(1);
+ if (match(','))
+ consume();
+ parse_whitespace();
+ }
+}
+
+float PathDataParser::parse_fractional_constant()
+{
+ StringBuilder builder;
+ bool floating_point = false;
+
+ while (!done() && isdigit(ch()))
+ builder.append(consume());
+
+ if (match('.')) {
+ floating_point = true;
+ builder.append('.');
+ consume();
+ while (!done() && isdigit(ch()))
+ builder.append(consume());
+ } else {
+ ASSERT(builder.length() > 0);
+ }
+
+ if (floating_point)
+ return strtof(builder.to_string().characters(), nullptr);
+ return builder.to_string().to_int().value();
+}
+
+float PathDataParser::parse_number()
+{
+ auto number = parse_fractional_constant();
+ if (match('e') || match('E'))
+ TODO();
+ return number;
+}
+
+float PathDataParser::parse_flag()
+{
+ if (!match('0') && !match('1'))
+ ASSERT_NOT_REACHED();
+ return consume() - '0';
+}
+
+int PathDataParser::parse_sign()
+{
+ if (match('-')) {
+ consume();
+ return -1;
+ }
+ if (match('+'))
+ consume();
+ return 1;
+}
+
+bool PathDataParser::match_whitespace() const
+{
+ if (done())
+ return false;
+ char c = ch();
+ return c == 0x9 || c == 0x20 || c == 0xa || c == 0xc || c == 0xd;
+}
+
+bool PathDataParser::match_comma_whitespace() const
+{
+ return match_whitespace() || match(',');
+}
+
+bool PathDataParser::match_coordinate() const
+{
+ return !done() && (isdigit(ch()) || ch() == '-' || ch() == '+' || ch() == '.');
+}
+
+SVGPathElement::SVGPathElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : SVGGeometryElement(document, qualified_name)
+{
+}
+
+RefPtr<Layout::Node> SVGPathElement::create_layout_node()
+{
+ auto style = document().style_resolver().resolve_style(*this);
+ if (style->display() == CSS::Display::None)
+ return nullptr;
+ return adopt(*new Layout::SVGPathBox(document(), *this, move(style)));
+}
+
+void SVGPathElement::parse_attribute(const FlyString& name, const String& value)
+{
+ SVGGeometryElement::parse_attribute(name, value);
+
+ if (name == "d")
+ m_instructions = PathDataParser(value).parse();
+}
+
+Gfx::Path& SVGPathElement::get_path()
+{
+ if (m_path.has_value())
+ return m_path.value();
+
+ Gfx::Path path;
+
+ for (auto& instruction : m_instructions) {
+ auto& absolute = instruction.absolute;
+ auto& data = instruction.data;
+
+#ifdef PATH_DEBUG
+ print_instruction(instruction);
+#endif
+
+ bool clear_last_control_point = true;
+
+ switch (instruction.type) {
+ case PathInstructionType::Move: {
+ Gfx::FloatPoint point = { data[0], data[1] };
+ if (absolute) {
+ path.move_to(point);
+ } else {
+ ASSERT(!path.segments().is_empty());
+ path.move_to(point + path.segments().last().point());
+ }
+ break;
+ }
+ case PathInstructionType::ClosePath:
+ path.close();
+ break;
+ case PathInstructionType::Line: {
+ Gfx::FloatPoint point = { data[0], data[1] };
+ if (absolute) {
+ path.line_to(point);
+ } else {
+ ASSERT(!path.segments().is_empty());
+ path.line_to(point + path.segments().last().point());
+ }
+ break;
+ }
+ case PathInstructionType::HorizontalLine: {
+ ASSERT(!path.segments().is_empty());
+ auto last_point = path.segments().last().point();
+ if (absolute) {
+ path.line_to(Gfx::FloatPoint { data[0], last_point.y() });
+ } else {
+ path.line_to(Gfx::FloatPoint { data[0] + last_point.x(), last_point.y() });
+ }
+ break;
+ }
+ case PathInstructionType::VerticalLine: {
+ ASSERT(!path.segments().is_empty());
+ auto last_point = path.segments().last().point();
+ if (absolute) {
+ path.line_to(Gfx::FloatPoint { last_point.x(), data[0] });
+ } else {
+ path.line_to(Gfx::FloatPoint { last_point.x(), data[0] + last_point.y() });
+ }
+ break;
+ }
+ case PathInstructionType::EllipticalArc: {
+ double rx = data[0];
+ double ry = data[1];
+ double x_axis_rotation = data[2] * M_DEG2RAD;
+ double large_arc_flag = data[3];
+ double sweep_flag = data[4];
+
+ double x_axis_rotation_c = cos(x_axis_rotation);
+ double x_axis_rotation_s = sin(x_axis_rotation);
+
+ auto& last_point = path.segments().last().point();
+
+ Gfx::FloatPoint next_point;
+
+ if (absolute) {
+ next_point = { data[5], data[6] };
+ } else {
+ next_point = { data[5] + last_point.x(), data[6] + last_point.y() };
+ }
+
+ // Step 1 of out-of-range radii correction
+ if (rx == 0.0 || ry == 0.0) {
+ path.line_to(next_point);
+ break;
+ }
+
+ // Step 2 of out-of-range radii correction
+ if (rx < 0)
+ rx *= -1.0;
+ if (ry < 0)
+ ry *= -1.0;
+
+ // Find (cx, cy), theta_1, theta_delta
+ // Step 1: Compute (x1', y1')
+ auto x_avg = (last_point.x() - next_point.x()) / 2.0f;
+ auto y_avg = (last_point.y() - next_point.y()) / 2.0f;
+ auto x1p = x_axis_rotation_c * x_avg + x_axis_rotation_s * y_avg;
+ auto y1p = -x_axis_rotation_s * x_avg + x_axis_rotation_c * y_avg;
+
+ // Step 2: Compute (cx', cy')
+ double x1p_sq = pow(x1p, 2.0);
+ double y1p_sq = pow(y1p, 2.0);
+ double rx_sq = pow(rx, 2.0);
+ double ry_sq = pow(ry, 2.0);
+
+ // Step 3 of out-of-range radii correction
+ double lambda = x1p_sq / rx_sq + y1p_sq / ry_sq;
+ double multiplier;
+
+ if (lambda > 1.0) {
+ auto lambda_sqrt = sqrt(lambda);
+ rx *= lambda_sqrt;
+ ry *= lambda_sqrt;
+ multiplier = 0.0;
+ } else {
+ double numerator = rx_sq * ry_sq - rx_sq * y1p_sq - ry_sq * x1p_sq;
+ double denominator = rx_sq * y1p_sq + ry_sq * x1p_sq;
+ multiplier = sqrt(numerator / denominator);
+ }
+
+ if (large_arc_flag == sweep_flag)
+ multiplier *= -1.0;
+
+ double cxp = multiplier * rx * y1p / ry;
+ double cyp = multiplier * -ry * x1p / rx;
+
+ // Step 3: Compute (cx, cy) from (cx', cy')
+ x_avg = (last_point.x() + next_point.x()) / 2.0f;
+ y_avg = (last_point.y() + next_point.y()) / 2.0f;
+ double cx = x_axis_rotation_c * cxp - x_axis_rotation_s * cyp + x_avg;
+ double cy = x_axis_rotation_s * cxp + x_axis_rotation_c * cyp + y_avg;
+
+ double theta_1 = atan2((y1p - cyp) / ry, (x1p - cxp) / rx);
+ double theta_2 = atan2((-y1p - cyp) / ry, (-x1p - cxp) / rx);
+
+ auto theta_delta = theta_2 - theta_1;
+
+ if (sweep_flag == 0 && theta_delta > 0.0f) {
+ theta_delta -= M_TAU;
+ } else if (sweep_flag != 0 && theta_delta < 0) {
+ theta_delta += M_TAU;
+ }
+
+ path.elliptical_arc_to(next_point, { cx, cy }, { rx, ry }, x_axis_rotation, theta_1, theta_delta);
+
+ break;
+ }
+ case PathInstructionType::QuadraticBezierCurve: {
+ clear_last_control_point = false;
+
+ Gfx::FloatPoint through = { data[0], data[1] };
+ Gfx::FloatPoint point = { data[2], data[3] };
+
+ if (absolute) {
+ path.quadratic_bezier_curve_to(through, point);
+ m_previous_control_point = through;
+ } else {
+ ASSERT(!path.segments().is_empty());
+ auto last_point = path.segments().last().point();
+ auto control_point = through + last_point;
+ path.quadratic_bezier_curve_to(control_point, point + last_point);
+ m_previous_control_point = control_point;
+ }
+ break;
+ }
+ case PathInstructionType::SmoothQuadraticBezierCurve: {
+ clear_last_control_point = false;
+
+ ASSERT(!path.segments().is_empty());
+ auto last_point = path.segments().last().point();
+
+ if (m_previous_control_point.is_null()) {
+ m_previous_control_point = last_point;
+ }
+
+ auto dx_end_control = last_point.dx_relative_to(m_previous_control_point);
+ auto dy_end_control = last_point.dy_relative_to(m_previous_control_point);
+ auto control_point = Gfx::FloatPoint { last_point.x() + dx_end_control, last_point.y() + dy_end_control };
+
+ Gfx::FloatPoint end_point = { data[0], data[1] };
+
+ if (absolute) {
+ path.quadratic_bezier_curve_to(control_point, end_point);
+ } else {
+ path.quadratic_bezier_curve_to(control_point, end_point + last_point);
+ }
+
+ m_previous_control_point = control_point;
+ break;
+ }
+
+ case PathInstructionType::Curve:
+ case PathInstructionType::SmoothCurve:
+ // Instead of crashing the browser every time we come across an SVG
+ // with these path instructions, let's just skip them
+ continue;
+ case PathInstructionType::Invalid:
+ ASSERT_NOT_REACHED();
+ }
+
+ if (clear_last_control_point) {
+ m_previous_control_point = Gfx::FloatPoint {};
+ }
+ }
+
+ m_path = path;
+ return m_path.value();
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/SVG/SVGPathElement.h b/Userland/Libraries/LibWeb/SVG/SVGPathElement.h
new file mode 100644
index 0000000000..738df73a13
--- /dev/null
+++ b/Userland/Libraries/LibWeb/SVG/SVGPathElement.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibGfx/Bitmap.h>
+#include <LibWeb/HTML/HTMLElement.h>
+#include <LibWeb/SVG/SVGGeometryElement.h>
+
+namespace Web::SVG {
+
+enum class PathInstructionType {
+ Move,
+ ClosePath,
+ Line,
+ HorizontalLine,
+ VerticalLine,
+ Curve,
+ SmoothCurve,
+ QuadraticBezierCurve,
+ SmoothQuadraticBezierCurve,
+ EllipticalArc,
+ Invalid,
+};
+
+struct PathInstruction {
+ PathInstructionType type;
+ bool absolute;
+ Vector<float> data;
+};
+
+class PathDataParser final {
+public:
+ PathDataParser(const String& source);
+ ~PathDataParser() = default;
+
+ Vector<PathInstruction> parse();
+
+private:
+ void parse_drawto();
+
+ void parse_moveto();
+ void parse_closepath();
+ void parse_lineto();
+ void parse_horizontal_lineto();
+ void parse_vertical_lineto();
+ void parse_curveto();
+ void parse_smooth_curveto();
+ void parse_quadratic_bezier_curveto();
+ void parse_smooth_quadratic_bezier_curveto();
+ void parse_elliptical_arc();
+
+ float parse_coordinate();
+ Vector<float> parse_coordinate_pair();
+ Vector<float> parse_coordinate_sequence();
+ Vector<Vector<float>> parse_coordinate_pair_sequence();
+ Vector<float> parse_coordinate_pair_double();
+ Vector<float> parse_coordinate_pair_triplet();
+ Vector<float> parse_elliptical_arg_argument();
+ void parse_whitespace(bool must_match_once = false);
+ void parse_comma_whitespace();
+ float parse_fractional_constant();
+ float parse_number();
+ float parse_flag();
+ // -1 if negative, +1 otherwise
+ int parse_sign();
+
+ bool match_whitespace() const;
+ bool match_comma_whitespace() const;
+ bool match_coordinate() const;
+ bool match(char c) const { return !done() && ch() == c; }
+
+ bool done() const { return m_cursor >= m_source.length(); }
+ char ch() const { return m_source[m_cursor]; }
+ char consume() { return m_source[m_cursor++]; }
+
+ String m_source;
+ size_t m_cursor { 0 };
+ Vector<PathInstruction> m_instructions;
+};
+
+class SVGPathElement final : public SVGGeometryElement {
+public:
+ using WrapperType = Bindings::SVGPathElementWrapper;
+
+ SVGPathElement(DOM::Document&, const QualifiedName& qualified_name);
+ virtual ~SVGPathElement() override = default;
+
+ virtual RefPtr<Layout::Node> create_layout_node() override;
+
+ virtual void parse_attribute(const FlyString& name, const String& value) override;
+
+ Gfx::Path& get_path();
+
+private:
+ Vector<PathInstruction> m_instructions;
+ Gfx::FloatPoint m_previous_control_point = {};
+ Optional<Gfx::Path> m_path;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/SVG/SVGPathElement.idl b/Userland/Libraries/LibWeb/SVG/SVGPathElement.idl
new file mode 100644
index 0000000000..d0c195bf25
--- /dev/null
+++ b/Userland/Libraries/LibWeb/SVG/SVGPathElement.idl
@@ -0,0 +1,3 @@
+interface SVGPathElement : SVGGeometryElement {
+
+};
diff --git a/Userland/Libraries/LibWeb/SVG/SVGSVGElement.cpp b/Userland/Libraries/LibWeb/SVG/SVGSVGElement.cpp
new file mode 100644
index 0000000000..ad0eb2e637
--- /dev/null
+++ b/Userland/Libraries/LibWeb/SVG/SVGSVGElement.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibGfx/Painter.h>
+#include <LibWeb/CSS/StyleResolver.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/DOM/Event.h>
+#include <LibWeb/Layout/SVGSVGBox.h>
+#include <LibWeb/SVG/SVGPathElement.h>
+#include <LibWeb/SVG/SVGSVGElement.h>
+#include <ctype.h>
+
+namespace Web::SVG {
+
+SVGSVGElement::SVGSVGElement(DOM::Document& document, const QualifiedName& qualified_name)
+ : SVGGraphicsElement(document, qualified_name)
+{
+}
+
+RefPtr<Layout::Node> SVGSVGElement::create_layout_node()
+{
+ auto style = document().style_resolver().resolve_style(*this);
+ if (style->display() == CSS::Display::None)
+ return nullptr;
+ return adopt(*new Layout::SVGSVGBox(document(), *this, move(style)));
+}
+
+unsigned SVGSVGElement::width() const
+{
+ return attribute(HTML::AttributeNames::width).to_uint().value_or(300);
+}
+
+unsigned SVGSVGElement::height() const
+{
+ return attribute(HTML::AttributeNames::height).to_uint().value_or(150);
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/SVG/SVGSVGElement.h b/Userland/Libraries/LibWeb/SVG/SVGSVGElement.h
new file mode 100644
index 0000000000..c84a483d07
--- /dev/null
+++ b/Userland/Libraries/LibWeb/SVG/SVGSVGElement.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibGfx/Bitmap.h>
+#include <LibWeb/SVG/SVGGraphicsElement.h>
+
+namespace Web::SVG {
+
+class SVGSVGElement final : public SVGGraphicsElement {
+public:
+ using WrapperType = Bindings::SVGSVGElementWrapper;
+
+ SVGSVGElement(DOM::Document&, const QualifiedName& qualified_name);
+
+ virtual RefPtr<Layout::Node> create_layout_node() override;
+
+ unsigned width() const;
+ unsigned height() const;
+
+private:
+ RefPtr<Gfx::Bitmap> m_bitmap;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/SVG/SVGSVGElement.idl b/Userland/Libraries/LibWeb/SVG/SVGSVGElement.idl
new file mode 100644
index 0000000000..7182cc2389
--- /dev/null
+++ b/Userland/Libraries/LibWeb/SVG/SVGSVGElement.idl
@@ -0,0 +1,3 @@
+interface SVGSVGElement : SVGGraphicsElement {
+
+};
diff --git a/Userland/Libraries/LibWeb/SVG/TagNames.cpp b/Userland/Libraries/LibWeb/SVG/TagNames.cpp
new file mode 100644
index 0000000000..654e914181
--- /dev/null
+++ b/Userland/Libraries/LibWeb/SVG/TagNames.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/SVG/TagNames.h>
+
+namespace Web::SVG::TagNames {
+
+#define __ENUMERATE_SVG_TAG(name) FlyString name;
+ENUMERATE_SVG_TAGS
+#undef __ENUMERATE_SVG_TAG
+
+[[gnu::constructor]] static void initialize()
+{
+ static bool s_initialized = false;
+ if (s_initialized)
+ return;
+
+#define __ENUMERATE_SVG_TAG(name) name = #name;
+ ENUMERATE_SVG_TAGS
+#undef __ENUMERATE_SVG_TAG
+
+ s_initialized = true;
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/SVG/TagNames.h b/Userland/Libraries/LibWeb/SVG/TagNames.h
new file mode 100644
index 0000000000..d24d5f95de
--- /dev/null
+++ b/Userland/Libraries/LibWeb/SVG/TagNames.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/FlyString.h>
+
+namespace Web::SVG::TagNames {
+
+#define ENUMERATE_SVG_GRAPHICS_TAGS \
+ __ENUMERATE_SVG_TAG(path) \
+ __ENUMERATE_SVG_TAG(svg)
+
+#define ENUMERATE_SVG_TAGS \
+ ENUMERATE_SVG_GRAPHICS_TAGS \
+ __ENUMERATE_SVG_TAG(desc) \
+ __ENUMERATE_SVG_TAG(foreignObject) \
+ __ENUMERATE_SVG_TAG(script) \
+ __ENUMERATE_SVG_TAG(title)
+
+#define __ENUMERATE_SVG_TAG(name) extern FlyString name;
+ENUMERATE_SVG_TAGS
+#undef __ENUMERATE_SVG_TAG
+
+}
diff --git a/Userland/Libraries/LibWeb/Scripts/GenerateStyleSheetSource.sh b/Userland/Libraries/LibWeb/Scripts/GenerateStyleSheetSource.sh
new file mode 100755
index 0000000000..aef61fcfae
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Scripts/GenerateStyleSheetSource.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+echo "namespace Web::CSS {"
+echo "extern const char $1[];"
+echo "const char $1[] = \"\\"
+grep -v '^ *#' < "$2" | while IFS= read -r line; do
+ echo "$line""\\"
+done
+echo "\";"
+echo "}"
diff --git a/Userland/Libraries/LibWeb/StylePropertiesModel.cpp b/Userland/Libraries/LibWeb/StylePropertiesModel.cpp
new file mode 100644
index 0000000000..123337db8e
--- /dev/null
+++ b/Userland/Libraries/LibWeb/StylePropertiesModel.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/QuickSort.h>
+#include <LibWeb/CSS/PropertyID.h>
+#include <LibWeb/CSS/StyleProperties.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/StylePropertiesModel.h>
+
+namespace Web {
+
+StylePropertiesModel::StylePropertiesModel(const CSS::StyleProperties& properties)
+ : m_properties(properties)
+{
+ properties.for_each_property([&](auto property_id, auto& property_value) {
+ Value value;
+ value.name = CSS::string_from_property_id(property_id);
+ value.value = property_value.to_string();
+ m_values.append(value);
+ });
+
+ quick_sort(m_values, [](auto& a, auto& b) { return a.name < b.name; });
+}
+
+int StylePropertiesModel::row_count(const GUI::ModelIndex&) const
+{
+ return m_values.size();
+}
+
+String StylePropertiesModel::column_name(int column_index) const
+{
+ switch (column_index) {
+ case Column::PropertyName:
+ return "Name";
+ case Column::PropertyValue:
+ return "Value";
+ default:
+ ASSERT_NOT_REACHED();
+ }
+}
+GUI::Variant StylePropertiesModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const
+{
+ auto& value = m_values[index.row()];
+ if (role == GUI::ModelRole::Display) {
+ if (index.column() == Column::PropertyName)
+ return value.name;
+ if (index.column() == Column::PropertyValue)
+ return value.value;
+ }
+ return {};
+}
+
+void StylePropertiesModel::update()
+{
+ did_update();
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/StylePropertiesModel.h b/Userland/Libraries/LibWeb/StylePropertiesModel.h
new file mode 100644
index 0000000000..2c260b62f1
--- /dev/null
+++ b/Userland/Libraries/LibWeb/StylePropertiesModel.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/NonnullRefPtrVector.h>
+#include <LibGUI/Model.h>
+
+namespace Web {
+
+class StyleProperties;
+
+class StylePropertiesModel final : public GUI::Model {
+public:
+ enum Column {
+ PropertyName,
+ PropertyValue,
+ __Count
+ };
+
+ static NonnullRefPtr<StylePropertiesModel> create(const CSS::StyleProperties& properties) { return adopt(*new StylePropertiesModel(properties)); }
+
+ virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override;
+ virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override { return Column::__Count; }
+ virtual String column_name(int) const override;
+ virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole) const override;
+ virtual void update() override;
+
+private:
+ explicit StylePropertiesModel(const CSS::StyleProperties& properties);
+ const CSS::StyleProperties& properties() const { return *m_properties; }
+
+ NonnullRefPtr<CSS::StyleProperties> m_properties;
+
+ struct Value {
+ String name;
+ String value;
+ };
+ Vector<Value> m_values;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Tests/DOM/Comment.js b/Userland/Libraries/LibWeb/Tests/DOM/Comment.js
new file mode 100644
index 0000000000..0dab522e9c
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Tests/DOM/Comment.js
@@ -0,0 +1,15 @@
+loadPage("file:///home/anon/web-tests/Pages/Comment.html");
+
+afterInitialPageLoad(() => {
+ test("Basic functionality", () => {
+ const comment = document.body.firstChild.nextSibling;
+ expect(comment).not.toBeNull();
+
+ // FIXME: Add this in once Comment's constructor is implemented.
+ //expect(comment).toBeInstanceOf(Comment);
+
+ expect(comment.nodeName).toBe("#comment");
+ expect(comment.data).toBe("This is a comment");
+ expect(comment).toHaveLength(17);
+ });
+});
diff --git a/Userland/Libraries/LibWeb/Tests/DOM/Element.js b/Userland/Libraries/LibWeb/Tests/DOM/Element.js
new file mode 100644
index 0000000000..16606829aa
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Tests/DOM/Element.js
@@ -0,0 +1,39 @@
+loadPage("file:///res/html/misc/innertext_textcontent.html");
+
+afterInitialPageLoad(() => {
+ test("Element.innerText", () => {
+ var p = document.getElementsByTagName("p")[0];
+ expect(p.innerText).toBe("This is a very small test page :^)");
+
+ // FIXME: Call this on p once that's supported.
+ var b = document.getElementsByTagName("b")[0];
+ b.innerText = "foo";
+ expect(b.innerText).toBe("foo");
+ expect(p.innerText).toBe("This is a foo test page :^)");
+
+ p.innerText = "bar";
+ expect(p.innerText).toBe("bar");
+
+ var p = document.getElementById("source");
+ // FIXME: The leading and trailing two spaces each are wrong.
+ // FIXME: The text should be affected by the text-transform:uppercase.
+ expect(p.innerText).toBe(` Take a look at
+how this text
+is interpreted below. `);
+ });
+
+ test("Element.namespaceURI basics", () => {
+ const htmlNamespace = "http://www.w3.org/1999/xhtml";
+ const p = document.getElementsByTagName("p")[0];
+ expect(p.namespaceURI).toBe(htmlNamespace);
+
+ // createElement always sets the namespace to the HTML namespace in HTML documents.
+ const svgElement = document.createElement("svg");
+ expect(svgElement.namespaceURI).toBe(htmlNamespace);
+
+ const svgNamespace = "http://www.w3.org/2000/svg";
+ p.innerHTML = "<svg></svg>";
+ const domSVGElement = p.getElementsByTagName("svg")[0];
+ expect(domSVGElement.namespaceURI).toBe(svgNamespace);
+ });
+});
diff --git a/Userland/Libraries/LibWeb/Tests/DOM/Node.js b/Userland/Libraries/LibWeb/Tests/DOM/Node.js
new file mode 100644
index 0000000000..5427d143bd
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Tests/DOM/Node.js
@@ -0,0 +1,28 @@
+loadPage("file:///res/html/misc/innertext_textcontent.html");
+
+afterInitialPageLoad(() => {
+ test("Node.textContent", () => {
+ var p = document.getElementsByTagName("p")[0];
+ expect(p.textContent).toBe("This is a very small test page :^)");
+ expect(p.firstChild.textContent).toBe("This is a ");
+ expect(p.firstChild.firstChild).toBe(null);
+
+ p.firstChild.textContent = "foo";
+ expect(p.firstChild.textContent).toBe("foo");
+ expect(p.firstChild.firstChild).toBe(null);
+ expect(p.textContent).toBe("foovery small test page :^)");
+
+ p.textContent = "bar";
+ expect(p.textContent).toBe("bar");
+ expect(p.firstChild.textContent).toBe("bar");
+ expect(p.firstChild.firstChild).toBe(null);
+
+ var p = document.getElementById("source");
+ expect(p.textContent).toBe(`
+ #source { color: red; } #text { text-transform: uppercase; }
+ Take a look athow this textis interpreted
+ below.
+ HIDDEN TEXT
+ `);
+ });
+});
diff --git a/Userland/Libraries/LibWeb/Tests/DOM/Text.js b/Userland/Libraries/LibWeb/Tests/DOM/Text.js
new file mode 100644
index 0000000000..1557a59d3f
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Tests/DOM/Text.js
@@ -0,0 +1,15 @@
+loadPage("file:///res/html/misc/blank.html");
+
+afterInitialPageLoad(() => {
+ test("Basic functionality", () => {
+ const title = document.getElementsByTagName("title")[0];
+ expect(title).toBeDefined();
+
+ // FIXME: Add this in once Text's constructor is implemented.
+ //expect(title.firstChild).toBeInstanceOf(Text);
+
+ expect(title.firstChild.nodeName).toBe("#text");
+ expect(title.firstChild.data).toBe("Blank");
+ expect(title.firstChild.length).toBe(5);
+ });
+});
diff --git a/Userland/Libraries/LibWeb/Tests/DOM/document.createComment.js b/Userland/Libraries/LibWeb/Tests/DOM/document.createComment.js
new file mode 100644
index 0000000000..a60d6bfcd5
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Tests/DOM/document.createComment.js
@@ -0,0 +1,13 @@
+loadPage("file:///res/html/misc/blank.html");
+
+afterInitialPageLoad(() => {
+ test("Basic functionality", () => {
+ const comment = document.createComment("Create Comment Test");
+
+ // FIXME: Add this in once Comment's constructor is implemented.
+ //expect(comment).toBeInstanceOf(Comment);
+ expect(comment.nodeName).toBe("#comment");
+ expect(comment.data).toBe("Create Comment Test");
+ expect(comment.length).toBe(19);
+ });
+});
diff --git a/Userland/Libraries/LibWeb/Tests/DOM/document.createDocumentFragment.js b/Userland/Libraries/LibWeb/Tests/DOM/document.createDocumentFragment.js
new file mode 100644
index 0000000000..bebc137313
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Tests/DOM/document.createDocumentFragment.js
@@ -0,0 +1,11 @@
+loadPage("file:///res/html/misc/blank.html");
+
+afterInitialPageLoad(() => {
+ test("Basic functionality", () => {
+ const fragment = document.createDocumentFragment();
+
+ // FIXME: Add this in once DocumentFragment's constructor is implemented.
+ //expect(fragment).toBeInstanceOf(DocumentFragment);
+ expect(fragment.nodeName).toBe("#document-fragment");
+ });
+});
diff --git a/Userland/Libraries/LibWeb/Tests/DOM/document.createTextNode.js b/Userland/Libraries/LibWeb/Tests/DOM/document.createTextNode.js
new file mode 100644
index 0000000000..bef06a81c7
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Tests/DOM/document.createTextNode.js
@@ -0,0 +1,13 @@
+loadPage("file:///res/html/misc/blank.html");
+
+afterInitialPageLoad(() => {
+ test("Basic functionality", () => {
+ const text = document.createTextNode("Create Text Test");
+
+ // FIXME: Add this in once Text's constructor is implemented.
+ //expect(text).toBeInstanceOf(Text);
+ expect(text.nodeName).toBe("#text");
+ expect(text.data).toBe("Create Text Test");
+ expect(text.length).toBe(16);
+ });
+});
diff --git a/Userland/Libraries/LibWeb/Tests/DOM/document.doctype.js b/Userland/Libraries/LibWeb/Tests/DOM/document.doctype.js
new file mode 100644
index 0000000000..5da52014d1
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Tests/DOM/document.doctype.js
@@ -0,0 +1,18 @@
+loadPage("file:///res/html/misc/blank.html");
+
+afterInitialPageLoad(() => {
+ test("Basic functionality", () => {
+ expect(document.compatMode).toBe("CSS1Compat");
+ expect(document.doctype).not.toBeNull();
+ expect(document.doctype.name).toBe("html");
+ expect(document.doctype.publicId).toBe("");
+ expect(document.doctype.systemId).toBe("");
+ });
+
+ libweb_tester.changePage("file:///res/html/misc/blank-no-doctype.html");
+
+ test("Quirks mode", () => {
+ expect(document.compatMode).toBe("BackCompat");
+ expect(document.doctype).toBeNull();
+ });
+});
diff --git a/Userland/Libraries/LibWeb/Tests/DOM/document.documentElement.js b/Userland/Libraries/LibWeb/Tests/DOM/document.documentElement.js
new file mode 100644
index 0000000000..86d885c709
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Tests/DOM/document.documentElement.js
@@ -0,0 +1,16 @@
+loadPage("file:///res/html/misc/blank.html");
+
+afterInitialPageLoad(() => {
+ test("Basic functionality", () => {
+ expect(document.documentElement).not.toBeNull();
+ // FIXME: Add this in once HTMLHtmlElement's constructor is implemented.
+ //expect(document.documentElement).toBeInstanceOf(HTMLHtmlElement);
+ expect(document.documentElement.nodeName).toBe("html");
+ });
+
+ // FIXME: Add this in once removeChild is implemented.
+ test.skip("Nullable", () => {
+ document.removeChild(document.documentElement);
+ expect(document.documentElement).toBeNull();
+ });
+});
diff --git a/Userland/Libraries/LibWeb/Tests/DOM/mixins/NonElementParentNode.js b/Userland/Libraries/LibWeb/Tests/DOM/mixins/NonElementParentNode.js
new file mode 100644
index 0000000000..5b269025f4
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Tests/DOM/mixins/NonElementParentNode.js
@@ -0,0 +1,19 @@
+loadPage("file:///home/anon/web-tests/Pages/ParentNode.html");
+
+afterInitialPageLoad(() => {
+ test("getElementById basics", () => {
+ const unique = document.getElementById("unique");
+ expect(unique).not.toBeNull();
+ expect(unique.nodeName).toBe("div");
+ expect(unique.id).toBe("unique");
+
+ const caseSensitive = document.getElementById("Unique");
+ expect(caseSensitive).toBeNull();
+
+ const firstDuplicate = document.getElementById("dupeId");
+ expect(firstDuplicate).not.toBeNull();
+ expect(firstDuplicate.nodeName).toBe("div");
+ expect(firstDuplicate.id).toBe("dupeId");
+ expect(firstDuplicate.innerHTML).toBe("First ID");
+ });
+});
diff --git a/Userland/Libraries/LibWeb/Tests/DOM/mixins/ParentNode.js b/Userland/Libraries/LibWeb/Tests/DOM/mixins/ParentNode.js
new file mode 100644
index 0000000000..3558c0352f
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Tests/DOM/mixins/ParentNode.js
@@ -0,0 +1,25 @@
+loadPage("file:///home/anon/web-tests/Pages/ParentNode.html");
+
+afterInitialPageLoad(() => {
+ test("querySelector basics", () => {
+ const firstDuplicateElement = document.querySelector(".duplicate");
+ expect(firstDuplicateElement).not.toBeNull();
+ expect(firstDuplicateElement.nodeName).toBe("div");
+ expect(firstDuplicateElement.innerHTML).toBe("First");
+
+ const noElement = document.querySelector(".nonexistent");
+ expect(noElement).toBeNull();
+ });
+
+ test("querySelectorAll basics", () => {
+ const allDuplicates = document.querySelectorAll(".duplicate");
+ expect(allDuplicates).toHaveLength(2);
+ expect(allDuplicates[0].nodeName).toBe("div");
+ expect(allDuplicates[0].innerHTML).toBe("First");
+ expect(allDuplicates[1].nodeName).toBe("div");
+ expect(allDuplicates[1].innerHTML).toBe("Second");
+
+ const noElements = document.querySelectorAll(".nonexistent");
+ expect(noElements).toHaveLength(0);
+ });
+});
diff --git a/Userland/Libraries/LibWeb/Tests/HTML/HTMLElement.js b/Userland/Libraries/LibWeb/Tests/HTML/HTMLElement.js
new file mode 100644
index 0000000000..2950bb45c5
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Tests/HTML/HTMLElement.js
@@ -0,0 +1,9 @@
+loadPage("file:///res/html/misc/welcome.html");
+
+afterInitialPageLoad(() => {
+ test("contentEditable attribute", () => {
+ expect(document.body.contentEditable).toBe("inherit");
+ expect(document.firstChild.nextSibling.nodeName).toBe("html");
+ expect(document.firstChild.nextSibling.contentEditable).toBe("true");
+ });
+});
diff --git a/Userland/Libraries/LibWeb/Tests/HTML/HTMLTemplateElement.js b/Userland/Libraries/LibWeb/Tests/HTML/HTMLTemplateElement.js
new file mode 100644
index 0000000000..dd6c66e1f2
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Tests/HTML/HTMLTemplateElement.js
@@ -0,0 +1,27 @@
+loadPage("file:///home/anon/web-tests/Pages/Template.html");
+
+afterInitialPageLoad(() => {
+ test("Basic functionality", () => {
+ const template = document.getElementById("template");
+ expect(template).not.toBeNull();
+
+ // The contents of a template element are not children of the actual element.
+ // The document fragment is not a child of the element either.
+ expect(template.firstChild).toBeNull();
+
+ // FIXME: Add this in once DocumentFragment's constructor is implemented.
+ //expect(template.content).toBeInstanceOf(DocumentFragment);
+ expect(template.content.nodeName).toBe("#document-fragment");
+
+ const templateDiv = template.content.getElementById("templatediv");
+ expect(templateDiv.nodeName).toBe("div");
+ expect(templateDiv.textContent).toBe("Hello template!");
+ });
+
+ test("Templates are inert (e.g. scripts won't run)", () => {
+ // The page has a template element with a script element in it.
+ // Templates are inert, for example, they won't run scripts.
+ // That script will set "templateScriptRan" to true if it ran.
+ expect(window.templateScriptRan).toBeUndefined();
+ });
+});
diff --git a/Userland/Libraries/LibWeb/Tests/HTML/document.body.js b/Userland/Libraries/LibWeb/Tests/HTML/document.body.js
new file mode 100644
index 0000000000..9fc1907583
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Tests/HTML/document.body.js
@@ -0,0 +1,55 @@
+loadPage("file:///res/html/misc/blank.html");
+
+afterInitialPageLoad(() => {
+ test("Basic functionality", () => {
+ expect(document.body).not.toBeNull();
+ // FIXME: Add this in once HTMLBodyElement's constructor is implemented.
+ //expect(document.body).toBeInstanceOf(HTMLBodyElement);
+ expect(document.body.nodeName).toBe("body");
+ });
+
+ // FIXME: Add this in once set_body is fully implemented.
+ test.skip("Setting body to a new body element", () => {
+ // Add something to body to see if it's gone afterwards
+ const p = document.createElement("p");
+ document.body.appendChild(p);
+
+ expect(document.body.firstChild).toBe(p);
+
+ const newBody = document.createElement("body");
+ document.body = newBody;
+
+ expect(document.body).not.toBeNull();
+ expect(document.body.nodeName).toBe("body");
+
+ // FIXME: Add this in once HTMLBodyElement's constructor is implemented.
+ //expect(document.body).toBeInstanceOf(HTMLBodyElement);
+
+ expect(document.body.firstChild).toBeNull();
+ });
+
+ // FIXME: Add this in once set_body is fully implemented.
+ test.skip("Setting body to a new frameset element", () => {
+ const newFrameSet = document.createElement("frameset");
+ document.body = newFrameSet;
+
+ expect(document.body).not.toBeNull();
+ expect(document.body.nodeName).toBe("frameset");
+
+ // FIXME: Add this in once HTMLFrameSetElement's constructor is implemented.
+ //expect(document.body).toBeInstanceOf(HTMLFrameSetElement);
+ });
+
+ // FIXME: Add this in once set_body is fully implemented.
+ test.skip("Setting body to an element that isn't body/frameset", () => {
+ expect(() => {
+ document.body = document.createElement("div");
+ }).toThrow(DOMException);
+ });
+
+ // FIXME: Add this in once removeChild is implemented.
+ test.skip("Nullable", () => {
+ document.documentElement.removeChild(document.body);
+ expect(document.body).toBeNull();
+ });
+});
diff --git a/Userland/Libraries/LibWeb/Tests/HTML/document.head.js b/Userland/Libraries/LibWeb/Tests/HTML/document.head.js
new file mode 100644
index 0000000000..5d997fe3dd
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Tests/HTML/document.head.js
@@ -0,0 +1,16 @@
+loadPage("file:///res/html/misc/blank.html");
+
+afterInitialPageLoad(() => {
+ test("Basic functionality", () => {
+ expect(document.head).not.toBeNull();
+ // FIXME: Add this in once HTMLHeadElement's constructor is implemented.
+ //expect(document.head).toBeInstanceOf(HTMLHeadElement);
+ expect(document.head.nodeName).toBe("head");
+ });
+
+ // FIXME: Add this in once removeChild is implemented.
+ test.skip("Nullable", () => {
+ document.documentElement.removeChild(document.head);
+ expect(document.head).toBeNull();
+ });
+});
diff --git a/Userland/Libraries/LibWeb/Tests/HTML/document.readyState.js b/Userland/Libraries/LibWeb/Tests/HTML/document.readyState.js
new file mode 100644
index 0000000000..12b6c1d8df
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Tests/HTML/document.readyState.js
@@ -0,0 +1,31 @@
+loadPage("file:///res/html/misc/blank.html");
+
+beforeInitialPageLoad(() => {
+ window.events = [];
+
+ document.addEventListener("readystatechange", () => {
+ window.events.push(document.readyState);
+ });
+
+ document.addEventListener("DOMContentLoaded", () => {
+ test("Ready state should be 'interactive' when 'DOMContentLoaded' fires", () => {
+ expect(document.readyState).toBe("interactive");
+ });
+ });
+
+ test("Ready state should be 'loading' initially", () => {
+ expect(document.readyState).toBe("loading");
+ });
+});
+
+afterInitialPageLoad(() => {
+ test("'interactive' should come before 'complete' and both should have happened", () => {
+ expect(window.events).toHaveLength(2);
+ expect(window.events[0]).toBe("interactive");
+ expect(window.events[1]).toBe("complete");
+ });
+
+ test("Ready state should be 'complete' after loading", () => {
+ expect(document.readyState).toBe("complete");
+ });
+});
diff --git a/Userland/Libraries/LibWeb/Tests/Pages/Comment.html b/Userland/Libraries/LibWeb/Tests/Pages/Comment.html
new file mode 100644
index 0000000000..84ae91695f
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Tests/Pages/Comment.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<!--This is a comment-->
+</body>
+</html>
diff --git a/Userland/Libraries/LibWeb/Tests/Pages/ParentNode.html b/Userland/Libraries/LibWeb/Tests/Pages/ParentNode.html
new file mode 100644
index 0000000000..536b5ab35d
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Tests/Pages/ParentNode.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+ <div class="duplicate">First</div>
+ <div class="duplicate">Second</div>
+ <div id="unique"></div>
+ <div id="dupeId">First ID</div>
+ <div id="dupeId">Second ID</div>
+</body>
+</html>
diff --git a/Userland/Libraries/LibWeb/Tests/Pages/Template.html b/Userland/Libraries/LibWeb/Tests/Pages/Template.html
new file mode 100644
index 0000000000..e4478f408f
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Tests/Pages/Template.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<template id="template">
+ <div id="templatediv">Hello template!</div>
+ <script>
+ // I shouldn't be run.
+ window.templateScriptRan = true;
+ </script>
+</template>
+</body>
+</html>
diff --git a/Userland/Libraries/LibWeb/Tests/Window/Base64.js b/Userland/Libraries/LibWeb/Tests/Window/Base64.js
new file mode 100644
index 0000000000..c272d70b74
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Tests/Window/Base64.js
@@ -0,0 +1,19 @@
+loadPage("file:///res/html/misc/blank.html");
+
+afterInitialPageLoad(() => {
+ test("atob", () => {
+ expect(atob("YQ==")).toBe("a");
+ expect(atob("YWE=")).toBe("aa");
+ expect(atob("YWFh")).toBe("aaa");
+ expect(atob("YWFhYQ==")).toBe("aaaa");
+ expect(atob("/w==")).toBe("\xff");
+ });
+
+ test("btoa", () => {
+ expect(btoa("a")).toBe("YQ==");
+ expect(btoa("aa")).toBe("YWE=");
+ expect(btoa("aaa")).toBe("YWFh");
+ expect(btoa("aaaa")).toBe("YWFhYQ==");
+ expect(btoa("\xff")).toBe("/w==");
+ });
+});
diff --git a/Userland/Libraries/LibWeb/Tests/Window/window.window_frames_self.js b/Userland/Libraries/LibWeb/Tests/Window/window.window_frames_self.js
new file mode 100644
index 0000000000..1d7799843e
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Tests/Window/window.window_frames_self.js
@@ -0,0 +1,8 @@
+loadPage("file:///res/html/misc/blank.html");
+
+afterInitialPageLoad(() => {
+ test("window.{window,frames,self} all return the Window object", () => {
+ expect(window.window).toBe(window.frames);
+ expect(window.window).toBe(window.self);
+ });
+});
diff --git a/Userland/Libraries/LibWeb/Tests/libweb_tester.d.ts b/Userland/Libraries/LibWeb/Tests/libweb_tester.d.ts
new file mode 100644
index 0000000000..23570e70f8
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Tests/libweb_tester.d.ts
@@ -0,0 +1,16 @@
+// NOTE: This file is only for syntax highlighting, documentation, etc.
+
+interface LibwebTester {
+ /**
+ * Changes the page to the specified URL. Everything afterwards will refer to the new page.
+ * @param url Page to load.
+ */
+ changePage(url: string): void;
+}
+
+interface Window {
+ /**
+ * Special test object used to ease test development for LibWeb.
+ */
+ readonly libweb_tester: LibwebTester;
+}
diff --git a/Userland/Libraries/LibWeb/Tests/test-common.js b/Userland/Libraries/LibWeb/Tests/test-common.js
new file mode 100644
index 0000000000..7fc4f914bf
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Tests/test-common.js
@@ -0,0 +1,31 @@
+// NOTE: The tester loads in LibJS's test-common to prevent duplication.
+
+// NOTE: "window.libweb_tester" is set to a special tester object.
+// See libweb_tester.d.ts for definitions.
+
+let __PageToLoad__;
+
+// This tells the tester which page to load.
+// This will only be checked when we look at which page the test wants to use.
+// Subsequent calls to loadPage in before/after initial load will be ignored.
+let loadPage;
+
+let __BeforeInitialPageLoad__ = () => {};
+
+// This function will be called just before loading the initial page.
+// This is useful for injecting event listeners.
+// Defaults to an empty function.
+let beforeInitialPageLoad;
+
+let __AfterInitialPageLoad__ = () => {};
+
+// This function will be called just after loading the initial page.
+// This is where the main bulk of the tests should be.
+// Defaults to an empty function.
+let afterInitialPageLoad;
+
+(() => {
+ loadPage = page => (__PageToLoad__ = page);
+ beforeInitialPageLoad = callback => (__BeforeInitialPageLoad__ = callback);
+ afterInitialPageLoad = callback => (__AfterInitialPageLoad__ = callback);
+})();
diff --git a/Userland/Libraries/LibWeb/TreeNode.h b/Userland/Libraries/LibWeb/TreeNode.h
new file mode 100644
index 0000000000..1673a52dc5
--- /dev/null
+++ b/Userland/Libraries/LibWeb/TreeNode.h
@@ -0,0 +1,443 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/Assertions.h>
+#include <AK/NonnullRefPtr.h>
+#include <AK/TypeCasts.h>
+#include <AK/Weakable.h>
+#include <LibWeb/Forward.h>
+
+namespace Web {
+
+template<typename T>
+class TreeNode : public Weakable<T> {
+public:
+ void ref()
+ {
+ ASSERT(!m_in_removed_last_ref);
+ ASSERT(m_ref_count);
+ ++m_ref_count;
+ }
+
+ void unref()
+ {
+ ASSERT(!m_in_removed_last_ref);
+ ASSERT(m_ref_count);
+ if (!--m_ref_count) {
+ if constexpr (IsBaseOf<DOM::Node, T>::value) {
+ m_in_removed_last_ref = true;
+ static_cast<T*>(this)->removed_last_ref();
+ } else {
+ delete static_cast<T*>(this);
+ }
+ return;
+ }
+ }
+ int ref_count() const { return m_ref_count; }
+
+ T* parent() { return m_parent; }
+ const T* parent() const { return m_parent; }
+
+ bool has_children() const { return m_first_child; }
+ T* next_sibling() { return m_next_sibling; }
+ T* previous_sibling() { return m_previous_sibling; }
+ T* first_child() { return m_first_child; }
+ T* last_child() { return m_last_child; }
+ const T* next_sibling() const { return m_next_sibling; }
+ const T* previous_sibling() const { return m_previous_sibling; }
+ const T* first_child() const { return m_first_child; }
+ const T* last_child() const { return m_last_child; }
+
+ int child_count() const
+ {
+ int count = 0;
+ for (auto* child = first_child(); child; child = child->next_sibling())
+ ++count;
+ return count;
+ }
+
+ T* child_at_index(int index)
+ {
+ int count = 0;
+ for (auto* child = first_child(); child; child = child->next_sibling()) {
+ if (count == index)
+ return child;
+ ++count;
+ }
+ return nullptr;
+ }
+
+ const T* child_at_index(int index) const
+ {
+ return const_cast<TreeNode*>(this)->child_at_index(index);
+ }
+
+ bool is_ancestor_of(const TreeNode&) const;
+
+ void prepend_child(NonnullRefPtr<T> node);
+ void append_child(NonnullRefPtr<T> node, bool notify = true);
+ void insert_before(NonnullRefPtr<T> node, RefPtr<T> child, bool notify = true);
+ NonnullRefPtr<T> remove_child(NonnullRefPtr<T> node);
+
+ void remove_all_children();
+
+ bool is_child_allowed(const T&) const { return true; }
+
+ T* next_in_pre_order()
+ {
+ if (first_child())
+ return first_child();
+ T* node;
+ if (!(node = next_sibling())) {
+ node = parent();
+ while (node && !node->next_sibling())
+ node = node->parent();
+ if (node)
+ node = node->next_sibling();
+ }
+ return node;
+ }
+
+ const T* next_in_pre_order() const
+ {
+ return const_cast<TreeNode*>(this)->next_in_pre_order();
+ }
+
+ bool is_before(const T& other) const
+ {
+ if (this == &other)
+ return false;
+ for (auto* node = this; node; node = node->next_in_pre_order()) {
+ if (node == &other)
+ return true;
+ }
+ return false;
+ }
+
+ template<typename Callback>
+ IterationDecision for_each_in_subtree(Callback callback) const
+ {
+ if (callback(static_cast<const T&>(*this)) == IterationDecision::Break)
+ return IterationDecision::Break;
+ for (auto* child = first_child(); child; child = child->next_sibling()) {
+ if (child->for_each_in_subtree(callback) == IterationDecision::Break)
+ return IterationDecision::Break;
+ }
+ return IterationDecision::Continue;
+ }
+
+ template<typename Callback>
+ IterationDecision for_each_in_subtree(Callback callback)
+ {
+ if (callback(static_cast<T&>(*this)) == IterationDecision::Break)
+ return IterationDecision::Break;
+ for (auto* child = first_child(); child; child = child->next_sibling()) {
+ if (child->for_each_in_subtree(callback) == IterationDecision::Break)
+ return IterationDecision::Break;
+ }
+ return IterationDecision::Continue;
+ }
+
+ template<typename U, typename Callback>
+ IterationDecision for_each_in_subtree_of_type(Callback callback)
+ {
+ if (is<U>(static_cast<const T&>(*this))) {
+ if (callback(static_cast<U&>(*this)) == IterationDecision::Break)
+ return IterationDecision::Break;
+ }
+ for (auto* child = first_child(); child; child = child->next_sibling()) {
+ if (child->template for_each_in_subtree_of_type<U>(callback) == IterationDecision::Break)
+ return IterationDecision::Break;
+ }
+ return IterationDecision::Continue;
+ }
+
+ template<typename U, typename Callback>
+ IterationDecision for_each_in_subtree_of_type(Callback callback) const
+ {
+ if (is<U>(static_cast<const T&>(*this))) {
+ if (callback(static_cast<const U&>(*this)) == IterationDecision::Break)
+ return IterationDecision::Break;
+ }
+ for (auto* child = first_child(); child; child = child->next_sibling()) {
+ if (child->template for_each_in_subtree_of_type<U>(callback) == IterationDecision::Break)
+ return IterationDecision::Break;
+ }
+ return IterationDecision::Continue;
+ }
+
+ template<typename Callback>
+ void for_each_child(Callback callback) const
+ {
+ return const_cast<TreeNode*>(this)->template for_each_child(move(callback));
+ }
+
+ template<typename Callback>
+ void for_each_child(Callback callback)
+ {
+ for (auto* node = first_child(); node; node = node->next_sibling())
+ callback(*node);
+ }
+
+ template<typename U, typename Callback>
+ void for_each_child_of_type(Callback callback)
+ {
+ for (auto* node = first_child(); node; node = node->next_sibling()) {
+ if (is<U>(node))
+ callback(downcast<U>(*node));
+ }
+ }
+
+ template<typename U, typename Callback>
+ void for_each_child_of_type(Callback callback) const
+ {
+ return const_cast<TreeNode*>(this)->template for_each_child_of_type<U>(move(callback));
+ }
+
+ template<typename U>
+ const U* next_sibling_of_type() const
+ {
+ return const_cast<TreeNode*>(this)->template next_sibling_of_type<U>();
+ }
+
+ template<typename U>
+ inline U* next_sibling_of_type()
+ {
+ for (auto* sibling = next_sibling(); sibling; sibling = sibling->next_sibling()) {
+ if (is<U>(*sibling))
+ return &downcast<U>(*sibling);
+ }
+ return nullptr;
+ }
+
+ template<typename U>
+ const U* previous_sibling_of_type() const
+ {
+ return const_cast<TreeNode*>(this)->template previous_sibling_of_type<U>();
+ }
+
+ template<typename U>
+ U* previous_sibling_of_type()
+ {
+ for (auto* sibling = previous_sibling(); sibling; sibling = sibling->previous_sibling()) {
+ if (is<U>(*sibling))
+ return &downcast<U>(*sibling);
+ }
+ return nullptr;
+ }
+
+ template<typename U>
+ const U* first_child_of_type() const
+ {
+ return const_cast<TreeNode*>(this)->template first_child_of_type<U>();
+ }
+
+ template<typename U>
+ const U* last_child_of_type() const
+ {
+ return const_cast<TreeNode*>(this)->template last_child_of_type<U>();
+ }
+
+ template<typename U>
+ U* first_child_of_type()
+ {
+ for (auto* child = first_child(); child; child = child->next_sibling()) {
+ if (is<U>(*child))
+ return &downcast<U>(*child);
+ }
+ return nullptr;
+ }
+
+ template<typename U>
+ U* last_child_of_type()
+ {
+ for (auto* child = last_child(); child; child = child->previous_sibling()) {
+ if (is<U>(*child))
+ return &downcast<U>(*child);
+ }
+ return nullptr;
+ }
+
+ template<typename U>
+ const U* first_ancestor_of_type() const
+ {
+ return const_cast<TreeNode*>(this)->template first_ancestor_of_type<U>();
+ }
+
+ template<typename U>
+ U* first_ancestor_of_type()
+ {
+ for (auto* ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
+ if (is<U>(*ancestor))
+ return &downcast<U>(*ancestor);
+ }
+ return nullptr;
+ }
+
+protected:
+ TreeNode() { }
+
+ bool m_deletion_has_begun { false };
+ bool m_in_removed_last_ref { false };
+
+private:
+ int m_ref_count { 1 };
+ T* m_parent { nullptr };
+ T* m_first_child { nullptr };
+ T* m_last_child { nullptr };
+ T* m_next_sibling { nullptr };
+ T* m_previous_sibling { nullptr };
+};
+
+template<typename T>
+inline void TreeNode<T>::remove_all_children()
+{
+ while (RefPtr<T> child = first_child())
+ remove_child(child.release_nonnull());
+}
+
+template<typename T>
+inline NonnullRefPtr<T> TreeNode<T>::remove_child(NonnullRefPtr<T> node)
+{
+ ASSERT(node->m_parent == this);
+
+ if (m_first_child == node)
+ m_first_child = node->m_next_sibling;
+
+ if (m_last_child == node)
+ m_last_child = node->m_previous_sibling;
+
+ if (node->m_next_sibling)
+ node->m_next_sibling->m_previous_sibling = node->m_previous_sibling;
+
+ if (node->m_previous_sibling)
+ node->m_previous_sibling->m_next_sibling = node->m_next_sibling;
+
+ node->m_next_sibling = nullptr;
+ node->m_previous_sibling = nullptr;
+ node->m_parent = nullptr;
+
+ node->removed_from(static_cast<T&>(*this));
+
+ node->unref();
+
+ static_cast<T*>(this)->children_changed();
+
+ return node;
+}
+
+template<typename T>
+inline void TreeNode<T>::append_child(NonnullRefPtr<T> node, bool notify)
+{
+ ASSERT(!node->m_parent);
+
+ if (!static_cast<T*>(this)->is_child_allowed(*node))
+ return;
+
+ if (m_last_child)
+ m_last_child->m_next_sibling = node.ptr();
+ node->m_previous_sibling = m_last_child;
+ node->m_parent = static_cast<T*>(this);
+ m_last_child = node.ptr();
+ if (!m_first_child)
+ m_first_child = m_last_child;
+ if (notify)
+ node->inserted_into(static_cast<T&>(*this));
+ [[maybe_unused]] auto& rc = node.leak_ref();
+
+ if (notify)
+ static_cast<T*>(this)->children_changed();
+}
+
+template<typename T>
+inline void TreeNode<T>::insert_before(NonnullRefPtr<T> node, RefPtr<T> child, bool notify)
+{
+ if (!child)
+ return append_child(move(node), notify);
+
+ ASSERT(!node->m_parent);
+ ASSERT(child->parent() == this);
+
+ if (!static_cast<T*>(this)->is_child_allowed(*node))
+ return;
+
+ node->m_previous_sibling = child->m_previous_sibling;
+ node->m_next_sibling = child;
+
+ if (child->m_previous_sibling)
+ child->m_previous_sibling->m_next_sibling = node;
+
+ if (m_first_child == child)
+ m_first_child = node;
+
+ child->m_previous_sibling = node;
+
+ if (m_first_child == child)
+ m_first_child = node;
+
+ node->m_parent = static_cast<T*>(this);
+ if (notify)
+ node->inserted_into(static_cast<T&>(*this));
+ [[maybe_unused]] auto& rc = node.leak_ref();
+
+ if (notify)
+ static_cast<T*>(this)->children_changed();
+}
+
+template<typename T>
+inline void TreeNode<T>::prepend_child(NonnullRefPtr<T> node)
+{
+ ASSERT(!node->m_parent);
+
+ if (!static_cast<T*>(this)->is_child_allowed(*node))
+ return;
+
+ if (m_first_child)
+ m_first_child->m_previous_sibling = node.ptr();
+ node->m_next_sibling = m_first_child;
+ node->m_parent = static_cast<T*>(this);
+ m_first_child = node.ptr();
+ if (!m_last_child)
+ m_last_child = m_first_child;
+ node->inserted_into(static_cast<T&>(*this));
+ [[maybe_unused]] auto& rc = node.leak_ref();
+
+ static_cast<T*>(this)->children_changed();
+}
+
+template<typename T>
+inline bool TreeNode<T>::is_ancestor_of(const TreeNode<T>& other) const
+{
+ for (auto* ancestor = other.parent(); ancestor; ancestor = ancestor->parent()) {
+ if (ancestor == this)
+ return true;
+ }
+ return false;
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/UIEvents/EventNames.cpp b/Userland/Libraries/LibWeb/UIEvents/EventNames.cpp
new file mode 100644
index 0000000000..4832ab493b
--- /dev/null
+++ b/Userland/Libraries/LibWeb/UIEvents/EventNames.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020, the SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/UIEvents/EventNames.h>
+
+namespace Web::UIEvents::EventNames {
+
+#define __ENUMERATE_UI_EVENT(name) FlyString name;
+ENUMERATE_UI_EVENTS
+#undef __ENUMERATE_UI_EVENT
+
+[[gnu::constructor]] static void initialize()
+{
+ static bool s_initialized = false;
+ if (s_initialized)
+ return;
+
+#define __ENUMERATE_UI_EVENT(name) \
+ name = #name;
+ ENUMERATE_UI_EVENTS
+#undef __ENUMERATE_UI_EVENT
+
+ s_initialized = true;
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/UIEvents/EventNames.h b/Userland/Libraries/LibWeb/UIEvents/EventNames.h
new file mode 100644
index 0000000000..3667a32f9b
--- /dev/null
+++ b/Userland/Libraries/LibWeb/UIEvents/EventNames.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020, the SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/FlyString.h>
+
+namespace Web::UIEvents::EventNames {
+
+// FIXME: This is not all of the events
+
+#define ENUMERATE_UI_EVENTS \
+ __ENUMERATE_UI_EVENT(click) \
+ __ENUMERATE_UI_EVENT(mousedown) \
+ __ENUMERATE_UI_EVENT(mouseenter) \
+ __ENUMERATE_UI_EVENT(mouseleave) \
+ __ENUMERATE_UI_EVENT(mousemove) \
+ __ENUMERATE_UI_EVENT(mouseout) \
+ __ENUMERATE_UI_EVENT(mouseover) \
+ __ENUMERATE_UI_EVENT(mouseup)
+
+#define __ENUMERATE_UI_EVENT(name) extern FlyString name;
+ENUMERATE_UI_EVENTS
+#undef __ENUMERATE_UI_EVENT
+
+}
diff --git a/Userland/Libraries/LibWeb/UIEvents/MouseEvent.cpp b/Userland/Libraries/LibWeb/UIEvents/MouseEvent.cpp
new file mode 100644
index 0000000000..e102a4e138
--- /dev/null
+++ b/Userland/Libraries/LibWeb/UIEvents/MouseEvent.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020, the SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibWeb/HTML/EventNames.h>
+#include <LibWeb/UIEvents/EventNames.h>
+#include <LibWeb/UIEvents/MouseEvent.h>
+
+namespace Web::UIEvents {
+
+MouseEvent::MouseEvent(const FlyString& event_name, i32 offset_x, i32 offset_y)
+ : UIEvent(event_name)
+ , m_offset_x(offset_x)
+ , m_offset_y(offset_y)
+{
+ set_event_characteristics();
+}
+
+MouseEvent::~MouseEvent()
+{
+}
+
+void MouseEvent::set_event_characteristics()
+{
+ if (type().is_one_of(EventNames::mousedown, EventNames::mousemove, EventNames::mouseout, EventNames::mouseover, EventNames::mouseup, HTML::EventNames::click)) {
+ set_bubbles(true);
+ set_cancelable(true);
+ set_composed(true);
+ }
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/UIEvents/MouseEvent.h b/Userland/Libraries/LibWeb/UIEvents/MouseEvent.h
new file mode 100644
index 0000000000..13fc8d3322
--- /dev/null
+++ b/Userland/Libraries/LibWeb/UIEvents/MouseEvent.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/TypeCasts.h>
+#include <LibWeb/UIEvents/UIEvent.h>
+
+namespace Web::UIEvents {
+
+class MouseEvent final : public UIEvents::UIEvent {
+public:
+ using WrapperType = Bindings::MouseEventWrapper;
+
+ static NonnullRefPtr<MouseEvent> create(const FlyString& event_name, i32 offset_x, i32 offset_y)
+ {
+ return adopt(*new MouseEvent(event_name, offset_x, offset_y));
+ }
+
+ virtual ~MouseEvent() override;
+
+ i32 offset_x() const { return m_offset_x; }
+ i32 offset_y() const { return m_offset_y; }
+
+protected:
+ MouseEvent(const FlyString& event_name, i32 offset_x, i32 offset_y);
+
+private:
+ void set_event_characteristics();
+
+ i32 m_offset_x { 0 };
+ i32 m_offset_y { 0 };
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/UIEvents/MouseEvent.idl b/Userland/Libraries/LibWeb/UIEvents/MouseEvent.idl
new file mode 100644
index 0000000000..2751f2353a
--- /dev/null
+++ b/Userland/Libraries/LibWeb/UIEvents/MouseEvent.idl
@@ -0,0 +1,6 @@
+interface MouseEvent : Event {
+
+ readonly attribute double offsetX;
+ readonly attribute double offsetY;
+
+};
diff --git a/Userland/Libraries/LibWeb/UIEvents/UIEvent.h b/Userland/Libraries/LibWeb/UIEvents/UIEvent.h
new file mode 100644
index 0000000000..cbb8ee7de9
--- /dev/null
+++ b/Userland/Libraries/LibWeb/UIEvents/UIEvent.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibWeb/DOM/Event.h>
+
+namespace Web::UIEvents {
+
+class UIEvent : public DOM::Event {
+public:
+ using WrapperType = Bindings::UIEventWrapper;
+
+ virtual ~UIEvent() override { }
+
+protected:
+ explicit UIEvent(const FlyString& event_name)
+ : Event(event_name)
+ {
+ }
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/UIEvents/UIEvent.idl b/Userland/Libraries/LibWeb/UIEvents/UIEvent.idl
new file mode 100644
index 0000000000..f885f4669a
--- /dev/null
+++ b/Userland/Libraries/LibWeb/UIEvents/UIEvent.idl
@@ -0,0 +1,3 @@
+interface UIEvent : Event {
+
+};
diff --git a/Userland/Libraries/LibWeb/URLEncoder.cpp b/Userland/Libraries/LibWeb/URLEncoder.cpp
new file mode 100644
index 0000000000..a6be0174ea
--- /dev/null
+++ b/Userland/Libraries/LibWeb/URLEncoder.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020, the SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/StringBuilder.h>
+#include <AK/URLParser.h>
+#include <LibWeb/URLEncoder.h>
+
+namespace Web {
+
+String urlencode(const Vector<URLQueryParam>& pairs)
+{
+ StringBuilder builder;
+ for (size_t i = 0; i < pairs.size(); ++i) {
+ builder.append(urlencode(pairs[i].name));
+ builder.append('=');
+ builder.append(urlencode(pairs[i].value));
+ if (i != pairs.size() - 1)
+ builder.append('&');
+ }
+ return builder.to_string();
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/URLEncoder.h b/Userland/Libraries/LibWeb/URLEncoder.h
new file mode 100644
index 0000000000..ad8d3d14aa
--- /dev/null
+++ b/Userland/Libraries/LibWeb/URLEncoder.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, the SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/String.h>
+#include <AK/Vector.h>
+
+namespace Web {
+
+struct URLQueryParam {
+ String name;
+ String value;
+};
+
+String urlencode(const Vector<URLQueryParam>&);
+
+}
diff --git a/Userland/Libraries/LibWeb/WebContentClient.cpp b/Userland/Libraries/LibWeb/WebContentClient.cpp
new file mode 100644
index 0000000000..870b691d3b
--- /dev/null
+++ b/Userland/Libraries/LibWeb/WebContentClient.cpp
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "WebContentClient.h"
+#include "OutOfProcessWebView.h"
+#include <AK/SharedBuffer.h>
+
+namespace Web {
+
+WebContentClient::WebContentClient(OutOfProcessWebView& view)
+ : IPC::ServerConnection<WebContentClientEndpoint, WebContentServerEndpoint>(*this, "/tmp/portal/webcontent")
+ , m_view(view)
+{
+ handshake();
+}
+
+void WebContentClient::handshake()
+{
+ auto response = send_sync<Messages::WebContentServer::Greet>(getpid());
+ set_my_client_id(response->client_id());
+ set_server_pid(response->server_pid());
+}
+
+void WebContentClient::handle(const Messages::WebContentClient::DidPaint& message)
+{
+#ifdef DEBUG_SPAM
+ dbg() << "handle: WebContentClient::DidPaint! content_rect=" << message.content_rect() << ", shbuf_id=" << message.shbuf_id();
+#endif
+ m_view.notify_server_did_paint({}, message.shbuf_id());
+}
+
+void WebContentClient::handle([[maybe_unused]] const Messages::WebContentClient::DidFinishLoading& message)
+{
+ m_view.notify_server_did_finish_loading({}, message.url());
+}
+
+void WebContentClient::handle(const Messages::WebContentClient::DidInvalidateContentRect& message)
+{
+#ifdef DEBUG_SPAM
+ dbg() << "handle: WebContentClient::DidInvalidateContentRect! content_rect=" << message.content_rect();
+#endif
+
+ // FIXME: Figure out a way to coalesce these messages to reduce unnecessary painting
+ m_view.notify_server_did_invalidate_content_rect({}, message.content_rect());
+}
+
+void WebContentClient::handle(const Messages::WebContentClient::DidChangeSelection&)
+{
+#ifdef DEBUG_SPAM
+ dbgln("handle: WebContentClient::DidChangeSelection!");
+#endif
+ m_view.notify_server_did_change_selection({});
+}
+
+void WebContentClient::handle(const Messages::WebContentClient::DidLayout& message)
+{
+#ifdef DEBUG_SPAM
+ dbg() << "handle: WebContentClient::DidLayout! content_size=" << message.content_size();
+#endif
+ m_view.notify_server_did_layout({}, message.content_size());
+}
+
+void WebContentClient::handle(const Messages::WebContentClient::DidChangeTitle& message)
+{
+#ifdef DEBUG_SPAM
+ dbg() << "handle: WebContentClient::DidChangeTitle! title=" << message.title();
+#endif
+ m_view.notify_server_did_change_title({}, message.title());
+}
+
+void WebContentClient::handle(const Messages::WebContentClient::DidRequestScrollIntoView& message)
+{
+#ifdef DEBUG_SPAM
+ dbg() << "handle: WebContentClient::DidRequestScrollIntoView! rect=" << message.rect();
+#endif
+ m_view.notify_server_did_request_scroll_into_view({}, message.rect());
+}
+
+void WebContentClient::handle(const Messages::WebContentClient::DidHoverLink& message)
+{
+#ifdef DEBUG_SPAM
+ dbg() << "handle: WebContentClient::DidHoverLink! url=" << message.url();
+#endif
+ m_view.notify_server_did_hover_link({}, message.url());
+}
+
+void WebContentClient::handle(const Messages::WebContentClient::DidUnhoverLink&)
+{
+#ifdef DEBUG_SPAM
+ dbgln("handle: WebContentClient::DidUnhoverLink!");
+#endif
+ m_view.notify_server_did_unhover_link({});
+}
+
+void WebContentClient::handle(const Messages::WebContentClient::DidClickLink& message)
+{
+ m_view.notify_server_did_click_link({}, message.url(), message.target(), message.modifiers());
+}
+
+void WebContentClient::handle(const Messages::WebContentClient::DidMiddleClickLink& message)
+{
+ m_view.notify_server_did_middle_click_link({}, message.url(), message.target(), message.modifiers());
+}
+
+void WebContentClient::handle(const Messages::WebContentClient::DidStartLoading& message)
+{
+ m_view.notify_server_did_start_loading({}, message.url());
+}
+
+void WebContentClient::handle(const Messages::WebContentClient::DidRequestContextMenu& message)
+{
+ m_view.notify_server_did_request_context_menu({}, message.content_position());
+}
+
+void WebContentClient::handle(const Messages::WebContentClient::DidRequestLinkContextMenu& message)
+{
+ m_view.notify_server_did_request_link_context_menu({}, message.content_position(), message.url(), message.target(), message.modifiers());
+}
+
+OwnPtr<Messages::WebContentClient::DidRequestAlertResponse> WebContentClient::handle(const Messages::WebContentClient::DidRequestAlert& message)
+{
+ m_view.notify_server_did_request_alert({}, message.message());
+ return make<Messages::WebContentClient::DidRequestAlertResponse>();
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/WebContentClient.h b/Userland/Libraries/LibWeb/WebContentClient.h
new file mode 100644
index 0000000000..2078882578
--- /dev/null
+++ b/Userland/Libraries/LibWeb/WebContentClient.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/HashMap.h>
+#include <LibIPC/ServerConnection.h>
+#include <WebContent/WebContentClientEndpoint.h>
+#include <WebContent/WebContentServerEndpoint.h>
+
+namespace Web {
+
+class OutOfProcessWebView;
+
+class WebContentClient
+ : public IPC::ServerConnection<WebContentClientEndpoint, WebContentServerEndpoint>
+ , public WebContentClientEndpoint {
+ C_OBJECT(WebContentClient);
+
+public:
+ virtual void handshake() override;
+
+private:
+ WebContentClient(OutOfProcessWebView&);
+
+ virtual void handle(const Messages::WebContentClient::DidPaint&) override;
+ virtual void handle(const Messages::WebContentClient::DidFinishLoading&) override;
+ virtual void handle(const Messages::WebContentClient::DidInvalidateContentRect&) override;
+ virtual void handle(const Messages::WebContentClient::DidChangeSelection&) override;
+ virtual void handle(const Messages::WebContentClient::DidLayout&) override;
+ virtual void handle(const Messages::WebContentClient::DidChangeTitle&) override;
+ virtual void handle(const Messages::WebContentClient::DidRequestScrollIntoView&) override;
+ virtual void handle(const Messages::WebContentClient::DidHoverLink&) override;
+ virtual void handle(const Messages::WebContentClient::DidUnhoverLink&) override;
+ virtual void handle(const Messages::WebContentClient::DidClickLink&) override;
+ virtual void handle(const Messages::WebContentClient::DidMiddleClickLink&) override;
+ virtual void handle(const Messages::WebContentClient::DidStartLoading&) override;
+ virtual void handle(const Messages::WebContentClient::DidRequestContextMenu&) override;
+ virtual void handle(const Messages::WebContentClient::DidRequestLinkContextMenu&) override;
+ virtual OwnPtr<Messages::WebContentClient::DidRequestAlertResponse> handle(const Messages::WebContentClient::DidRequestAlert&) override;
+
+ OutOfProcessWebView& m_view;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/WebViewHooks.h b/Userland/Libraries/LibWeb/WebViewHooks.h
new file mode 100644
index 0000000000..198526e019
--- /dev/null
+++ b/Userland/Libraries/LibWeb/WebViewHooks.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/Function.h>
+#include <LibGfx/Forward.h>
+#include <LibWeb/Forward.h>
+
+namespace Web {
+
+class WebViewHooks {
+public:
+ Function<void(const Gfx::IntPoint& screen_position)> on_context_menu_request;
+ Function<void(const URL&, const String& target, unsigned modifiers)> on_link_click;
+ Function<void(const URL&, const Gfx::IntPoint& screen_position)> on_link_context_menu_request;
+ Function<void(const URL&, const Gfx::IntPoint& screen_position, const Gfx::ShareableBitmap&)> on_image_context_menu_request;
+ Function<void(const URL&, const String& target, unsigned modifiers)> on_link_middle_click;
+ Function<void(const URL&)> on_link_hover;
+ Function<void(const String&)> on_title_change;
+ Function<void(const URL&)> on_load_start;
+ Function<void(const URL&)> on_load_finish;
+ Function<void(const Gfx::Bitmap&)> on_favicon_change;
+ Function<void(const URL&)> on_url_drop;
+ Function<void(DOM::Document*)> on_set_document;
+};
+
+}