Skip to content

Commit

Permalink
LibWeb: Decode linked style sheets before parsing them
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
awesomekling committed Jul 4, 2023
1 parent efad31d commit 5767dd1
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PASS
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.pass {
display: block;
}

div {
display: none;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<head><link rel="stylesheet" href="style-sheet-with-byte-order-mark.css"></head><div class="pass">PASS
42 changes: 33 additions & 9 deletions Userland/Libraries/LibWeb/HTML/HTMLLinkElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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);

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());
}
}

// 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 {
Expand Down

0 comments on commit 5767dd1

Please sign in to comment.