1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-20 12:15:07 +00:00

LibWeb: Decode linked style sheets before parsing them

This fixes an issue where a BOM at the head of a style sheet would be
passed verbatim to the parser, who would then interpret it as an ident
token and (after some confusion) fail to parse the first rule, but then
carry on with the rest of the sheet.
This commit is contained in:
Andreas Kling 2023-07-04 09:50:47 +02:00
parent 251ea54cc8
commit fb727332f9
4 changed files with 42 additions and 9 deletions

View file

@ -10,6 +10,7 @@
#include <AK/ByteBuffer.h>
#include <AK/Debug.h>
#include <AK/URL.h>
#include <LibTextCodec/Decoder.h>
#include <LibWeb/CSS/Parser/Parser.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/Event.h>
@ -341,18 +342,41 @@ void HTMLLinkElement::process_stylesheet_resource(bool success, Fetch::Infrastru
// The CSS environment encoding is the result of running the following steps: [CSSSYNTAX]
// 1. If the element has a charset attribute, get an encoding from that attribute's value. If that succeeds, return the resulting encoding. [ENCODING]
// 2. Otherwise, return the document's character encoding. [DOM]
m_loaded_style_sheet = parse_css_stylesheet(CSS::Parser::ParsingContext(document(), *response.url()), body_bytes.template get<ByteBuffer>());
if (m_loaded_style_sheet) {
m_loaded_style_sheet->set_owner_node(this);
m_loaded_style_sheet->set_media(attribute(HTML::AttributeNames::media));
document().style_sheets().add_sheet(*m_loaded_style_sheet);
DeprecatedString encoding;
if (auto charset = attribute(HTML::AttributeNames::charset); !charset.is_null())
encoding = charset;
else
encoding = document().encoding_or_default();
auto decoder = TextCodec::decoder_for(encoding);
if (!decoder.has_value()) {
// If we don't support the encoding yet, let's error out instead of trying to decode it as something it's most likely not.
dbgln("FIXME: Style sheet encoding '{}' is not supported yet", encoding);
dispatch_event(*DOM::Event::create(realm(), HTML::EventNames::error).release_value_but_fixme_should_propagate_errors());
} else {
dbgln_if(CSS_LOADER_DEBUG, "HTMLLinkElement: Failed to parse stylesheet: {}", resource()->url());
}
auto const& encoded_string = body_bytes.get<ByteBuffer>();
auto maybe_decoded_string = TextCodec::convert_input_to_utf8_using_given_decoder_unless_there_is_a_byte_order_mark(*decoder, encoded_string);
if (maybe_decoded_string.is_error()) {
dbgln("Style sheet {} claimed to be '{}' but decoding failed", response.url().value_or(AK::URL()), encoding);
dispatch_event(*DOM::Event::create(realm(), HTML::EventNames::error).release_value_but_fixme_should_propagate_errors());
} else {
auto const decoded_string = maybe_decoded_string.release_value();
m_loaded_style_sheet = parse_css_stylesheet(CSS::Parser::ParsingContext(document(), *response.url()), decoded_string);
// 2. Fire an event named load at el.
dispatch_event(*DOM::Event::create(realm(), HTML::EventNames::load).release_value_but_fixme_should_propagate_errors());
if (m_loaded_style_sheet) {
m_loaded_style_sheet->set_owner_node(this);
m_loaded_style_sheet->set_media(attribute(HTML::AttributeNames::media));
document().style_sheets().add_sheet(*m_loaded_style_sheet);
} else {
dbgln_if(CSS_LOADER_DEBUG, "HTMLLinkElement: Failed to parse stylesheet: {}", resource()->url());
}
// 2. Fire an event named load at el.
dispatch_event(*DOM::Event::create(realm(), HTML::EventNames::load).release_value_but_fixme_should_propagate_errors());
}
}
}
// 5. Otherwise, fire an event named error at el.
else {