HTMLObjectElement.cpp 28 KB

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