mirror of
https://github.com/RGBCube/serenity
synced 2025-07-10 06:17:35 +00:00
LibWeb: Don't tolerate unit-less lengths (except 0) in standards mode
"width: 500" is not a valid CSS property in standards mode and should be ignored. To plumb the quirks-mode flag into CSS parsing, this patch adds a new CSS::ParsingContext object that must be passed to the CSS parser. Currently it only allows you to check the quirks-mode flag. In the future it will be a good place to put additional information needed for things like relative URL resolution, etc. This narrows <div class=parser> on ACID2 to the correct width. :^)
This commit is contained in:
parent
c1acf67715
commit
9e642827fc
7 changed files with 221 additions and 112 deletions
|
@ -52,7 +52,7 @@ static StyleSheet& default_stylesheet()
|
||||||
if (!sheet) {
|
if (!sheet) {
|
||||||
extern const char default_stylesheet_source[];
|
extern const char default_stylesheet_source[];
|
||||||
String css = default_stylesheet_source;
|
String css = default_stylesheet_source;
|
||||||
sheet = parse_css(css).leak_ref();
|
sheet = parse_css(CSS::ParsingContext(), css).leak_ref();
|
||||||
}
|
}
|
||||||
return *sheet;
|
return *sheet;
|
||||||
}
|
}
|
||||||
|
@ -205,6 +205,8 @@ static inline void set_property_border_style(StyleProperties& style, const Style
|
||||||
|
|
||||||
static void set_property_expanding_shorthands(StyleProperties& style, CSS::PropertyID property_id, const StyleValue& value, Document& document)
|
static void set_property_expanding_shorthands(StyleProperties& style, CSS::PropertyID property_id, const StyleValue& value, Document& document)
|
||||||
{
|
{
|
||||||
|
CSS::ParsingContext context(document);
|
||||||
|
|
||||||
if (property_id == CSS::PropertyID::Border) {
|
if (property_id == CSS::PropertyID::Border) {
|
||||||
set_property_expanding_shorthands(style, CSS::PropertyID::BorderTop, value, document);
|
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::BorderRight, value, document);
|
||||||
|
@ -248,7 +250,7 @@ static void set_property_expanding_shorthands(StyleProperties& style, CSS::Prope
|
||||||
auto parts = split_on_whitespace(value.to_string());
|
auto parts = split_on_whitespace(value.to_string());
|
||||||
|
|
||||||
if (parts.size() == 1) {
|
if (parts.size() == 1) {
|
||||||
if (auto value = parse_line_style(parts[0])) {
|
if (auto value = parse_line_style(context, parts[0])) {
|
||||||
set_property_border_style(style, value.release_nonnull(), edge);
|
set_property_border_style(style, value.release_nonnull(), edge);
|
||||||
set_property_border_color(style, ColorStyleValue::create(Gfx::Color::Black), 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);
|
set_property_border_width(style, LengthStyleValue::create(Length(3, Length::Type::Px)), edge);
|
||||||
|
@ -261,19 +263,19 @@ static void set_property_expanding_shorthands(StyleProperties& style, CSS::Prope
|
||||||
RefPtr<StringStyleValue> line_style_value;
|
RefPtr<StringStyleValue> line_style_value;
|
||||||
|
|
||||||
for (auto& part : parts) {
|
for (auto& part : parts) {
|
||||||
if (auto value = parse_line_width(part)) {
|
if (auto value = parse_line_width(context, part)) {
|
||||||
if (line_width_value)
|
if (line_width_value)
|
||||||
return;
|
return;
|
||||||
line_width_value = move(value);
|
line_width_value = move(value);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (auto value = parse_color(part)) {
|
if (auto value = parse_color(context, part)) {
|
||||||
if (color_value)
|
if (color_value)
|
||||||
return;
|
return;
|
||||||
color_value = move(value);
|
color_value = move(value);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (auto value = parse_line_style(part)) {
|
if (auto value = parse_line_style(context, part)) {
|
||||||
if (line_style_value)
|
if (line_style_value)
|
||||||
return;
|
return;
|
||||||
line_style_value = move(value);
|
line_style_value = move(value);
|
||||||
|
@ -296,10 +298,16 @@ static void set_property_expanding_shorthands(StyleProperties& style, CSS::Prope
|
||||||
if (property_id == CSS::PropertyID::BorderStyle) {
|
if (property_id == CSS::PropertyID::BorderStyle) {
|
||||||
auto parts = split_on_whitespace(value.to_string());
|
auto parts = split_on_whitespace(value.to_string());
|
||||||
if (value.is_string() && parts.size() == 3) {
|
if (value.is_string() && parts.size() == 3) {
|
||||||
style.set_property(CSS::PropertyID::BorderTopStyle, parse_css_value(parts[0]));
|
auto top = parse_css_value(context, parts[0]);
|
||||||
style.set_property(CSS::PropertyID::BorderRightStyle, parse_css_value(parts[1]));
|
auto right = parse_css_value(context, parts[1]);
|
||||||
style.set_property(CSS::PropertyID::BorderBottomStyle, parse_css_value(parts[2]));
|
auto bottom = parse_css_value(context, parts[2]);
|
||||||
style.set_property(CSS::PropertyID::BorderLeftStyle, parse_css_value(parts[1]));
|
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 {
|
} else {
|
||||||
style.set_property(CSS::PropertyID::BorderTopStyle, value);
|
style.set_property(CSS::PropertyID::BorderTopStyle, value);
|
||||||
style.set_property(CSS::PropertyID::BorderRightStyle, value);
|
style.set_property(CSS::PropertyID::BorderRightStyle, value);
|
||||||
|
@ -312,12 +320,14 @@ static void set_property_expanding_shorthands(StyleProperties& style, CSS::Prope
|
||||||
if (property_id == CSS::PropertyID::BorderWidth) {
|
if (property_id == CSS::PropertyID::BorderWidth) {
|
||||||
auto parts = split_on_whitespace(value.to_string());
|
auto parts = split_on_whitespace(value.to_string());
|
||||||
if (value.is_string() && parts.size() == 2) {
|
if (value.is_string() && parts.size() == 2) {
|
||||||
auto vertical_border_width = parse_css_value(parts[0]);
|
auto vertical_border_width = parse_css_value(context, parts[0]);
|
||||||
auto horizontal_border_width = parse_css_value(parts[1]);
|
auto horizontal_border_width = parse_css_value(context, parts[1]);
|
||||||
style.set_property(CSS::PropertyID::BorderTopWidth, vertical_border_width);
|
if (vertical_border_width && horizontal_border_width) {
|
||||||
style.set_property(CSS::PropertyID::BorderRightWidth, horizontal_border_width);
|
style.set_property(CSS::PropertyID::BorderTopWidth, *vertical_border_width);
|
||||||
style.set_property(CSS::PropertyID::BorderBottomWidth, vertical_border_width);
|
style.set_property(CSS::PropertyID::BorderRightWidth, *horizontal_border_width);
|
||||||
style.set_property(CSS::PropertyID::BorderLeftWidth, horizontal_border_width);
|
style.set_property(CSS::PropertyID::BorderBottomWidth, *vertical_border_width);
|
||||||
|
style.set_property(CSS::PropertyID::BorderLeftWidth, *horizontal_border_width);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
style.set_property(CSS::PropertyID::BorderTopWidth, value);
|
style.set_property(CSS::PropertyID::BorderTopWidth, value);
|
||||||
style.set_property(CSS::PropertyID::BorderRightWidth, value);
|
style.set_property(CSS::PropertyID::BorderRightWidth, value);
|
||||||
|
@ -330,10 +340,16 @@ static void set_property_expanding_shorthands(StyleProperties& style, CSS::Prope
|
||||||
if (property_id == CSS::PropertyID::BorderColor) {
|
if (property_id == CSS::PropertyID::BorderColor) {
|
||||||
auto parts = split_on_whitespace(value.to_string());
|
auto parts = split_on_whitespace(value.to_string());
|
||||||
if (value.is_string() && parts.size() == 4) {
|
if (value.is_string() && parts.size() == 4) {
|
||||||
style.set_property(CSS::PropertyID::BorderTopColor, parse_css_value(parts[0]));
|
auto top = parse_css_value(context, parts[0]);
|
||||||
style.set_property(CSS::PropertyID::BorderRightColor, parse_css_value(parts[1]));
|
auto right = parse_css_value(context, parts[1]);
|
||||||
style.set_property(CSS::PropertyID::BorderBottomColor, parse_css_value(parts[2]));
|
auto bottom = parse_css_value(context, parts[2]);
|
||||||
style.set_property(CSS::PropertyID::BorderLeftColor, parse_css_value(parts[3]));
|
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 {
|
} else {
|
||||||
style.set_property(CSS::PropertyID::BorderTopColor, value);
|
style.set_property(CSS::PropertyID::BorderTopColor, value);
|
||||||
style.set_property(CSS::PropertyID::BorderRightColor, value);
|
style.set_property(CSS::PropertyID::BorderRightColor, value);
|
||||||
|
@ -351,7 +367,10 @@ static void set_property_expanding_shorthands(StyleProperties& style, CSS::Prope
|
||||||
auto parts = split_on_whitespace(value.to_string());
|
auto parts = split_on_whitespace(value.to_string());
|
||||||
NonnullRefPtrVector<StyleValue> values;
|
NonnullRefPtrVector<StyleValue> values;
|
||||||
for (auto& part : parts) {
|
for (auto& part : parts) {
|
||||||
values.append(parse_css_value(part));
|
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
|
// HACK: Disallow more than one color value in a 'background' shorthand
|
||||||
|
@ -393,33 +412,39 @@ static void set_property_expanding_shorthands(StyleProperties& style, CSS::Prope
|
||||||
if (value.is_string()) {
|
if (value.is_string()) {
|
||||||
auto parts = split_on_whitespace(value.to_string());
|
auto parts = split_on_whitespace(value.to_string());
|
||||||
if (value.is_string() && parts.size() == 2) {
|
if (value.is_string() && parts.size() == 2) {
|
||||||
auto vertical = parse_css_value(parts[0]);
|
auto vertical = parse_css_value(context, parts[0]);
|
||||||
auto horizontal = parse_css_value(parts[1]);
|
auto horizontal = parse_css_value(context, parts[1]);
|
||||||
style.set_property(CSS::PropertyID::MarginTop, vertical);
|
if (vertical && horizontal) {
|
||||||
style.set_property(CSS::PropertyID::MarginBottom, vertical);
|
style.set_property(CSS::PropertyID::MarginTop, *vertical);
|
||||||
style.set_property(CSS::PropertyID::MarginLeft, horizontal);
|
style.set_property(CSS::PropertyID::MarginBottom, *vertical);
|
||||||
style.set_property(CSS::PropertyID::MarginRight, horizontal);
|
style.set_property(CSS::PropertyID::MarginLeft, *horizontal);
|
||||||
|
style.set_property(CSS::PropertyID::MarginRight, *horizontal);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (value.is_string() && parts.size() == 3) {
|
if (value.is_string() && parts.size() == 3) {
|
||||||
auto top = parse_css_value(parts[0]);
|
auto top = parse_css_value(context, parts[0]);
|
||||||
auto horizontal = parse_css_value(parts[1]);
|
auto horizontal = parse_css_value(context, parts[1]);
|
||||||
auto bottom = parse_css_value(parts[2]);
|
auto bottom = parse_css_value(context, parts[2]);
|
||||||
style.set_property(CSS::PropertyID::MarginTop, top);
|
if (top && horizontal && bottom) {
|
||||||
style.set_property(CSS::PropertyID::MarginBottom, bottom);
|
style.set_property(CSS::PropertyID::MarginTop, *top);
|
||||||
style.set_property(CSS::PropertyID::MarginLeft, horizontal);
|
style.set_property(CSS::PropertyID::MarginBottom, *bottom);
|
||||||
style.set_property(CSS::PropertyID::MarginRight, horizontal);
|
style.set_property(CSS::PropertyID::MarginLeft, *horizontal);
|
||||||
|
style.set_property(CSS::PropertyID::MarginRight, *horizontal);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (value.is_string() && parts.size() == 4) {
|
if (value.is_string() && parts.size() == 4) {
|
||||||
auto top = parse_css_value(parts[0]);
|
auto top = parse_css_value(context, parts[0]);
|
||||||
auto right = parse_css_value(parts[1]);
|
auto right = parse_css_value(context, parts[1]);
|
||||||
auto bottom = parse_css_value(parts[2]);
|
auto bottom = parse_css_value(context, parts[2]);
|
||||||
auto left = parse_css_value(parts[3]);
|
auto left = parse_css_value(context, parts[3]);
|
||||||
style.set_property(CSS::PropertyID::MarginTop, top);
|
if (top && right && bottom && left) {
|
||||||
style.set_property(CSS::PropertyID::MarginBottom, bottom);
|
style.set_property(CSS::PropertyID::MarginTop, *top);
|
||||||
style.set_property(CSS::PropertyID::MarginLeft, left);
|
style.set_property(CSS::PropertyID::MarginBottom, *bottom);
|
||||||
style.set_property(CSS::PropertyID::MarginRight, right);
|
style.set_property(CSS::PropertyID::MarginLeft, *left);
|
||||||
|
style.set_property(CSS::PropertyID::MarginRight, *right);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
dbg() << "Unsure what to do with CSS margin value '" << value.to_string() << "'";
|
dbg() << "Unsure what to do with CSS margin value '" << value.to_string() << "'";
|
||||||
|
@ -439,33 +464,39 @@ static void set_property_expanding_shorthands(StyleProperties& style, CSS::Prope
|
||||||
if (value.is_string()) {
|
if (value.is_string()) {
|
||||||
auto parts = split_on_whitespace(value.to_string());
|
auto parts = split_on_whitespace(value.to_string());
|
||||||
if (value.is_string() && parts.size() == 2) {
|
if (value.is_string() && parts.size() == 2) {
|
||||||
auto vertical = parse_css_value(parts[0]);
|
auto vertical = parse_css_value(context, parts[0]);
|
||||||
auto horizontal = parse_css_value(parts[1]);
|
auto horizontal = parse_css_value(context, parts[1]);
|
||||||
style.set_property(CSS::PropertyID::PaddingTop, vertical);
|
if (vertical && horizontal) {
|
||||||
style.set_property(CSS::PropertyID::PaddingBottom, vertical);
|
style.set_property(CSS::PropertyID::PaddingTop, *vertical);
|
||||||
style.set_property(CSS::PropertyID::PaddingLeft, horizontal);
|
style.set_property(CSS::PropertyID::PaddingBottom, *vertical);
|
||||||
style.set_property(CSS::PropertyID::PaddingRight, horizontal);
|
style.set_property(CSS::PropertyID::PaddingLeft, *horizontal);
|
||||||
|
style.set_property(CSS::PropertyID::PaddingRight, *horizontal);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (value.is_string() && parts.size() == 3) {
|
if (value.is_string() && parts.size() == 3) {
|
||||||
auto top = parse_css_value(parts[0]);
|
auto top = parse_css_value(context, parts[0]);
|
||||||
auto horizontal = parse_css_value(parts[1]);
|
auto horizontal = parse_css_value(context, parts[1]);
|
||||||
auto bottom = parse_css_value(parts[2]);
|
auto bottom = parse_css_value(context, parts[2]);
|
||||||
style.set_property(CSS::PropertyID::PaddingTop, top);
|
if (top && bottom && horizontal) {
|
||||||
style.set_property(CSS::PropertyID::PaddingBottom, bottom);
|
style.set_property(CSS::PropertyID::PaddingTop, *top);
|
||||||
style.set_property(CSS::PropertyID::PaddingLeft, horizontal);
|
style.set_property(CSS::PropertyID::PaddingBottom, *bottom);
|
||||||
style.set_property(CSS::PropertyID::PaddingRight, horizontal);
|
style.set_property(CSS::PropertyID::PaddingLeft, *horizontal);
|
||||||
|
style.set_property(CSS::PropertyID::PaddingRight, *horizontal);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (value.is_string() && parts.size() == 4) {
|
if (value.is_string() && parts.size() == 4) {
|
||||||
auto top = parse_css_value(parts[0]);
|
auto top = parse_css_value(context, parts[0]);
|
||||||
auto right = parse_css_value(parts[1]);
|
auto right = parse_css_value(context, parts[1]);
|
||||||
auto bottom = parse_css_value(parts[2]);
|
auto bottom = parse_css_value(context, parts[2]);
|
||||||
auto left = parse_css_value(parts[3]);
|
auto left = parse_css_value(context, parts[3]);
|
||||||
style.set_property(CSS::PropertyID::PaddingTop, top);
|
if (top && bottom && left && right) {
|
||||||
style.set_property(CSS::PropertyID::PaddingBottom, bottom);
|
style.set_property(CSS::PropertyID::PaddingTop, *top);
|
||||||
style.set_property(CSS::PropertyID::PaddingLeft, left);
|
style.set_property(CSS::PropertyID::PaddingBottom, *bottom);
|
||||||
style.set_property(CSS::PropertyID::PaddingRight, right);
|
style.set_property(CSS::PropertyID::PaddingLeft, *left);
|
||||||
|
style.set_property(CSS::PropertyID::PaddingRight, *right);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
dbg() << "Unsure what to do with CSS padding value '" << value.to_string() << "'";
|
dbg() << "Unsure what to do with CSS padding value '" << value.to_string() << "'";
|
||||||
|
@ -477,8 +508,10 @@ static void set_property_expanding_shorthands(StyleProperties& style, CSS::Prope
|
||||||
if (property_id == CSS::PropertyID::ListStyle) {
|
if (property_id == CSS::PropertyID::ListStyle) {
|
||||||
auto parts = split_on_whitespace(value.to_string());
|
auto parts = split_on_whitespace(value.to_string());
|
||||||
if (!parts.is_empty()) {
|
if (!parts.is_empty()) {
|
||||||
auto value = parse_css_value(parts[0]);
|
auto value = parse_css_value(context, parts[0]);
|
||||||
style.set_property(CSS::PropertyID::ListStyleType, value);
|
if (!value)
|
||||||
|
return;
|
||||||
|
style.set_property(CSS::PropertyID::ListStyleType, value.release_nonnull());
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -522,7 +555,7 @@ NonnullRefPtr<StyleProperties> StyleResolver::resolve_style(const Element& eleme
|
||||||
|
|
||||||
auto style_attribute = element.attribute(HTML::AttributeNames::style);
|
auto style_attribute = element.attribute(HTML::AttributeNames::style);
|
||||||
if (!style_attribute.is_null()) {
|
if (!style_attribute.is_null()) {
|
||||||
if (auto declaration = parse_css_declaration(style_attribute)) {
|
if (auto declaration = parse_css_declaration(CSS::ParsingContext(document()), style_attribute)) {
|
||||||
for (auto& property : declaration->properties()) {
|
for (auto& property : declaration->properties()) {
|
||||||
set_property_expanding_shorthands(style, property.property_id, property.value, m_document);
|
set_property_expanding_shorthands(style, property.property_id, property.value, m_document);
|
||||||
}
|
}
|
||||||
|
|
|
@ -346,7 +346,7 @@ NonnullRefPtrVector<Element> Document::get_elements_by_tag_name(const String& ta
|
||||||
|
|
||||||
RefPtr<Element> Document::query_selector(const StringView& selector_text)
|
RefPtr<Element> Document::query_selector(const StringView& selector_text)
|
||||||
{
|
{
|
||||||
auto selector = parse_selector(selector_text);
|
auto selector = parse_selector(CSS::ParsingContext(*this), selector_text);
|
||||||
if (!selector.has_value())
|
if (!selector.has_value())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
|
@ -366,7 +366,7 @@ RefPtr<Element> Document::query_selector(const StringView& selector_text)
|
||||||
|
|
||||||
NonnullRefPtrVector<Element> Document::query_selector_all(const StringView& selector_text)
|
NonnullRefPtrVector<Element> Document::query_selector_all(const StringView& selector_text)
|
||||||
{
|
{
|
||||||
auto selector = parse_selector(selector_text);
|
auto selector = parse_selector(CSS::ParsingContext(*this), selector_text);
|
||||||
if (!selector.has_value())
|
if (!selector.has_value())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
|
|
|
@ -63,7 +63,7 @@ void HTMLLinkElement::resource_did_load()
|
||||||
|
|
||||||
dbg() << "HTMLLinkElement: Resource did load, looks good! " << href();
|
dbg() << "HTMLLinkElement: Resource did load, looks good! " << href();
|
||||||
|
|
||||||
auto sheet = parse_css(resource()->encoded_data());
|
auto sheet = parse_css(CSS::ParsingContext(document()), resource()->encoded_data());
|
||||||
if (!sheet) {
|
if (!sheet) {
|
||||||
dbg() << "HTMLLinkElement: Failed to parse stylesheet: " << href();
|
dbg() << "HTMLLinkElement: Failed to parse stylesheet: " << href();
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -48,7 +48,7 @@ void HTMLStyleElement::children_changed()
|
||||||
if (is<Text>(child))
|
if (is<Text>(child))
|
||||||
builder.append(to<Text>(child).text_content());
|
builder.append(to<Text>(child).text_content());
|
||||||
});
|
});
|
||||||
m_stylesheet = parse_css(builder.to_string());
|
m_stylesheet = parse_css(CSS::ParsingContext(document()), builder.to_string());
|
||||||
if (m_stylesheet)
|
if (m_stylesheet)
|
||||||
document().style_sheets().add_sheet(*m_stylesheet);
|
document().style_sheets().add_sheet(*m_stylesheet);
|
||||||
else
|
else
|
||||||
|
|
|
@ -42,7 +42,8 @@ void HTMLTableElement::apply_presentational_hints(StyleProperties& style) const
|
||||||
{
|
{
|
||||||
for_each_attribute([&](auto& name, auto& value) {
|
for_each_attribute([&](auto& name, auto& value) {
|
||||||
if (name == HTML::AttributeNames::width) {
|
if (name == HTML::AttributeNames::width) {
|
||||||
style.set_property(CSS::PropertyID::Width, parse_css_value(value));
|
if (auto parsed_value = parse_css_value(CSS::ParsingContext(document()), value))
|
||||||
|
style.set_property(CSS::PropertyID::Width, parsed_value.release_nonnull());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (name == HTML::AttributeNames::bgcolor) {
|
if (name == HTML::AttributeNames::bgcolor) {
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include <AK/HashMap.h>
|
#include <AK/HashMap.h>
|
||||||
#include <LibWeb/CSS/PropertyID.h>
|
#include <LibWeb/CSS/PropertyID.h>
|
||||||
#include <LibWeb/CSS/StyleSheet.h>
|
#include <LibWeb/CSS/StyleSheet.h>
|
||||||
|
#include <LibWeb/DOM/Document.h>
|
||||||
#include <LibWeb/Parser/CSSParser.h>
|
#include <LibWeb/Parser/CSSParser.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -46,6 +47,24 @@
|
||||||
|
|
||||||
namespace Web {
|
namespace Web {
|
||||||
|
|
||||||
|
namespace CSS {
|
||||||
|
|
||||||
|
ParsingContext::ParsingContext()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ParsingContext::ParsingContext(const Document& document)
|
||||||
|
: m_document(&document)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ParsingContext::in_quirks_mode() const
|
||||||
|
{
|
||||||
|
return m_document ? m_document->in_quirks_mode() : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
static CSS::ValueID value_id_for_palette_string(const StringView& string)
|
static CSS::ValueID value_id_for_palette_string(const StringView& string)
|
||||||
{
|
{
|
||||||
if (string == "desktop-background")
|
if (string == "desktop-background")
|
||||||
|
@ -160,7 +179,7 @@ static CSS::ValueID value_id_for_palette_string(const StringView& string)
|
||||||
return CSS::ValueID::Invalid;
|
return CSS::ValueID::Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Optional<Color> parse_css_color(const StringView& view)
|
static Optional<Color> parse_css_color(const CSS::ParsingContext&, const StringView& view)
|
||||||
{
|
{
|
||||||
if (view.equals_ignoring_case("transparent"))
|
if (view.equals_ignoring_case("transparent"))
|
||||||
return Color::from_rgba(0x00000000);
|
return Color::from_rgba(0x00000000);
|
||||||
|
@ -245,35 +264,72 @@ static Optional<float> try_parse_float(const StringView& string)
|
||||||
return is_negative ? -value : value;
|
return is_negative ? -value : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Optional<float> parse_number(const StringView& view)
|
static Optional<float> parse_number(const CSS::ParsingContext& context, const StringView& view)
|
||||||
{
|
{
|
||||||
if (view.ends_with('%'))
|
if (view.ends_with('%'))
|
||||||
return parse_number(view.substring_view(0, view.length() - 1));
|
return parse_number(context, view.substring_view(0, view.length() - 1));
|
||||||
|
|
||||||
// FIXME: Maybe we should have "ends_with_ignoring_case()" ?
|
// FIXME: Maybe we should have "ends_with_ignoring_case()" ?
|
||||||
if (view.to_string().to_lowercase().ends_with("px"))
|
if (view.to_string().to_lowercase().ends_with("px"))
|
||||||
return parse_number(view.substring_view(0, view.length() - 2));
|
return parse_number(context, view.substring_view(0, view.length() - 2));
|
||||||
if (view.to_string().to_lowercase().ends_with("rem"))
|
if (view.to_string().to_lowercase().ends_with("rem"))
|
||||||
return parse_number(view.substring_view(0, view.length() - 3));
|
return parse_number(context, view.substring_view(0, view.length() - 3));
|
||||||
if (view.to_string().to_lowercase().ends_with("em"))
|
if (view.to_string().to_lowercase().ends_with("em"))
|
||||||
return parse_number(view.substring_view(0, view.length() - 2));
|
return parse_number(context, view.substring_view(0, view.length() - 2));
|
||||||
|
if (view == "0")
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// NOTE: We don't allow things like "width: 100" in standards mode.
|
||||||
|
if (!context.in_quirks_mode())
|
||||||
|
return {};
|
||||||
|
|
||||||
return try_parse_float(view);
|
return try_parse_float(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
NonnullRefPtr<StyleValue> parse_css_value(const StringView& string)
|
static Length parse_length(const CSS::ParsingContext& context, const StringView& view, bool& is_bad_length)
|
||||||
{
|
{
|
||||||
auto number = parse_number(string);
|
Length::Type type = Length::Type::Undefined;
|
||||||
if (number.has_value()) {
|
Optional<float> value;
|
||||||
if (string.ends_with('%'))
|
|
||||||
return LengthStyleValue::create(Length(number.value(), Length::Type::Percentage));
|
if (view.ends_with('%')) {
|
||||||
if (string.ends_with("em"))
|
type = Length::Type::Percentage;
|
||||||
return LengthStyleValue::create(Length(number.value(), Length::Type::Em));
|
value = try_parse_float(view.substring_view(0, view.length() - 1));
|
||||||
if (string.ends_with("rem"))
|
} else if (view.to_string().to_lowercase().ends_with("px")) {
|
||||||
return LengthStyleValue::create(Length(number.value(), Length::Type::Rem));
|
type = Length::Type::Px;
|
||||||
return LengthStyleValue::create(Length(number.value(), Length::Type::Px));
|
value = try_parse_float(view.substring_view(0, view.length() - 2));
|
||||||
|
} else if (view.to_string().to_lowercase().ends_with("rem")) {
|
||||||
|
type = Length::Type::Rem;
|
||||||
|
value = try_parse_float(view.substring_view(0, view.length() - 3));
|
||||||
|
} else if (view.to_string().to_lowercase().ends_with("em")) {
|
||||||
|
type = Length::Type::Em;
|
||||||
|
value = try_parse_float(view.substring_view(0, view.length() - 2));
|
||||||
|
} else if (view == "0") {
|
||||||
|
type = Length::Type::Px;
|
||||||
|
value = 0;
|
||||||
|
} else if (context.in_quirks_mode()) {
|
||||||
|
type = 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 Length(value.value(), type);
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<StyleValue> parse_css_value(const CSS::ParsingContext& context, const StringView& string)
|
||||||
|
{
|
||||||
|
bool is_bad_length = false;
|
||||||
|
auto length = parse_length(context, string, is_bad_length);
|
||||||
|
if (is_bad_length)
|
||||||
|
return nullptr;
|
||||||
|
if (!length.is_undefined())
|
||||||
|
return LengthStyleValue::create(length);
|
||||||
|
|
||||||
if (string.equals_ignoring_case("inherit"))
|
if (string.equals_ignoring_case("inherit"))
|
||||||
return InheritStyleValue::create();
|
return InheritStyleValue::create();
|
||||||
if (string.equals_ignoring_case("initial"))
|
if (string.equals_ignoring_case("initial"))
|
||||||
|
@ -281,7 +337,7 @@ NonnullRefPtr<StyleValue> parse_css_value(const StringView& string)
|
||||||
if (string.equals_ignoring_case("auto"))
|
if (string.equals_ignoring_case("auto"))
|
||||||
return LengthStyleValue::create(Length::make_auto());
|
return LengthStyleValue::create(Length::make_auto());
|
||||||
|
|
||||||
auto color = parse_css_color(string);
|
auto color = parse_css_color(context, string);
|
||||||
if (color.has_value())
|
if (color.has_value())
|
||||||
return ColorStyleValue::create(color.value());
|
return ColorStyleValue::create(color.value());
|
||||||
|
|
||||||
|
@ -295,26 +351,26 @@ NonnullRefPtr<StyleValue> parse_css_value(const StringView& string)
|
||||||
return StringStyleValue::create(string);
|
return StringStyleValue::create(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<LengthStyleValue> parse_line_width(const StringView& part)
|
RefPtr<LengthStyleValue> parse_line_width(const CSS::ParsingContext& context, const StringView& part)
|
||||||
{
|
{
|
||||||
NonnullRefPtr<StyleValue> value = parse_css_value(part);
|
auto value = parse_css_value(context, part);
|
||||||
if (value->is_length())
|
if (value && value->is_length())
|
||||||
return static_ptr_cast<LengthStyleValue>(value);
|
return static_ptr_cast<LengthStyleValue>(value);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<ColorStyleValue> parse_color(const StringView& part)
|
RefPtr<ColorStyleValue> parse_color(const CSS::ParsingContext& context, const StringView& part)
|
||||||
{
|
{
|
||||||
NonnullRefPtr<StyleValue> value = parse_css_value(part);
|
auto value = parse_css_value(context, part);
|
||||||
if (value->is_color())
|
if (value && value->is_color())
|
||||||
return static_ptr_cast<ColorStyleValue>(value);
|
return static_ptr_cast<ColorStyleValue>(value);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<StringStyleValue> parse_line_style(const StringView& part)
|
RefPtr<StringStyleValue> parse_line_style(const CSS::ParsingContext& context, const StringView& part)
|
||||||
{
|
{
|
||||||
NonnullRefPtr<StyleValue> parsed_value = parse_css_value(part);
|
auto parsed_value = parse_css_value(context, part);
|
||||||
if (!parsed_value->is_string())
|
if (!parsed_value || !parsed_value->is_string())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
auto value = static_ptr_cast<StringStyleValue>(parsed_value);
|
auto value = static_ptr_cast<StringStyleValue>(parsed_value);
|
||||||
if (value->to_string() == "dotted")
|
if (value->to_string() == "dotted")
|
||||||
|
@ -334,8 +390,9 @@ RefPtr<StringStyleValue> parse_line_style(const StringView& part)
|
||||||
|
|
||||||
class CSSParser {
|
class CSSParser {
|
||||||
public:
|
public:
|
||||||
CSSParser(const StringView& input)
|
CSSParser(const CSS::ParsingContext& context, const StringView& input)
|
||||||
: css(input)
|
: m_context(context)
|
||||||
|
, css(input)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -769,7 +826,10 @@ public:
|
||||||
if (property_id == CSS::PropertyID::Invalid) {
|
if (property_id == CSS::PropertyID::Invalid) {
|
||||||
dbg() << "CSSParser: Unrecognized property '" << property_name << "'";
|
dbg() << "CSSParser: Unrecognized property '" << property_name << "'";
|
||||||
}
|
}
|
||||||
return StyleProperty { property_id, parse_css_value(property_value), important };
|
auto value = parse_css_value(m_context, property_value);
|
||||||
|
if (!value)
|
||||||
|
return {};
|
||||||
|
return StyleProperty { property_id, value.release_nonnull(), important };
|
||||||
}
|
}
|
||||||
|
|
||||||
void parse_declaration()
|
void parse_declaration()
|
||||||
|
@ -841,6 +901,8 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
CSS::ParsingContext m_context;
|
||||||
|
|
||||||
NonnullRefPtrVector<StyleRule> rules;
|
NonnullRefPtrVector<StyleRule> rules;
|
||||||
|
|
||||||
struct CurrentRule {
|
struct CurrentRule {
|
||||||
|
@ -856,25 +918,25 @@ private:
|
||||||
StringView css;
|
StringView css;
|
||||||
};
|
};
|
||||||
|
|
||||||
Optional<Selector> parse_selector(const StringView& selector_text)
|
Optional<Selector> parse_selector(const CSS::ParsingContext& context, const StringView& selector_text)
|
||||||
{
|
{
|
||||||
CSSParser parser(selector_text);
|
CSSParser parser(context, selector_text);
|
||||||
return parser.parse_individual_selector();
|
return parser.parse_individual_selector();
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<StyleSheet> parse_css(const StringView& css)
|
RefPtr<StyleSheet> parse_css(const CSS::ParsingContext& context, const StringView& css)
|
||||||
{
|
{
|
||||||
if (css.is_empty())
|
if (css.is_empty())
|
||||||
return StyleSheet::create({});
|
return StyleSheet::create({});
|
||||||
CSSParser parser(css);
|
CSSParser parser(context, css);
|
||||||
return parser.parse_sheet();
|
return parser.parse_sheet();
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<StyleDeclaration> parse_css_declaration(const StringView& css)
|
RefPtr<StyleDeclaration> parse_css_declaration(const CSS::ParsingContext& context, const StringView& css)
|
||||||
{
|
{
|
||||||
if (css.is_empty())
|
if (css.is_empty())
|
||||||
return StyleDeclaration::create({});
|
return StyleDeclaration::create({});
|
||||||
CSSParser parser(css);
|
CSSParser parser(context, css);
|
||||||
return parser.parse_standalone_declaration();
|
return parser.parse_standalone_declaration();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,13 +31,26 @@
|
||||||
|
|
||||||
namespace Web {
|
namespace Web {
|
||||||
|
|
||||||
RefPtr<StyleSheet> parse_css(const StringView&);
|
namespace CSS {
|
||||||
RefPtr<StyleDeclaration> parse_css_declaration(const StringView&);
|
class ParsingContext {
|
||||||
NonnullRefPtr<StyleValue> parse_css_value(const StringView&);
|
public:
|
||||||
Optional<Selector> parse_selector(const StringView&);
|
ParsingContext();
|
||||||
|
explicit ParsingContext(const Document&);
|
||||||
|
|
||||||
RefPtr<LengthStyleValue> parse_line_width(const StringView&);
|
bool in_quirks_mode() const;
|
||||||
RefPtr<ColorStyleValue> parse_color(const StringView&);
|
|
||||||
RefPtr<StringStyleValue> parse_line_style(const StringView&);
|
private:
|
||||||
|
const Document* m_document { nullptr };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<StyleSheet> parse_css(const CSS::ParsingContext&, const StringView&);
|
||||||
|
RefPtr<StyleDeclaration> parse_css_declaration(const CSS::ParsingContext&, const StringView&);
|
||||||
|
RefPtr<StyleValue> parse_css_value(const CSS::ParsingContext&, const StringView&);
|
||||||
|
Optional<Selector> parse_selector(const CSS::ParsingContext&, const StringView&);
|
||||||
|
|
||||||
|
RefPtr<LengthStyleValue> parse_line_width(const CSS::ParsingContext&, const StringView&);
|
||||||
|
RefPtr<ColorStyleValue> parse_color(const CSS::ParsingContext&, const StringView&);
|
||||||
|
RefPtr<StringStyleValue> parse_line_style(const CSS::ParsingContext&, const StringView&);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue