Coverage Report

Created: 2025-03-04 07:22

/src/serenity/Userland/Libraries/LibWeb/HTML/HTMLObjectElement.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2020-2023, Andreas Kling <kling@serenityos.org>
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#include <LibGfx/Bitmap.h>
8
#include <LibWeb/Bindings/HTMLObjectElementPrototype.h>
9
#include <LibWeb/CSS/StyleComputer.h>
10
#include <LibWeb/CSS/StyleValues/CSSKeywordValue.h>
11
#include <LibWeb/CSS/StyleValues/LengthStyleValue.h>
12
#include <LibWeb/DOM/Document.h>
13
#include <LibWeb/DOM/DocumentLoading.h>
14
#include <LibWeb/DOM/Event.h>
15
#include <LibWeb/HTML/DecodedImageData.h>
16
#include <LibWeb/HTML/HTMLMediaElement.h>
17
#include <LibWeb/HTML/HTMLObjectElement.h>
18
#include <LibWeb/HTML/ImageRequest.h>
19
#include <LibWeb/HTML/Numbers.h>
20
#include <LibWeb/HTML/Parser/HTMLParser.h>
21
#include <LibWeb/HTML/PotentialCORSRequest.h>
22
#include <LibWeb/Layout/ImageBox.h>
23
#include <LibWeb/Loader/ResourceLoader.h>
24
#include <LibWeb/MimeSniff/MimeType.h>
25
#include <LibWeb/MimeSniff/Resource.h>
26
27
namespace Web::HTML {
28
29
JS_DEFINE_ALLOCATOR(HTMLObjectElement);
30
31
HTMLObjectElement::HTMLObjectElement(DOM::Document& document, DOM::QualifiedName qualified_name)
32
0
    : NavigableContainer(document, move(qualified_name))
33
0
{
34
    // https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element
35
    // Whenever one of the following conditions occur:
36
    // - the element is created,
37
    // ...the user agent must queue an element task on the DOM manipulation task source given
38
    // the object element to run the following steps to (re)determine what the object element represents.
39
    // This task being queued or actively running must delay the load event of the element's node document.
40
0
    queue_element_task_to_run_object_representation_steps();
41
0
}
42
43
0
HTMLObjectElement::~HTMLObjectElement() = default;
44
45
void HTMLObjectElement::initialize(JS::Realm& realm)
46
0
{
47
0
    Base::initialize(realm);
48
0
    WEB_SET_PROTOTYPE_FOR_INTERFACE(HTMLObjectElement);
49
0
}
50
51
void HTMLObjectElement::visit_edges(Cell::Visitor& visitor)
52
0
{
53
0
    Base::visit_edges(visitor);
54
0
    visitor.visit(m_resource_request);
55
0
}
56
57
void HTMLObjectElement::form_associated_element_attribute_changed(FlyString const& name, Optional<String> const&)
58
0
{
59
    // https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element
60
    // Whenever one of the following conditions occur:
61
0
    if (
62
        // - the element's classid attribute is set, changed, or removed,
63
0
        (name == HTML::AttributeNames::classid) ||
64
        // - the element's classid attribute is not present, and its data attribute is set, changed, or removed,
65
0
        (!has_attribute(HTML::AttributeNames::classid) && name == HTML::AttributeNames::data) ||
66
        // - neither the element's classid attribute nor its data attribute are present, and its type attribute is set, changed, or removed,
67
0
        (!has_attribute(HTML::AttributeNames::classid) && !has_attribute(HTML::AttributeNames::data) && name == HTML::AttributeNames::type)) {
68
69
        // ...the user agent must queue an element task on the DOM manipulation task source given
70
        // the object element to run the following steps to (re)determine what the object element represents.
71
        // This task being queued or actively running must delay the load event of the element's node document.
72
0
        queue_element_task_to_run_object_representation_steps();
73
0
    }
74
0
}
75
76
void HTMLObjectElement::form_associated_element_was_removed(DOM::Node*)
77
0
{
78
0
    destroy_the_child_navigable();
79
0
}
80
81
void HTMLObjectElement::apply_presentational_hints(CSS::StyleProperties& style) const
82
0
{
83
0
    for_each_attribute([&](auto& name, auto& value) {
84
0
        if (name == HTML::AttributeNames::align) {
85
0
            if (value.equals_ignoring_ascii_case("center"sv))
86
0
                style.set_property(CSS::PropertyID::TextAlign, CSS::CSSKeywordValue::create(CSS::Keyword::Center));
87
0
            else if (value.equals_ignoring_ascii_case("middle"sv))
88
0
                style.set_property(CSS::PropertyID::TextAlign, CSS::CSSKeywordValue::create(CSS::Keyword::Middle));
89
0
        } else if (name == HTML::AttributeNames::border) {
90
0
            if (auto parsed_value = parse_non_negative_integer(value); parsed_value.has_value() && *parsed_value > 0) {
91
0
                auto width_style_value = CSS::LengthStyleValue::create(CSS::Length::make_px(*parsed_value));
92
0
                style.set_property(CSS::PropertyID::BorderTopWidth, width_style_value);
93
0
                style.set_property(CSS::PropertyID::BorderRightWidth, width_style_value);
94
0
                style.set_property(CSS::PropertyID::BorderBottomWidth, width_style_value);
95
0
                style.set_property(CSS::PropertyID::BorderLeftWidth, width_style_value);
96
97
0
                auto border_style_value = CSS::CSSKeywordValue::create(CSS::Keyword::Solid);
98
0
                style.set_property(CSS::PropertyID::BorderTopStyle, border_style_value);
99
0
                style.set_property(CSS::PropertyID::BorderRightStyle, border_style_value);
100
0
                style.set_property(CSS::PropertyID::BorderBottomStyle, border_style_value);
101
0
                style.set_property(CSS::PropertyID::BorderLeftStyle, border_style_value);
102
0
            }
103
0
        }
104
        // https://html.spec.whatwg.org/multipage/rendering.html#attributes-for-embedded-content-and-images:maps-to-the-dimension-property-3
105
0
        else if (name == HTML::AttributeNames::height) {
106
0
            if (auto parsed_value = parse_dimension_value(value)) {
107
0
                style.set_property(CSS::PropertyID::Height, *parsed_value);
108
0
            }
109
0
        }
110
        // https://html.spec.whatwg.org/multipage/rendering.html#attributes-for-embedded-content-and-images:maps-to-the-dimension-property
111
0
        else if (name == HTML::AttributeNames::hspace) {
112
0
            if (auto parsed_value = parse_dimension_value(value)) {
113
0
                style.set_property(CSS::PropertyID::MarginLeft, *parsed_value);
114
0
                style.set_property(CSS::PropertyID::MarginRight, *parsed_value);
115
0
            }
116
0
        } else if (name == HTML::AttributeNames::vspace) {
117
0
            if (auto parsed_value = parse_dimension_value(value)) {
118
0
                style.set_property(CSS::PropertyID::MarginTop, *parsed_value);
119
0
                style.set_property(CSS::PropertyID::MarginBottom, *parsed_value);
120
0
            }
121
0
        } else if (name == HTML::AttributeNames::width) {
122
0
            if (auto parsed_value = parse_dimension_value(value)) {
123
0
                style.set_property(CSS::PropertyID::Width, *parsed_value);
124
0
            }
125
0
        }
126
0
    });
127
0
}
128
129
// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#attr-object-data
130
String HTMLObjectElement::data() const
131
0
{
132
0
    auto data = get_attribute(HTML::AttributeNames::data);
133
0
    if (!data.has_value())
134
0
        return {};
135
136
0
    return MUST(document().parse_url(*data).to_string());
137
0
}
138
139
JS::GCPtr<Layout::Node> HTMLObjectElement::create_layout_node(NonnullRefPtr<CSS::StyleProperties> style)
140
0
{
141
0
    switch (m_representation) {
142
0
    case Representation::Children:
143
0
        return NavigableContainer::create_layout_node(move(style));
144
0
    case Representation::NestedBrowsingContext:
145
        // FIXME: Actually paint the nested browsing context's document, similar to how iframes are painted with FrameBox and NestedBrowsingContextPaintable.
146
0
        return nullptr;
147
0
    case Representation::Image:
148
0
        if (image_data())
149
0
            return heap().allocate_without_realm<Layout::ImageBox>(document(), *this, move(style), *this);
150
0
        break;
151
0
    default:
152
0
        break;
153
0
    }
154
155
0
    return nullptr;
156
0
}
157
158
bool HTMLObjectElement::has_ancestor_media_element_or_object_element_not_showing_fallback_content() const
159
0
{
160
0
    for (auto const* ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
161
0
        if (is<HTMLMediaElement>(*ancestor))
162
0
            return true;
163
164
0
        if (is<HTMLObjectElement>(*ancestor)) {
165
0
            auto& ancestor_object = static_cast<HTMLObjectElement const&>(*ancestor);
166
0
            if (ancestor_object.m_representation != Representation::Children)
167
0
                return true;
168
0
        }
169
0
    }
170
171
0
    return false;
172
0
}
173
174
// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element:queue-an-element-task
175
void HTMLObjectElement::queue_element_task_to_run_object_representation_steps()
176
0
{
177
0
    queue_an_element_task(HTML::Task::Source::DOMManipulation, [&]() {
178
        // 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.
179
180
        // 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.
181
0
        if (!document().browsing_context() || !document().is_fully_active())
182
0
            return run_object_representation_fallback_steps();
183
0
        if (has_ancestor_media_element_or_object_element_not_showing_fallback_content())
184
0
            return run_object_representation_fallback_steps();
185
186
        // FIXME: 3. If the classid attribute is present, and has a value that isn't the empty string, then: if the user agent can find a plugin suitable according to the value of the classid attribute, and plugins aren't being sandboxed, then that plugin should be used, and the value of the data attribute, if any, should be passed to the plugin. If no suitable plugin can be found, or if the plugin reports an error, jump to the step below labeled fallback.
187
188
        // 4. If the data attribute is present and its value is not the empty string, then:
189
0
        if (auto maybe_data = get_attribute(HTML::AttributeNames::data); maybe_data.has_value() && !maybe_data->is_empty()) {
190
            // 1. If the type attribute is present and its value is not a type that the user agent supports, and is not a type that the user agent can find a plugin for, then the user agent may jump to the step below labeled fallback without fetching the content to examine its real type.
191
192
            // 2. Parse a URL given the data attribute, relative to the element's node document.
193
0
            auto url = document().parse_url(*maybe_data);
194
195
            // 3. If that failed, fire an event named error at the element, then jump to the step below labeled fallback.
196
0
            if (!url.is_valid()) {
197
0
                dispatch_event(DOM::Event::create(realm(), HTML::EventNames::error));
198
0
                return run_object_representation_fallback_steps();
199
0
            }
200
201
            // 4. Let request be a new request whose URL is the resulting URL record, client is the element's node document's relevant settings object, destination is "object", credentials mode is "include", mode is "navigate", and whose use-URL-credentials flag is set.
202
0
            auto request = LoadRequest::create_for_url_on_page(url, &document().page());
203
204
            // 5. Fetch request, with processResponseEndOfBody given response res set to finalize and report timing with res, the element's node document's relevant global object, and "object".
205
            //    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.
206
0
            set_resource(ResourceLoader::the().load_resource(Resource::Type::Generic, request));
207
208
            // 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.
209
210
            // NOTE: The request is always asynchronous, even if it is cached or succeeded/failed immediately. Allow the callbacks below to invoke
211
            //       the fallback steps. This prevents the fallback layout from flashing very briefly between here and the resource loading.
212
0
            return;
213
0
        }
214
215
        // 5. If the data attribute is absent but the type attribute is present, and the user agent can find a plugin suitable according to the value of the type attribute, and plugins aren't being sandboxed, then that plugin should be used. If these conditions cannot be met, or if the plugin reports an error, jump to the step below labeled fallback. Otherwise return; once the plugin 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.
216
0
        run_object_representation_fallback_steps();
217
0
    });
218
0
}
219
220
// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element:concept-event-fire-2
221
void HTMLObjectElement::resource_did_fail()
222
0
{
223
    // 4.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.
224
0
    dispatch_event(DOM::Event::create(realm(), HTML::EventNames::error));
225
0
    run_object_representation_fallback_steps();
226
0
}
227
228
// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#object-type-detection
229
void HTMLObjectElement::resource_did_load()
230
0
{
231
    // 4.8. Determine the resource type, as follows:
232
233
    // 1. Let the resource type be unknown.
234
0
    Optional<String> resource_type;
235
236
    // FIXME: 2. 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.
237
    // FIXME: 3. If there is a type attribute present on the object element, and that attribute's value is not a type that the user agent supports, but it is a type that a plugin supports, then let the resource type be the type specified in that type attribute, and jump to the step below labeled handler.
238
239
    // 4. Run the appropriate set of steps from the following list:
240
    // * If the resource has associated Content-Type metadata
241
0
    if (auto maybe_content_type = resource()->response_headers().get("Content-Type"sv); maybe_content_type.has_value()) {
242
0
        auto& content_type = maybe_content_type.value();
243
244
        // 1. Let binary be false.
245
0
        bool binary = false;
246
247
        // 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.
248
0
        if (content_type == "text/plain"sv) {
249
0
            auto supplied_type = MimeSniff::MimeType::parse(content_type);
250
0
            auto computed_type = MimeSniff::Resource::sniff(resource()->encoded_data(), MimeSniff::SniffingConfiguration {
251
0
                                                                                            .sniffing_context = MimeSniff::SniffingContext::TextOrBinary,
252
0
                                                                                            .supplied_type = move(supplied_type),
253
0
                                                                                        });
254
0
            if (computed_type.essence() != "text/plain"sv)
255
0
                binary = true;
256
0
        }
257
258
        // 3. If the type specified in the resource's Content-Type metadata is "application/octet-stream", then set binary to true.
259
0
        if (content_type == "application/octet-stream"sv)
260
0
            binary = true;
261
262
        // 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.
263
0
        if (!binary)
264
0
            return run_object_representation_handler_steps(content_type);
265
266
        // 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:
267
0
        if (auto type = this->type(); !type.is_empty() && (type != "application/octet-stream"sv)) {
268
            // 1. If the attribute's value is a type that a plugin supports, or 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.
269
            // FIXME: This only partially implements this step.
270
0
            if (type.starts_with_bytes("image/"sv))
271
0
                resource_type = move(type);
272
273
            // 2. Jump to the step below labeled handler.
274
0
        }
275
0
    }
276
    // * Otherwise, if the resource does not have associated Content-Type metadata
277
0
    else {
278
0
        Optional<String> tentative_type;
279
280
        // 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.
281
        //    Otherwise, let tentative type be the computed type of the resource.
282
0
        if (auto type = this->type(); !type.is_empty())
283
0
            tentative_type = move(type);
284
285
        // FIXME: For now, ignore application/ MIME types as we cannot render yet them anyways. We will need to implement the MIME type sniffing
286
        //        algorithm in order to map all unknown MIME types to "application/octet-stream".
287
0
        else if (auto type = resource()->mime_type(); !type.starts_with("application/"sv))
288
0
            tentative_type = MUST(String::from_byte_string(type));
289
290
        // 2. If tentative type is not application/octet-stream, then let resource type be tentative type and jump to the step below labeled handler.
291
0
        if (tentative_type.has_value() && tentative_type != "application/octet-stream"sv)
292
0
            resource_type = move(tentative_type);
293
0
    }
294
295
    // FIXME: 5. If applying the URL parser algorithm to the URL of the specified resource (after any redirects) results in a URL record whose path component matches a pattern that a plugin supports, then let resource type be the type that that plugin can handle.
296
0
    run_object_representation_handler_steps(resource_type.has_value() ? resource_type->to_byte_string() : ByteString::empty());
297
0
}
298
299
// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element:plugin-11
300
void HTMLObjectElement::run_object_representation_handler_steps(Optional<ByteString> resource_type)
301
0
{
302
    // 4.9. Handler: Handle the content as given by the first of the following cases that matches:
303
304
    // * FIXME: If the resource type is not a type that the user agent supports, but it is a type that a plugin supports
305
    //     If the object element's nested browsing context is non-null, then it must be discarded and then set to null.
306
    //     If plugins are being sandboxed, then jump to the step below labeled fallback.
307
    //     Otherwise, the user agent should use the plugin that supports resource type and pass the content of the resource to that plugin. If the plugin reports an error, then jump to the step below labeled fallback.
308
309
0
    if (!resource_type.has_value()) {
310
0
        run_object_representation_fallback_steps();
311
0
        return;
312
0
    }
313
0
    auto mime_type = MimeSniff::MimeType::parse(*resource_type);
314
315
    // * If the resource type is an XML MIME type, or if the resource type does not start with "image/"
316
0
    if (mime_type.has_value() && can_load_document_with_type(*mime_type) && (mime_type->is_xml() || !mime_type->is_image())) {
317
        // If the object element's content navigable is null, then create a new child navigable for the element.
318
0
        if (!m_content_navigable && in_a_document_tree()) {
319
0
            MUST(create_new_child_navigable());
320
0
            set_content_navigable_initialized();
321
0
        }
322
323
        // NOTE: Creating a new nested browsing context can fail if the document is not attached to a browsing context
324
0
        if (!m_content_navigable)
325
0
            return;
326
327
        // If the URL of the given resource does not match about:blank, then navigate the element's nested browsing context to that resource, with historyHandling set to "replace" and the source browsing context set to the object element's node document's browsing context. (The data attribute of the object element doesn't get updated if the browsing context gets further navigated to other locations.)
328
0
        if (auto const& url = resource()->url(); url != "about:blank"sv)
329
0
            MUST(m_content_navigable->navigate({ .url = url,
330
0
                .source_document = document(),
331
0
                .history_handling = Bindings::NavigationHistoryBehavior::Replace }));
332
333
        // The object element represents its nested browsing context.
334
0
        run_object_representation_completed_steps(Representation::NestedBrowsingContext);
335
0
    }
336
337
    // * If the resource type starts with "image/", and support for images has not been disabled
338
    // FIXME: Handle disabling image support.
339
0
    else if (resource_type.has_value() && resource_type->starts_with("image/"sv)) {
340
        // Destroy the child navigable of the object element.
341
0
        destroy_the_child_navigable();
342
343
        // Apply the image sniffing rules to determine the type of the image.
344
        // The object element represents the specified image.
345
        // If the image cannot be rendered, e.g. because it is malformed or in an unsupported format, jump to the step below labeled fallback.
346
0
        if (!resource()->has_encoded_data())
347
0
            return run_object_representation_fallback_steps();
348
349
0
        load_image();
350
0
    }
351
352
    // * Otherwise
353
0
    else {
354
        // The given resource type is not supported. Jump to the step below labeled fallback.
355
0
        run_object_representation_fallback_steps();
356
0
    }
357
0
}
358
359
// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element:the-object-element-19
360
void HTMLObjectElement::run_object_representation_completed_steps(Representation representation)
361
0
{
362
    // 4.10. The element's contents are not part of what the object element represents.
363
    // 4.11. If the object element does not represent its nested browsing context, 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.
364
0
    if (representation != Representation::NestedBrowsingContext) {
365
0
        queue_an_element_task(HTML::Task::Source::DOMManipulation, [&]() {
366
0
            dispatch_event(DOM::Event::create(realm(), HTML::EventNames::load));
367
0
        });
368
0
    }
369
370
0
    update_layout_and_child_objects(representation);
371
372
    // 4.12. Return.
373
0
}
374
375
// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element:the-object-element-23
376
void HTMLObjectElement::run_object_representation_fallback_steps()
377
0
{
378
    // 4. Fallback: The object element represents the element's children. This is the element's fallback content. Destroy the child navigable for the element.
379
0
    destroy_the_child_navigable();
380
381
0
    update_layout_and_child_objects(Representation::Children);
382
0
}
383
384
void HTMLObjectElement::load_image()
385
0
{
386
    // NOTE: This currently reloads the image instead of reusing the resource we've already downloaded.
387
0
    auto data = get_attribute_value(HTML::AttributeNames::data);
388
0
    auto url = document().parse_url(data);
389
0
    m_resource_request = HTML::SharedResourceRequest::get_or_create(realm(), document().page(), url);
390
0
    m_resource_request->add_callbacks(
391
0
        [this] {
392
0
            run_object_representation_completed_steps(Representation::Image);
393
0
        },
394
0
        [this] {
395
0
            run_object_representation_fallback_steps();
396
0
        });
397
398
0
    if (m_resource_request->needs_fetching()) {
399
0
        auto request = HTML::create_potential_CORS_request(vm(), url, Fetch::Infrastructure::Request::Destination::Image, HTML::CORSSettingAttribute::NoCORS);
400
0
        request->set_client(&document().relevant_settings_object());
401
0
        m_resource_request->fetch_resource(realm(), request);
402
0
    }
403
0
}
404
405
void HTMLObjectElement::update_layout_and_child_objects(Representation representation)
406
0
{
407
0
    if ((m_representation == Representation::Children && representation != Representation::Children)
408
0
        || (m_representation != Representation::Children && representation == Representation::Children)) {
409
0
        for_each_child_of_type<HTMLObjectElement>([](auto& object) {
410
0
            object.queue_element_task_to_run_object_representation_steps();
411
0
            return IterationDecision::Continue;
412
0
        });
413
0
    }
414
415
0
    m_representation = representation;
416
0
    invalidate_style(DOM::StyleInvalidationReason::HTMLObjectElementUpdateLayoutAndChildObjects);
417
0
    document().invalidate_layout_tree();
418
0
}
419
420
// https://html.spec.whatwg.org/multipage/interaction.html#dom-tabindex
421
i32 HTMLObjectElement::default_tab_index_value() const
422
0
{
423
    // See the base function for the spec comments.
424
0
    return 0;
425
0
}
426
427
JS::GCPtr<DecodedImageData> HTMLObjectElement::image_data() const
428
0
{
429
0
    if (!m_resource_request)
430
0
        return nullptr;
431
0
    return m_resource_request->image_data();
432
0
}
433
434
bool HTMLObjectElement::is_image_available() const
435
0
{
436
0
    return image_data() != nullptr;
437
0
}
438
439
Optional<CSSPixels> HTMLObjectElement::intrinsic_width() const
440
0
{
441
0
    if (auto image_data = this->image_data())
442
0
        return image_data->intrinsic_width();
443
0
    return {};
444
0
}
445
446
Optional<CSSPixels> HTMLObjectElement::intrinsic_height() const
447
0
{
448
0
    if (auto image_data = this->image_data())
449
0
        return image_data->intrinsic_height();
450
0
    return {};
451
0
}
452
453
Optional<CSSPixelFraction> HTMLObjectElement::intrinsic_aspect_ratio() const
454
0
{
455
0
    if (auto image_data = this->image_data())
456
0
        return image_data->intrinsic_aspect_ratio();
457
0
    return {};
458
0
}
459
460
RefPtr<Gfx::ImmutableBitmap> HTMLObjectElement::current_image_bitmap(Gfx::IntSize size) const
461
0
{
462
0
    if (auto image_data = this->image_data())
463
0
        return image_data->bitmap(0, size);
464
0
    return nullptr;
465
0
}
466
467
void HTMLObjectElement::set_visible_in_viewport(bool)
468
0
{
469
    // FIXME: Loosen grip on image data when it's not visible, e.g via volatile memory.
470
0
}
471
472
}