mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-27 14:58:46 +00:00
This eliminates the use of ResourceLoader in HTMLObjectElement. The spec steps around fetching have been slightly updated since we've last looked at this, so those are updated here. Regarding the text test change: we cannot rely on the data: URL being fetched synchronously. It will occur on a deferred task now. This does match the behavior of other browsers, as they also will not have run the fallback representation steps as of DOMContentLoaded.
563 lines
26 KiB
C++
563 lines
26 KiB
C++
/*
|
|
* Copyright (c) 2020-2024, Andreas Kling <andreas@ladybird.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <LibGfx/Bitmap.h>
|
|
#include <LibWeb/Bindings/HTMLObjectElementPrototype.h>
|
|
#include <LibWeb/CSS/StyleComputer.h>
|
|
#include <LibWeb/CSS/StyleValues/CSSKeywordValue.h>
|
|
#include <LibWeb/CSS/StyleValues/DisplayStyleValue.h>
|
|
#include <LibWeb/CSS/StyleValues/LengthStyleValue.h>
|
|
#include <LibWeb/DOM/Document.h>
|
|
#include <LibWeb/DOM/DocumentLoadEventDelayer.h>
|
|
#include <LibWeb/DOM/DocumentLoading.h>
|
|
#include <LibWeb/DOM/Event.h>
|
|
#include <LibWeb/Fetch/Fetching/Fetching.h>
|
|
#include <LibWeb/Fetch/Infrastructure/FetchAlgorithms.h>
|
|
#include <LibWeb/Fetch/Infrastructure/HTTP/Requests.h>
|
|
#include <LibWeb/HTML/DecodedImageData.h>
|
|
#include <LibWeb/HTML/HTMLMediaElement.h>
|
|
#include <LibWeb/HTML/HTMLObjectElement.h>
|
|
#include <LibWeb/HTML/ImageRequest.h>
|
|
#include <LibWeb/HTML/Numbers.h>
|
|
#include <LibWeb/HTML/Parser/HTMLParser.h>
|
|
#include <LibWeb/HTML/PotentialCORSRequest.h>
|
|
#include <LibWeb/Layout/ImageBox.h>
|
|
#include <LibWeb/Layout/NavigableContainerViewport.h>
|
|
#include <LibWeb/Loader/ResourceLoader.h>
|
|
#include <LibWeb/MimeSniff/MimeType.h>
|
|
#include <LibWeb/MimeSniff/Resource.h>
|
|
|
|
namespace Web::HTML {
|
|
|
|
GC_DEFINE_ALLOCATOR(HTMLObjectElement);
|
|
|
|
HTMLObjectElement::HTMLObjectElement(DOM::Document& document, DOM::QualifiedName qualified_name)
|
|
: NavigableContainer(document, move(qualified_name))
|
|
{
|
|
// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element:potentially-delays-the-load-event
|
|
set_potentially_delays_the_load_event(true);
|
|
|
|
// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element
|
|
// Whenever one of the following conditions occur:
|
|
// - the element is created,
|
|
// ...the user agent must queue an element task on the DOM manipulation task source given the object element to run
|
|
// the following steps to (re)determine what the object element represents.
|
|
queue_element_task_to_run_object_representation_steps();
|
|
}
|
|
|
|
HTMLObjectElement::~HTMLObjectElement() = default;
|
|
|
|
void HTMLObjectElement::initialize(JS::Realm& realm)
|
|
{
|
|
Base::initialize(realm);
|
|
WEB_SET_PROTOTYPE_FOR_INTERFACE(HTMLObjectElement);
|
|
}
|
|
|
|
void HTMLObjectElement::visit_edges(Cell::Visitor& visitor)
|
|
{
|
|
Base::visit_edges(visitor);
|
|
visitor.visit(m_resource_request);
|
|
}
|
|
|
|
void HTMLObjectElement::form_associated_element_attribute_changed(FlyString const& name, Optional<String> const&)
|
|
{
|
|
// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element
|
|
// Whenever one of the following conditions occur:
|
|
if (
|
|
// - the element's classid attribute is set, changed, or removed,
|
|
(name == HTML::AttributeNames::classid) ||
|
|
// - the element's classid attribute is not present, and its data attribute is set, changed, or removed,
|
|
(!has_attribute(HTML::AttributeNames::classid) && name == HTML::AttributeNames::data) ||
|
|
// - neither the element's classid attribute nor its data attribute are present, and its type attribute is set, changed, or removed,
|
|
(!has_attribute(HTML::AttributeNames::classid) && !has_attribute(HTML::AttributeNames::data) && name == HTML::AttributeNames::type)) {
|
|
// ...the user agent must queue an element task on the DOM manipulation task source given the object element to run
|
|
// the following steps to (re)determine what the object element represents.
|
|
queue_element_task_to_run_object_representation_steps();
|
|
}
|
|
}
|
|
|
|
void HTMLObjectElement::form_associated_element_was_removed(DOM::Node*)
|
|
{
|
|
destroy_the_child_navigable();
|
|
}
|
|
|
|
void HTMLObjectElement::apply_presentational_hints(CSS::StyleProperties& style) const
|
|
{
|
|
for_each_attribute([&](auto& name, auto& value) {
|
|
if (name == HTML::AttributeNames::align) {
|
|
if (value.equals_ignoring_ascii_case("center"sv))
|
|
style.set_property(CSS::PropertyID::TextAlign, CSS::CSSKeywordValue::create(CSS::Keyword::Center));
|
|
else if (value.equals_ignoring_ascii_case("middle"sv))
|
|
style.set_property(CSS::PropertyID::TextAlign, CSS::CSSKeywordValue::create(CSS::Keyword::Middle));
|
|
} else if (name == HTML::AttributeNames::border) {
|
|
if (auto parsed_value = parse_non_negative_integer(value); parsed_value.has_value()) {
|
|
auto width_style_value = CSS::LengthStyleValue::create(CSS::Length::make_px(*parsed_value));
|
|
style.set_property(CSS::PropertyID::BorderTopWidth, width_style_value);
|
|
style.set_property(CSS::PropertyID::BorderRightWidth, width_style_value);
|
|
style.set_property(CSS::PropertyID::BorderBottomWidth, width_style_value);
|
|
style.set_property(CSS::PropertyID::BorderLeftWidth, width_style_value);
|
|
|
|
auto border_style_value = CSS::CSSKeywordValue::create(CSS::Keyword::Solid);
|
|
style.set_property(CSS::PropertyID::BorderTopStyle, border_style_value);
|
|
style.set_property(CSS::PropertyID::BorderRightStyle, border_style_value);
|
|
style.set_property(CSS::PropertyID::BorderBottomStyle, border_style_value);
|
|
style.set_property(CSS::PropertyID::BorderLeftStyle, border_style_value);
|
|
}
|
|
}
|
|
// https://html.spec.whatwg.org/multipage/rendering.html#attributes-for-embedded-content-and-images:maps-to-the-dimension-property-3
|
|
else if (name == HTML::AttributeNames::height) {
|
|
if (auto parsed_value = parse_dimension_value(value)) {
|
|
style.set_property(CSS::PropertyID::Height, *parsed_value);
|
|
}
|
|
}
|
|
// https://html.spec.whatwg.org/multipage/rendering.html#attributes-for-embedded-content-and-images:maps-to-the-dimension-property
|
|
else if (name == HTML::AttributeNames::hspace) {
|
|
if (auto parsed_value = parse_dimension_value(value)) {
|
|
style.set_property(CSS::PropertyID::MarginLeft, *parsed_value);
|
|
style.set_property(CSS::PropertyID::MarginRight, *parsed_value);
|
|
}
|
|
} else if (name == HTML::AttributeNames::vspace) {
|
|
if (auto parsed_value = parse_dimension_value(value)) {
|
|
style.set_property(CSS::PropertyID::MarginTop, *parsed_value);
|
|
style.set_property(CSS::PropertyID::MarginBottom, *parsed_value);
|
|
}
|
|
} else if (name == HTML::AttributeNames::width) {
|
|
if (auto parsed_value = parse_dimension_value(value)) {
|
|
style.set_property(CSS::PropertyID::Width, *parsed_value);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#attr-object-data
|
|
String HTMLObjectElement::data() const
|
|
{
|
|
auto data = get_attribute(HTML::AttributeNames::data);
|
|
if (!data.has_value())
|
|
return {};
|
|
|
|
return document().encoding_parse_url(*data).to_string();
|
|
}
|
|
|
|
GC::Ptr<Layout::Node> HTMLObjectElement::create_layout_node(CSS::StyleProperties style)
|
|
{
|
|
switch (m_representation) {
|
|
case Representation::Children:
|
|
return NavigableContainer::create_layout_node(move(style));
|
|
case Representation::ContentNavigable:
|
|
return heap().allocate<Layout::NavigableContainerViewport>(document(), *this, move(style));
|
|
case Representation::Image:
|
|
if (image_data())
|
|
return heap().allocate<Layout::ImageBox>(document(), *this, move(style), *this);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void HTMLObjectElement::adjust_computed_style(CSS::StyleProperties& style)
|
|
{
|
|
// https://drafts.csswg.org/css-display-3/#unbox
|
|
if (style.display().is_contents())
|
|
style.set_property(CSS::PropertyID::Display, CSS::DisplayStyleValue::create(CSS::Display::from_short(CSS::Display::Short::None)));
|
|
}
|
|
|
|
bool HTMLObjectElement::has_ancestor_media_element_or_object_element_not_showing_fallback_content() const
|
|
{
|
|
for (auto const* ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
|
|
if (is<HTMLMediaElement>(*ancestor))
|
|
return true;
|
|
|
|
if (is<HTMLObjectElement>(*ancestor)) {
|
|
auto& ancestor_object = static_cast<HTMLObjectElement const&>(*ancestor);
|
|
if (ancestor_object.m_representation != Representation::Children)
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element:queue-an-element-task
|
|
void HTMLObjectElement::queue_element_task_to_run_object_representation_steps()
|
|
{
|
|
// This task being queued or actively running must delay the load event of the element's node document.
|
|
m_document_load_event_delayer_for_object_representation_task.emplace(document());
|
|
|
|
queue_an_element_task(HTML::Task::Source::DOMManipulation, [this]() {
|
|
ScopeGuard guard { [&]() { m_document_load_event_delayer_for_object_representation_task.clear(); } };
|
|
|
|
auto& realm = this->realm();
|
|
auto& vm = realm.vm();
|
|
|
|
// FIXME: 1. If the user has indicated a preference that this object element's fallback content be shown instead of the
|
|
// element's usual behavior, then jump to the step below labeled fallback.
|
|
|
|
// 2. If the element has an ancestor media element, or has an ancestor object element that is not showing its
|
|
// fallback content, or if the element is not in a document whose browsing context is non-null, or if the
|
|
// element's node document is not fully active, or if the element is still in the stack of open elements of
|
|
// an HTML parser or XML parser, or if the element is not being rendered, then jump to the step below labeled
|
|
// fallback.
|
|
// FIXME: Handle the element being in the stack of open elements.
|
|
// FIXME: Handle the element not being rendered.
|
|
if (!document().browsing_context() || !document().is_fully_active()) {
|
|
run_object_representation_fallback_steps();
|
|
return;
|
|
}
|
|
if (has_ancestor_media_element_or_object_element_not_showing_fallback_content()) {
|
|
run_object_representation_fallback_steps();
|
|
return;
|
|
}
|
|
|
|
// 3. If the data attribute is present and its value is not the empty string, then:
|
|
if (auto data = get_attribute(HTML::AttributeNames::data); data.has_value() && !data->is_empty()) {
|
|
// 1. If the type attribute is present and its value is not a type that the user agent supports, then the user
|
|
// agent may jump to the step below labeled fallback without fetching the content to examine its real type.
|
|
|
|
// 2. Let url be the result of encoding-parsing a URL given the data attribute's value, relative to the element's node document.
|
|
auto url = document().encoding_parse_url(*data);
|
|
|
|
// 3. If url is failure, then fire an event named error at the element and jump to the step below labeled fallback.
|
|
if (!url.is_valid()) {
|
|
dispatch_event(DOM::Event::create(realm, HTML::EventNames::error));
|
|
run_object_representation_fallback_steps();
|
|
return;
|
|
}
|
|
|
|
// 4. Let request be a new request whose URL is url, client is the element's node document's relevant settings
|
|
// object, destination is "object", credentials mode is "include", mode is "navigate", initiator type is
|
|
// "object", and whose use-URL-credentials flag is set.
|
|
auto request = Fetch::Infrastructure::Request::create(vm);
|
|
request->set_url(move(url));
|
|
request->set_client(&document().relevant_settings_object());
|
|
request->set_destination(Fetch::Infrastructure::Request::Destination::Object);
|
|
request->set_credentials_mode(Fetch::Infrastructure::Request::CredentialsMode::Include);
|
|
request->set_mode(Fetch::Infrastructure::Request::Mode::Navigate);
|
|
request->set_initiator_type(Fetch::Infrastructure::Request::InitiatorType::Object);
|
|
request->set_use_url_credentials(true);
|
|
|
|
Fetch::Infrastructure::FetchAlgorithms::Input fetch_algorithms_input {};
|
|
fetch_algorithms_input.process_response = [this](GC::Ref<Fetch::Infrastructure::Response> response) {
|
|
auto& realm = this->realm();
|
|
auto& global = document().realm().global_object();
|
|
|
|
if (response->is_network_error()) {
|
|
resource_did_fail();
|
|
return;
|
|
}
|
|
|
|
if (response->type() == Fetch::Infrastructure::Response::Type::Opaque || response->type() == Fetch::Infrastructure::Response::Type::OpaqueRedirect) {
|
|
auto& filtered_response = static_cast<Fetch::Infrastructure::FilteredResponse&>(*response);
|
|
response = filtered_response.internal_response();
|
|
}
|
|
|
|
auto on_data_read = GC::create_function(realm.heap(), [this, response](ByteBuffer data) {
|
|
resource_did_load(response, data);
|
|
});
|
|
auto on_error = GC::create_function(realm.heap(), [this](JS::Value) {
|
|
resource_did_fail();
|
|
});
|
|
|
|
VERIFY(response->body());
|
|
response->body()->fully_read(realm, on_data_read, on_error, GC::Ref { global });
|
|
};
|
|
|
|
// 5. Fetch request.
|
|
auto result = Fetch::Fetching::fetch(realm, request, Fetch::Infrastructure::FetchAlgorithms::create(vm, move(fetch_algorithms_input)));
|
|
if (result.is_error()) {
|
|
resource_did_fail();
|
|
return;
|
|
}
|
|
|
|
// Fetching the resource must delay the load event of the element's node document until the task that is
|
|
// queued by the networking task source once the resource has been fetched (defined next) has been run.
|
|
m_document_load_event_delayer_for_resource_load.emplace(document());
|
|
|
|
// 6. If the resource is not yet available (e.g. because the resource was not available in the cache, so that
|
|
// loading the resource required making a request over the network), then jump to the step below labeled
|
|
// fallback. The task that is queued by the networking task source once the resource is available must
|
|
// restart this algorithm from this step. Resources can load incrementally; user agents may opt to consider
|
|
// a resource "available" whenever enough data has been obtained to begin processing the resource.
|
|
|
|
// NOTE: The request is always asynchronous, even if it is cached or succeeded/failed immediately. Allow the
|
|
// response callback to invoke the fallback steps. This prevents the fallback layout from flashing very
|
|
// briefly between here and the resource loading.
|
|
}
|
|
});
|
|
}
|
|
|
|
// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element:concept-event-fire-2
|
|
void HTMLObjectElement::resource_did_fail()
|
|
{
|
|
ScopeGuard guard { [&]() { m_document_load_event_delayer_for_resource_load.clear(); } };
|
|
|
|
// 3.7. If the load failed (e.g. there was an HTTP 404 error, there was a DNS error), fire an event named error at
|
|
// the element, then jump to the step below labeled fallback.
|
|
dispatch_event(DOM::Event::create(realm(), HTML::EventNames::error));
|
|
run_object_representation_fallback_steps();
|
|
}
|
|
|
|
// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#object-type-detection
|
|
void HTMLObjectElement::resource_did_load(Fetch::Infrastructure::Response const& response, ReadonlyBytes data)
|
|
{
|
|
ScopeGuard guard { [&]() { m_document_load_event_delayer_for_resource_load.clear(); } };
|
|
|
|
// 3.8. Determine the resource type, as follows:
|
|
|
|
// 1. Let the resource type be unknown.
|
|
Optional<MimeSniff::MimeType> resource_type;
|
|
|
|
// FIXME: 3. If the user agent is configured to strictly obey Content-Type headers for this resource, and the resource has
|
|
// associated Content-Type metadata, then let the resource type be the type specified in the resource's Content-Type
|
|
// metadata, and jump to the step below labeled handler.
|
|
|
|
// 3. Run the appropriate set of steps from the following list:
|
|
// -> If the resource has associated Content-Type metadata
|
|
if (auto content_type = response.header_list()->extract_mime_type(); content_type.has_value()) {
|
|
// 1. Let binary be false.
|
|
bool binary = false;
|
|
|
|
// 2. If the type specified in the resource's Content-Type metadata is "text/plain", and the result of applying
|
|
// the rules for distinguishing if a resource is text or binary to the resource is that the resource is not
|
|
// text/plain, then set binary to true.
|
|
if (content_type->essence() == "text/plain"sv) {
|
|
auto computed_type = MimeSniff::Resource::sniff(
|
|
data,
|
|
MimeSniff::SniffingConfiguration {
|
|
.sniffing_context = MimeSniff::SniffingContext::TextOrBinary,
|
|
.supplied_type = content_type,
|
|
});
|
|
|
|
if (computed_type.essence() != "text/plain"sv)
|
|
binary = true;
|
|
}
|
|
|
|
// 3. If the type specified in the resource's Content-Type metadata is "application/octet-stream", then set binary to true.
|
|
else if (content_type->essence() == "application/octet-stream"sv) {
|
|
binary = true;
|
|
}
|
|
|
|
// 4. If binary is false, then let the resource type be the type specified in the resource's Content-Type metadata,
|
|
// and jump to the step below labeled handler.
|
|
if (!binary) {
|
|
resource_type = move(content_type);
|
|
}
|
|
|
|
// 5. If there is a type attribute present on the object element, and its value is not application/octet-stream,
|
|
// then run the following steps:
|
|
else if (auto type = this->type(); !type.is_empty() && (type != "application/octet-stream"sv)) {
|
|
// 1. If the attribute's value is a type that starts with "image/" that is not also an XML MIME type, then
|
|
// let the resource type be the type specified in that type attribute.
|
|
if (type.starts_with_bytes("image/"sv)) {
|
|
auto parsed_type = MimeSniff::MimeType::parse(type);
|
|
|
|
if (parsed_type.has_value() && !parsed_type->is_xml())
|
|
resource_type = move(parsed_type);
|
|
}
|
|
|
|
// 2. Jump to the step below labeled handler.
|
|
}
|
|
}
|
|
// -> Otherwise, if the resource does not have associated Content-Type metadata
|
|
else {
|
|
Optional<MimeSniff::MimeType> tentative_type;
|
|
|
|
// 1. If there is a type attribute present on the object element, then let the tentative type be the type specified in that type attribute.
|
|
// Otherwise, let tentative type be the computed type of the resource.
|
|
if (auto type = this->type(); !type.is_empty())
|
|
tentative_type = MimeSniff::MimeType::parse(type);
|
|
else
|
|
tentative_type = MimeSniff::Resource::sniff(data);
|
|
|
|
// 2. If tentative type is not application/octet-stream, then let resource type be tentative type and jump to the
|
|
// step below labeled handler.
|
|
if (tentative_type.has_value() && tentative_type->essence() != "application/octet-stream"sv)
|
|
resource_type = move(tentative_type);
|
|
}
|
|
|
|
if (resource_type.has_value())
|
|
run_object_representation_handler_steps(response, *resource_type, data);
|
|
else
|
|
run_object_representation_fallback_steps();
|
|
}
|
|
|
|
// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element:plugin-11
|
|
void HTMLObjectElement::run_object_representation_handler_steps(Fetch::Infrastructure::Response const& response, MimeSniff::MimeType const& resource_type, ReadonlyBytes data)
|
|
{
|
|
// 3.9. Handler: Handle the content as given by the first of the following cases that matches:
|
|
|
|
// -> If the resource type is an XML MIME type, or if the resource type does not start with "image/"
|
|
if (can_load_document_with_type(resource_type) && (resource_type.is_xml() || !resource_type.is_image())) {
|
|
// If the object element's content navigable is null, then create a new child navigable for the element.
|
|
if (!m_content_navigable && in_a_document_tree()) {
|
|
MUST(create_new_child_navigable());
|
|
set_content_navigable_initialized();
|
|
}
|
|
|
|
// NOTE: Creating a new nested browsing context can fail if the document is not attached to a browsing context
|
|
if (!m_content_navigable)
|
|
return;
|
|
|
|
// Let response be the response from fetch.
|
|
|
|
// If response's URL does not match about:blank, then navigate the element's content navigable to response's URL
|
|
// using the element's node document, with historyHandling set to "replace".
|
|
if (response.url().has_value() && !url_matches_about_blank(*response.url())) {
|
|
MUST(m_content_navigable->navigate({
|
|
.url = *response.url(),
|
|
.source_document = document(),
|
|
.history_handling = Bindings::NavigationHistoryBehavior::Replace,
|
|
}));
|
|
}
|
|
|
|
// The object element represents its content navigable.
|
|
run_object_representation_completed_steps(Representation::ContentNavigable);
|
|
}
|
|
|
|
// -> If the resource type starts with "image/", and support for images has not been disabled
|
|
// FIXME: Handle disabling image support.
|
|
else if (resource_type.is_image()) {
|
|
// Destroy a child navigable given the object element.
|
|
destroy_the_child_navigable();
|
|
|
|
// Apply the image sniffing rules to determine the type of the image.
|
|
// The object element represents the specified image.
|
|
// If the image cannot be rendered, e.g. because it is malformed or in an unsupported format, jump to the step
|
|
// below labeled fallback.
|
|
if (data.is_empty()) {
|
|
run_object_representation_fallback_steps();
|
|
return;
|
|
}
|
|
|
|
load_image();
|
|
}
|
|
|
|
// -> Otherwise
|
|
else {
|
|
// The given resource type is not supported. Jump to the step below labeled fallback.
|
|
run_object_representation_fallback_steps();
|
|
}
|
|
}
|
|
|
|
// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element:the-object-element-19
|
|
void HTMLObjectElement::run_object_representation_completed_steps(Representation representation)
|
|
{
|
|
// 3.10. The element's contents are not part of what the object element represents.
|
|
|
|
// 3.11. If the object element does not represent its content navigable, then once the resource is completely loaded,
|
|
// queue an element task on the DOM manipulation task source given the object element to fire an event named
|
|
// load at the element.
|
|
if (representation != Representation::ContentNavigable) {
|
|
queue_an_element_task(HTML::Task::Source::DOMManipulation, [&]() {
|
|
dispatch_event(DOM::Event::create(realm(), HTML::EventNames::load));
|
|
});
|
|
}
|
|
|
|
update_layout_and_child_objects(representation);
|
|
|
|
// 3.12. Return.
|
|
}
|
|
|
|
// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element:the-object-element-23
|
|
void HTMLObjectElement::run_object_representation_fallback_steps()
|
|
{
|
|
// 4. Fallback: The object element represents the element's children. This is the element's fallback content.
|
|
// Destroy a child navigable given the element.
|
|
destroy_the_child_navigable();
|
|
|
|
update_layout_and_child_objects(Representation::Children);
|
|
}
|
|
|
|
void HTMLObjectElement::load_image()
|
|
{
|
|
// FIXME: This currently reloads the image instead of reusing the resource we've already downloaded.
|
|
auto data = get_attribute_value(HTML::AttributeNames::data);
|
|
auto url = document().encoding_parse_url(data);
|
|
m_resource_request = HTML::SharedResourceRequest::get_or_create(realm(), document().page(), url);
|
|
m_resource_request->add_callbacks(
|
|
[this] {
|
|
run_object_representation_completed_steps(Representation::Image);
|
|
},
|
|
[this] {
|
|
run_object_representation_fallback_steps();
|
|
});
|
|
|
|
if (m_resource_request->needs_fetching()) {
|
|
auto request = HTML::create_potential_CORS_request(vm(), url, Fetch::Infrastructure::Request::Destination::Image, HTML::CORSSettingAttribute::NoCORS);
|
|
request->set_client(&document().relevant_settings_object());
|
|
m_resource_request->fetch_resource(realm(), request);
|
|
}
|
|
}
|
|
|
|
void HTMLObjectElement::update_layout_and_child_objects(Representation representation)
|
|
{
|
|
if (representation == Representation::Children) {
|
|
for_each_child_of_type<HTMLObjectElement>([](auto& object) {
|
|
object.queue_element_task_to_run_object_representation_steps();
|
|
return IterationDecision::Continue;
|
|
});
|
|
}
|
|
|
|
m_representation = representation;
|
|
invalidate_style(DOM::StyleInvalidationReason::HTMLObjectElementUpdateLayoutAndChildObjects);
|
|
document().invalidate_layout_tree();
|
|
}
|
|
|
|
// https://html.spec.whatwg.org/multipage/interaction.html#dom-tabindex
|
|
i32 HTMLObjectElement::default_tab_index_value() const
|
|
{
|
|
// See the base function for the spec comments.
|
|
return 0;
|
|
}
|
|
|
|
GC::Ptr<DecodedImageData> HTMLObjectElement::image_data() const
|
|
{
|
|
if (!m_resource_request)
|
|
return nullptr;
|
|
return m_resource_request->image_data();
|
|
}
|
|
|
|
bool HTMLObjectElement::is_image_available() const
|
|
{
|
|
return image_data() != nullptr;
|
|
}
|
|
|
|
Optional<CSSPixels> HTMLObjectElement::intrinsic_width() const
|
|
{
|
|
if (auto image_data = this->image_data())
|
|
return image_data->intrinsic_width();
|
|
return {};
|
|
}
|
|
|
|
Optional<CSSPixels> HTMLObjectElement::intrinsic_height() const
|
|
{
|
|
if (auto image_data = this->image_data())
|
|
return image_data->intrinsic_height();
|
|
return {};
|
|
}
|
|
|
|
Optional<CSSPixelFraction> HTMLObjectElement::intrinsic_aspect_ratio() const
|
|
{
|
|
if (auto image_data = this->image_data())
|
|
return image_data->intrinsic_aspect_ratio();
|
|
return {};
|
|
}
|
|
|
|
RefPtr<Gfx::ImmutableBitmap> HTMLObjectElement::current_image_bitmap(Gfx::IntSize size) const
|
|
{
|
|
if (auto image_data = this->image_data())
|
|
return image_data->bitmap(0, size);
|
|
return nullptr;
|
|
}
|
|
|
|
void HTMLObjectElement::set_visible_in_viewport(bool)
|
|
{
|
|
// FIXME: Loosen grip on image data when it's not visible, e.g via volatile memory.
|
|
}
|
|
|
|
}
|