mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 02:47:35 +00:00
Libraries: Move to Userland/Libraries/
This commit is contained in:
parent
dc28c07fa5
commit
13d7c09125
1857 changed files with 266 additions and 274 deletions
3
Userland/Libraries/LibWeb/CSS/.gitignore
vendored
Normal file
3
Userland/Libraries/LibWeb/CSS/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
DefaultStyleSheetSource.cpp
|
||||
PropertyID.cpp
|
||||
PropertyID.h
|
161
Userland/Libraries/LibWeb/CSS/ComputedValues.h
Normal file
161
Userland/Libraries/LibWeb/CSS/ComputedValues.h
Normal file
|
@ -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; }
|
||||
};
|
||||
|
||||
}
|
196
Userland/Libraries/LibWeb/CSS/Default.css
Normal file
196
Userland/Libraries/LibWeb/CSS/Default.css
Normal file
|
@ -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;
|
||||
}
|
122
Userland/Libraries/LibWeb/CSS/Identifiers.json
Normal file
122
Userland/Libraries/LibWeb/CSS/Identifiers.json
Normal file
|
@ -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"
|
||||
]
|
103
Userland/Libraries/LibWeb/CSS/Length.cpp
Normal file
103
Userland/Libraries/LibWeb/CSS/Length.cpp
Normal file
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
181
Userland/Libraries/LibWeb/CSS/Length.h
Normal file
181
Userland/Libraries/LibWeb/CSS/Length.h
Normal file
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
40
Userland/Libraries/LibWeb/CSS/LengthBox.h
Normal file
40
Userland/Libraries/LibWeb/CSS/LengthBox.h
Normal file
|
@ -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() };
|
||||
};
|
||||
|
||||
}
|
902
Userland/Libraries/LibWeb/CSS/Parser/CSSParser.cpp
Normal file
902
Userland/Libraries/LibWeb/CSS/Parser/CSSParser.cpp
Normal file
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
59
Userland/Libraries/LibWeb/CSS/Parser/CSSParser.h
Normal file
59
Userland/Libraries/LibWeb/CSS/Parser/CSSParser.h
Normal file
|
@ -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&);
|
||||
|
||||
}
|
368
Userland/Libraries/LibWeb/CSS/Properties.json
Normal file
368
Userland/Libraries/LibWeb/CSS/Properties.json
Normal file
|
@ -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"
|
||||
}
|
||||
}
|
3
Userland/Libraries/LibWeb/CSS/QuirksMode.css
Normal file
3
Userland/Libraries/LibWeb/CSS/QuirksMode.css
Normal file
|
@ -0,0 +1,3 @@
|
|||
table {
|
||||
text-align: left;
|
||||
}
|
67
Userland/Libraries/LibWeb/CSS/Selector.cpp
Normal file
67
Userland/Libraries/LibWeb/CSS/Selector.cpp
Normal file
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
106
Userland/Libraries/LibWeb/CSS/Selector.h
Normal file
106
Userland/Libraries/LibWeb/CSS/Selector.h
Normal file
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
172
Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp
Normal file
172
Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp
Normal file
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
36
Userland/Libraries/LibWeb/CSS/SelectorEngine.h
Normal file
36
Userland/Libraries/LibWeb/CSS/SelectorEngine.h
Normal file
|
@ -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&);
|
||||
|
||||
}
|
40
Userland/Libraries/LibWeb/CSS/StyleDeclaration.cpp
Normal file
40
Userland/Libraries/LibWeb/CSS/StyleDeclaration.cpp
Normal file
|
@ -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()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
58
Userland/Libraries/LibWeb/CSS/StyleDeclaration.h
Normal file
58
Userland/Libraries/LibWeb/CSS/StyleDeclaration.h
Normal file
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
74
Userland/Libraries/LibWeb/CSS/StyleInvalidator.cpp
Normal file
74
Userland/Libraries/LibWeb/CSS/StyleInvalidator.cpp
Normal file
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
46
Userland/Libraries/LibWeb/CSS/StyleInvalidator.h
Normal file
46
Userland/Libraries/LibWeb/CSS/StyleInvalidator.h
Normal file
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
484
Userland/Libraries/LibWeb/CSS/StyleProperties.cpp
Normal file
484
Userland/Libraries/LibWeb/CSS/StyleProperties.cpp
Normal file
|
@ -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 {};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
96
Userland/Libraries/LibWeb/CSS/StyleProperties.h
Normal file
96
Userland/Libraries/LibWeb/CSS/StyleProperties.h
Normal file
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
598
Userland/Libraries/LibWeb/CSS/StyleResolver.cpp
Normal file
598
Userland/Libraries/LibWeb/CSS/StyleResolver.cpp
Normal file
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
65
Userland/Libraries/LibWeb/CSS/StyleResolver.h
Normal file
65
Userland/Libraries/LibWeb/CSS/StyleResolver.h
Normal file
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
41
Userland/Libraries/LibWeb/CSS/StyleRule.cpp
Normal file
41
Userland/Libraries/LibWeb/CSS/StyleRule.cpp
Normal file
|
@ -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()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
57
Userland/Libraries/LibWeb/CSS/StyleRule.h
Normal file
57
Userland/Libraries/LibWeb/CSS/StyleRule.h
Normal file
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
40
Userland/Libraries/LibWeb/CSS/StyleSheet.cpp
Normal file
40
Userland/Libraries/LibWeb/CSS/StyleSheet.cpp
Normal file
|
@ -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()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
52
Userland/Libraries/LibWeb/CSS/StyleSheet.h
Normal file
52
Userland/Libraries/LibWeb/CSS/StyleSheet.h
Normal file
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
41
Userland/Libraries/LibWeb/CSS/StyleSheetList.cpp
Normal file
41
Userland/Libraries/LibWeb/CSS/StyleSheetList.cpp
Normal file
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
52
Userland/Libraries/LibWeb/CSS/StyleSheetList.h
Normal file
52
Userland/Libraries/LibWeb/CSS/StyleSheetList.h
Normal file
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
194
Userland/Libraries/LibWeb/CSS/StyleValue.cpp
Normal file
194
Userland/Libraries/LibWeb/CSS/StyleValue.cpp
Normal file
|
@ -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({});
|
||||
}
|
||||
|
||||
}
|
357
Userland/Libraries/LibWeb/CSS/StyleValue.h
Normal file
357
Userland/Libraries/LibWeb/CSS/StyleValue.h
Normal file
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue