HTMLObjectElement.cpp 28 KB

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