HTMLObjectElement.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. /*
  2. * Copyright (c) 2020-2024, Andreas Kling <andreas@ladybird.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibGfx/Bitmap.h>
  7. #include <LibWeb/Bindings/HTMLObjectElementPrototype.h>
  8. #include <LibWeb/CSS/StyleComputer.h>
  9. #include <LibWeb/CSS/StyleValues/CSSKeywordValue.h>
  10. #include <LibWeb/CSS/StyleValues/DisplayStyleValue.h>
  11. #include <LibWeb/CSS/StyleValues/LengthStyleValue.h>
  12. #include <LibWeb/DOM/Document.h>
  13. #include <LibWeb/DOM/DocumentLoadEventDelayer.h>
  14. #include <LibWeb/DOM/DocumentLoading.h>
  15. #include <LibWeb/DOM/Event.h>
  16. #include <LibWeb/Fetch/Fetching/Fetching.h>
  17. #include <LibWeb/Fetch/Infrastructure/FetchAlgorithms.h>
  18. #include <LibWeb/Fetch/Infrastructure/HTTP/Requests.h>
  19. #include <LibWeb/HTML/DecodedImageData.h>
  20. #include <LibWeb/HTML/HTMLMediaElement.h>
  21. #include <LibWeb/HTML/HTMLObjectElement.h>
  22. #include <LibWeb/HTML/ImageRequest.h>
  23. #include <LibWeb/HTML/Numbers.h>
  24. #include <LibWeb/HTML/Parser/HTMLParser.h>
  25. #include <LibWeb/HTML/PotentialCORSRequest.h>
  26. #include <LibWeb/Layout/ImageBox.h>
  27. #include <LibWeb/Layout/NavigableContainerViewport.h>
  28. #include <LibWeb/Loader/ResourceLoader.h>
  29. #include <LibWeb/MimeSniff/MimeType.h>
  30. #include <LibWeb/MimeSniff/Resource.h>
  31. namespace Web::HTML {
  32. GC_DEFINE_ALLOCATOR(HTMLObjectElement);
  33. HTMLObjectElement::HTMLObjectElement(DOM::Document& document, DOM::QualifiedName qualified_name)
  34. : NavigableContainer(document, move(qualified_name))
  35. {
  36. // https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element:potentially-delays-the-load-event
  37. set_potentially_delays_the_load_event(true);
  38. // https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element
  39. // Whenever one of the following conditions occur:
  40. // - the element is created,
  41. // ...the user agent must queue an element task on the DOM manipulation task source given the object element to run
  42. // the following steps to (re)determine what the object element represents.
  43. queue_element_task_to_run_object_representation_steps();
  44. }
  45. HTMLObjectElement::~HTMLObjectElement() = default;
  46. void HTMLObjectElement::initialize(JS::Realm& realm)
  47. {
  48. Base::initialize(realm);
  49. WEB_SET_PROTOTYPE_FOR_INTERFACE(HTMLObjectElement);
  50. }
  51. void HTMLObjectElement::visit_edges(Cell::Visitor& visitor)
  52. {
  53. Base::visit_edges(visitor);
  54. visitor.visit(m_resource_request);
  55. }
  56. void HTMLObjectElement::form_associated_element_attribute_changed(FlyString const& name, Optional<String> const&)
  57. {
  58. // https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element
  59. // Whenever one of the following conditions occur:
  60. if (
  61. // - the element's classid attribute is set, changed, or removed,
  62. (name == HTML::AttributeNames::classid) ||
  63. // - the element's classid attribute is not present, and its data attribute is set, changed, or removed,
  64. (!has_attribute(HTML::AttributeNames::classid) && name == HTML::AttributeNames::data) ||
  65. // - neither the element's classid attribute nor its data attribute are present, and its type attribute is set, changed, or removed,
  66. (!has_attribute(HTML::AttributeNames::classid) && !has_attribute(HTML::AttributeNames::data) && name == HTML::AttributeNames::type)) {
  67. // ...the user agent must queue an element task on the DOM manipulation task source given the object element to run
  68. // the following steps to (re)determine what the object element represents.
  69. queue_element_task_to_run_object_representation_steps();
  70. }
  71. }
  72. void HTMLObjectElement::form_associated_element_was_removed(DOM::Node*)
  73. {
  74. destroy_the_child_navigable();
  75. }
  76. void HTMLObjectElement::apply_presentational_hints(CSS::StyleProperties& style) const
  77. {
  78. for_each_attribute([&](auto& name, auto& value) {
  79. if (name == HTML::AttributeNames::align) {
  80. if (value.equals_ignoring_ascii_case("center"sv))
  81. style.set_property(CSS::PropertyID::TextAlign, CSS::CSSKeywordValue::create(CSS::Keyword::Center));
  82. else if (value.equals_ignoring_ascii_case("middle"sv))
  83. style.set_property(CSS::PropertyID::TextAlign, CSS::CSSKeywordValue::create(CSS::Keyword::Middle));
  84. } else if (name == HTML::AttributeNames::border) {
  85. if (auto parsed_value = parse_non_negative_integer(value); parsed_value.has_value()) {
  86. auto width_style_value = CSS::LengthStyleValue::create(CSS::Length::make_px(*parsed_value));
  87. style.set_property(CSS::PropertyID::BorderTopWidth, width_style_value);
  88. style.set_property(CSS::PropertyID::BorderRightWidth, width_style_value);
  89. style.set_property(CSS::PropertyID::BorderBottomWidth, width_style_value);
  90. style.set_property(CSS::PropertyID::BorderLeftWidth, width_style_value);
  91. auto border_style_value = CSS::CSSKeywordValue::create(CSS::Keyword::Solid);
  92. style.set_property(CSS::PropertyID::BorderTopStyle, border_style_value);
  93. style.set_property(CSS::PropertyID::BorderRightStyle, border_style_value);
  94. style.set_property(CSS::PropertyID::BorderBottomStyle, border_style_value);
  95. style.set_property(CSS::PropertyID::BorderLeftStyle, border_style_value);
  96. }
  97. }
  98. // https://html.spec.whatwg.org/multipage/rendering.html#attributes-for-embedded-content-and-images:maps-to-the-dimension-property-3
  99. else if (name == HTML::AttributeNames::height) {
  100. if (auto parsed_value = parse_dimension_value(value)) {
  101. style.set_property(CSS::PropertyID::Height, *parsed_value);
  102. }
  103. }
  104. // https://html.spec.whatwg.org/multipage/rendering.html#attributes-for-embedded-content-and-images:maps-to-the-dimension-property
  105. else if (name == HTML::AttributeNames::hspace) {
  106. if (auto parsed_value = parse_dimension_value(value)) {
  107. style.set_property(CSS::PropertyID::MarginLeft, *parsed_value);
  108. style.set_property(CSS::PropertyID::MarginRight, *parsed_value);
  109. }
  110. } else if (name == HTML::AttributeNames::vspace) {
  111. if (auto parsed_value = parse_dimension_value(value)) {
  112. style.set_property(CSS::PropertyID::MarginTop, *parsed_value);
  113. style.set_property(CSS::PropertyID::MarginBottom, *parsed_value);
  114. }
  115. } else if (name == HTML::AttributeNames::width) {
  116. if (auto parsed_value = parse_dimension_value(value)) {
  117. style.set_property(CSS::PropertyID::Width, *parsed_value);
  118. }
  119. }
  120. });
  121. }
  122. // https://html.spec.whatwg.org/multipage/iframe-embed-object.html#attr-object-data
  123. String HTMLObjectElement::data() const
  124. {
  125. auto data = get_attribute(HTML::AttributeNames::data);
  126. if (!data.has_value())
  127. return {};
  128. return document().encoding_parse_url(*data).to_string();
  129. }
  130. GC::Ptr<Layout::Node> HTMLObjectElement::create_layout_node(CSS::StyleProperties style)
  131. {
  132. switch (m_representation) {
  133. case Representation::Children:
  134. return NavigableContainer::create_layout_node(move(style));
  135. case Representation::ContentNavigable:
  136. return heap().allocate<Layout::NavigableContainerViewport>(document(), *this, move(style));
  137. case Representation::Image:
  138. if (image_data())
  139. return heap().allocate<Layout::ImageBox>(document(), *this, move(style), *this);
  140. break;
  141. default:
  142. break;
  143. }
  144. return nullptr;
  145. }
  146. void HTMLObjectElement::adjust_computed_style(CSS::StyleProperties& style)
  147. {
  148. // https://drafts.csswg.org/css-display-3/#unbox
  149. if (style.display().is_contents())
  150. style.set_property(CSS::PropertyID::Display, CSS::DisplayStyleValue::create(CSS::Display::from_short(CSS::Display::Short::None)));
  151. }
  152. bool HTMLObjectElement::has_ancestor_media_element_or_object_element_not_showing_fallback_content() const
  153. {
  154. for (auto const* ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
  155. if (is<HTMLMediaElement>(*ancestor))
  156. return true;
  157. if (is<HTMLObjectElement>(*ancestor)) {
  158. auto& ancestor_object = static_cast<HTMLObjectElement const&>(*ancestor);
  159. if (ancestor_object.m_representation != Representation::Children)
  160. return true;
  161. }
  162. }
  163. return false;
  164. }
  165. // https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element:queue-an-element-task
  166. void HTMLObjectElement::queue_element_task_to_run_object_representation_steps()
  167. {
  168. // This task being queued or actively running must delay the load event of the element's node document.
  169. m_document_load_event_delayer_for_object_representation_task.emplace(document());
  170. queue_an_element_task(HTML::Task::Source::DOMManipulation, [this]() {
  171. ScopeGuard guard { [&]() { m_document_load_event_delayer_for_object_representation_task.clear(); } };
  172. auto& realm = this->realm();
  173. auto& vm = realm.vm();
  174. // FIXME: 1. If the user has indicated a preference that this object element's fallback content be shown instead of the
  175. // element's usual behavior, then jump to the step below labeled fallback.
  176. // 2. If the element has an ancestor media element, or has an ancestor object element that is not showing its
  177. // fallback content, or if the element is not in a document whose browsing context is non-null, or if the
  178. // element's node document is not fully active, or if the element is still in the stack of open elements of
  179. // an HTML parser or XML parser, or if the element is not being rendered, then jump to the step below labeled
  180. // fallback.
  181. // FIXME: Handle the element being in the stack of open elements.
  182. // FIXME: Handle the element not being rendered.
  183. if (!document().browsing_context() || !document().is_fully_active()) {
  184. run_object_representation_fallback_steps();
  185. return;
  186. }
  187. if (has_ancestor_media_element_or_object_element_not_showing_fallback_content()) {
  188. run_object_representation_fallback_steps();
  189. return;
  190. }
  191. // 3. If the data attribute is present and its value is not the empty string, then:
  192. if (auto data = get_attribute(HTML::AttributeNames::data); data.has_value() && !data->is_empty()) {
  193. // 1. If the type attribute is present and its value is not a type that the user agent supports, then the user
  194. // agent may jump to the step below labeled fallback without fetching the content to examine its real type.
  195. // 2. Let url be the result of encoding-parsing a URL given the data attribute's value, relative to the element's node document.
  196. auto url = document().encoding_parse_url(*data);
  197. // 3. If url is failure, then fire an event named error at the element and jump to the step below labeled fallback.
  198. if (!url.is_valid()) {
  199. dispatch_event(DOM::Event::create(realm, HTML::EventNames::error));
  200. run_object_representation_fallback_steps();
  201. return;
  202. }
  203. // 4. Let request be a new request whose URL is url, client is the element's node document's relevant settings
  204. // object, destination is "object", credentials mode is "include", mode is "navigate", initiator type is
  205. // "object", and whose use-URL-credentials flag is set.
  206. auto request = Fetch::Infrastructure::Request::create(vm);
  207. request->set_url(move(url));
  208. request->set_client(&document().relevant_settings_object());
  209. request->set_destination(Fetch::Infrastructure::Request::Destination::Object);
  210. request->set_credentials_mode(Fetch::Infrastructure::Request::CredentialsMode::Include);
  211. request->set_mode(Fetch::Infrastructure::Request::Mode::Navigate);
  212. request->set_initiator_type(Fetch::Infrastructure::Request::InitiatorType::Object);
  213. request->set_use_url_credentials(true);
  214. Fetch::Infrastructure::FetchAlgorithms::Input fetch_algorithms_input {};
  215. fetch_algorithms_input.process_response = [this](GC::Ref<Fetch::Infrastructure::Response> response) {
  216. auto& realm = this->realm();
  217. auto& global = document().realm().global_object();
  218. if (response->is_network_error()) {
  219. resource_did_fail();
  220. return;
  221. }
  222. if (response->type() == Fetch::Infrastructure::Response::Type::Opaque || response->type() == Fetch::Infrastructure::Response::Type::OpaqueRedirect) {
  223. auto& filtered_response = static_cast<Fetch::Infrastructure::FilteredResponse&>(*response);
  224. response = filtered_response.internal_response();
  225. }
  226. auto on_data_read = GC::create_function(realm.heap(), [this, response](ByteBuffer data) {
  227. resource_did_load(response, data);
  228. });
  229. auto on_error = GC::create_function(realm.heap(), [this](JS::Value) {
  230. resource_did_fail();
  231. });
  232. VERIFY(response->body());
  233. response->body()->fully_read(realm, on_data_read, on_error, GC::Ref { global });
  234. };
  235. // 5. Fetch request.
  236. auto result = Fetch::Fetching::fetch(realm, request, Fetch::Infrastructure::FetchAlgorithms::create(vm, move(fetch_algorithms_input)));
  237. if (result.is_error()) {
  238. resource_did_fail();
  239. return;
  240. }
  241. // Fetching the resource must delay the load event of the element's node document until the task that is
  242. // queued by the networking task source once the resource has been fetched (defined next) has been run.
  243. m_document_load_event_delayer_for_resource_load.emplace(document());
  244. // 6. If the resource is not yet available (e.g. because the resource was not available in the cache, so that
  245. // loading the resource required making a request over the network), then jump to the step below labeled
  246. // fallback. The task that is queued by the networking task source once the resource is available must
  247. // restart this algorithm from this step. Resources can load incrementally; user agents may opt to consider
  248. // a resource "available" whenever enough data has been obtained to begin processing the resource.
  249. // NOTE: The request is always asynchronous, even if it is cached or succeeded/failed immediately. Allow the
  250. // response callback to invoke the fallback steps. This prevents the fallback layout from flashing very
  251. // briefly between here and the resource loading.
  252. }
  253. });
  254. }
  255. // https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element:concept-event-fire-2
  256. void HTMLObjectElement::resource_did_fail()
  257. {
  258. ScopeGuard guard { [&]() { m_document_load_event_delayer_for_resource_load.clear(); } };
  259. // 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
  260. // the element, then jump to the step below labeled fallback.
  261. dispatch_event(DOM::Event::create(realm(), HTML::EventNames::error));
  262. run_object_representation_fallback_steps();
  263. }
  264. // https://html.spec.whatwg.org/multipage/iframe-embed-object.html#object-type-detection
  265. void HTMLObjectElement::resource_did_load(Fetch::Infrastructure::Response const& response, ReadonlyBytes data)
  266. {
  267. ScopeGuard guard { [&]() { m_document_load_event_delayer_for_resource_load.clear(); } };
  268. // 3.8. Determine the resource type, as follows:
  269. // 1. Let the resource type be unknown.
  270. Optional<MimeSniff::MimeType> resource_type;
  271. // FIXME: 3. If the user agent is configured to strictly obey Content-Type headers for this resource, and the resource has
  272. // associated Content-Type metadata, then let the resource type be the type specified in the resource's Content-Type
  273. // metadata, and jump to the step below labeled handler.
  274. // 3. Run the appropriate set of steps from the following list:
  275. // -> If the resource has associated Content-Type metadata
  276. if (auto content_type = response.header_list()->extract_mime_type(); content_type.has_value()) {
  277. // 1. Let binary be false.
  278. bool binary = false;
  279. // 2. If the type specified in the resource's Content-Type metadata is "text/plain", and the result of applying
  280. // the rules for distinguishing if a resource is text or binary to the resource is that the resource is not
  281. // text/plain, then set binary to true.
  282. if (content_type->essence() == "text/plain"sv) {
  283. auto computed_type = MimeSniff::Resource::sniff(
  284. data,
  285. MimeSniff::SniffingConfiguration {
  286. .sniffing_context = MimeSniff::SniffingContext::TextOrBinary,
  287. .supplied_type = content_type,
  288. });
  289. if (computed_type.essence() != "text/plain"sv)
  290. binary = true;
  291. }
  292. // 3. If the type specified in the resource's Content-Type metadata is "application/octet-stream", then set binary to true.
  293. else if (content_type->essence() == "application/octet-stream"sv) {
  294. binary = true;
  295. }
  296. // 4. If binary is false, then let the resource type be the type specified in the resource's Content-Type metadata,
  297. // and jump to the step below labeled handler.
  298. if (!binary) {
  299. resource_type = move(content_type);
  300. }
  301. // 5. If there is a type attribute present on the object element, and its value is not application/octet-stream,
  302. // then run the following steps:
  303. else if (auto type = this->type(); !type.is_empty() && (type != "application/octet-stream"sv)) {
  304. // 1. If the attribute's value is a type that starts with "image/" that is not also an XML MIME type, then
  305. // let the resource type be the type specified in that type attribute.
  306. if (type.starts_with_bytes("image/"sv)) {
  307. auto parsed_type = MimeSniff::MimeType::parse(type);
  308. if (parsed_type.has_value() && !parsed_type->is_xml())
  309. resource_type = move(parsed_type);
  310. }
  311. // 2. Jump to the step below labeled handler.
  312. }
  313. }
  314. // -> Otherwise, if the resource does not have associated Content-Type metadata
  315. else {
  316. Optional<MimeSniff::MimeType> tentative_type;
  317. // 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.
  318. // Otherwise, let tentative type be the computed type of the resource.
  319. if (auto type = this->type(); !type.is_empty())
  320. tentative_type = MimeSniff::MimeType::parse(type);
  321. else
  322. tentative_type = MimeSniff::Resource::sniff(data);
  323. // 2. If tentative type is not application/octet-stream, then let resource type be tentative type and jump to the
  324. // step below labeled handler.
  325. if (tentative_type.has_value() && tentative_type->essence() != "application/octet-stream"sv)
  326. resource_type = move(tentative_type);
  327. }
  328. if (resource_type.has_value())
  329. run_object_representation_handler_steps(response, *resource_type, data);
  330. else
  331. run_object_representation_fallback_steps();
  332. }
  333. // https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element:plugin-11
  334. void HTMLObjectElement::run_object_representation_handler_steps(Fetch::Infrastructure::Response const& response, MimeSniff::MimeType const& resource_type, ReadonlyBytes data)
  335. {
  336. // 3.9. Handler: Handle the content as given by the first of the following cases that matches:
  337. // -> If the resource type is an XML MIME type, or if the resource type does not start with "image/"
  338. if (can_load_document_with_type(resource_type) && (resource_type.is_xml() || !resource_type.is_image())) {
  339. // If the object element's content navigable is null, then create a new child navigable for the element.
  340. if (!m_content_navigable && in_a_document_tree()) {
  341. MUST(create_new_child_navigable());
  342. set_content_navigable_initialized();
  343. }
  344. // NOTE: Creating a new nested browsing context can fail if the document is not attached to a browsing context
  345. if (!m_content_navigable)
  346. return;
  347. // Let response be the response from fetch.
  348. // If response's URL does not match about:blank, then navigate the element's content navigable to response's URL
  349. // using the element's node document, with historyHandling set to "replace".
  350. if (response.url().has_value() && !url_matches_about_blank(*response.url())) {
  351. MUST(m_content_navigable->navigate({
  352. .url = *response.url(),
  353. .source_document = document(),
  354. .history_handling = Bindings::NavigationHistoryBehavior::Replace,
  355. }));
  356. }
  357. // The object element represents its content navigable.
  358. run_object_representation_completed_steps(Representation::ContentNavigable);
  359. }
  360. // -> If the resource type starts with "image/", and support for images has not been disabled
  361. // FIXME: Handle disabling image support.
  362. else if (resource_type.is_image()) {
  363. // Destroy a child navigable given the object element.
  364. destroy_the_child_navigable();
  365. // Apply the image sniffing rules to determine the type of the image.
  366. // The object element represents the specified image.
  367. // If the image cannot be rendered, e.g. because it is malformed or in an unsupported format, jump to the step
  368. // below labeled fallback.
  369. if (data.is_empty()) {
  370. run_object_representation_fallback_steps();
  371. return;
  372. }
  373. load_image();
  374. }
  375. // -> Otherwise
  376. else {
  377. // The given resource type is not supported. Jump to the step below labeled fallback.
  378. run_object_representation_fallback_steps();
  379. }
  380. }
  381. // https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element:the-object-element-19
  382. void HTMLObjectElement::run_object_representation_completed_steps(Representation representation)
  383. {
  384. // 3.10. The element's contents are not part of what the object element represents.
  385. // 3.11. If the object element does not represent its content navigable, then once the resource is completely loaded,
  386. // queue an element task on the DOM manipulation task source given the object element to fire an event named
  387. // load at the element.
  388. if (representation != Representation::ContentNavigable) {
  389. queue_an_element_task(HTML::Task::Source::DOMManipulation, [&]() {
  390. dispatch_event(DOM::Event::create(realm(), HTML::EventNames::load));
  391. });
  392. }
  393. update_layout_and_child_objects(representation);
  394. // 3.12. Return.
  395. }
  396. // https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element:the-object-element-23
  397. void HTMLObjectElement::run_object_representation_fallback_steps()
  398. {
  399. // 4. Fallback: The object element represents the element's children. This is the element's fallback content.
  400. // Destroy a child navigable given the element.
  401. destroy_the_child_navigable();
  402. update_layout_and_child_objects(Representation::Children);
  403. }
  404. void HTMLObjectElement::load_image()
  405. {
  406. // FIXME: This currently reloads the image instead of reusing the resource we've already downloaded.
  407. auto data = get_attribute_value(HTML::AttributeNames::data);
  408. auto url = document().encoding_parse_url(data);
  409. m_resource_request = HTML::SharedResourceRequest::get_or_create(realm(), document().page(), url);
  410. m_resource_request->add_callbacks(
  411. [this] {
  412. run_object_representation_completed_steps(Representation::Image);
  413. },
  414. [this] {
  415. run_object_representation_fallback_steps();
  416. });
  417. if (m_resource_request->needs_fetching()) {
  418. auto request = HTML::create_potential_CORS_request(vm(), url, Fetch::Infrastructure::Request::Destination::Image, HTML::CORSSettingAttribute::NoCORS);
  419. request->set_client(&document().relevant_settings_object());
  420. m_resource_request->fetch_resource(realm(), request);
  421. }
  422. }
  423. void HTMLObjectElement::update_layout_and_child_objects(Representation representation)
  424. {
  425. if (representation == Representation::Children) {
  426. for_each_child_of_type<HTMLObjectElement>([](auto& object) {
  427. object.queue_element_task_to_run_object_representation_steps();
  428. return IterationDecision::Continue;
  429. });
  430. }
  431. m_representation = representation;
  432. invalidate_style(DOM::StyleInvalidationReason::HTMLObjectElementUpdateLayoutAndChildObjects);
  433. document().invalidate_layout_tree();
  434. }
  435. // https://html.spec.whatwg.org/multipage/interaction.html#dom-tabindex
  436. i32 HTMLObjectElement::default_tab_index_value() const
  437. {
  438. // See the base function for the spec comments.
  439. return 0;
  440. }
  441. GC::Ptr<DecodedImageData> HTMLObjectElement::image_data() const
  442. {
  443. if (!m_resource_request)
  444. return nullptr;
  445. return m_resource_request->image_data();
  446. }
  447. bool HTMLObjectElement::is_image_available() const
  448. {
  449. return image_data() != nullptr;
  450. }
  451. Optional<CSSPixels> HTMLObjectElement::intrinsic_width() const
  452. {
  453. if (auto image_data = this->image_data())
  454. return image_data->intrinsic_width();
  455. return {};
  456. }
  457. Optional<CSSPixels> HTMLObjectElement::intrinsic_height() const
  458. {
  459. if (auto image_data = this->image_data())
  460. return image_data->intrinsic_height();
  461. return {};
  462. }
  463. Optional<CSSPixelFraction> HTMLObjectElement::intrinsic_aspect_ratio() const
  464. {
  465. if (auto image_data = this->image_data())
  466. return image_data->intrinsic_aspect_ratio();
  467. return {};
  468. }
  469. RefPtr<Gfx::ImmutableBitmap> HTMLObjectElement::current_image_bitmap(Gfx::IntSize size) const
  470. {
  471. if (auto image_data = this->image_data())
  472. return image_data->bitmap(0, size);
  473. return nullptr;
  474. }
  475. void HTMLObjectElement::set_visible_in_viewport(bool)
  476. {
  477. // FIXME: Loosen grip on image data when it's not visible, e.g via volatile memory.
  478. }
  479. }