Compare commits

...

9 commits

Author SHA1 Message Date
Gingeh
82a286612f
Merge 7fe49734d7 into d6bcd3fb0b 2024-11-21 09:12:03 +01:00
Shannon Booth
d6bcd3fb0b LibWeb: Make CallbackType take a realm instead of settings object
Some checks are pending
CI / Lagom (false, FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, macos-15, macOS, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, ubuntu-24.04, Linux, GNU) (push) Waiting to run
CI / Lagom (true, NO_FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (macos-14, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (ubuntu-24.04, Linux, Linux-x86_64) (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Push notes / build (push) Waiting to run
In line with the ShadowRealm proposal changes in the WebIDL spec:
webidl#1437 and supporting changes in HTML spec.

This is required for ShadowRealms as they have no relevant settings
object on the shadow realm, so fixes a crash in the QueueingStrategy
test in this commit.
2024-11-20 18:01:21 -07:00
Shannon Booth
d527c5df5d LibWeb: Allow using queuing strategies on globals other than Window
These interfaces are exposed on *, meaning it should work for workers
and our newly added shadow realm global object by being stored on the
universal global scope mixin.
2024-11-20 18:01:21 -07:00
rmg-x
13f349aea2 LibWeb/Fetch: Implement blob range section of scheme fetch specification 2024-11-21 00:26:58 +00:00
rmg-x
84f673515b LibWeb/XHR: Use normalized header value when validating contents
This was not matching specification and caused valid requests to be
rejected.
2024-11-21 00:26:58 +00:00
rmg-x
4e48298414 LibWeb/Fetch: Implement build_content_range(start, end, full_length) 2024-11-21 00:26:58 +00:00
rmg-x
bf5cf720b5 LibWeb/Fetch: Bring parse_single_range_header_value() up to spec
The previous implementation wasn't using the latest specification steps.
2024-11-21 00:26:58 +00:00
Gingeh
7fe49734d7 LibWeb: Improve grid-template-area parsing and serialization 2024-11-20 15:02:48 +11:00
Gingeh
66ea341a61 LibWeb: Move CSS character type definitions to new header 2024-11-20 15:02:48 +11:00
44 changed files with 922 additions and 247 deletions

View file

@ -673,7 +673,7 @@ void queue_mutation_observer_microtask(DOM::Document const& document)
// 4. If records is not empty, then invoke mos callback with « records, mo », and mo. If this throws an exception, catch it, and report the exception.
if (!records.is_empty()) {
auto& callback = mutation_observer->callback();
auto& realm = callback.callback_context->realm();
auto& realm = callback.callback_context;
auto wrapped_records = MUST(JS::Array::create(realm, 0));
for (size_t i = 0; i < records.size(); ++i) {

View file

@ -0,0 +1,73 @@
/*
* Copyright (c) 2024, the Ladybird developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/CharacterTypes.h>
#include <AK/Types.h>
namespace Web::CSS {
// https://www.w3.org/TR/css-syntax-3/#digit
constexpr bool is_digit(u32 code_point)
{
// A code point between U+0030 DIGIT ZERO (0) and U+0039 DIGIT NINE (9) inclusive.
return code_point >= 0x30 && code_point <= 0x39;
}
// https://www.w3.org/TR/css-syntax-3/#hex-digit
constexpr bool is_hex_digit(u32 code_point)
{
// A digit,
// or a code point between U+0041 LATIN CAPITAL LETTER A (A) and U+0046 LATIN CAPITAL LETTER F (F) inclusive,
// or a code point between U+0061 LATIN SMALL LETTER A (a) and U+0066 LATIN SMALL LETTER F (f) inclusive.
return is_digit(code_point) || (code_point >= 0x41 && code_point <= 0x46) || (code_point >= 0x61 && code_point <= 0x66);
}
// https://www.w3.org/TR/css-syntax-3/#ident-start-code-point
constexpr bool is_ident_start_code_point(u32 code_point)
{
// A letter, a non-ASCII code point, or U+005F LOW LINE (_).
// Note: the is_unicode condition is used to reject the Tokenizer's EOF codepoint.
return is_ascii_alpha(code_point) || (!is_ascii(code_point) && is_unicode(code_point)) || code_point == '_';
}
// https://www.w3.org/TR/css-syntax-3/#ident-code-point
constexpr bool is_ident_code_point(u32 code_point)
{
// An ident-start code point, a digit, or U+002D HYPHEN-MINUS (-).
return is_ident_start_code_point(code_point) || is_ascii_digit(code_point) || code_point == '-';
}
// https://www.w3.org/TR/css-syntax-3/#non-printable-code-point
constexpr bool is_non_printable_code_point(u32 code_point)
{
return code_point <= 0x8 || code_point == 0xB || (code_point >= 0xE && code_point <= 0x1F) || code_point == 0x7F;
}
// https://www.w3.org/TR/css-syntax-3/#newline
constexpr inline bool is_newline(u32 code_point)
{
// U+000A LINE FEED.
// Note that U+000D CARRIAGE RETURN and U+000C FORM FEED are not included in this definition,
// as they are converted to U+000A LINE FEED during preprocessing.
return code_point == 0xA;
}
// https://www.w3.org/TR/css-syntax-3/#whitespace
constexpr bool is_whitespace(u32 code_point)
{
// A newline, U+0009 CHARACTER TABULATION, or U+0020 SPACE.
return is_newline(code_point) || code_point == '\t' || code_point == ' ';
}
// https://www.w3.org/TR/css-syntax-3/#maximum-allowed-code-point
constexpr bool is_greater_than_maximum_allowed_code_point(u32 code_point)
{
// The greatest code point defined by Unicode: U+10FFFF.
return code_point > 0x10FFFF;
}
}

View file

@ -22,6 +22,7 @@
#include <LibWeb/CSS/CSSStyleSheet.h>
#include <LibWeb/CSS/CSSStyleValue.h>
#include <LibWeb/CSS/CalculatedOr.h>
#include <LibWeb/CSS/CharacterTypes.h>
#include <LibWeb/CSS/EdgeRect.h>
#include <LibWeb/CSS/MediaList.h>
#include <LibWeb/CSS/Parser/Parser.h>
@ -7585,20 +7586,60 @@ RefPtr<CSSStyleValue> Parser::parse_grid_shorthand_value(TokenStream<ComponentVa
RefPtr<CSSStyleValue> Parser::parse_grid_template_areas_value(TokenStream<ComponentValue>& tokens)
{
// none | <string>+
Vector<Vector<String>> grid_area_rows;
if (auto none = parse_all_as_single_keyword_value(tokens, Keyword::None))
return GridTemplateAreaStyleValue::create(move(grid_area_rows));
return GridTemplateAreaStyleValue::create({});
auto is_full_stop = [](u32 code_point) {
return code_point == '.';
};
auto consume_while = [](Utf8CodePointIterator& code_points, AK::Function<bool(u32)> predicate) {
StringBuilder builder;
while (!code_points.done() && predicate(*code_points)) {
builder.append_code_point(*code_points);
++code_points;
}
return MUST(builder.to_string());
};
Vector<Vector<String>> grid_area_rows;
Optional<size_t> column_count;
auto transaction = tokens.begin_transaction();
while (tokens.has_next_token() && tokens.next_token().is(Token::Type::String)) {
Vector<String> grid_area_columns;
auto const parts = MUST(tokens.consume_a_token().token().string().to_string().split(' '));
for (auto& part : parts) {
grid_area_columns.append(part);
auto string = tokens.consume_a_token().token().string().code_points();
auto code_points = string.begin();
while (!code_points.done()) {
if (is_whitespace(*code_points)) {
consume_while(code_points, is_whitespace);
} else if (is_full_stop(*code_points)) {
consume_while(code_points, *is_full_stop);
grid_area_columns.append("."_string);
} else if (is_ident_code_point(*code_points)) {
auto token = consume_while(code_points, is_ident_code_point);
grid_area_columns.append(move(token));
} else {
return nullptr;
}
}
if (grid_area_columns.is_empty())
return nullptr;
if (column_count.has_value()) {
if (grid_area_columns.size() != column_count)
return nullptr;
} else {
column_count = grid_area_columns.size();
}
grid_area_rows.append(move(grid_area_columns));
}
// FIXME: If a named grid area spans multiple grid cells, but those cells do not form a single filled-in rectangle, the declaration is invalid.
transaction.commit();
return GridTemplateAreaStyleValue::create(grid_area_rows);
}

View file

@ -5,12 +5,12 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/CharacterTypes.h>
#include <AK/Debug.h>
#include <AK/FloatingPointStringConversions.h>
#include <AK/SourceLocation.h>
#include <AK/Vector.h>
#include <LibTextCodec/Decoder.h>
#include <LibWeb/CSS/CharacterTypes.h>
#include <LibWeb/CSS/Parser/Tokenizer.h>
#include <LibWeb/Infra/Strings.h>
@ -35,41 +35,11 @@ static inline bool is_quotation_mark(u32 code_point)
return code_point == 0x22;
}
static inline bool is_greater_than_maximum_allowed_code_point(u32 code_point)
{
return code_point > 0x10FFFF;
}
static inline bool is_low_line(u32 code_point)
{
return code_point == 0x5F;
}
// https://www.w3.org/TR/css-syntax-3/#ident-start-code-point
static inline bool is_ident_start_code_point(u32 code_point)
{
// FIXME: We use !is_ascii() for "non-ASCII code point" in the spec, but it's not quite right -
// it treats EOF as a valid! The spec also lacks a definition of code point. For now, the
// !is_eof() check is a hack, but it should work.
return !is_eof(code_point) && (is_ascii_alpha(code_point) || !is_ascii(code_point) || is_low_line(code_point));
}
static inline bool is_hyphen_minus(u32 code_point)
{
return code_point == 0x2D;
}
// https://www.w3.org/TR/css-syntax-3/#ident-code-point
static inline bool is_ident_code_point(u32 code_point)
{
return is_ident_start_code_point(code_point) || is_ascii_digit(code_point) || is_hyphen_minus(code_point);
}
static inline bool is_non_printable(u32 code_point)
{
return code_point <= 0x8 || code_point == 0xB || (code_point >= 0xE && code_point <= 0x1F) || code_point == 0x7F;
}
static inline bool is_number_sign(u32 code_point)
{
return code_point == 0x23;
@ -110,11 +80,6 @@ static inline bool is_full_stop(u32 code_point)
return code_point == 0x2E;
}
static inline bool is_newline(u32 code_point)
{
return code_point == 0xA;
}
static inline bool is_asterisk(u32 code_point)
{
return code_point == 0x2A;
@ -170,11 +135,6 @@ static inline bool is_closed_curly_bracket(u32 code_point)
return code_point == 0x7D;
}
static inline bool is_whitespace(u32 code_point)
{
return code_point == 0x9 || code_point == 0xA || code_point == 0x20;
}
static inline bool is_percent(u32 code_point)
{
return code_point == 0x25;
@ -400,14 +360,14 @@ u32 Tokenizer::consume_escaped_code_point()
auto input = next_code_point();
// hex digit
if (is_ascii_hex_digit(input)) {
if (is_hex_digit(input)) {
// Consume as many hex digits as possible, but no more than 5.
// Note that this means 1-6 hex digits have been consumed in total.
StringBuilder builder;
builder.append_code_point(input);
size_t counter = 0;
while (is_ascii_hex_digit(peek_code_point()) && counter++ < 5) {
while (is_hex_digit(peek_code_point()) && counter++ < 5) {
builder.append_code_point(next_code_point());
}
@ -517,7 +477,7 @@ Number Tokenizer::consume_a_number()
// 3. While the next input code point is a digit, consume it and append it to repr.
for (;;) {
auto digits = peek_code_point();
if (!is_ascii_digit(digits))
if (!is_digit(digits))
break;
repr.append_code_point(next_code_point());
@ -525,7 +485,7 @@ Number Tokenizer::consume_a_number()
// 4. If the next 2 input code points are U+002E FULL STOP (.) followed by a digit, then:
auto maybe_number = peek_twin();
if (is_full_stop(maybe_number.first) && is_ascii_digit(maybe_number.second)) {
if (is_full_stop(maybe_number.first) && is_digit(maybe_number.second)) {
// 1. Consume them.
// 2. Append them to repr.
repr.append_code_point(next_code_point());
@ -537,7 +497,7 @@ Number Tokenizer::consume_a_number()
// 4. While the next input code point is a digit, consume it and append it to repr.
for (;;) {
auto digit = peek_code_point();
if (!is_ascii_digit(digit))
if (!is_digit(digit))
break;
repr.append_code_point(next_code_point());
@ -549,17 +509,17 @@ Number Tokenizer::consume_a_number()
// or U+002B PLUS SIGN (+), followed by a digit, then:
auto maybe_exp = peek_triplet();
if ((is_E(maybe_exp.first) || is_e(maybe_exp.first))
&& (((is_plus_sign(maybe_exp.second) || is_hyphen_minus(maybe_exp.second)) && is_ascii_digit(maybe_exp.third))
|| (is_ascii_digit(maybe_exp.second)))) {
&& (((is_plus_sign(maybe_exp.second) || is_hyphen_minus(maybe_exp.second)) && is_digit(maybe_exp.third))
|| (is_digit(maybe_exp.second)))) {
// 1. Consume them.
// 2. Append them to repr.
if (is_plus_sign(maybe_exp.second) || is_hyphen_minus(maybe_exp.second)) {
if (is_ascii_digit(maybe_exp.third)) {
if (is_digit(maybe_exp.third)) {
repr.append_code_point(next_code_point());
repr.append_code_point(next_code_point());
repr.append_code_point(next_code_point());
}
} else if (is_ascii_digit(maybe_exp.second)) {
} else if (is_digit(maybe_exp.second)) {
repr.append_code_point(next_code_point());
repr.append_code_point(next_code_point());
}
@ -570,7 +530,7 @@ Number Tokenizer::consume_a_number()
// 4. While the next input code point is a digit, consume it and append it to repr.
for (;;) {
auto digits = peek_code_point();
if (!is_ascii_digit(digits))
if (!is_digit(digits))
break;
repr.append_code_point(next_code_point());
@ -713,7 +673,7 @@ Token Tokenizer::consume_a_url_token()
// U+0027 APOSTROPHE (')
// U+0028 LEFT PARENTHESIS (()
// non-printable code point
if (is_quotation_mark(input) || is_apostrophe(input) || is_left_paren(input) || is_non_printable(input)) {
if (is_quotation_mark(input) || is_apostrophe(input) || is_left_paren(input) || is_non_printable_code_point(input)) {
// This is a parse error. Consume the remnants of a bad url, create a <bad-url-token>, and return it.
log_parse_error();
consume_the_remnants_of_a_bad_url();
@ -855,12 +815,12 @@ bool Tokenizer::would_start_a_number(U32Triplet values)
// U+002D HYPHEN-MINUS (-)
if (is_plus_sign(values.first) || is_hyphen_minus(values.first)) {
// If the second code point is a digit, return true.
if (is_ascii_digit(values.second))
if (is_digit(values.second))
return true;
// Otherwise, if the second code point is a U+002E FULL STOP (.) and the third
// code point is a digit, return true.
if (is_full_stop(values.second) && is_ascii_digit(values.third))
if (is_full_stop(values.second) && is_digit(values.third))
return true;
// Otherwise, return false.
@ -870,10 +830,10 @@ bool Tokenizer::would_start_a_number(U32Triplet values)
// U+002E FULL STOP (.)
if (is_full_stop(values.first))
// If the second code point is a digit, return true. Otherwise, return false.
return is_ascii_digit(values.second);
return is_digit(values.second);
// digit
if (is_ascii_digit(values.first))
if (is_digit(values.first))
// Return true.
return true;
@ -1328,7 +1288,7 @@ Token Tokenizer::consume_a_token()
}
// digit
if (is_ascii_digit(input)) {
if (is_digit(input)) {
dbgln_if(CSS_TOKENIZER_DEBUG, "is digit");
// Reconsume the current input code point, consume a numeric token, and return it.
reconsume_current_input_code_point();

View file

@ -8,6 +8,7 @@
*/
#include "GridTemplateAreaStyleValue.h"
#include <LibWeb/CSS/Serialize.h>
namespace Web::CSS {
@ -23,13 +24,15 @@ String GridTemplateAreaStyleValue::to_string() const
StringBuilder builder;
for (size_t y = 0; y < m_grid_template_area.size(); ++y) {
if (y != 0)
builder.append(' ');
StringBuilder row_builder;
for (size_t x = 0; x < m_grid_template_area[y].size(); ++x) {
builder.appendff("{}", m_grid_template_area[y][x]);
if (x < m_grid_template_area[y].size() - 1)
builder.append(" "sv);
if (x != 0)
row_builder.append(' ');
row_builder.appendff("{}", m_grid_template_area[y][x]);
}
if (y < m_grid_template_area.size() - 1)
builder.append(", "sv);
serialize_a_string(builder, row_builder.string_view());
}
return MUST(builder.to_string());
}

View file

@ -4203,7 +4203,7 @@ void Document::start_intersection_observing_a_lazy_loading_element(Element& elem
// Spec Note: This allows for fetching the image during scrolling, when it does not yet — but is about to — intersect the viewport.
auto options = IntersectionObserver::IntersectionObserverInit {};
auto wrapped_callback = realm.heap().allocate<WebIDL::CallbackType>(callback, Bindings::principal_host_defined_environment_settings_object(realm));
auto wrapped_callback = realm.heap().allocate<WebIDL::CallbackType>(callback, realm);
m_lazy_load_intersection_observer = IntersectionObserver::IntersectionObserver::construct_impl(realm, wrapped_callback, options).release_value_but_fixme_should_propagate_errors();
}

View file

@ -492,7 +492,8 @@ WebIDL::CallbackType* EventTarget::get_current_value_of_event_handler(FlyString
function->set_script_or_module({});
// 12. Set eventHandler's value to the result of creating a Web IDL EventHandler callback function object whose object reference is function and whose callback context is settings object.
event_handler->value = GC::Ptr(realm.heap().allocate<WebIDL::CallbackType>(*function, settings_object));
// FIXME: Update this comment once the ShadowRealm proposal is merged to pass realm.
event_handler->value = GC::Ptr(realm.heap().allocate<WebIDL::CallbackType>(*function, realm));
}
// 4. Return eventHandler's value.
@ -584,7 +585,7 @@ void EventTarget::activate_event_handler(FlyString const& name, HTML::EventHandl
0, "", &realm);
// NOTE: As per the spec, the callback context is arbitrary.
auto callback = realm.heap().allocate<WebIDL::CallbackType>(*callback_function, Bindings::principal_host_defined_environment_settings_object(realm));
auto callback = realm.heap().allocate<WebIDL::CallbackType>(*callback_function, realm);
// 5. Let listener be a new event listener whose type is the event handler event type corresponding to eventHandler and callback is callback.
auto listener = realm.heap().allocate<DOMEventListener>();

View file

@ -860,33 +860,79 @@ WebIDL::ExceptionOr<GC::Ref<PendingResponse>> scheme_fetch(JS::Realm& realm, Inf
auto content_type_header = Infrastructure::Header::from_string_pair("Content-Type"sv, type);
response->header_list()->append(move(content_type_header));
}
// FIXME: 9. Otherwise:
// 9. Otherwise:
else {
// 1. Set responses range-requested flag.
response->set_range_requested(true);
// 2. Let rangeHeader be the result of getting `Range` from requests header list.
auto const range_header = request->header_list()->get("Range"sv.bytes()).value_or(ByteBuffer {});
// 3. Let rangeValue be the result of parsing a single range header value given rangeHeader and true.
auto maybe_range_value = Infrastructure::parse_single_range_header_value(range_header, true);
// 4. If rangeValue is failure, then return a network error.
if (!maybe_range_value.has_value())
return PendingResponse::create(vm, request, Infrastructure::Response::network_error(vm, "Failed to parse single range header value"sv));
// 5. Let (rangeStart, rangeEnd) be rangeValue.
auto& [range_start, range_end] = maybe_range_value.value();
// 6. If rangeStart is null:
// 1. Set rangeStart to fullLength rangeEnd.
// 2. Set rangeEnd to rangeStart + rangeEnd 1.
if (!range_start.has_value()) {
VERIFY(range_end.has_value());
// 1. Set rangeStart to fullLength rangeEnd.
range_start = full_length - *range_end;
// 2. Set rangeEnd to rangeStart + rangeEnd 1.
range_end = *range_start + *range_end - 1;
}
// 7. Otherwise:
// 1. If rangeStart is greater than or equal to fullLength, then return a network error.
// 2. If rangeEnd is null or rangeEnd is greater than or equal to fullLength, then set rangeEnd to fullLength 1.
else {
// 1. If rangeStart is greater than or equal to fullLength, then return a network error.
if (*range_start >= full_length)
return PendingResponse::create(vm, request, Infrastructure::Response::network_error(vm, "rangeStart is greater than or equal to fullLength"sv));
// 2. If rangeEnd is null or rangeEnd is greater than or equal to fullLength, then set rangeEnd to fullLength 1.
if (!range_end.has_value() || *range_end >= full_length)
range_end = full_length - 1;
}
// 8. Let slicedBlob be the result of invoking slice blob given blob, rangeStart, rangeEnd + 1, and type.
auto sliced_blob = TRY(blob->slice(*range_start, *range_end + 1, type));
// 9. Let slicedBodyWithType be the result of safely extracting slicedBlob.
auto sliced_body_with_type = TRY(safely_extract_body(realm, sliced_blob->raw_bytes()));
// 10. Set responses body to slicedBodyWithTypes body.
response->set_body(sliced_body_with_type.body);
// 11. Let serializedSlicedLength be slicedBlobs size, serialized and isomorphic encoded.
// 12. Let contentRange be `bytes `.
// 13. Append rangeStart, serialized and isomorphic encoded, to contentRange.
// 14. Append 0x2D (-) to contentRange.
// 15. Append rangeEnd, serialized and isomorphic encoded to contentRange.
// 16. Append 0x2F (/) to contentRange.
// 17. Append serializedFullLength to contentRange.
// 18. Set responses status to 206.
// 19. Set responses status message to `Partial Content`.
// 20. Set responses header list to « (`Content-Length`, serializedSlicedLength), (`Content-Type`, type), (`Content-Range`, contentRange) ».
return PendingResponse::create(vm, request, Infrastructure::Response::network_error(vm, "Request has a 'blob:' URL with a Content-Range header, which is currently unsupported"sv));
auto serialized_sliced_length = String::number(sliced_blob->size());
// 12. Let contentRange be the result of invoking build a content range given rangeStart, rangeEnd, and fullLength.
auto content_range = Infrastructure::build_content_range(*range_start, *range_end, full_length);
// 13. Set responses status to 206.
response->set_status(206);
// 14. Set responses status message to `Partial Content`.
response->set_status_message(MUST(ByteBuffer::copy("Partial Content"sv.bytes())));
// 15. Set responses header list to «
// (`Content-Length`, serializedSlicedLength),
auto content_length_header = Infrastructure::Header::from_string_pair("Content-Length"sv, serialized_sliced_length);
response->header_list()->append(move(content_length_header));
// (`Content-Type`, type),
auto content_type_header = Infrastructure::Header::from_string_pair("Content-Type"sv, type);
response->header_list()->append(move(content_type_header));
// (`Content-Range`, contentRange) ».
auto content_range_header = Infrastructure::Header::from_string_pair("Content-Range"sv, content_range);
response->header_list()->append(move(content_range_header));
}
// 10. Return response.

View file

@ -32,6 +32,11 @@ inline constexpr Array HTTP_TAB_OR_SPACE_BYTES = {
0x09, 0x20
};
constexpr bool is_http_tab_or_space(u32 const code_point)
{
return code_point == 0x09 || code_point == 0x20;
}
enum class HttpQuotedStringExtractValue {
No,
Yes,

View file

@ -544,8 +544,8 @@ bool is_cors_safelisted_request_header(Header const& header)
}
// `range`
else if (name.equals_ignoring_ascii_case("range"sv)) {
// 1. Let rangeValue be the result of parsing a single range header value given value.
auto range_value = parse_single_range_header_value(value);
// 1. Let rangeValue be the result of parsing a single range header value given value and false.
auto range_value = parse_single_range_header_value(value, false);
// 2. If rangeValue is failure, then return false.
if (!range_value.has_value())
@ -832,51 +832,84 @@ Variant<Vector<ByteBuffer>, ExtractHeaderParseFailure, Empty> extract_header_lis
return values;
}
// https://fetch.spec.whatwg.org/#build-a-content-range
ByteString build_content_range(u64 const& range_start, u64 const& range_end, u64 const& full_length)
{
// 1. Let contentRange be `bytes `.
// 2. Append rangeStart, serialized and isomorphic encoded, to contentRange.
// 3. Append 0x2D (-) to contentRange.
// 4. Append rangeEnd, serialized and isomorphic encoded to contentRange.
// 5. Append 0x2F (/) to contentRange.
// 6. Append fullLength, serialized and isomorphic encoded to contentRange.
// 7. Return contentRange.
return ByteString::formatted("bytes {}-{}/{}", String::number(range_start), String::number(range_end), String::number(full_length));
}
// https://fetch.spec.whatwg.org/#simple-range-header-value
Optional<RangeHeaderValue> parse_single_range_header_value(ReadonlyBytes value)
Optional<RangeHeaderValue> parse_single_range_header_value(ReadonlyBytes const value, bool const allow_whitespace)
{
// 1. Let data be the isomorphic decoding of value.
auto data = Infra::isomorphic_decode(value);
auto const data = Infra::isomorphic_decode(value);
// 2. If data does not start with "bytes=", then return failure.
if (!data.starts_with_bytes("bytes="sv))
// 2. If data does not start with "bytes", then return failure.
if (!data.starts_with_bytes("bytes"sv))
return {};
// 3. Let position be a position variable for data, initially pointing at the 6th code point of data.
// 3. Let position be a position variable for data, initially pointing at the 5th code point of data.
auto lexer = GenericLexer { data };
lexer.ignore(6);
lexer.ignore(5);
// 4. Let rangeStart be the result of collecting a sequence of code points that are ASCII digits, from data given position.
// 4. If allowWhitespace is true, collect a sequence of code points that are HTTP tab or space, from data given position.
if (allow_whitespace)
lexer.consume_while(is_http_tab_or_space);
// 5. If the code point at position within data is not U+003D (=), then return failure.
// 6. Advance position by 1.
if (!lexer.consume_specific('='))
return {};
// 7. If allowWhitespace is true, collect a sequence of code points that are HTTP tab or space, from data given position.
if (allow_whitespace)
lexer.consume_while(is_http_tab_or_space);
// 8. Let rangeStart be the result of collecting a sequence of code points that are ASCII digits, from data given position.
auto range_start = lexer.consume_while(is_ascii_digit);
// 5. Let rangeStartValue be rangeStart, interpreted as decimal number, if rangeStart is not the empty string; otherwise null.
// 9. Let rangeStartValue be rangeStart, interpreted as decimal number, if rangeStart is not the empty string; otherwise null.
auto range_start_value = range_start.to_number<u64>();
// 6. If the code point at position within data is not U+002D (-), then return failure.
// 7. Advance position by 1.
// 10. If allowWhitespace is true, collect a sequence of code points that are HTTP tab or space, from data given position.
if (allow_whitespace)
lexer.consume_while(is_http_tab_or_space);
// 11. If the code point at position within data is not U+002D (-), then return failure.
// 12. Advance position by 1.
if (!lexer.consume_specific('-'))
return {};
// 8. Let rangeEnd be the result of collecting a sequence of code points that are ASCII digits, from data given position.
// 13. If allowWhitespace is true, collect a sequence of code points that are HTTP tab or space, from data given position.
if (allow_whitespace)
lexer.consume_while(is_http_tab_or_space);
// 14. Let rangeEnd be the result of collecting a sequence of code points that are ASCII digits, from data given position.
auto range_end = lexer.consume_while(is_ascii_digit);
// 9. Let rangeEndValue be rangeEnd, interpreted as decimal number, if rangeEnd is not the empty string; otherwise null.
// 15. Let rangeEndValue be rangeEnd, interpreted as decimal number, if rangeEnd is not the empty string; otherwise null.
auto range_end_value = range_end.to_number<u64>();
// 10. If position is not past the end of data, then return failure.
// 16. If position is not past the end of data, then return failure.
if (!lexer.is_eof())
return {};
// 11. If rangeEndValue and rangeStartValue are null, then return failure.
// 17. If rangeEndValue and rangeStartValue are null, then return failure.
if (!range_end_value.has_value() && !range_start_value.has_value())
return {};
// 12. If rangeStartValue and rangeEndValue are numbers, and rangeStartValue is greater than rangeEndValue, then return failure.
// 18. If rangeStartValue and rangeEndValue are numbers, and rangeStartValue is greater than rangeEndValue, then return failure.
if (range_start_value.has_value() && range_end_value.has_value() && *range_start_value > *range_end_value)
return {};
// 13. Return (rangeStartValue, rangeEndValue).
// NOTE: The range end or start can be omitted, e.g., `bytes=0-` or `bytes=-500` are valid ranges.
// 19. Return (rangeStartValue, rangeEndValue).
return RangeHeaderValue { move(range_start_value), move(range_end_value) };
}

View file

@ -93,7 +93,8 @@ struct ExtractHeaderParseFailure {
[[nodiscard]] bool is_request_body_header_name(ReadonlyBytes);
[[nodiscard]] Optional<Vector<ByteBuffer>> extract_header_values(Header const&);
[[nodiscard]] Variant<Vector<ByteBuffer>, ExtractHeaderParseFailure, Empty> extract_header_list_values(ReadonlyBytes, HeaderList const&);
[[nodiscard]] Optional<RangeHeaderValue> parse_single_range_header_value(ReadonlyBytes);
[[nodiscard]] ByteString build_content_range(u64 const& range_start, u64 const& range_end, u64 const& full_length);
[[nodiscard]] Optional<RangeHeaderValue> parse_single_range_header_value(ReadonlyBytes, bool);
[[nodiscard]] ByteBuffer default_user_agent_value();
}

View file

@ -44,6 +44,7 @@ void CustomElementRegistry::visit_edges(Visitor& visitor)
}
// https://webidl.spec.whatwg.org/#es-callback-function
// https://github.com/whatwg/html/pull/9893
static JS::ThrowCompletionOr<GC::Ref<WebIDL::CallbackType>> convert_value_to_callback_function(JS::VM& vm, JS::Value value)
{
// FIXME: De-duplicate this from the IDL generator.
@ -51,8 +52,8 @@ static JS::ThrowCompletionOr<GC::Ref<WebIDL::CallbackType>> convert_value_to_cal
if (!value.is_function())
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAFunction, value.to_string_without_side_effects());
// 2. Return the IDL callback function type value that represents a reference to the same object that V represents, with the incumbent settings object as the callback context.
return vm.heap().allocate<WebIDL::CallbackType>(value.as_object(), HTML::incumbent_settings_object());
// 2. Return the IDL callback function type value that represents a reference to the same object that V represents, with the incumbent realm as the callback context.
return vm.heap().allocate<WebIDL::CallbackType>(value.as_object(), HTML::incumbent_realm());
}
// https://webidl.spec.whatwg.org/#es-sequence

View file

@ -215,7 +215,7 @@ WebIDL::ExceptionOr<void> HTMLDialogElement::show_modal()
return JS::js_undefined();
},
0, "", &realm());
auto cancel_callback = realm().heap().allocate<WebIDL::CallbackType>(*cancel_callback_function, Bindings::principal_host_defined_environment_settings_object(realm()));
auto cancel_callback = realm().heap().allocate<WebIDL::CallbackType>(*cancel_callback_function, realm());
m_close_watcher->add_event_listener_without_options(HTML::EventNames::cancel, DOM::IDLEventListener::create(realm(), cancel_callback));
// - closeAction being to close the dialog given this and null.
auto close_callback_function = JS::NativeFunction::create(
@ -225,7 +225,7 @@ WebIDL::ExceptionOr<void> HTMLDialogElement::show_modal()
return JS::js_undefined();
},
0, "", &realm());
auto close_callback = realm().heap().allocate<WebIDL::CallbackType>(*close_callback_function, Bindings::principal_host_defined_environment_settings_object(realm()));
auto close_callback = realm().heap().allocate<WebIDL::CallbackType>(*close_callback_function, realm());
m_close_watcher->add_event_listener_without_options(HTML::EventNames::close, DOM::IDLEventListener::create(realm(), close_callback));
// FIXME: 16. Set this's previously focused element to the focused element.

View file

@ -899,7 +899,7 @@ void HTMLInputElement::create_text_input_shadow_tree()
return JS::js_undefined();
},
0, "", &realm());
auto mouseup_callback = realm().heap().allocate<WebIDL::CallbackType>(*mouseup_callback_function, Bindings::principal_host_defined_environment_settings_object(realm()));
auto mouseup_callback = realm().heap().allocate<WebIDL::CallbackType>(*mouseup_callback_function, realm());
DOM::AddEventListenerOptions mouseup_listener_options;
mouseup_listener_options.once = true;
@ -912,7 +912,7 @@ void HTMLInputElement::create_text_input_shadow_tree()
return JS::js_undefined();
},
0, "", &realm());
auto step_up_callback = realm().heap().allocate<WebIDL::CallbackType>(*up_callback_function, Bindings::principal_host_defined_environment_settings_object(realm()));
auto step_up_callback = realm().heap().allocate<WebIDL::CallbackType>(*up_callback_function, realm());
up_button->add_event_listener_without_options(UIEvents::EventNames::mousedown, DOM::IDLEventListener::create(realm(), step_up_callback));
up_button->add_event_listener_without_options(UIEvents::EventNames::mouseup, DOM::IDLEventListener::create(realm(), mouseup_callback));
@ -934,7 +934,7 @@ void HTMLInputElement::create_text_input_shadow_tree()
return JS::js_undefined();
},
0, "", &realm());
auto step_down_callback = realm().heap().allocate<WebIDL::CallbackType>(*down_callback_function, Bindings::principal_host_defined_environment_settings_object(realm()));
auto step_down_callback = realm().heap().allocate<WebIDL::CallbackType>(*down_callback_function, realm());
down_button->add_event_listener_without_options(UIEvents::EventNames::mousedown, DOM::IDLEventListener::create(realm(), step_down_callback));
down_button->add_event_listener_without_options(UIEvents::EventNames::mouseup, DOM::IDLEventListener::create(realm(), mouseup_callback));
}
@ -993,7 +993,7 @@ void HTMLInputElement::create_file_input_shadow_tree()
};
auto on_button_click_function = JS::NativeFunction::create(realm, move(on_button_click), 0, "", &realm);
auto on_button_click_callback = realm.heap().allocate<WebIDL::CallbackType>(on_button_click_function, Bindings::principal_host_defined_environment_settings_object(realm));
auto on_button_click_callback = realm.heap().allocate<WebIDL::CallbackType>(on_button_click_function, realm);
m_file_button->add_event_listener_without_options(UIEvents::EventNames::click, DOM::IDLEventListener::create(realm, on_button_click_callback));
update_file_input_shadow_tree();
@ -1065,7 +1065,7 @@ void HTMLInputElement::create_range_input_shadow_tree()
return JS::js_undefined();
},
0, "", &realm());
auto keydown_callback = realm().heap().allocate<WebIDL::CallbackType>(*keydown_callback_function, Bindings::principal_host_defined_environment_settings_object(realm()));
auto keydown_callback = realm().heap().allocate<WebIDL::CallbackType>(*keydown_callback_function, realm());
add_event_listener_without_options(UIEvents::EventNames::keydown, DOM::IDLEventListener::create(realm(), keydown_callback));
auto wheel_callback_function = JS::NativeFunction::create(
@ -1080,7 +1080,7 @@ void HTMLInputElement::create_range_input_shadow_tree()
return JS::js_undefined();
},
0, "", &realm());
auto wheel_callback = realm().heap().allocate<WebIDL::CallbackType>(*wheel_callback_function, Bindings::principal_host_defined_environment_settings_object(realm()));
auto wheel_callback = realm().heap().allocate<WebIDL::CallbackType>(*wheel_callback_function, realm());
add_event_listener_without_options(UIEvents::EventNames::wheel, DOM::IDLEventListener::create(realm(), wheel_callback));
auto update_slider_by_mouse = [this](JS::VM& vm) {
@ -1103,7 +1103,7 @@ void HTMLInputElement::create_range_input_shadow_tree()
return JS::js_undefined();
},
0, "", &realm());
auto mousemove_callback = realm().heap().allocate<WebIDL::CallbackType>(*mousemove_callback_function, Bindings::principal_host_defined_environment_settings_object(realm()));
auto mousemove_callback = realm().heap().allocate<WebIDL::CallbackType>(*mousemove_callback_function, realm());
auto mousemove_listener = DOM::IDLEventListener::create(realm(), mousemove_callback);
auto& window = static_cast<HTML::Window&>(relevant_global_object(*this));
window.add_event_listener_without_options(UIEvents::EventNames::mousemove, mousemove_listener);
@ -1115,7 +1115,7 @@ void HTMLInputElement::create_range_input_shadow_tree()
return JS::js_undefined();
},
0, "", &realm());
auto mouseup_callback = realm().heap().allocate<WebIDL::CallbackType>(*mouseup_callback_function, Bindings::principal_host_defined_environment_settings_object(realm()));
auto mouseup_callback = realm().heap().allocate<WebIDL::CallbackType>(*mouseup_callback_function, realm());
DOM::AddEventListenerOptions mouseup_listener_options;
mouseup_listener_options.once = true;
window.add_event_listener(UIEvents::EventNames::mouseup, DOM::IDLEventListener::create(realm(), mouseup_callback), mouseup_listener_options);
@ -1123,7 +1123,7 @@ void HTMLInputElement::create_range_input_shadow_tree()
return JS::js_undefined();
},
0, "", &realm());
auto mousedown_callback = realm().heap().allocate<WebIDL::CallbackType>(*mousedown_callback_function, Bindings::principal_host_defined_environment_settings_object(realm()));
auto mousedown_callback = realm().heap().allocate<WebIDL::CallbackType>(*mousedown_callback_function, realm());
add_event_listener_without_options(UIEvents::EventNames::mousedown, DOM::IDLEventListener::create(realm(), mousedown_callback));
}

View file

@ -41,6 +41,7 @@ void ShadowRealmGlobalScope::initialize_web_interfaces()
void ShadowRealmGlobalScope::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
UniversalGlobalScopeMixin::visit_edges(visitor);
}
}

View file

@ -12,6 +12,7 @@
#include <AK/Utf8View.h>
#include <AK/Vector.h>
#include <LibGC/Function.h>
#include <LibJS/Runtime/NativeFunction.h>
#include <LibWeb/HTML/Scripting/ExceptionReporter.h>
#include <LibWeb/HTML/StructuredSerialize.h>
#include <LibWeb/HTML/StructuredSerializeOptions.h>
@ -27,6 +28,12 @@ namespace Web::HTML {
UniversalGlobalScopeMixin::~UniversalGlobalScopeMixin() = default;
void UniversalGlobalScopeMixin::visit_edges(GC::Cell::Visitor& visitor)
{
visitor.visit(m_count_queuing_strategy_size_function);
visitor.visit(m_byte_length_queuing_strategy_size_function);
}
// https://html.spec.whatwg.org/multipage/webappapis.html#dom-btoa
WebIDL::ExceptionOr<String> UniversalGlobalScopeMixin::btoa(String const& data) const
{
@ -101,4 +108,52 @@ WebIDL::ExceptionOr<JS::Value> UniversalGlobalScopeMixin::structured_clone(JS::V
return deserialized;
}
// https://streams.spec.whatwg.org/#count-queuing-strategy-size-function
GC::Ref<WebIDL::CallbackType> UniversalGlobalScopeMixin::count_queuing_strategy_size_function()
{
auto& realm = HTML::relevant_realm(this_impl());
if (!m_count_queuing_strategy_size_function) {
// 1. Let steps be the following steps:
auto steps = [](auto const&) {
// 1. Return 1.
return 1.0;
};
// 2. Let F be ! CreateBuiltinFunction(steps, 0, "size", « », globalObjects relevant Realm).
auto function = JS::NativeFunction::create(realm, move(steps), 0, "size", &realm);
// 3. Set globalObjects count queuing strategy size function to a Function that represents a reference to F, with callback context equal to globalObjects relevant settings object.
// FIXME: Update spec comment to pass globalObject's relevant realm once Streams spec is updated for ShadowRealm spec
m_count_queuing_strategy_size_function = realm.create<WebIDL::CallbackType>(*function, realm);
}
return GC::Ref { *m_count_queuing_strategy_size_function };
}
// https://streams.spec.whatwg.org/#byte-length-queuing-strategy-size-function
GC::Ref<WebIDL::CallbackType> UniversalGlobalScopeMixin::byte_length_queuing_strategy_size_function()
{
auto& realm = HTML::relevant_realm(this_impl());
if (!m_byte_length_queuing_strategy_size_function) {
// 1. Let steps be the following steps, given chunk:
auto steps = [](JS::VM& vm) {
auto chunk = vm.argument(0);
// 1. Return ? GetV(chunk, "byteLength").
return chunk.get(vm, vm.names.byteLength);
};
// 2. Let F be ! CreateBuiltinFunction(steps, 1, "size", « », globalObjects relevant Realm).
auto function = JS::NativeFunction::create(realm, move(steps), 1, "size", &realm);
// 3. Set globalObjects byte length queuing strategy size function to a Function that represents a reference to F, with callback context equal to globalObjects relevant settings object.
// FIXME: Update spec comment to pass globalObject's relevant realm once Streams spec is updated for ShadowRealm spec
m_byte_length_queuing_strategy_size_function = realm.create<WebIDL::CallbackType>(*function, realm);
}
return GC::Ref { *m_byte_length_queuing_strategy_size_function };
}
}

View file

@ -28,6 +28,19 @@ public:
WebIDL::ExceptionOr<String> atob(String const& data) const;
void queue_microtask(WebIDL::CallbackType&);
WebIDL::ExceptionOr<JS::Value> structured_clone(JS::Value, StructuredSerializeOptions const&) const;
GC::Ref<WebIDL::CallbackType> count_queuing_strategy_size_function();
GC::Ref<WebIDL::CallbackType> byte_length_queuing_strategy_size_function();
protected:
void visit_edges(GC::Cell::Visitor&);
private:
// https://streams.spec.whatwg.org/#count-queuing-strategy-size-function
GC::Ptr<WebIDL::CallbackType> m_count_queuing_strategy_size_function;
// https://streams.spec.whatwg.org/#byte-length-queuing-strategy-size-function
GC::Ptr<WebIDL::CallbackType> m_byte_length_queuing_strategy_size_function;
};
}

View file

@ -116,6 +116,7 @@ void Window::visit_edges(JS::Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
WindowOrWorkerGlobalScopeMixin::visit_edges(visitor);
UniversalGlobalScopeMixin::visit_edges(visitor);
visitor.visit(m_associated_document);
visitor.visit(m_current_event);
@ -127,8 +128,6 @@ void Window::visit_edges(JS::Cell::Visitor& visitor)
visitor.visit(m_animation_frame_callback_driver);
visitor.visit(m_pdf_viewer_plugin_objects);
visitor.visit(m_pdf_viewer_mime_type_objects);
visitor.visit(m_count_queuing_strategy_size_function);
visitor.visit(m_byte_length_queuing_strategy_size_function);
visitor.visit(m_close_watcher_manager);
}
@ -662,52 +661,6 @@ Vector<GC::Ref<MimeType>> Window::pdf_viewer_mime_type_objects()
return m_pdf_viewer_mime_type_objects;
}
// https://streams.spec.whatwg.org/#count-queuing-strategy-size-function
GC::Ref<WebIDL::CallbackType> Window::count_queuing_strategy_size_function()
{
auto& realm = this->realm();
if (!m_count_queuing_strategy_size_function) {
// 1. Let steps be the following steps:
auto steps = [](auto const&) {
// 1. Return 1.
return 1.0;
};
// 2. Let F be ! CreateBuiltinFunction(steps, 0, "size", « », globalObjects relevant Realm).
auto function = JS::NativeFunction::create(realm, move(steps), 0, "size", &realm);
// 3. Set globalObjects count queuing strategy size function to a Function that represents a reference to F, with callback context equal to globalObjects relevant settings object.
m_count_queuing_strategy_size_function = realm.create<WebIDL::CallbackType>(*function, relevant_settings_object(*this));
}
return GC::Ref { *m_count_queuing_strategy_size_function };
}
// https://streams.spec.whatwg.org/#byte-length-queuing-strategy-size-function
GC::Ref<WebIDL::CallbackType> Window::byte_length_queuing_strategy_size_function()
{
auto& realm = this->realm();
if (!m_byte_length_queuing_strategy_size_function) {
// 1. Let steps be the following steps, given chunk:
auto steps = [](JS::VM& vm) {
auto chunk = vm.argument(0);
// 1. Return ? GetV(chunk, "byteLength").
return chunk.get(vm, vm.names.byteLength);
};
// 2. Let F be ! CreateBuiltinFunction(steps, 1, "size", « », globalObjects relevant Realm).
auto function = JS::NativeFunction::create(realm, move(steps), 1, "size", &realm);
// 3. Set globalObjects byte length queuing strategy size function to a Function that represents a reference to F, with callback context equal to globalObjects relevant settings object.
m_byte_length_queuing_strategy_size_function = realm.create<WebIDL::CallbackType>(*function, relevant_settings_object(*this));
}
return GC::Ref { *m_byte_length_queuing_strategy_size_function };
}
static bool s_inspector_object_exposed = false;
static bool s_internals_object_exposed = false;

View file

@ -141,9 +141,6 @@ public:
CrossOriginPropertyDescriptorMap const& cross_origin_property_descriptor_map() const { return m_cross_origin_property_descriptor_map; }
CrossOriginPropertyDescriptorMap& cross_origin_property_descriptor_map() { return m_cross_origin_property_descriptor_map; }
GC::Ref<WebIDL::CallbackType> count_queuing_strategy_size_function();
GC::Ref<WebIDL::CallbackType> byte_length_queuing_strategy_size_function();
// JS API functions
GC::Ref<WindowProxy> window() const;
GC::Ref<WindowProxy> self() const;
@ -313,12 +310,6 @@ private:
// https://html.spec.whatwg.org/multipage/interaction.html#last-history-action-activation-timestamp
HighResolutionTime::DOMHighResTimeStamp m_last_history_action_activation_timestamp { AK::Infinity<double> };
// https://streams.spec.whatwg.org/#count-queuing-strategy-size-function
GC::Ptr<WebIDL::CallbackType> m_count_queuing_strategy_size_function;
// https://streams.spec.whatwg.org/#byte-length-queuing-strategy-size-function
GC::Ptr<WebIDL::CallbackType> m_byte_length_queuing_strategy_size_function;
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-window-status
// When the Window object is created, the attribute must be set to the empty string. It does not do anything else.
String m_status;

View file

@ -45,6 +45,7 @@ void WorkerGlobalScope::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
WindowOrWorkerGlobalScopeMixin::visit_edges(visitor);
UniversalGlobalScopeMixin::visit_edges(visitor);
visitor.visit(m_location);
visitor.visit(m_navigator);

View file

@ -100,7 +100,7 @@ void ResizeObserver::disconnect()
void ResizeObserver::invoke_callback(ReadonlySpan<GC::Ref<ResizeObserverEntry>> entries) const
{
auto& callback = *m_callback;
auto& realm = callback.callback_context->realm();
auto& realm = callback.callback_context;
auto wrapped_records = MUST(JS::Array::create(realm, 0));
for (size_t i = 0; i < entries.size(); ++i) {

View file

@ -5428,7 +5428,7 @@ JS::ThrowCompletionOr<GC::Root<WebIDL::CallbackType>> property_to_callback(JS::V
if (!property.is_function())
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAFunction, property.to_string_without_side_effects());
return vm.heap().allocate<WebIDL::CallbackType>(property.as_object(), HTML::incumbent_settings_object(), operation_returns_promise);
return vm.heap().allocate<WebIDL::CallbackType>(property.as_object(), HTML::incumbent_realm(), operation_returns_promise);
}
// https://streams.spec.whatwg.org/#set-up-readable-byte-stream-controller-from-underlying-source

View file

@ -7,7 +7,7 @@
#include <LibWeb/Bindings/ByteLengthQueuingStrategyPrototype.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/HTML/Window.h>
#include <LibWeb/HTML/UniversalGlobalScope.h>
#include <LibWeb/Streams/ByteLengthQueuingStrategy.h>
namespace Web::Streams {
@ -34,7 +34,9 @@ ByteLengthQueuingStrategy::~ByteLengthQueuingStrategy() = default;
GC::Ref<WebIDL::CallbackType> ByteLengthQueuingStrategy::size()
{
// 1. Return this's relevant global object's byte length queuing strategy size function.
return verify_cast<HTML::Window>(HTML::relevant_global_object(*this)).byte_length_queuing_strategy_size_function();
auto* global = dynamic_cast<HTML::UniversalGlobalScopeMixin*>(&HTML::relevant_global_object(*this));
VERIFY(global);
return global->byte_length_queuing_strategy_size_function();
}
void ByteLengthQueuingStrategy::initialize(JS::Realm& realm)

View file

@ -7,7 +7,7 @@
#include <LibWeb/Bindings/CountQueuingStrategyPrototype.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/HTML/Window.h>
#include <LibWeb/HTML/UniversalGlobalScope.h>
#include <LibWeb/Streams/CountQueuingStrategy.h>
namespace Web::Streams {
@ -34,7 +34,9 @@ CountQueuingStrategy::~CountQueuingStrategy() = default;
GC::Ref<WebIDL::CallbackType> CountQueuingStrategy::size()
{
// 1. Return this's relevant global object's count queuing strategy size function.
return verify_cast<HTML::Window>(HTML::relevant_global_object(*this)).count_queuing_strategy_size_function();
auto* global = dynamic_cast<HTML::UniversalGlobalScopeMixin*>(&HTML::relevant_global_object(*this));
VERIFY(global);
return global->count_queuing_strategy_size_function();
}
void CountQueuingStrategy::initialize(JS::Realm& realm)

View file

@ -167,9 +167,8 @@ JS::Completion call_user_object_operation(WebIDL::CallbackType& callback, String
// 4. Let relevant realm be Os associated Realm.
auto& relevant_realm = object->shape().realm();
// FIXME: We should get the realm directly from the callback context.
// 5. Let stored realm be values callback context.
auto& stored_realm = callback.callback_context->realm();
auto& stored_realm = callback.callback_context;
// 6. Prepare to run script with relevant realm.
HTML::prepare_to_run_script(relevant_realm);
@ -252,9 +251,8 @@ JS::Completion invoke_callback(WebIDL::CallbackType& callback, Optional<JS::Valu
// 5. Let relevant realm be Fs associated Realm.
auto& relevant_realm = function_object->shape().realm();
// FIXME: We should get the realm directly from the callback context.
// 6. Let stored realm be values callback context.
auto& stored_realm = callback.callback_context->realm();
auto& stored_realm = callback.callback_context;
// 8. Prepare to run script with relevant realm.
HTML::prepare_to_run_script(relevant_realm);
@ -297,9 +295,8 @@ JS::Completion construct(WebIDL::CallbackType& callback, GC::MarkedVector<JS::Va
if (!JS::Value(function_object).is_constructor())
return relevant_realm.vm().template throw_completion<JS::TypeError>(JS::ErrorType::NotAConstructor, JS::Value(function_object).to_string_without_side_effects());
// FIXME: We should get the realm directly from the callback context.
// 4. Let stored realm be callables callback context.
auto& stored_realm = callback.callback_context->realm();
auto& stored_realm = callback.callback_context;
// 5. Prepare to run script with relevant realm.
HTML::prepare_to_run_script(relevant_realm);

View file

@ -12,7 +12,7 @@ namespace Web::WebIDL {
GC_DEFINE_ALLOCATOR(CallbackType);
CallbackType::CallbackType(JS::Object& callback, HTML::EnvironmentSettingsObject& callback_context, OperationReturnsPromise operation_returns_promise)
CallbackType::CallbackType(JS::Object& callback, JS::Realm& callback_context, OperationReturnsPromise operation_returns_promise)
: callback(callback)
, callback_context(callback_context)
, operation_returns_promise(operation_returns_promise)

View file

@ -24,12 +24,13 @@ class CallbackType final : public JS::Cell {
GC_DECLARE_ALLOCATOR(CallbackType);
public:
CallbackType(JS::Object& callback, HTML::EnvironmentSettingsObject& callback_context, OperationReturnsPromise = OperationReturnsPromise::No);
CallbackType(JS::Object& callback, JS::Realm& callback_context, OperationReturnsPromise = OperationReturnsPromise::No);
GC::Ref<JS::Object> callback;
// https://webidl.spec.whatwg.org/#dfn-callback-context
GC::Ref<HTML::EnvironmentSettingsObject> callback_context;
// NOTE: This is a Realm per ShadowRealm proposal https://github.com/whatwg/webidl/pull/1437
GC::Ref<JS::Realm> callback_context;
// Non-standard property used to distinguish Promise-returning callbacks in callback-related AOs
OperationReturnsPromise operation_returns_promise;

View file

@ -433,7 +433,7 @@ WebIDL::ExceptionOr<void> XMLHttpRequest::set_request_header(String const& name_
// 4. If name is not a header name or value is not a header value, then throw a "SyntaxError" DOMException.
if (!Fetch::Infrastructure::is_header_name(name))
return WebIDL::SyntaxError::create(realm, "Header name contains invalid characters."_string);
if (!Fetch::Infrastructure::is_header_value(value))
if (!Fetch::Infrastructure::is_header_value(normalized_value))
return WebIDL::SyntaxError::create(realm, "Header value contains invalid characters."_string);
auto header = Fetch::Infrastructure::Header {

View file

@ -554,7 +554,7 @@ static void generate_to_cpp(SourceGenerator& generator, ParameterType& parameter
if (!@js_name@@js_suffix@.is_object())
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObject, @js_name@@js_suffix@.to_string_without_side_effects());
auto callback_type = vm.heap().allocate<WebIDL::CallbackType>(@js_name@@js_suffix@.as_object(), HTML::incumbent_settings_object());
auto callback_type = vm.heap().allocate<WebIDL::CallbackType>(@js_name@@js_suffix@.as_object(), HTML::incumbent_realm());
@cpp_name@ = TRY(throw_dom_exception_if_needed(vm, [&] { return @cpp_type@::create(realm, *callback_type); }));
}
)~~~");
@ -563,7 +563,7 @@ static void generate_to_cpp(SourceGenerator& generator, ParameterType& parameter
if (!@js_name@@js_suffix@.is_object())
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObject, @js_name@@js_suffix@.to_string_without_side_effects());
auto callback_type = vm.heap().allocate<WebIDL::CallbackType>(@js_name@@js_suffix@.as_object(), HTML::incumbent_settings_object());
auto callback_type = vm.heap().allocate<WebIDL::CallbackType>(@js_name@@js_suffix@.as_object(), HTML::incumbent_realm());
auto @cpp_name@ = adopt_ref(*new @cpp_type@(callback_type));
)~~~");
}
@ -916,16 +916,16 @@ static void generate_to_cpp(SourceGenerator& generator, ParameterType& parameter
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAFunction, @js_name@@js_suffix@.to_string_without_side_effects());
)~~~");
}
// 2. Return the IDL callback function type value that represents a reference to the same object that V represents, with the incumbent settings object as the callback context.
// 2. Return the IDL callback function type value that represents a reference to the same object that V represents, with the incumbent realm as the callback context.
if (parameter.type->is_nullable() || callback_function.is_legacy_treat_non_object_as_null) {
callback_function_generator.append(R"~~~(
GC::Ptr<WebIDL::CallbackType> @cpp_name@;
if (@js_name@@js_suffix@.is_object())
@cpp_name@ = vm.heap().allocate<WebIDL::CallbackType>(@js_name@@js_suffix@.as_object(), HTML::incumbent_settings_object(), @operation_returns_promise@);
@cpp_name@ = vm.heap().allocate<WebIDL::CallbackType>(@js_name@@js_suffix@.as_object(), HTML::incumbent_realm(), @operation_returns_promise@);
)~~~");
} else {
callback_function_generator.append(R"~~~(
auto @cpp_name@ = vm.heap().allocate<WebIDL::CallbackType>(@js_name@@js_suffix@.as_object(), HTML::incumbent_settings_object(), @operation_returns_promise@);
auto @cpp_name@ = vm.heap().allocate<WebIDL::CallbackType>(@js_name@@js_suffix@.as_object(), HTML::incumbent_realm(), @operation_returns_promise@);
)~~~");
}
} else if (parameter.type->name() == "sequence") {
@ -1282,7 +1282,7 @@ static void generate_to_cpp(SourceGenerator& generator, ParameterType& parameter
if (includes_callable) {
union_generator.append(R"~~~(
if (@js_name@@js_suffix@_object.is_function())
return vm.heap().allocate<WebIDL::CallbackType>(@js_name@@js_suffix@.as_function(), HTML::incumbent_settings_object());
return vm.heap().allocate<WebIDL::CallbackType>(@js_name@@js_suffix@.as_function(), HTML::incumbent_realm());
)~~~");
}

View file

@ -0,0 +1,5 @@
CountQueuingStrategy | size1 === size2 -> true
ByteLengthQueuingStrategy | size1 === size2 -> true
CountQueuingStrategy | size1 === size2 -> true
ByteLengthQueuingStrategy | size1 === size2 -> true

View file

@ -0,0 +1,56 @@
Summary
Harness status: OK
Rerun
Found 45 tests
36 Pass
9 Fail
Details
Result Test Name MessagePass 'grid' with: grid-template-areas: none;
Pass 'grid' with: grid-template-areas: "a";
Pass 'grid' with: grid-template-areas: ".";
Pass 'grid' with: grid-template-areas: "lower UPPER 10 -minus _low 1-st ©copy_right line¶";
Pass 'grid' with: grid-template-areas: "a b";
Pass 'grid' with: grid-template-areas: "a b" "c d";
Pass 'grid' with: grid-template-areas: "a b" "c d";
Pass 'grid' with: grid-template-areas: "a b""c d";
Pass 'grid' with: grid-template-areas: "a b" "c d";
Pass 'grid' with: grid-template-areas: "a b" "c d";
Pass 'grid' with: grid-template-areas: "a b" "a b";
Pass 'grid' with: grid-template-areas: "a a" "b b";
Pass 'grid' with: grid-template-areas: ". a ." "b a c";
Pass 'grid' with: grid-template-areas: ".. a ..." "b a c";
Pass 'grid' with: grid-template-areas: ".a..." "b a c";
Pass 'grid' with: grid-template-areas: "head head" "nav main" "foot .";
Pass 'grid' with: grid-template-areas: "head head" "nav main" "foot ....";
Pass 'grid' with: grid-template-areas: "head head" "nav main" "foot.";
Pass 'grid' with: grid-template-areas: ". header header ." "nav main main main" "nav footer footer .";
Pass 'grid' with: grid-template-areas: "... header header ...." "nav main main main" "nav footer footer ....";
Pass 'grid' with: grid-template-areas: "...header header...." "nav main main main" "nav footer footer....";
Pass 'grid' with: grid-template-areas: "title stats" "score stats" "board board" "ctrls ctrls";
Pass 'grid' with: grid-template-areas: "title board" "stats board" "score ctrls";
Pass 'grid' with: grid-template-areas: ". a" "b a" ". a";
Pass 'grid' with: grid-template-areas: ".. a" "b a" "... a";
Pass 'grid' with: grid-template-areas: "..a" "b a" ".a";
Pass 'grid' with: grid-template-areas: "a a a" "b b b";
Pass 'grid' with: grid-template-areas: ". ." "a a";
Pass 'grid' with: grid-template-areas: "... ...." "a a";
Pass 'grid' with: grid-template-areas: a;
Pass 'grid' with: grid-template-areas: "a" "b c";
Pass 'grid' with: grid-template-areas: "a b" "c" "d e";
Pass 'grid' with: grid-template-areas: "a b c" "d e";
Pass 'grid' with: grid-template-areas: "a b"-"c d";
Pass 'grid' with: grid-template-areas: "a b" - "c d";
Pass 'grid' with: grid-template-areas: "a b" . "c d";
Fail 'grid' with: grid-template-areas: "a b a";
Fail 'grid' with: grid-template-areas: "a" "b" "a";
Fail 'grid' with: grid-template-areas: "a b" "b b";
Fail 'grid' with: grid-template-areas: "b a" "b b";
Fail 'grid' with: grid-template-areas: "a b" "b a";
Fail 'grid' with: grid-template-areas: "a ." ". a";
Fail 'grid' with: grid-template-areas: ",";
Fail 'grid' with: grid-template-areas: "10%";
Fail 'grid' with: grid-template-areas: "USD$";

View file

@ -6,15 +6,15 @@ Rerun
Found 121 tests
26 Pass
95 Fail
31 Pass
90 Fail
Details
Result Test Name MessageFail e.style.cssText = grid: auto-flow auto / 100px 100px should set grid
Fail e.style.cssText = grid: auto-flow auto / 100px 100px should set grid-template-areas
Pass e.style.cssText = grid: auto-flow auto / 100px 100px; grid-template-areas: "one two" "three four" should set grid
Fail e.style.cssText = grid: auto-flow auto / 100px 100px; grid-template-areas: "one two" "three four" should set grid-auto-flow
Fail e.style.cssText = grid: auto-flow auto / 100px 100px; grid-template-areas: "one two" "three four" should set grid-auto-rows
Fail e.style.cssText = grid: auto-flow auto / 100px 100px; grid-template-areas: "one two" "three four" should set grid-template-areas
Pass e.style.cssText = grid: auto-flow auto / 100px 100px; grid-template-areas: "one two" "three four" should set grid-template-areas
Fail e.style.cssText = grid: 30px 40px / 50px 60px; grid-auto-flow: column should set grid
Pass e.style.cssText = grid: 30px 40px / 50px 60px; grid-auto-flow: column should set grid-auto-flow
Fail e.style.cssText = grid: 30px 40px / 50px 60px; grid-auto-flow: column should set grid-template
@ -115,18 +115,18 @@ Fail e.style.cssText = grid: auto-flow 1px / none; grid-template-rows: repeat(au
Fail e.style.cssText = grid: auto-flow 1px / none; grid-template-rows: repeat(auto-fit, 3px) should set grid-template-columns
Fail e.style.cssText = grid: auto-flow 1px / none; grid-template-rows: repeat(auto-fit, 3px) should set grid-template-rows
Fail e.style.cssText = grid-template-rows: auto auto; grid-template-columns: repeat(2, 3px); grid-template-areas: "one two" "three four" should set grid
Fail e.style.cssText = grid-template-rows: auto auto; grid-template-columns: repeat(2, 3px); grid-template-areas: "one two" "three four" should set grid-template-areas
Pass e.style.cssText = grid-template-rows: auto auto; grid-template-columns: repeat(2, 3px); grid-template-areas: "one two" "three four" should set grid-template-areas
Pass e.style.cssText = grid-template-rows: auto auto; grid-template-columns: repeat(2, 3px); grid-template-areas: "one two" "three four" should set grid-template-columns
Pass e.style.cssText = grid-template-rows: auto auto; grid-template-columns: repeat(2, 3px); grid-template-areas: "one two" "three four" should set grid-template-rows
Fail e.style.cssText = grid-template-rows: auto auto; grid-template-columns: repeat(2, 1fr); grid-template-areas: "one two" "three four" should set grid
Fail e.style.cssText = grid-template-rows: auto auto; grid-template-columns: repeat(2, 1fr); grid-template-areas: "one two" "three four" should set grid-template-areas
Pass e.style.cssText = grid-template-rows: auto auto; grid-template-columns: repeat(2, 1fr); grid-template-areas: "one two" "three four" should set grid-template-areas
Pass e.style.cssText = grid-template-rows: auto auto; grid-template-columns: repeat(2, 1fr); grid-template-areas: "one two" "three four" should set grid-template-columns
Pass e.style.cssText = grid-template-rows: auto auto; grid-template-columns: repeat(2, 1fr); grid-template-areas: "one two" "three four" should set grid-template-rows
Fail e.style.cssText = grid-template-rows: auto auto; grid-template-columns: repeat(auto-fill, 3px); grid-template-areas: "one two" "three four" should set grid
Fail e.style.cssText = grid-template-rows: auto auto; grid-template-columns: repeat(auto-fill, 3px); grid-template-areas: "one two" "three four" should set grid-template-areas
Pass e.style.cssText = grid-template-rows: auto auto; grid-template-columns: repeat(auto-fill, 3px); grid-template-areas: "one two" "three four" should set grid-template-areas
Fail e.style.cssText = grid-template-rows: auto auto; grid-template-columns: repeat(auto-fill, 3px); grid-template-areas: "one two" "three four" should set grid-template-columns
Pass e.style.cssText = grid-template-rows: auto auto; grid-template-columns: repeat(auto-fill, 3px); grid-template-areas: "one two" "three four" should set grid-template-rows
Fail e.style.cssText = grid-template-rows: auto auto; grid-template-columns: repeat(auto-fit, 3px); grid-template-areas: "one two" "three four" should set grid
Fail e.style.cssText = grid-template-rows: auto auto; grid-template-columns: repeat(auto-fit, 3px); grid-template-areas: "one two" "three four" should set grid-template-areas
Pass e.style.cssText = grid-template-rows: auto auto; grid-template-columns: repeat(auto-fit, 3px); grid-template-areas: "one two" "three four" should set grid-template-areas
Fail e.style.cssText = grid-template-rows: auto auto; grid-template-columns: repeat(auto-fit, 3px); grid-template-areas: "one two" "three four" should set grid-template-columns
Pass e.style.cssText = grid-template-rows: auto auto; grid-template-columns: repeat(auto-fit, 3px); grid-template-areas: "one two" "three four" should set grid-template-rows

View file

@ -6,8 +6,8 @@ Rerun
Found 63 tests
18 Pass
45 Fail
21 Pass
42 Fail
Details
Result Test Name MessageFail e.style['grid'] = "none" should set grid-auto-columns
Fail e.style['grid'] = "none" should set grid-auto-flow
@ -40,21 +40,21 @@ Pass e.style['grid'] = "fit-content(calc(-0.5em + 10px)) / fit-content(calc(0.5e
Fail e.style['grid'] = "[header-top] \"a a a\" [header-bottom] [main-top] \"b b b\" 1fr [main-bottom] / auto 1fr auto" should set grid-auto-columns
Fail e.style['grid'] = "[header-top] \"a a a\" [header-bottom] [main-top] \"b b b\" 1fr [main-bottom] / auto 1fr auto" should set grid-auto-flow
Fail e.style['grid'] = "[header-top] \"a a a\" [header-bottom] [main-top] \"b b b\" 1fr [main-bottom] / auto 1fr auto" should set grid-auto-rows
Fail e.style['grid'] = "[header-top] \"a a a\" [header-bottom] [main-top] \"b b b\" 1fr [main-bottom] / auto 1fr auto" should set grid-template-areas
Pass e.style['grid'] = "[header-top] \"a a a\" [header-bottom] [main-top] \"b b b\" 1fr [main-bottom] / auto 1fr auto" should set grid-template-areas
Pass e.style['grid'] = "[header-top] \"a a a\" [header-bottom] [main-top] \"b b b\" 1fr [main-bottom] / auto 1fr auto" should set grid-template-columns
Fail e.style['grid'] = "[header-top] \"a a a\" [header-bottom] [main-top] \"b b b\" 1fr [main-bottom] / auto 1fr auto" should set grid-template-rows
Pass e.style['grid'] = "[header-top] \"a a a\" [header-bottom] [main-top] \"b b b\" 1fr [main-bottom] / auto 1fr auto" should not set unrelated longhands
Fail e.style['grid'] = " \"a a a\" \"b b b\" 1fr/ auto 1fr auto" should set grid-auto-columns
Fail e.style['grid'] = " \"a a a\" \"b b b\" 1fr/ auto 1fr auto" should set grid-auto-flow
Fail e.style['grid'] = " \"a a a\" \"b b b\" 1fr/ auto 1fr auto" should set grid-auto-rows
Fail e.style['grid'] = " \"a a a\" \"b b b\" 1fr/ auto 1fr auto" should set grid-template-areas
Pass e.style['grid'] = " \"a a a\" \"b b b\" 1fr/ auto 1fr auto" should set grid-template-areas
Pass e.style['grid'] = " \"a a a\" \"b b b\" 1fr/ auto 1fr auto" should set grid-template-columns
Fail e.style['grid'] = " \"a a a\" \"b b b\" 1fr/ auto 1fr auto" should set grid-template-rows
Pass e.style['grid'] = " \"a a a\" \"b b b\" 1fr/ auto 1fr auto" should not set unrelated longhands
Fail e.style['grid'] = " [] \"a a a\" [] [] \"b b b\" 1fr [] / [] auto 1fr [] auto []" should set grid-auto-columns
Fail e.style['grid'] = " [] \"a a a\" [] [] \"b b b\" 1fr [] / [] auto 1fr [] auto []" should set grid-auto-flow
Fail e.style['grid'] = " [] \"a a a\" [] [] \"b b b\" 1fr [] / [] auto 1fr [] auto []" should set grid-auto-rows
Fail e.style['grid'] = " [] \"a a a\" [] [] \"b b b\" 1fr [] / [] auto 1fr [] auto []" should set grid-template-areas
Pass e.style['grid'] = " [] \"a a a\" [] [] \"b b b\" 1fr [] / [] auto 1fr [] auto []" should set grid-template-areas
Fail e.style['grid'] = " [] \"a a a\" [] [] \"b b b\" 1fr [] / [] auto 1fr [] auto []" should set grid-template-columns
Fail e.style['grid'] = " [] \"a a a\" [] [] \"b b b\" 1fr [] / [] auto 1fr [] auto []" should set grid-template-rows
Pass e.style['grid'] = " [] \"a a a\" [] [] \"b b b\" 1fr [] / [] auto 1fr [] auto []" should not set unrelated longhands

View file

@ -6,15 +6,14 @@ Rerun
Found 9 tests
1 Pass
8 Fail
9 Pass
Details
Result Test Name MessagePass Property grid-template-areas value 'none'
Fail Property grid-template-areas value '"first"'
Fail Property grid-template-areas value '"first second"'
Fail Property grid-template-areas value '"1st 2nd 3rd"'
Fail Property grid-template-areas value '"first second" "third fourth"'
Fail Property grid-template-areas value '"first second" "third ." "1st 2nd" "3rd 4th"'
Fail Property grid-template-areas value '" a b "'
Fail Property grid-template-areas value '"c d"'
Fail Property grid-template-areas value '"first ..."'
Pass Property grid-template-areas value '"first"'
Pass Property grid-template-areas value '"first second"'
Pass Property grid-template-areas value '"1st 2nd 3rd"'
Pass Property grid-template-areas value '"first second" "third fourth"'
Pass Property grid-template-areas value '"first second" "third ." "1st 2nd" "3rd 4th"'
Pass Property grid-template-areas value '" a b "'
Pass Property grid-template-areas value '"c d"'
Pass Property grid-template-areas value '"first ..."'

View file

@ -6,17 +6,16 @@ Rerun
Found 11 tests
6 Pass
5 Fail
11 Pass
Details
Result Test Name MessagePass e.style['grid-template-areas'] = "auto" should not set the property value
Pass e.style['grid-template-areas'] = "none \"first\"" should not set the property value
Pass e.style['grid-template-areas'] = "\"first\" none" should not set the property value
Pass e.style['grid-template-areas'] = "\"\"" should not set the property value
Pass e.style['grid-template-areas'] = "\" \"" should not set the property value
Fail e.style['grid-template-areas'] = "\".\" \"\"" should not set the property value
Fail e.style['grid-template-areas'] = "\".\" \" \"" should not set the property value
Fail e.style['grid-template-areas'] = "\"first\" \"\"" should not set the property value
Fail e.style['grid-template-areas'] = "\"first\" \"\" \"second\"" should not set the property value
Fail e.style['grid-template-areas'] = "\"first\" \" \"" should not set the property value
Pass e.style['grid-template-areas'] = "\".\" \"\"" should not set the property value
Pass e.style['grid-template-areas'] = "\".\" \" \"" should not set the property value
Pass e.style['grid-template-areas'] = "\"first\" \"\"" should not set the property value
Pass e.style['grid-template-areas'] = "\"first\" \"\" \"second\"" should not set the property value
Pass e.style['grid-template-areas'] = "\"first\" \" \"" should not set the property value
Pass e.style['grid-template-areas'] = "\"\" none" should not set the property value

View file

@ -6,12 +6,11 @@ Rerun
Found 6 tests
4 Pass
2 Fail
6 Pass
Details
Result Test Name MessagePass "grid-template-areas: 'a';" should be valid.
Pass "grid-template-areas: '.';" should be valid.
Pass "grid-template-areas: '';" should be invalid.
Fail "grid-template-areas: '' '';" should be invalid.
Fail "grid-template-areas: '$';" should be invalid.
Pass "grid-template-areas: '' '';" should be invalid.
Pass "grid-template-areas: '$';" should be invalid.
Pass "grid-template-areas: ' ';" should be invalid.

View file

@ -6,15 +6,14 @@ Rerun
Found 9 tests
1 Pass
8 Fail
9 Pass
Details
Result Test Name MessagePass e.style['grid-template-areas'] = "none" should set the property value
Fail e.style['grid-template-areas'] = "\"first\"" should set the property value
Fail e.style['grid-template-areas'] = "\"first second\"" should set the property value
Fail e.style['grid-template-areas'] = "\"1st 2nd 3rd\"" should set the property value
Fail e.style['grid-template-areas'] = "\"first second\" \"third fourth\"" should set the property value
Fail e.style['grid-template-areas'] = "\"first second\" \"third .\" \"1st 2nd\" \"3rd 4th\"" should set the property value
Fail e.style['grid-template-areas'] = "\" a \t b \"" should set the property value
Fail e.style['grid-template-areas'] = "\"c\td\"" should set the property value
Fail e.style['grid-template-areas'] = "\"first ...\"" should set the property value
Pass e.style['grid-template-areas'] = "\"first\"" should set the property value
Pass e.style['grid-template-areas'] = "\"first second\"" should set the property value
Pass e.style['grid-template-areas'] = "\"1st 2nd 3rd\"" should set the property value
Pass e.style['grid-template-areas'] = "\"first second\" \"third fourth\"" should set the property value
Pass e.style['grid-template-areas'] = "\"first second\" \"third .\" \"1st 2nd\" \"3rd 4th\"" should set the property value
Pass e.style['grid-template-areas'] = "\" a \t b \"" should set the property value
Pass e.style['grid-template-areas'] = "\"c\td\"" should set the property value
Pass e.style['grid-template-areas'] = "\"first ...\"" should set the property value

View file

@ -6,8 +6,8 @@ Rerun
Found 24 tests
15 Pass
9 Fail
18 Pass
6 Fail
Details
Result Test Name MessagePass e.style['grid-template'] = "none" should set grid-template-areas
Fail e.style['grid-template'] = "none" should set grid-template-columns
@ -21,15 +21,15 @@ Pass e.style['grid-template'] = "fit-content(calc(-0.5em + 10px)) / fit-content(
Pass e.style['grid-template'] = "fit-content(calc(-0.5em + 10px)) / fit-content(calc(0.5em + 10px))" should set grid-template-columns
Pass e.style['grid-template'] = "fit-content(calc(-0.5em + 10px)) / fit-content(calc(0.5em + 10px))" should set grid-template-rows
Pass e.style['grid-template'] = "fit-content(calc(-0.5em + 10px)) / fit-content(calc(0.5em + 10px))" should not set unrelated longhands
Fail e.style['grid-template'] = "[header-top] \"a a a\" [header-bottom] [main-top] \"b b b\" 1fr [main-bottom] / auto 1fr auto" should set grid-template-areas
Pass e.style['grid-template'] = "[header-top] \"a a a\" [header-bottom] [main-top] \"b b b\" 1fr [main-bottom] / auto 1fr auto" should set grid-template-areas
Pass e.style['grid-template'] = "[header-top] \"a a a\" [header-bottom] [main-top] \"b b b\" 1fr [main-bottom] / auto 1fr auto" should set grid-template-columns
Fail e.style['grid-template'] = "[header-top] \"a a a\" [header-bottom] [main-top] \"b b b\" 1fr [main-bottom] / auto 1fr auto" should set grid-template-rows
Pass e.style['grid-template'] = "[header-top] \"a a a\" [header-bottom] [main-top] \"b b b\" 1fr [main-bottom] / auto 1fr auto" should not set unrelated longhands
Fail e.style['grid-template'] = " \"a a a\" \"b b b\" 1fr/ auto 1fr auto" should set grid-template-areas
Pass e.style['grid-template'] = " \"a a a\" \"b b b\" 1fr/ auto 1fr auto" should set grid-template-areas
Pass e.style['grid-template'] = " \"a a a\" \"b b b\" 1fr/ auto 1fr auto" should set grid-template-columns
Fail e.style['grid-template'] = " \"a a a\" \"b b b\" 1fr/ auto 1fr auto" should set grid-template-rows
Pass e.style['grid-template'] = " \"a a a\" \"b b b\" 1fr/ auto 1fr auto" should not set unrelated longhands
Fail e.style['grid-template'] = " [] \"a a a\" [] [] \"b b b\" 1fr [] / [] auto 1fr [] auto []" should set grid-template-areas
Pass e.style['grid-template'] = " [] \"a a a\" [] [] \"b b b\" 1fr [] / [] auto 1fr [] auto []" should set grid-template-areas
Fail e.style['grid-template'] = " [] \"a a a\" [] [] \"b b b\" 1fr [] / [] auto 1fr [] auto []" should set grid-template-columns
Fail e.style['grid-template'] = " [] \"a a a\" [] [] \"b b b\" 1fr [] / [] auto 1fr [] auto []" should set grid-template-rows
Pass e.style['grid-template'] = " [] \"a a a\" [] [] \"b b b\" 1fr [] / [] auto 1fr [] auto []" should not set unrelated longhands

View file

@ -0,0 +1,37 @@
Summary
Harness status: OK
Rerun
Found 27 tests
27 Pass
Details
Result Test Name MessagePass A simple blob range request.
Pass A blob range request with no type.
Pass A blob range request with no end.
Pass A blob range request with no start.
Pass A simple blob range request with whitespace.
Pass Blob content with short content and a large range end
Pass Blob content with short content and a range end matching content length
Pass Blob range with whitespace before and after hyphen
Pass Blob range with whitespace after hyphen
Pass Blob range with whitespace around equals sign
Pass Blob range with no value
Pass Blob range with incorrect range header
Pass Blob range with incorrect range header #2
Pass Blob range with incorrect range header #3
Pass Blob range request with multiple range values
Pass Blob range request with multiple range values and whitespace
Pass Blob range request with trailing comma
Pass Blob range with no start or end
Pass Blob range request with short range end
Pass Blob range start should be an ASCII digit
Pass Blob range should have a dash
Pass Blob range end should be an ASCII digit
Pass Blob range should include '-'
Pass Blob range should include '='
Pass Blob range should include 'bytes='
Pass Blob content with short content and a large range start
Pass Blob content with short content and a range start matching the content length

View file

@ -0,0 +1,25 @@
<script src="../include.js"></script>
<script>
test(() => {
const realm = new ShadowRealm();
const result = realm.evaluate(`
(() => {
let str = "";
for (const QueuingStrategy of [CountQueuingStrategy, ByteLengthQueuingStrategy]) {
const size1 = (new QueuingStrategy({ highWaterMark: 5 })).size;
const size2 = (new QueuingStrategy({ highWaterMark: 10 })).size;
str += \`\${QueuingStrategy.name} | size1 === size2 -> \${size1 === size2}\n\`;
}
return str;
})()
`);
println(result);
for (const QueuingStrategy of [CountQueuingStrategy, ByteLengthQueuingStrategy]) {
const size1 = (new QueuingStrategy({ highWaterMark: 5 })).size;
const size2 = (new QueuingStrategy({ highWaterMark: 10 })).size;
println(`${QueuingStrategy.name} | size1 === size2 -> ${size1 === size2}`);
}
})
</script>

View file

@ -0,0 +1,72 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>CSS Grid Layout Test: Support for 'grid-template-ares' property</title>
<link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com">
<link rel="help" href="http://www.w3.org/TR/css-grid-1/#grid-template-areas-property" title="5.2 Named Areas: the 'grid-template-areas' property">
<meta name="flags" content="ahem dom">
<meta name="assert" content="This test checks that 'grid-template-areas' is supported in a grid. So you can define the grid structure.">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="support/testing-utils.js"></script>
<style>
#grid {
display: grid;
}
</style>
<div id="log"></div>
<div id="grid"></div>
<script>
// Single values.
TestingUtils.testGridTemplateAreas('grid', 'none', 'none');
TestingUtils.testGridTemplateAreas('grid', '"a"', '"a"');
TestingUtils.testGridTemplateAreas('grid', '"."', '"."');
TestingUtils.testGridTemplateAreas('grid', '"lower UPPER 10 -minus _low 1-st ©copy_right line¶"', '"lower UPPER 10 -minus _low 1-st ©copy_right line¶"');
TestingUtils.testGridTemplateAreas('grid', '"a b"', '"a b"');
TestingUtils.testGridTemplateAreas('grid', '"a b" "c d"', '"a b" "c d"');
TestingUtils.testGridTemplateAreas('grid', '"a b" "c d"', '"a b" "c d"');
TestingUtils.testGridTemplateAreas('grid', '"a b""c d"', '"a b" "c d"');
TestingUtils.testGridTemplateAreas('grid', '"a b"\t"c d"', '"a b" "c d"');
TestingUtils.testGridTemplateAreas('grid', '"a b"\n"c d"', '"a b" "c d"');
TestingUtils.testGridTemplateAreas('grid', '"a b" "a b"', '"a b" "a b"');
TestingUtils.testGridTemplateAreas('grid', '"a a" "b b"', '"a a" "b b"');
TestingUtils.testGridTemplateAreas('grid', '". a ." "b a c"', '". a ." "b a c"');
TestingUtils.testGridTemplateAreas('grid', '".. a ..." "b a c"', '". a ." "b a c"');
TestingUtils.testGridTemplateAreas('grid', '".a..." "b a c"', '". a ." "b a c"');
TestingUtils.testGridTemplateAreas('grid', '"head head" "nav main" "foot ."', '"head head" "nav main" "foot ."');
TestingUtils.testGridTemplateAreas('grid', '"head head" "nav main" "foot ...."', '"head head" "nav main" "foot ."');
TestingUtils.testGridTemplateAreas('grid', '"head head" "nav main" "foot."', '"head head" "nav main" "foot ."');
TestingUtils.testGridTemplateAreas('grid', '". header header ." "nav main main main" "nav footer footer ."', '". header header ." "nav main main main" "nav footer footer ."');
TestingUtils.testGridTemplateAreas('grid', '"... header header ...." "nav main main main" "nav footer footer ...."', '". header header ." "nav main main main" "nav footer footer ."');
TestingUtils.testGridTemplateAreas('grid', '"...header header...." "nav main main main" "nav footer footer...."', '". header header ." "nav main main main" "nav footer footer ."');
TestingUtils.testGridTemplateAreas('grid', '"title stats" "score stats" "board board" "ctrls ctrls"', '"title stats" "score stats" "board board" "ctrls ctrls"');
TestingUtils.testGridTemplateAreas('grid', '"title board" "stats board" "score ctrls"', '"title board" "stats board" "score ctrls"');
TestingUtils.testGridTemplateAreas('grid', '". a" "b a" ". a"', '". a" "b a" ". a"');
TestingUtils.testGridTemplateAreas('grid', '".. a" "b a" "... a"', '". a" "b a" ". a"');
TestingUtils.testGridTemplateAreas('grid', '"..a" "b a" ".a"', '". a" "b a" ". a"');
TestingUtils.testGridTemplateAreas('grid', '"a a a" "b b b"', '"a a a" "b b b"');
TestingUtils.testGridTemplateAreas('grid', '". ." "a a"', '". ." "a a"');
TestingUtils.testGridTemplateAreas('grid', '"... ...." "a a"', '". ." "a a"');
// Reset values.
document.getElementById('grid').style.gridTemplateAreas = '';
// Wrong values.
TestingUtils.testGridTemplateAreas('grid', 'a', 'none');
TestingUtils.testGridTemplateAreas('grid', '"a" "b c"', 'none');
TestingUtils.testGridTemplateAreas('grid', '"a b" "c" "d e"', 'none');
TestingUtils.testGridTemplateAreas('grid', '"a b c" "d e"', 'none');
TestingUtils.testGridTemplateAreas('grid', '"a b"-"c d"', 'none');
TestingUtils.testGridTemplateAreas('grid', '"a b" - "c d"', 'none');
TestingUtils.testGridTemplateAreas('grid', '"a b" . "c d"', 'none');
TestingUtils.testGridTemplateAreas('grid', '"a b a"', 'none');
TestingUtils.testGridTemplateAreas('grid', '"a" "b" "a"', 'none');
TestingUtils.testGridTemplateAreas('grid', '"a b" "b b"', 'none');
TestingUtils.testGridTemplateAreas('grid', '"b a" "b b"', 'none');
TestingUtils.testGridTemplateAreas('grid', '"a b" "b a"', 'none');
TestingUtils.testGridTemplateAreas('grid', '"a ." ". a"', 'none');
TestingUtils.testGridTemplateAreas('grid', '","', 'none');
TestingUtils.testGridTemplateAreas('grid', '"10%"', 'none');
TestingUtils.testGridTemplateAreas('grid', '"USD$"', 'none');
</script>

View file

@ -0,0 +1,43 @@
var TestingUtils = (function() {
function checkGridTemplateColumns(element, value) {
if (!Array.isArray(value))
value = new Array(value);
assert_in_array(getComputedStyle(element).gridTemplateColumns, value, "gridTemplateColumns");
}
function checkGridTemplateRows(element, value) {
if (!Array.isArray(value))
value = new Array(value);
assert_in_array(getComputedStyle(element).gridTemplateRows, value, "gridTemplateRows");
}
function testGridTemplateColumnsRows(gridId, columnsStyle, rowsStyle, columnsComputedValue, rowsComputedValue, label) {
test(function() {
var grid = document.getElementById(gridId);
grid.style.gridTemplateColumns = columnsStyle;
grid.style.gridTemplateRows = rowsStyle;
checkGridTemplateColumns(grid, columnsComputedValue);
checkGridTemplateRows(grid, rowsComputedValue);
}, (label ? label + " " : "") + "'" + gridId + "' with: grid-template-columns: " + columnsStyle + "; and grid-template-rows: " + rowsStyle + ";");
}
function checkGridTemplateAreas(element, value) {
if (!Array.isArray(value))
value = new Array(value);
assert_in_array(getComputedStyle(element).gridTemplateAreas, value, "gridTemplateAreas");
}
function testGridTemplateAreas(gridId, style, value) {
test(function() {
var grid = document.getElementById(gridId);
grid.style.gridTemplateAreas = style;
checkGridTemplateAreas(grid, value);
}, "'" + gridId + "' with: grid-template-areas: " + style + ";");
}
return {
testGridTemplateColumnsRows: testGridTemplateColumnsRows,
testGridTemplateAreas: testGridTemplateAreas
}
})();

View file

@ -0,0 +1,15 @@
<!doctype html>
<meta charset=utf-8>
<script>
self.GLOBAL = {
isWindow: function() { return true; },
isWorker: function() { return false; },
isShadowRealm: function() { return false; },
};
</script>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<div id=log></div>
<script src="../xhr/blob-range.any.js"></script>

View file

@ -0,0 +1,246 @@
// See also /fetch/range/blob.any.js
const supportedBlobRange = [
{
name: "A simple blob range request.",
data: ["A simple Hello, World! example"],
type: "text/plain",
range: "bytes=9-21",
content_length: 13,
content_range: "bytes 9-21/30",
result: "Hello, World!",
},
{
name: "A blob range request with no type.",
data: ["A simple Hello, World! example"],
type: undefined,
range: "bytes=9-21",
content_length: 13,
content_range: "bytes 9-21/30",
result: "Hello, World!",
},
{
name: "A blob range request with no end.",
data: ["Range with no end"],
type: "text/plain",
range: "bytes=11-",
content_length: 6,
content_range: "bytes 11-16/17",
result: "no end",
},
{
name: "A blob range request with no start.",
data: ["Range with no start"],
type: "text/plain",
range: "bytes=-8",
content_length: 8,
content_range: "bytes 11-18/19",
result: "no start",
},
{
name: "A simple blob range request with whitespace.",
data: ["A simple Hello, World! example"],
type: "text/plain",
range: "bytes= \t9-21",
content_length: 13,
content_range: "bytes 9-21/30",
result: "Hello, World!",
},
{
name: "Blob content with short content and a large range end",
data: ["Not much here"],
type: "text/plain",
range: "bytes=4-100000000000",
content_length: 9,
content_range: "bytes 4-12/13",
result: "much here",
},
{
name: "Blob content with short content and a range end matching content length",
data: ["Not much here"],
type: "text/plain",
range: "bytes=4-13",
content_length: 9,
content_range: "bytes 4-12/13",
result: "much here",
},
{
name: "Blob range with whitespace before and after hyphen",
data: ["Valid whitespace #1"],
type: "text/plain",
range: "bytes=5 - 10",
content_length: 6,
content_range: "bytes 5-10/19",
result: " white",
},
{
name: "Blob range with whitespace after hyphen",
data: ["Valid whitespace #2"],
type: "text/plain",
range: "bytes=-\t 5",
content_length: 5,
content_range: "bytes 14-18/19",
result: "ce #2",
},
{
name: "Blob range with whitespace around equals sign",
data: ["Valid whitespace #3"],
type: "text/plain",
range: "bytes \t =\t 6-",
content_length: 13,
content_range: "bytes 6-18/19",
result: "whitespace #3",
},
];
const unsupportedBlobRange = [
{
name: "Blob range with no value",
data: ["Blob range should have a value"],
type: "text/plain",
range: "",
},
{
name: "Blob range with incorrect range header",
data: ["A"],
type: "text/plain",
range: "byte=0-"
},
{
name: "Blob range with incorrect range header #2",
data: ["A"],
type: "text/plain",
range: "bytes"
},
{
name: "Blob range with incorrect range header #3",
data: ["A"],
type: "text/plain",
range: "bytes\t \t"
},
{
name: "Blob range request with multiple range values",
data: ["Multiple ranges are not currently supported"],
type: "text/plain",
range: "bytes=0-5,15-",
},
{
name: "Blob range request with multiple range values and whitespace",
data: ["Multiple ranges are not currently supported"],
type: "text/plain",
range: "bytes=0-5, 15-",
},
{
name: "Blob range request with trailing comma",
data: ["Range with invalid trailing comma"],
type: "text/plain",
range: "bytes=0-5,",
},
{
name: "Blob range with no start or end",
data: ["Range with no start or end"],
type: "text/plain",
range: "bytes=-",
},
{
name: "Blob range request with short range end",
data: ["Range end should be greater than range start"],
type: "text/plain",
range: "bytes=10-5",
},
{
name: "Blob range start should be an ASCII digit",
data: ["Range start must be an ASCII digit"],
type: "text/plain",
range: "bytes=x-5",
},
{
name: "Blob range should have a dash",
data: ["Blob range should have a dash"],
type: "text/plain",
range: "bytes=5",
},
{
name: "Blob range end should be an ASCII digit",
data: ["Range end must be an ASCII digit"],
type: "text/plain",
range: "bytes=5-x",
},
{
name: "Blob range should include '-'",
data: ["Range end must include '-'"],
type: "text/plain",
range: "bytes=x",
},
{
name: "Blob range should include '='",
data: ["Range end must include '='"],
type: "text/plain",
range: "bytes 5-",
},
{
name: "Blob range should include 'bytes='",
data: ["Range end must include 'bytes='"],
type: "text/plain",
range: "5-",
},
{
name: "Blob content with short content and a large range start",
data: ["Not much here"],
type: "text/plain",
range: "bytes=100000-",
},
{
name: "Blob content with short content and a range start matching the content length",
data: ["Not much here"],
type: "text/plain",
range: "bytes=13-",
},
];
supportedBlobRange.forEach(({ name, data, type, range, content_length, content_range, result }) => {
promise_test(async t => {
const blob = new Blob(data, { "type" : type });
const blobURL = URL.createObjectURL(blob);
t.add_cleanup(() => URL.revokeObjectURL(blobURL));
const xhr = new XMLHttpRequest();
xhr.open("GET", blobURL);
xhr.responseType = "text";
xhr.setRequestHeader("Range", range);
await new Promise(resolve => {
xhr.onloadend = resolve;
xhr.send();
});
assert_equals(xhr.status, 206, "HTTP status is 206");
assert_equals(xhr.getResponseHeader("Content-Type"), type || "", "Content-Type is " + xhr.getResponseHeader("Content-Type"));
assert_equals(xhr.getResponseHeader("Content-Length"), content_length.toString(), "Content-Length is " + xhr.getResponseHeader("Content-Length"));
assert_equals(xhr.getResponseHeader("Content-Range"), content_range, "Content-Range is " + xhr.getResponseHeader("Content-Range"));
assert_equals(xhr.responseText, result, "Response's body is correct");
const all = xhr.getAllResponseHeaders().toLowerCase();
assert_true(all.includes(`content-type: ${type || ""}`), "Expected Content-Type in getAllResponseHeaders()");
assert_true(all.includes(`content-length: ${content_length}`), "Expected Content-Length in getAllResponseHeaders()");
assert_true(all.includes(`content-range: ${content_range}`), "Expected Content-Range in getAllResponseHeaders()")
}, name);
});
unsupportedBlobRange.forEach(({ name, data, type, range }) => {
promise_test(t => {
const blob = new Blob(data, { "type" : type });
const blobURL = URL.createObjectURL(blob);
t.add_cleanup(() => URL.revokeObjectURL(blobURL));
const xhr = new XMLHttpRequest();
xhr.open("GET", blobURL, false);
xhr.setRequestHeader("Range", range);
assert_throws_dom("NetworkError", () => xhr.send());
xhr.open("GET", blobURL);
xhr.setRequestHeader("Range", range);
xhr.responseType = "text";
return new Promise((resolve, reject) => {
xhr.onload = reject;
xhr.onerror = resolve;
xhr.send();
});
}, name);
});