mirror of
https://github.com/RGBCube/serenity
synced 2025-05-23 18:35:07 +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
|
@ -27,6 +27,7 @@
|
|||
#include <AK/HashMap.h>
|
||||
#include <LibWeb/CSS/PropertyID.h>
|
||||
#include <LibWeb/CSS/StyleSheet.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/Parser/CSSParser.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
|
@ -46,6 +47,24 @@
|
|||
|
||||
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)
|
||||
{
|
||||
if (string == "desktop-background")
|
||||
|
@ -160,7 +179,7 @@ static CSS::ValueID value_id_for_palette_string(const StringView& string)
|
|||
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"))
|
||||
return Color::from_rgba(0x00000000);
|
||||
|
@ -245,35 +264,72 @@ static Optional<float> try_parse_float(const StringView& string)
|
|||
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('%'))
|
||||
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()" ?
|
||||
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"))
|
||||
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"))
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
if (number.has_value()) {
|
||||
if (string.ends_with('%'))
|
||||
return LengthStyleValue::create(Length(number.value(), Length::Type::Percentage));
|
||||
if (string.ends_with("em"))
|
||||
return LengthStyleValue::create(Length(number.value(), Length::Type::Em));
|
||||
if (string.ends_with("rem"))
|
||||
return LengthStyleValue::create(Length(number.value(), Length::Type::Rem));
|
||||
return LengthStyleValue::create(Length(number.value(), Length::Type::Px));
|
||||
Length::Type type = Length::Type::Undefined;
|
||||
Optional<float> value;
|
||||
|
||||
if (view.ends_with('%')) {
|
||||
type = Length::Type::Percentage;
|
||||
value = try_parse_float(view.substring_view(0, view.length() - 1));
|
||||
} else if (view.to_string().to_lowercase().ends_with("px")) {
|
||||
type = 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"))
|
||||
return InheritStyleValue::create();
|
||||
if (string.equals_ignoring_case("initial"))
|
||||
|
@ -281,7 +337,7 @@ NonnullRefPtr<StyleValue> parse_css_value(const StringView& string)
|
|||
if (string.equals_ignoring_case("auto"))
|
||||
return LengthStyleValue::create(Length::make_auto());
|
||||
|
||||
auto color = parse_css_color(string);
|
||||
auto color = parse_css_color(context, string);
|
||||
if (color.has_value())
|
||||
return ColorStyleValue::create(color.value());
|
||||
|
||||
|
@ -295,26 +351,26 @@ NonnullRefPtr<StyleValue> parse_css_value(const StringView& 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);
|
||||
if (value->is_length())
|
||||
auto value = parse_css_value(context, part);
|
||||
if (value && value->is_length())
|
||||
return static_ptr_cast<LengthStyleValue>(value);
|
||||
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);
|
||||
if (value->is_color())
|
||||
auto value = parse_css_value(context, part);
|
||||
if (value && value->is_color())
|
||||
return static_ptr_cast<ColorStyleValue>(value);
|
||||
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);
|
||||
if (!parsed_value->is_string())
|
||||
auto parsed_value = parse_css_value(context, part);
|
||||
if (!parsed_value || !parsed_value->is_string())
|
||||
return nullptr;
|
||||
auto value = static_ptr_cast<StringStyleValue>(parsed_value);
|
||||
if (value->to_string() == "dotted")
|
||||
|
@ -334,8 +390,9 @@ RefPtr<StringStyleValue> parse_line_style(const StringView& part)
|
|||
|
||||
class CSSParser {
|
||||
public:
|
||||
CSSParser(const StringView& input)
|
||||
: css(input)
|
||||
CSSParser(const CSS::ParsingContext& context, const StringView& input)
|
||||
: m_context(context)
|
||||
, css(input)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -769,7 +826,10 @@ public:
|
|||
if (property_id == CSS::PropertyID::Invalid) {
|
||||
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()
|
||||
|
@ -841,6 +901,8 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
CSS::ParsingContext m_context;
|
||||
|
||||
NonnullRefPtrVector<StyleRule> rules;
|
||||
|
||||
struct CurrentRule {
|
||||
|
@ -856,25 +918,25 @@ private:
|
|||
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();
|
||||
}
|
||||
|
||||
RefPtr<StyleSheet> parse_css(const StringView& css)
|
||||
RefPtr<StyleSheet> parse_css(const CSS::ParsingContext& context, const StringView& css)
|
||||
{
|
||||
if (css.is_empty())
|
||||
return StyleSheet::create({});
|
||||
CSSParser parser(css);
|
||||
CSSParser parser(context, css);
|
||||
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())
|
||||
return StyleDeclaration::create({});
|
||||
CSSParser parser(css);
|
||||
CSSParser parser(context, css);
|
||||
return parser.parse_standalone_declaration();
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue