mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-10-19 07:33:20 +00:00
Compare commits
11 commits
5df216218b
...
9b8f6b8108
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9b8f6b8108 | ||
![]() |
fc9233f198 | ||
![]() |
27cb5d8c1e | ||
![]() |
188384710a | ||
![]() |
9cd0f9c445 | ||
![]() |
b684bc0a9d | ||
![]() |
2af071380e | ||
![]() |
9651969708 | ||
![]() |
3708fc6aa7 | ||
![]() |
29fb63c928 | ||
![]() |
c23ed104e5 |
23 changed files with 300 additions and 55 deletions
5
.github/workflows/ci-flatpak.yml
vendored
5
.github/workflows/ci-flatpak.yml
vendored
|
@ -23,15 +23,16 @@ jobs:
|
|||
&& contains(github.event.pull_request.labels.*.name, 'flatpak')
|
||||
name: Flatpak ${{ matrix.arch }}
|
||||
|
||||
# FIXME: Temporarily run on GitHub runners until Blacksmith runners have overlay redirect_dir enabled.
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
arch: ['x86_64']
|
||||
runner_labels: ['["blacksmith-8vcpu-ubuntu-2404"]']
|
||||
runner_labels: ['["ubuntu-24.04"]']
|
||||
|
||||
include:
|
||||
- arch: 'aarch64'
|
||||
runner_labels: '["blacksmith-8vcpu-ubuntu-2404-arm"]'
|
||||
runner_labels: '["ubuntu-24.04-arm"]'
|
||||
|
||||
secrets: inherit
|
||||
uses: ./.github/workflows/flatpak-template.yml
|
||||
|
|
2
.github/workflows/flatpak-template.yml
vendored
2
.github/workflows/flatpak-template.yml
vendored
|
@ -24,7 +24,5 @@ jobs:
|
|||
with:
|
||||
bundle: Ladybird.flatpak
|
||||
manifest-path: Meta/CMake/flatpak/org.ladybird.Ladybird.json
|
||||
cache: 'true'
|
||||
arch: ${{ inputs.arch }}
|
||||
# Note: default cache key is 'flatpak-builder-${arch}-${sha256(manifestPath)}'
|
||||
upload-artifact: 'true'
|
||||
|
|
6
.github/workflows/nightly-lagom.yml
vendored
6
.github/workflows/nightly-lagom.yml
vendored
|
@ -81,15 +81,17 @@ jobs:
|
|||
flatpak:
|
||||
if: github.repository == 'LadybirdBrowser/ladybird'
|
||||
name: Flatpak ${{ matrix.arch }}
|
||||
|
||||
# FIXME: Temporarily run on GitHub runners until Blacksmith runners have overlay redirect_dir enabled.
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
arch: [ 'x86_64' ]
|
||||
runner_labels: [ '["blacksmith-8vcpu-ubuntu-2404"]' ]
|
||||
runner_labels: [ '["ubuntu-24.04"]' ]
|
||||
|
||||
include:
|
||||
- arch: 'aarch64'
|
||||
runner_labels: '["blacksmith-8vcpu-ubuntu-2404-arm"]'
|
||||
runner_labels: '["ubuntu-24.04-arm"]'
|
||||
|
||||
secrets: inherit
|
||||
uses: ./.github/workflows/flatpak-template.yml
|
||||
|
|
|
@ -157,13 +157,19 @@ ALWAYS_INLINE ThrowCompletionOr<GC::Ref<Object>> construct(VM& vm, FunctionObjec
|
|||
|
||||
// 10.1.13 OrdinaryCreateFromConstructor ( constructor, intrinsicDefaultProto [ , internalSlotsList ] ), https://tc39.es/ecma262/#sec-ordinarycreatefromconstructor
|
||||
template<typename T, typename... Args>
|
||||
ThrowCompletionOr<GC::Ref<T>> ordinary_create_from_constructor(VM& vm, FunctionObject const& constructor, GC::Ref<Object> (Intrinsics::*intrinsic_default_prototype)(), Args&&... args)
|
||||
ALWAYS_INLINE ThrowCompletionOr<GC::Ref<T>> ordinary_create_from_constructor(VM& vm, Realm& realm, FunctionObject const& constructor, GC::Ref<Object> (Intrinsics::*intrinsic_default_prototype)(), Args&&... args)
|
||||
{
|
||||
auto& realm = *vm.current_realm();
|
||||
auto* prototype = TRY(get_prototype_from_constructor(vm, constructor, intrinsic_default_prototype));
|
||||
return realm.create<T>(forward<Args>(args)..., *prototype);
|
||||
}
|
||||
|
||||
// 10.1.13 OrdinaryCreateFromConstructor ( constructor, intrinsicDefaultProto [ , internalSlotsList ] ), https://tc39.es/ecma262/#sec-ordinarycreatefromconstructor
|
||||
template<typename T, typename... Args>
|
||||
ALWAYS_INLINE ThrowCompletionOr<GC::Ref<T>> ordinary_create_from_constructor(VM& vm, FunctionObject const& constructor, GC::Ref<Object> (Intrinsics::*intrinsic_default_prototype)(), Args&&... args)
|
||||
{
|
||||
return ordinary_create_from_constructor<T>(vm, *vm.current_realm(), constructor, intrinsic_default_prototype, forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// 7.3.35 AddValueToKeyedGroup ( groups, key, value ), https://tc39.es/ecma262/#sec-add-value-to-keyed-group
|
||||
template<typename GroupsType, typename KeyType>
|
||||
void add_value_to_keyed_group(VM& vm, GroupsType& groups, KeyType key, Value value)
|
||||
|
|
|
@ -549,7 +549,7 @@ FLATTEN ThrowCompletionOr<Value> ECMAScriptFunctionObject::internal_call(Executi
|
|||
}
|
||||
|
||||
// 10.2.2 [[Construct]] ( argumentsList, newTarget ), https://tc39.es/ecma262/#sec-ecmascript-function-objects-construct-argumentslist-newtarget
|
||||
ThrowCompletionOr<GC::Ref<Object>> ECMAScriptFunctionObject::internal_construct(ExecutionContext& callee_context, FunctionObject& new_target)
|
||||
FLATTEN ThrowCompletionOr<GC::Ref<Object>> ECMAScriptFunctionObject::internal_construct(ExecutionContext& callee_context, FunctionObject& new_target)
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
|
||||
|
@ -566,14 +566,14 @@ ThrowCompletionOr<GC::Ref<Object>> ECMAScriptFunctionObject::internal_construct(
|
|||
// 3. If kind is base, then
|
||||
if (kind == ConstructorKind::Base) {
|
||||
// a. Let thisArgument be ? OrdinaryCreateFromConstructor(newTarget, "%Object.prototype%").
|
||||
this_argument = TRY(ordinary_create_from_constructor<Object>(vm, new_target, &Intrinsics::object_prototype, ConstructWithPrototypeTag::Tag));
|
||||
this_argument = TRY(ordinary_create_from_constructor<Object>(vm, *realm(), new_target, &Intrinsics::object_prototype, ConstructWithPrototypeTag::Tag));
|
||||
}
|
||||
|
||||
// 4. Let calleeContext be PrepareForOrdinaryCall(F, newTarget).
|
||||
prepare_for_ordinary_call(vm, callee_context, &new_target);
|
||||
|
||||
// 5. Assert: calleeContext is now the running execution context.
|
||||
VERIFY(&vm.running_execution_context() == &callee_context);
|
||||
ASSERT(&vm.running_execution_context() == &callee_context);
|
||||
|
||||
// 6. If kind is base, then
|
||||
if (kind == ConstructorKind::Base) {
|
||||
|
@ -628,7 +628,7 @@ ThrowCompletionOr<GC::Ref<Object>> ECMAScriptFunctionObject::internal_construct(
|
|||
auto this_binding = TRY(constructor_env->get_this_binding(vm));
|
||||
|
||||
// 16. Assert: Type(thisBinding) is Object.
|
||||
VERIFY(this_binding.is_object());
|
||||
ASSERT(this_binding.is_object());
|
||||
|
||||
// 17. Return thisBinding.
|
||||
return this_binding.as_object();
|
||||
|
@ -765,7 +765,7 @@ void ECMAScriptFunctionObject::ordinary_call_bind_this(VM& vm, ExecutionContext&
|
|||
this_value = MUST(this_argument.to_object(vm));
|
||||
|
||||
// ii. NOTE: ToObject produces wrapper objects using calleeRealm.
|
||||
VERIFY(vm.current_realm() == callee_realm);
|
||||
ASSERT(vm.current_realm() == callee_realm);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -908,7 +908,7 @@ ThrowCompletionOr<Value> ECMAScriptFunctionObject::ordinary_call_evaluate_body(V
|
|||
if (kind() == FunctionKind::Async)
|
||||
return AsyncFunctionDriverWrapper::create(realm, generator_object);
|
||||
|
||||
VERIFY(kind() == FunctionKind::Generator);
|
||||
ASSERT(kind() == FunctionKind::Generator);
|
||||
return generator_object;
|
||||
}
|
||||
|
||||
|
|
|
@ -50,6 +50,11 @@ ALWAYS_INLINE constexpr FloatMatrix3x3 generate_rgb_to_xyz_matrix(FloatVector2 r
|
|||
return vectors_to_matrix(matrix_row(matrix, 0) * scale_vector, matrix_row(matrix, 1) * scale_vector, matrix_row(matrix, 2) * scale_vector);
|
||||
}
|
||||
|
||||
constexpr FloatVector2 BT_470_BG_WHITE = { 0.310f, 0.316f };
|
||||
constexpr FloatVector2 BT_470_BG_RED = { 0.67f, 0.33f };
|
||||
constexpr FloatVector2 BT_470_BG_GREEN = { 0.21f, 0.71f };
|
||||
constexpr FloatVector2 BT_470_BG_BLUE = { 0.14f, 0.08f };
|
||||
|
||||
constexpr FloatVector2 ILLUMINANT_D65 = { 0.3127f, 0.3290f };
|
||||
|
||||
constexpr FloatVector2 BT_709_RED = { 0.64f, 0.33f };
|
||||
|
@ -64,14 +69,18 @@ constexpr FloatVector2 BT_2020_RED = { 0.708f, 0.292f };
|
|||
constexpr FloatVector2 BT_2020_GREEN = { 0.170f, 0.797f };
|
||||
constexpr FloatVector2 BT_2020_BLUE = { 0.131f, 0.046f };
|
||||
|
||||
constexpr FloatMatrix3x3 bt_2020_rgb_to_xyz = generate_rgb_to_xyz_matrix(BT_2020_RED, BT_2020_GREEN, BT_2020_BLUE, ILLUMINANT_D65);
|
||||
constexpr FloatMatrix3x3 bt_470_bg_rgb_to_xyz = generate_rgb_to_xyz_matrix(BT_470_BG_RED, BT_470_BG_GREEN, BT_470_BG_BLUE, BT_470_BG_WHITE);
|
||||
constexpr FloatMatrix3x3 bt_709_rgb_to_xyz = generate_rgb_to_xyz_matrix(BT_709_RED, BT_709_GREEN, BT_709_BLUE, ILLUMINANT_D65);
|
||||
constexpr FloatMatrix3x3 bt_601_rgb_to_xyz = generate_rgb_to_xyz_matrix(BT_601_RED, BT_601_GREEN, BT_601_BLUE, ILLUMINANT_D65);
|
||||
constexpr FloatMatrix3x3 bt_2020_rgb_to_xyz = generate_rgb_to_xyz_matrix(BT_2020_RED, BT_2020_GREEN, BT_2020_BLUE, ILLUMINANT_D65);
|
||||
|
||||
DecoderErrorOr<FloatMatrix3x3> get_conversion_matrix(ColorPrimaries input_primaries, ColorPrimaries output_primaries)
|
||||
{
|
||||
FloatMatrix3x3 input_conversion_matrix;
|
||||
switch (input_primaries) {
|
||||
case ColorPrimaries::BT470BG:
|
||||
input_conversion_matrix = bt_470_bg_rgb_to_xyz;
|
||||
break;
|
||||
case ColorPrimaries::BT709:
|
||||
input_conversion_matrix = bt_709_rgb_to_xyz;
|
||||
break;
|
||||
|
@ -87,6 +96,9 @@ DecoderErrorOr<FloatMatrix3x3> get_conversion_matrix(ColorPrimaries input_primar
|
|||
|
||||
FloatMatrix3x3 output_conversion_matrix;
|
||||
switch (output_primaries) {
|
||||
case ColorPrimaries::BT470BG:
|
||||
output_conversion_matrix = bt_470_bg_rgb_to_xyz.inverse();
|
||||
break;
|
||||
case ColorPrimaries::BT709:
|
||||
output_conversion_matrix = bt_709_rgb_to_xyz.inverse();
|
||||
break;
|
||||
|
|
|
@ -21,7 +21,6 @@ enum class NetworkError {
|
|||
MalformedUrl,
|
||||
InvalidContentEncoding,
|
||||
RequestServerDied,
|
||||
CacheReadFailed,
|
||||
Unknown,
|
||||
};
|
||||
|
||||
|
@ -48,8 +47,6 @@ constexpr StringView network_error_to_string(NetworkError network_error)
|
|||
return "Response could not be decoded with its Content-Encoding"sv;
|
||||
case NetworkError::RequestServerDied:
|
||||
return "RequestServer is currently unavailable"sv;
|
||||
case NetworkError::CacheReadFailed:
|
||||
return "RequestServer encountered an error reading a cached HTTP response"sv;
|
||||
case NetworkError::Unknown:
|
||||
return "An unexpected network error occurred"sv;
|
||||
}
|
||||
|
|
|
@ -53,6 +53,12 @@ void CSSImportRule::visit_edges(Cell::Visitor& visitor)
|
|||
void CSSImportRule::set_parent_style_sheet(CSSStyleSheet* parent_style_sheet)
|
||||
{
|
||||
Base::set_parent_style_sheet(parent_style_sheet);
|
||||
|
||||
if (m_style_sheet && parent_style_sheet) {
|
||||
for (auto owning_document_or_shadow_root : parent_style_sheet->owning_documents_or_shadow_roots())
|
||||
m_style_sheet->add_owning_document_or_shadow_root(*owning_document_or_shadow_root);
|
||||
}
|
||||
|
||||
// Crude detection of whether we're already fetching.
|
||||
if (m_style_sheet || m_document_load_event_delayer.has_value())
|
||||
return;
|
||||
|
@ -174,9 +180,13 @@ void CSSImportRule::set_style_sheet(GC::Ref<CSSStyleSheet> style_sheet)
|
|||
{
|
||||
m_style_sheet = style_sheet;
|
||||
m_style_sheet->set_owner_css_rule(this);
|
||||
m_document->style_computer().invalidate_rule_cache();
|
||||
m_document->style_computer().load_fonts_from_sheet(*m_style_sheet);
|
||||
m_document->invalidate_style(DOM::StyleInvalidationReason::CSSImportRule);
|
||||
|
||||
if (m_parent_style_sheet) {
|
||||
for (auto owning_document_or_shadow_root : m_parent_style_sheet->owning_documents_or_shadow_roots())
|
||||
m_style_sheet->add_owning_document_or_shadow_root(*owning_document_or_shadow_root);
|
||||
}
|
||||
|
||||
m_style_sheet->invalidate_owners(DOM::StyleInvalidationReason::CSSImportRule);
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/cssom/#dom-cssimportrule-media
|
||||
|
|
|
@ -321,11 +321,31 @@ void CSSStyleSheet::add_owning_document_or_shadow_root(DOM::Node& document_or_sh
|
|||
{
|
||||
VERIFY(document_or_shadow_root.is_document() || document_or_shadow_root.is_shadow_root());
|
||||
m_owning_documents_or_shadow_roots.set(document_or_shadow_root);
|
||||
|
||||
// All owning documents or shadow roots must be part of the same document so we only need to load this style
|
||||
// sheet's fonts against the document of the first
|
||||
if (this->owning_documents_or_shadow_roots().size() == 1)
|
||||
document_or_shadow_root.document().style_computer().load_fonts_from_sheet(*this);
|
||||
|
||||
for (auto const& import_rule : m_import_rules) {
|
||||
if (import_rule->loaded_style_sheet())
|
||||
import_rule->loaded_style_sheet()->add_owning_document_or_shadow_root(document_or_shadow_root);
|
||||
}
|
||||
}
|
||||
|
||||
void CSSStyleSheet::remove_owning_document_or_shadow_root(DOM::Node& document_or_shadow_root)
|
||||
{
|
||||
m_owning_documents_or_shadow_roots.remove(document_or_shadow_root);
|
||||
|
||||
// All owning documents or shadow roots must be part of the same document so we only need to unload this style
|
||||
// sheet's fonts once we have none remaining.
|
||||
if (this->owning_documents_or_shadow_roots().size() == 0)
|
||||
document_or_shadow_root.document().style_computer().unload_fonts_from_sheet(*this);
|
||||
|
||||
for (auto const& import_rule : m_import_rules) {
|
||||
if (import_rule->loaded_style_sheet())
|
||||
import_rule->loaded_style_sheet()->remove_owning_document_or_shadow_root(document_or_shadow_root);
|
||||
}
|
||||
}
|
||||
|
||||
void CSSStyleSheet::invalidate_owners(DOM::StyleInvalidationReason reason)
|
||||
|
@ -342,11 +362,6 @@ GC::Ptr<DOM::Document> CSSStyleSheet::owning_document() const
|
|||
if (!m_owning_documents_or_shadow_roots.is_empty())
|
||||
return (*m_owning_documents_or_shadow_roots.begin())->document();
|
||||
|
||||
if (m_owner_css_rule && m_owner_css_rule->parent_style_sheet()) {
|
||||
if (auto document = m_owner_css_rule->parent_style_sheet()->owning_document())
|
||||
return document;
|
||||
}
|
||||
|
||||
if (auto* element = const_cast<CSSStyleSheet*>(this)->owner_node())
|
||||
return element->document();
|
||||
|
||||
|
|
|
@ -66,6 +66,7 @@ public:
|
|||
bool evaluate_media_queries(DOM::Document const&);
|
||||
void for_each_effective_keyframes_at_rule(Function<void(CSSKeyframesRule const&)> const& callback) const;
|
||||
|
||||
HashTable<GC::Ptr<DOM::Node>> owning_documents_or_shadow_roots() const { return m_owning_documents_or_shadow_roots; }
|
||||
void add_owning_document_or_shadow_root(DOM::Node& document_or_shadow_root);
|
||||
void remove_owning_document_or_shadow_root(DOM::Node& document_or_shadow_root);
|
||||
void invalidate_owners(DOM::StyleInvalidationReason);
|
||||
|
|
|
@ -142,8 +142,13 @@ Length::ResolutionContext Length::ResolutionContext::for_document(DOM::Document
|
|||
auto const& initial_font = document.style_computer().initial_font();
|
||||
Gfx::FontPixelMetrics const& initial_font_metrics = initial_font.pixel_metrics();
|
||||
Length::FontMetrics font_metrics { CSSPixels { initial_font.pixel_size() }, initial_font_metrics, InitialValues::line_height() };
|
||||
CSSPixelRect viewport_rect;
|
||||
|
||||
if (document.navigable())
|
||||
viewport_rect = document.navigable()->viewport_rect();
|
||||
|
||||
return Length::ResolutionContext {
|
||||
.viewport_rect = document.navigable()->viewport_rect(),
|
||||
.viewport_rect = viewport_rect,
|
||||
.font_metrics = font_metrics,
|
||||
.root_font_metrics = font_metrics,
|
||||
};
|
||||
|
|
|
@ -77,7 +77,7 @@ ParsedFontFace ParsedFontFace::from_descriptors(CSSFontFaceDescriptors const& de
|
|||
font_family = extract_font_name(*value);
|
||||
|
||||
ComputationContext computation_context {
|
||||
.length_resolution_context = Length::ResolutionContext::for_window(*descriptors.parent_rule()->parent_style_sheet()->owning_document()->window())
|
||||
.length_resolution_context = Length::ResolutionContext::for_document(*descriptors.parent_rule()->parent_style_sheet()->owning_document())
|
||||
};
|
||||
|
||||
Optional<int> weight;
|
||||
|
|
|
@ -117,7 +117,6 @@ void StyleSheetList::add_sheet(CSSStyleSheet& sheet)
|
|||
}
|
||||
|
||||
document().style_computer().invalidate_rule_cache();
|
||||
document().style_computer().load_fonts_from_sheet(sheet);
|
||||
document_or_shadow_root().invalidate_style(DOM::StyleInvalidationReason::StyleSheetListAddSheet);
|
||||
}
|
||||
|
||||
|
@ -132,7 +131,6 @@ void StyleSheetList::remove_sheet(CSSStyleSheet& sheet)
|
|||
return;
|
||||
}
|
||||
|
||||
m_document_or_shadow_root->document().style_computer().unload_fonts_from_sheet(sheet);
|
||||
m_document_or_shadow_root->document().style_computer().invalidate_rule_cache();
|
||||
document_or_shadow_root().invalidate_style(DOM::StyleInvalidationReason::StyleSheetListRemoveSheet);
|
||||
}
|
||||
|
|
|
@ -31,7 +31,6 @@ GC::Ref<WebIDL::ObservableArray> create_adopted_style_sheets_list(Node& document
|
|||
return WebIDL::NotAllowedError::create(document_or_shadow_root.realm(), "Sharing a StyleSheet between documents is not allowed."_utf16);
|
||||
|
||||
style_sheet.add_owning_document_or_shadow_root(document_or_shadow_root);
|
||||
document_or_shadow_root.document().style_computer().load_fonts_from_sheet(style_sheet);
|
||||
document_or_shadow_root.document().style_computer().invalidate_rule_cache();
|
||||
document_or_shadow_root.invalidate_style(DOM::StyleInvalidationReason::AdoptedStyleSheetsList);
|
||||
return {};
|
||||
|
@ -43,7 +42,6 @@ GC::Ref<WebIDL::ObservableArray> create_adopted_style_sheets_list(Node& document
|
|||
auto& style_sheet = static_cast<CSS::CSSStyleSheet&>(object);
|
||||
|
||||
style_sheet.remove_owning_document_or_shadow_root(document_or_shadow_root);
|
||||
document_or_shadow_root.document().style_computer().unload_fonts_from_sheet(style_sheet);
|
||||
document_or_shadow_root.document().style_computer().invalidate_rule_cache();
|
||||
document_or_shadow_root.invalidate_style(DOM::StyleInvalidationReason::AdoptedStyleSheetsList);
|
||||
return {};
|
||||
|
|
|
@ -346,6 +346,10 @@ void CacheEntryReader::pipe_error(Error error)
|
|||
{
|
||||
dbgln("\033[31;1mError transferring cache to pipe for\033[0m {}: {}", m_url, error);
|
||||
|
||||
// FIXME: We may not want to actually remove the cache file for all errors. For now, let's assume the file is not
|
||||
// useable at this point and remove it.
|
||||
remove();
|
||||
|
||||
if (m_on_pipe_error)
|
||||
m_on_pipe_error(m_bytes_piped);
|
||||
|
||||
|
|
|
@ -117,18 +117,24 @@ struct ConnectionFromClient::ActiveRequest : public Weakable<ActiveRequest> {
|
|||
i32 request_id { 0 };
|
||||
WeakPtr<ConnectionFromClient> client;
|
||||
int writer_fd { 0 };
|
||||
HTTP::HeaderMap headers;
|
||||
bool got_all_headers { false };
|
||||
bool is_connect_only { false };
|
||||
size_t downloaded_so_far { 0 };
|
||||
URL::URL url;
|
||||
ByteString method;
|
||||
Optional<String> reason_phrase;
|
||||
ByteBuffer body;
|
||||
|
||||
AllocatingMemoryStream send_buffer;
|
||||
NonnullRefPtr<Core::Notifier> write_notifier;
|
||||
bool done_fetching { false };
|
||||
|
||||
Optional<long> http_status_code;
|
||||
HTTP::HeaderMap headers;
|
||||
bool got_all_headers { false };
|
||||
|
||||
Optional<size_t> start_offset_of_resumed_response;
|
||||
size_t bytes_transferred_to_client { 0 };
|
||||
|
||||
Optional<CacheEntryWriter&> cache_entry;
|
||||
UnixDateTime request_start_time;
|
||||
|
||||
|
@ -161,9 +167,42 @@ struct ConnectionFromClient::ActiveRequest : public Weakable<ActiveRequest> {
|
|||
|
||||
ErrorOr<void> write_queued_bytes_without_blocking()
|
||||
{
|
||||
auto available_bytes = send_buffer.used_buffer_size();
|
||||
|
||||
// If we've received a response to a range request that is not the partial content (206) we requested, we must
|
||||
// only transfer the subset of data that WebContent now needs. We discard all received bytes up to the expected
|
||||
// start of the remaining data, and then transfer the remaining bytes.
|
||||
if (start_offset_of_resumed_response.has_value()) {
|
||||
if (http_status_code == 206) {
|
||||
start_offset_of_resumed_response.clear();
|
||||
} else if (http_status_code == 200) {
|
||||
// All bytes currently available have already been transferred. Discard them entirely.
|
||||
if (bytes_transferred_to_client + available_bytes <= *start_offset_of_resumed_response) {
|
||||
bytes_transferred_to_client += available_bytes;
|
||||
|
||||
MUST(send_buffer.discard(available_bytes));
|
||||
return {};
|
||||
}
|
||||
|
||||
// Some bytes currently available have already been transferred. Discard those bytes and transfer the rest.
|
||||
if (bytes_transferred_to_client + available_bytes > *start_offset_of_resumed_response) {
|
||||
auto bytes_to_discard = *start_offset_of_resumed_response - bytes_transferred_to_client;
|
||||
bytes_transferred_to_client += bytes_to_discard;
|
||||
available_bytes -= bytes_to_discard;
|
||||
|
||||
MUST(send_buffer.discard(bytes_to_discard));
|
||||
}
|
||||
|
||||
start_offset_of_resumed_response.clear();
|
||||
} else {
|
||||
return Error::from_string_literal("Unacceptable status code for resumed HTTP request");
|
||||
}
|
||||
}
|
||||
|
||||
Vector<u8> bytes_to_send;
|
||||
bytes_to_send.resize(send_buffer.used_buffer_size());
|
||||
bytes_to_send.resize(available_bytes);
|
||||
send_buffer.peek_some(bytes_to_send);
|
||||
|
||||
auto result = Core::System::write(this->writer_fd, bytes_to_send);
|
||||
if (result.is_error()) {
|
||||
if (result.error().code() != EAGAIN) {
|
||||
|
@ -180,7 +219,9 @@ struct ConnectionFromClient::ActiveRequest : public Weakable<ActiveRequest> {
|
|||
cache_entry.clear();
|
||||
}
|
||||
|
||||
bytes_transferred_to_client += result.value();
|
||||
MUST(send_buffer.discard(result.value()));
|
||||
|
||||
write_notifier->set_enabled(!send_buffer.is_eof());
|
||||
if (send_buffer.is_eof() && done_fetching)
|
||||
schedule_self_destruction();
|
||||
|
@ -217,16 +258,26 @@ struct ConnectionFromClient::ActiveRequest : public Weakable<ActiveRequest> {
|
|||
|
||||
void flush_headers_if_needed()
|
||||
{
|
||||
if (!http_status_code.has_value())
|
||||
http_status_code = acquire_http_status_code();
|
||||
|
||||
if (got_all_headers)
|
||||
return;
|
||||
got_all_headers = true;
|
||||
long http_status_code = 0;
|
||||
auto result = curl_easy_getinfo(easy, CURLINFO_RESPONSE_CODE, &http_status_code);
|
||||
VERIFY(result == CURLE_OK);
|
||||
client->async_headers_became_available(request_id, headers, http_status_code, reason_phrase);
|
||||
|
||||
client->async_headers_became_available(request_id, headers, *http_status_code, reason_phrase);
|
||||
|
||||
if (g_disk_cache.has_value())
|
||||
cache_entry = g_disk_cache->create_entry(url, method, http_status_code, reason_phrase, headers, request_start_time);
|
||||
cache_entry = g_disk_cache->create_entry(url, method, *http_status_code, reason_phrase, headers, request_start_time);
|
||||
}
|
||||
|
||||
long acquire_http_status_code() const
|
||||
{
|
||||
long code = 0;
|
||||
auto result = curl_easy_getinfo(easy, CURLINFO_RESPONSE_CODE, &code);
|
||||
VERIFY(result == CURLE_OK);
|
||||
|
||||
return code;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -483,6 +534,11 @@ void ConnectionFromClient::start_request(i32, ByteString, URL::URL, HTTP::Header
|
|||
{
|
||||
VERIFY(0 && "RequestServer::ConnectionFromClient::start_request is not implemented");
|
||||
}
|
||||
|
||||
void ConnectionFromClient::issue_network_request(i32, ByteString, URL::URL, HTTP::HeaderMap, ByteBuffer, Core::ProxyData, Optional<ResumeRequestForFailedCacheEntry>)
|
||||
{
|
||||
VERIFY(0 && "RequestServer::ConnectionFromClient::issue_network_request is not implemented");
|
||||
}
|
||||
#else
|
||||
void ConnectionFromClient::start_request(i32 request_id, ByteString method, URL::URL url, HTTP::HeaderMap request_headers, ByteBuffer request_body, Core::ProxyData proxy_data)
|
||||
{
|
||||
|
@ -504,25 +560,37 @@ void ConnectionFromClient::start_request(i32 request_id, ByteString method, URL:
|
|||
async_request_finished(request_id, bytes_sent, {}, {});
|
||||
MUST(Core::System::close(writer_fd));
|
||||
},
|
||||
[this, request_id, writer_fd](auto bytes_sent) {
|
||||
// FIXME: We should switch to a network request automatically if reading from cache has failed.
|
||||
async_request_finished(request_id, bytes_sent, {}, Requests::NetworkError::CacheReadFailed);
|
||||
(void)Core::System::close(writer_fd);
|
||||
[this, request_id, writer_fd, method = move(method), url = move(url), request_headers = move(request_headers), request_body = move(request_body), proxy_data](auto bytes_sent) mutable {
|
||||
// FIXME: We should really also have a way to validate the data once CacheEntry is storing its crc.
|
||||
ResumeRequestForFailedCacheEntry resume_request {
|
||||
.start_offset = bytes_sent,
|
||||
.writer_fd = writer_fd,
|
||||
};
|
||||
|
||||
issue_network_request(request_id, move(method), move(url), move(request_headers), move(request_body), proxy_data, resume_request);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
issue_network_request(request_id, move(method), move(url), move(request_headers), move(request_body), proxy_data);
|
||||
}
|
||||
|
||||
void ConnectionFromClient::issue_network_request(i32 request_id, ByteString method, URL::URL url, HTTP::HeaderMap request_headers, ByteBuffer request_body, Core::ProxyData proxy_data, Optional<ResumeRequestForFailedCacheEntry> resume_request)
|
||||
{
|
||||
auto host = url.serialized_host().to_byte_string();
|
||||
|
||||
m_resolver->dns.lookup(host, DNS::Messages::Class::IN, { DNS::Messages::ResourceType::A, DNS::Messages::ResourceType::AAAA }, { .validate_dnssec_locally = g_dns_info.validate_dnssec_locally })
|
||||
->when_rejected([this, request_id](auto const& error) {
|
||||
->when_rejected([this, request_id, resume_request](auto const& error) {
|
||||
dbgln("StartRequest: DNS lookup failed: {}", error);
|
||||
// FIXME: Implement timing info for DNS lookup failure.
|
||||
async_request_finished(request_id, 0, {}, Requests::NetworkError::UnableToResolveHost);
|
||||
|
||||
if (resume_request.has_value())
|
||||
MUST(Core::System::close(resume_request->writer_fd));
|
||||
})
|
||||
.when_resolved([this, request_id, host = move(host), url = move(url), method = move(method), request_body = move(request_body), request_headers = move(request_headers), proxy_data](auto const& dns_result) mutable {
|
||||
.when_resolved([this, request_id, host = move(host), url = move(url), method = move(method), request_body = move(request_body), request_headers = move(request_headers), proxy_data, resume_request](auto const& dns_result) mutable {
|
||||
if (dns_result->is_empty() || !dns_result->has_cached_addresses()) {
|
||||
dbgln("StartRequest: DNS lookup failed for '{}'", host);
|
||||
// FIXME: Implement timing info for DNS lookup failure.
|
||||
|
@ -538,16 +606,23 @@ void ConnectionFromClient::start_request(i32 request_id, ByteString method, URL:
|
|||
return;
|
||||
}
|
||||
|
||||
auto fds_or_error = Core::System::pipe2(O_NONBLOCK);
|
||||
if (fds_or_error.is_error()) {
|
||||
dbgln("StartRequest: Failed to create pipe: {}", fds_or_error.error());
|
||||
return;
|
||||
}
|
||||
int writer_fd = 0;
|
||||
|
||||
auto fds = fds_or_error.release_value();
|
||||
auto writer_fd = fds[1];
|
||||
auto reader_fd = fds[0];
|
||||
async_request_started(request_id, IPC::File::adopt_fd(reader_fd));
|
||||
if (resume_request.has_value()) {
|
||||
writer_fd = resume_request->writer_fd;
|
||||
} else {
|
||||
auto fds_or_error = Core::System::pipe2(O_NONBLOCK);
|
||||
if (fds_or_error.is_error()) {
|
||||
dbgln("StartRequest: Failed to create pipe: {}", fds_or_error.error());
|
||||
return;
|
||||
}
|
||||
|
||||
auto fds = fds_or_error.release_value();
|
||||
auto reader_fd = fds[0];
|
||||
writer_fd = fds[1];
|
||||
|
||||
async_request_started(request_id, IPC::File::adopt_fd(reader_fd));
|
||||
}
|
||||
|
||||
auto request = make<ActiveRequest>(*this, m_curl_multi, easy, request_id, writer_fd);
|
||||
request->url = url;
|
||||
|
@ -614,6 +689,14 @@ void ConnectionFromClient::start_request(i32 request_id, ByteString method, URL:
|
|||
request->curl_string_lists.append(curl_headers);
|
||||
}
|
||||
|
||||
if (resume_request.has_value()) {
|
||||
auto range = ByteString::formatted("{}-", resume_request->start_offset);
|
||||
set_option(CURLOPT_RANGE, range.characters());
|
||||
|
||||
request->got_all_headers = true; // Don't re-send the headers for resumed requests.
|
||||
request->start_offset_of_resumed_response = resume_request->start_offset;
|
||||
}
|
||||
|
||||
// FIXME: Set up proxy if applicable
|
||||
(void)proxy_data;
|
||||
|
||||
|
|
|
@ -56,6 +56,12 @@ private:
|
|||
virtual void websocket_close(i64 websocket_id, u16, ByteString) override;
|
||||
virtual Messages::RequestServer::WebsocketSetCertificateResponse websocket_set_certificate(i64, ByteString, ByteString) override;
|
||||
|
||||
struct ResumeRequestForFailedCacheEntry {
|
||||
size_t start_offset { 0 };
|
||||
int writer_fd { 0 };
|
||||
};
|
||||
void issue_network_request(i32 request_id, ByteString, URL::URL, HTTP::HeaderMap, ByteBuffer, Core::ProxyData, Optional<ResumeRequestForFailedCacheEntry> = {});
|
||||
|
||||
HashMap<i32, RefPtr<WebSocket::WebSocket>> m_websockets;
|
||||
|
||||
struct ActiveRequest;
|
||||
|
|
6
Tests/LibWeb/Crash/CSS/font-face-dom-parser.html
Normal file
6
Tests/LibWeb/Crash/CSS/font-face-dom-parser.html
Normal file
|
@ -0,0 +1,6 @@
|
|||
<script>
|
||||
new DOMParser().parseFromString(
|
||||
`<style>@font-face { font-family: a; src: url(); }</style>`,
|
||||
"text/html"
|
||||
);
|
||||
</script>
|
|
@ -0,0 +1,18 @@
|
|||
<!DOCTYPE html>
|
||||
<body>
|
||||
<style id="style">
|
||||
/*
|
||||
The below data url is equivalent to:
|
||||
@font-face {
|
||||
font-family: Icons;
|
||||
src: url(does/not/matter.ttf);
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
*/
|
||||
@import url("data:text/css;charset=utf-8;base64,QGZvbnQtZmFjZSB7DQogICAgZm9udC1mYW1pbHk6IEljb25zOw0KICAgIHNyYzogdXJsKGRvZXMvbm90L21hdHRlci50dGYpOw0KICAgIGZvbnQtd2VpZ2h0OiA0MDA7DQogICAgZm9udC1zdHlsZTogbm9ybWFsOw0KfQ==");
|
||||
</style>
|
||||
<script>
|
||||
style.remove();
|
||||
</script>
|
||||
</body>
|
BIN
Tests/LibWeb/Ref/data/fail.woff
Normal file
BIN
Tests/LibWeb/Ref/data/fail.woff
Normal file
Binary file not shown.
BIN
Tests/LibWeb/Ref/data/pass.woff
Normal file
BIN
Tests/LibWeb/Ref/data/pass.woff
Normal file
Binary file not shown.
|
@ -0,0 +1,35 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<body>
|
||||
<iframe
|
||||
id="iframe"
|
||||
width="500"
|
||||
srcdoc="
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: test;
|
||||
src: url(../data/pass.woff) format(woff);
|
||||
}
|
||||
|
||||
#foo {
|
||||
font-family: test;
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<p>Test passes if the word PASS appears below.</p>
|
||||
<p id='foo'>P</p>
|
||||
</body>
|
||||
</html>"
|
||||
onload="waitForFonts()"
|
||||
></iframe>
|
||||
<script>
|
||||
function waitForFonts() {
|
||||
iframe.contentDocument.fonts.ready.then(() => {
|
||||
document.documentElement.classList = "";
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,50 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<link
|
||||
rel="match"
|
||||
href="../../expected/font-face-descriptor-relative-length-iframe-ref.html"
|
||||
/>
|
||||
</head>
|
||||
<body>
|
||||
<iframe
|
||||
id="iframe"
|
||||
width="500"
|
||||
srcdoc="
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: test;
|
||||
src: url(../../data/fail.woff) format(woff);
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: test;
|
||||
src: url(../../data/pass.woff) format(woff);
|
||||
/* Should compute to '100' because 100vw = 500px */
|
||||
font-weight: calc(100 + (sign(100vw - 500px) * 1000));
|
||||
}
|
||||
|
||||
#foo {
|
||||
font-family: test;
|
||||
font-weight: 100;
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<p>Test passes if the word PASS appears below.</p>
|
||||
<p id='foo'>P</p>
|
||||
</body>
|
||||
</html>"
|
||||
onload="waitForFonts()"
|
||||
></iframe>
|
||||
<script>
|
||||
function waitForFonts() {
|
||||
iframe.contentDocument.fonts.ready.then(() => {
|
||||
document.documentElement.classList = "";
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
Add table
Add a link
Reference in a new issue