HTMLLinkElement.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. /*
  2. * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2021, the SerenityOS developers.
  4. * Copyright (c) 2021, Sam Atkins <atkinssj@serenityos.org>
  5. * Copyright (c) 2023, Srikavin Ramkumar <me@srikavin.me>
  6. *
  7. * SPDX-License-Identifier: BSD-2-Clause
  8. */
  9. #include <AK/ByteBuffer.h>
  10. #include <AK/Debug.h>
  11. #include <AK/URL.h>
  12. #include <LibWeb/CSS/Parser/Parser.h>
  13. #include <LibWeb/DOM/Document.h>
  14. #include <LibWeb/DOM/Event.h>
  15. #include <LibWeb/Fetch/Fetching/Fetching.h>
  16. #include <LibWeb/Fetch/Infrastructure/FetchAlgorithms.h>
  17. #include <LibWeb/Fetch/Infrastructure/HTTP/Requests.h>
  18. #include <LibWeb/Fetch/Infrastructure/HTTP/Responses.h>
  19. #include <LibWeb/HTML/EventNames.h>
  20. #include <LibWeb/HTML/HTMLLinkElement.h>
  21. #include <LibWeb/HTML/PotentialCORSRequest.h>
  22. #include <LibWeb/Infra/CharacterTypes.h>
  23. #include <LibWeb/Loader/ResourceLoader.h>
  24. #include <LibWeb/Page/Page.h>
  25. #include <LibWeb/Platform/ImageCodecPlugin.h>
  26. namespace Web::HTML {
  27. HTMLLinkElement::HTMLLinkElement(DOM::Document& document, DOM::QualifiedName qualified_name)
  28. : HTMLElement(document, move(qualified_name))
  29. {
  30. }
  31. HTMLLinkElement::~HTMLLinkElement() = default;
  32. JS::ThrowCompletionOr<void> HTMLLinkElement::initialize(JS::Realm& realm)
  33. {
  34. MUST_OR_THROW_OOM(Base::initialize(realm));
  35. set_prototype(&Bindings::ensure_web_prototype<Bindings::HTMLLinkElementPrototype>(realm, "HTMLLinkElement"));
  36. return {};
  37. }
  38. void HTMLLinkElement::inserted()
  39. {
  40. HTMLElement::inserted();
  41. // FIXME: Handle alternate stylesheets properly
  42. if (m_relationship & Relationship::Stylesheet && !(m_relationship & Relationship::Alternate)) {
  43. // https://html.spec.whatwg.org/multipage/links.html#link-type-stylesheet:fetch-and-process-the-linked-resource
  44. // The appropriate times to fetch and process this type of link are:
  45. // - When the external resource link is created on a link element that is already browsing-context connected.
  46. // - When the external resource link's link element becomes browsing-context connected.
  47. fetch_and_process_linked_resource();
  48. }
  49. // FIXME: Follow spec for fetching and processing these attributes as well
  50. if (m_relationship & Relationship::Preload) {
  51. // FIXME: Respect the "as" attribute.
  52. LoadRequest request;
  53. request.set_url(document().parse_url(attribute(HTML::AttributeNames::href)));
  54. m_preload_resource = ResourceLoader::the().load_resource(Resource::Type::Generic, request);
  55. } else if (m_relationship & Relationship::DNSPrefetch) {
  56. ResourceLoader::the().prefetch_dns(document().parse_url(attribute(HTML::AttributeNames::href)));
  57. } else if (m_relationship & Relationship::Preconnect) {
  58. ResourceLoader::the().preconnect(document().parse_url(attribute(HTML::AttributeNames::href)));
  59. } else if (m_relationship & Relationship::Icon) {
  60. auto favicon_url = document().parse_url(href());
  61. auto favicon_request = LoadRequest::create_for_url_on_page(favicon_url, document().page());
  62. set_resource(ResourceLoader::the().load_resource(Resource::Type::Generic, favicon_request));
  63. }
  64. }
  65. bool HTMLLinkElement::has_loaded_icon() const
  66. {
  67. return m_relationship & Relationship::Icon && resource() && resource()->is_loaded() && resource()->has_encoded_data();
  68. }
  69. void HTMLLinkElement::parse_attribute(DeprecatedFlyString const& name, DeprecatedString const& value)
  70. {
  71. // 4.6.7 Link types - https://html.spec.whatwg.org/multipage/links.html#linkTypes
  72. if (name == HTML::AttributeNames::rel) {
  73. m_relationship = 0;
  74. // Keywords are always ASCII case-insensitive, and must be compared as such.
  75. auto lowercased_value = value.to_lowercase();
  76. // To determine which link types apply to a link, a, area, or form element,
  77. // the element's rel attribute must be split on ASCII whitespace.
  78. // The resulting tokens are the keywords for the link types that apply to that element.
  79. auto parts = lowercased_value.split_view(Infra::is_ascii_whitespace);
  80. for (auto& part : parts) {
  81. if (part == "stylesheet"sv)
  82. m_relationship |= Relationship::Stylesheet;
  83. else if (part == "alternate"sv)
  84. m_relationship |= Relationship::Alternate;
  85. else if (part == "preload"sv)
  86. m_relationship |= Relationship::Preload;
  87. else if (part == "dns-prefetch"sv)
  88. m_relationship |= Relationship::DNSPrefetch;
  89. else if (part == "preconnect"sv)
  90. m_relationship |= Relationship::Preconnect;
  91. else if (part == "icon"sv)
  92. m_relationship |= Relationship::Icon;
  93. }
  94. }
  95. if (m_relationship & Relationship::Stylesheet) {
  96. if (name == HTML::AttributeNames::disabled && m_loaded_style_sheet)
  97. document().style_sheets().remove_sheet(*m_loaded_style_sheet);
  98. // https://html.spec.whatwg.org/multipage/links.html#link-type-stylesheet:fetch-and-process-the-linked-resource
  99. // The appropriate times to fetch and process this type of link are:
  100. if (
  101. // - When the href attribute of the link element of an external resource link that is already browsing-context connected is changed.
  102. name == AttributeNames::href ||
  103. // - When the disabled attribute of the link element of an external resource link that is already browsing-context connected is set, changed, or removed.
  104. name == AttributeNames::disabled ||
  105. // - When the crossorigin attribute of the link element of an external resource link that is already browsing-context connected is set, changed, or removed.
  106. name == AttributeNames::crossorigin
  107. // FIXME: - When the type attribute of the link element of an external resource link that is already browsing-context connected is set or changed to a value that does not or no longer matches the Content-Type metadata of the previous obtained external resource, if any.
  108. // FIXME: - When the type attribute of the link element of an external resource link that is already browsing-context connected, but was previously not obtained due to the type attribute specifying an unsupported type, is removed or changed.
  109. ) {
  110. fetch_and_process_linked_resource();
  111. }
  112. }
  113. }
  114. void HTMLLinkElement::resource_did_fail()
  115. {
  116. dbgln_if(CSS_LOADER_DEBUG, "HTMLLinkElement: Resource did fail. URL: {}", resource()->url());
  117. }
  118. void HTMLLinkElement::resource_did_load()
  119. {
  120. VERIFY(resource());
  121. VERIFY(m_relationship & (Relationship::Icon));
  122. if (m_relationship & Relationship::Icon) {
  123. resource_did_load_favicon();
  124. m_document_load_event_delayer.clear();
  125. }
  126. }
  127. void HTMLLinkElement::did_remove_attribute(DeprecatedFlyString const& attr)
  128. {
  129. if (m_relationship & Relationship::Stylesheet) {
  130. // https://html.spec.whatwg.org/multipage/links.html#link-type-stylesheet:fetch-and-process-the-linked-resource
  131. // The appropriate times to fetch and process this type of link are:
  132. if (
  133. // - When the href attribute of the link element of an external resource link that is already browsing-context connected is changed.
  134. attr == AttributeNames::href ||
  135. // - When the disabled attribute of the link element of an external resource link that is already browsing-context connected is set, changed, or removed.
  136. attr == AttributeNames::disabled ||
  137. // - When the crossorigin attribute of the link element of an external resource link that is already browsing-context connected is set, changed, or removed.
  138. attr == AttributeNames::crossorigin
  139. // FIXME: - When the type attribute of the link element of an external resource link that is already browsing-context connected, but was previously not obtained due to the type attribute specifying an unsupported type, is removed or changed.
  140. ) {
  141. fetch_and_process_linked_resource();
  142. }
  143. }
  144. }
  145. // https://html.spec.whatwg.org/multipage/semantics.html#create-link-options-from-element
  146. HTMLLinkElement::LinkProcessingOptions HTMLLinkElement::create_link_options()
  147. {
  148. // 1. Let document be el's node document.
  149. auto& document = this->document();
  150. // 2. Let options be a new link processing options with
  151. LinkProcessingOptions options;
  152. // FIXME: destination the result of translating the state of el's as attribute
  153. // crossorigin the state of el's crossorigin content attribute
  154. options.crossorigin = cors_setting_attribute_from_keyword(
  155. has_attribute(AttributeNames::crossorigin) ? String::from_deprecated_string(get_attribute(AttributeNames::crossorigin)).release_value_but_fixme_should_propagate_errors()
  156. : Optional<String> {});
  157. // FIXME: referrer policy the state of el's referrerpolicy content attribute
  158. // FIXME: source set el's source set
  159. // base URL document's URL
  160. options.base_url = document.url();
  161. // origin document's origin
  162. options.origin = document.origin();
  163. // environment document's relevant settings object
  164. options.environment = &document.relevant_settings_object();
  165. // policy container document's policy container
  166. options.policy_container = document.policy_container();
  167. // document document
  168. options.document = &document;
  169. // FIXME: cryptographic nonce metadata The current value of el's [[CryptographicNonce]] internal slot
  170. // 3. If el has an href attribute, then set options's href to the value of el's href attribute.
  171. if (has_attribute(AttributeNames::href))
  172. options.href = String::from_deprecated_string(get_attribute(AttributeNames::href)).release_value_but_fixme_should_propagate_errors();
  173. // 4. If el has an integrity attribute, then set options's integrity to the value of el's integrity content attribute.
  174. if (has_attribute(AttributeNames::integrity))
  175. options.integrity = String::from_deprecated_string(get_attribute(AttributeNames::integrity)).release_value_but_fixme_should_propagate_errors();
  176. // 5. If el has a type attribute, then set options's type to the value of el's type attribute.
  177. if (has_attribute(AttributeNames::type))
  178. options.type = String::from_deprecated_string(get_attribute(AttributeNames::type)).release_value_but_fixme_should_propagate_errors();
  179. // FIXME: 6. Assert: options's href is not the empty string, or options's source set is not null.
  180. // A link element with neither an href or an imagesrcset does not represent a link.
  181. // 7. Return options.
  182. return options;
  183. }
  184. // https://html.spec.whatwg.org/multipage/semantics.html#create-a-link-request
  185. JS::GCPtr<Fetch::Infrastructure::Request> HTMLLinkElement::create_link_request(HTMLLinkElement::LinkProcessingOptions const& options)
  186. {
  187. // 1. Assert: options's href is not the empty string.
  188. // FIXME: 2. If options's destination is not a destination, then return null.
  189. // 3. Parse a URL given options's href, relative to options's base URL. If that fails, then return null. Otherwise, let url be the resulting URL record.
  190. auto url = options.base_url.complete_url(options.href);
  191. if (!url.is_valid())
  192. return nullptr;
  193. // 4. Let request be the result of creating a potential-CORS request given url, options's destination, and options's crossorigin.
  194. auto request = create_potential_CORS_request(vm(), url, options.destination, options.crossorigin);
  195. // 5. Set request's policy container to options's policy container.
  196. request->set_policy_container(options.policy_container);
  197. // 6. Set request's integrity metadata to options's integrity.
  198. request->set_integrity_metadata(options.integrity);
  199. // 7. Set request's cryptographic nonce metadata to options's cryptographic nonce metadata.
  200. request->set_cryptographic_nonce_metadata(options.cryptographic_nonce_metadata);
  201. // 8. Set request's referrer policy to options's referrer policy.
  202. request->set_referrer_policy(options.referrer_policy);
  203. // 9. Set request's client to options's environment.
  204. request->set_client(options.environment);
  205. // 10. Return request.
  206. return request;
  207. }
  208. // https://html.spec.whatwg.org/multipage/semantics.html#fetch-and-process-the-linked-resource
  209. void HTMLLinkElement::fetch_and_process_linked_resource()
  210. {
  211. default_fetch_and_process_linked_resource();
  212. }
  213. // https://html.spec.whatwg.org/multipage/semantics.html#default-fetch-and-process-the-linked-resource
  214. void HTMLLinkElement::default_fetch_and_process_linked_resource()
  215. {
  216. // https://html.spec.whatwg.org/multipage/semantics.html#the-link-element:attr-link-href-4
  217. // If both the href and imagesrcset attributes are absent, then the element does not define a link.
  218. // FIXME: Support imagesrcset attribute
  219. if (!has_attribute(AttributeNames::href) || href().is_empty())
  220. return;
  221. // 1. Let options be the result of creating link options from el.
  222. auto options = create_link_options();
  223. // 2. Let request be the result of creating a link request given options.
  224. auto request = create_link_request(options);
  225. // 3. If request is null, then return.
  226. if (request == nullptr) {
  227. return;
  228. }
  229. // FIXME: 4. Set request's synchronous flag.
  230. // 5. Run the linked resource fetch setup steps, given el and request. If the result is false, then return.
  231. if (!linked_resource_fetch_setup_steps(*request))
  232. return;
  233. // 6. Set request's initiator type to "css" if el's rel attribute contains the keyword stylesheet; "link" otherwise.
  234. if (m_relationship & Relationship::Stylesheet) {
  235. request->set_initiator_type(Fetch::Infrastructure::Request::InitiatorType::CSS);
  236. } else {
  237. request->set_initiator_type(Fetch::Infrastructure::Request::InitiatorType::Link);
  238. }
  239. // 7. Fetch request with processResponseConsumeBody set to the following steps given response response and null, failure, or a byte sequence bodyBytes:
  240. Fetch::Fetching::fetch(
  241. realm(), *request,
  242. Fetch::Infrastructure::FetchAlgorithms::create(vm(),
  243. { .process_request_body_chunk_length = {},
  244. .process_request_end_of_body = {},
  245. .process_early_hints_response = {},
  246. .process_response = {},
  247. .process_response_end_of_body = {},
  248. .process_response_consume_body = [this, hr = options](auto response, auto body_bytes) {
  249. // 1. Let success be true.
  250. bool success = true;
  251. // 2. If either of the following conditions are met:
  252. // - bodyBytes is null or failure; or
  253. // - response's status is not an ok status,
  254. // then set success to false.
  255. // NOTE: content-specific errors, e.g., CSS parse errors or PNG decoding errors, do not affect success.
  256. if (body_bytes.template has<Empty>()) {
  257. // CORS cross-origin responses in the No CORS request mode provide an opaque filtered response, which is the original response
  258. // with certain attributes removed/changed.
  259. // The relevant effect it has is setting the body to `null`, which means `body_bytes` has `Empty` here. This effectively
  260. // disables cross-origin linked resources (e.g. stylesheets).
  261. // However, the web actually depends on this, especially for stylesheets retrieved from a cross-origin CDN. For example,
  262. // Shopify websites request stylesheets from `cdn.shopify.com` and Substack websites request stylesheets from `substackcdn.com`.
  263. // This makes this a specification bug, as this code was written from it.
  264. // The workaround is to read the actual body from the unfiltered response and then call `process_linked_resource` from there.
  265. // This _should_ be safe to do, as linked resource fetches do not include credentials (i.e. cookies and the Authorization
  266. // header), so it cannot provide personalized responses.
  267. // FIXME: Replace this workaround with a proper fix that has landed in the specification.
  268. // See: https://github.com/whatwg/html/issues/9066
  269. if (is<Fetch::Infrastructure::OpaqueFilteredResponse>(response.ptr())) {
  270. auto unsafe_response = static_cast<Fetch::Infrastructure::OpaqueFilteredResponse const&>(*response).internal_response();
  271. if (unsafe_response->body().has_value()) {
  272. // NOTE: `this` and `unsafe_response` are protected by `fully_read` using JS::SafeFunction.
  273. auto process_body = [this, unsafe_response](ByteBuffer bytes) {
  274. process_linked_resource(true, unsafe_response, bytes);
  275. };
  276. // NOTE: `this` and `unsafe_response` are protected by `fully_read` using JS::SafeFunction.
  277. auto process_body_error = [this, unsafe_response](auto) {
  278. process_linked_resource(false, unsafe_response, Fetch::Infrastructure::FetchAlgorithms::ConsumeBodyFailureTag {});
  279. };
  280. unsafe_response->body()->fully_read(realm(), move(process_body), move(process_body_error), JS::NonnullGCPtr { realm().global_object() }).release_value_but_fixme_should_propagate_errors();
  281. return;
  282. } else {
  283. success = false;
  284. }
  285. } else {
  286. success = false;
  287. }
  288. } else if (body_bytes.template has<Fetch::Infrastructure::FetchAlgorithms::ConsumeBodyFailureTag>() || !Fetch::Infrastructure::is_ok_status(response->status())) {
  289. success = false;
  290. }
  291. // FIXME: 3. Otherwise, wait for the link resource's critical subresources to finish loading.
  292. // 4. Process the linked resource given el, success, response, and bodyBytes.
  293. process_linked_resource(success, response, body_bytes);
  294. } }))
  295. .release_value_but_fixme_should_propagate_errors();
  296. }
  297. // https://html.spec.whatwg.org/multipage/links.html#link-type-stylesheet:process-the-linked-resource
  298. void HTMLLinkElement::process_stylesheet_resource(bool success, Fetch::Infrastructure::Response const& response, Variant<Empty, Fetch::Infrastructure::FetchAlgorithms::ConsumeBodyFailureTag, ByteBuffer> body_bytes)
  299. {
  300. // 1. If the resource's Content-Type metadata is not text/css, then set success to false.
  301. auto extracted_mime_type = response.header_list()->extract_mime_type().release_value_but_fixme_should_propagate_errors();
  302. if (!extracted_mime_type.has_value() || extracted_mime_type->essence() != "text/css") {
  303. success = false;
  304. }
  305. // FIXME: 2. If el no longer creates an external resource link that contributes to the styling processing model,
  306. // or if, since the resource in question was fetched, it has become appropriate to fetch it again, then return.
  307. // 3. If el has an associated CSS style sheet, remove the CSS style sheet.
  308. if (m_loaded_style_sheet) {
  309. document().style_sheets().remove_sheet(*m_loaded_style_sheet);
  310. m_loaded_style_sheet = nullptr;
  311. }
  312. // 4. If success is true, then:
  313. if (success) {
  314. // 1. Create a CSS style sheet with the following properties:
  315. // type
  316. // text/css
  317. // location
  318. // The resulting URL string determined during the fetch and process the linked resource algorithm.
  319. // owner node
  320. // element
  321. // media
  322. // The media attribute of element.
  323. // title
  324. // The title attribute of element, if element is in a document tree, or the empty string otherwise.
  325. // alternate flag
  326. // Set if the link is an alternative style sheet and element's explicitly enabled is false; unset otherwise.
  327. // origin-clean flag
  328. // Set if the resource is CORS-same-origin; unset otherwise.
  329. // parent CSS style sheet
  330. // owner CSS rule
  331. // null
  332. // disabled flag
  333. // Left at its default value.
  334. // CSS rules
  335. // Left uninitialized.
  336. //
  337. // The CSS environment encoding is the result of running the following steps: [CSSSYNTAX]
  338. // 1. If the element has a charset attribute, get an encoding from that attribute's value. If that succeeds, return the resulting encoding. [ENCODING]
  339. // 2. Otherwise, return the document's character encoding. [DOM]
  340. m_loaded_style_sheet = parse_css_stylesheet(CSS::Parser::ParsingContext(document(), *response.url()), body_bytes.template get<ByteBuffer>());
  341. if (m_loaded_style_sheet) {
  342. m_loaded_style_sheet->set_owner_node(this);
  343. document().style_sheets().add_sheet(*m_loaded_style_sheet);
  344. } else {
  345. dbgln_if(CSS_LOADER_DEBUG, "HTMLLinkElement: Failed to parse stylesheet: {}", resource()->url());
  346. }
  347. // 2. Fire an event named load at el.
  348. dispatch_event(*DOM::Event::create(realm(), HTML::EventNames::load).release_value_but_fixme_should_propagate_errors());
  349. }
  350. // 5. Otherwise, fire an event named error at el.
  351. else {
  352. dispatch_event(*DOM::Event::create(realm(), HTML::EventNames::error).release_value_but_fixme_should_propagate_errors());
  353. }
  354. // FIXME: 6. If el contributes a script-blocking style sheet, then:
  355. // FIXME: 1. Assert: el's node document's script-blocking style sheet counter is greater than 0.
  356. // FIXME: 2. Decrement el's node document's script-blocking style sheet counter by 1.
  357. // 7. Unblock rendering on el.
  358. m_document_load_event_delayer.clear();
  359. }
  360. // https://html.spec.whatwg.org/multipage/semantics.html#process-the-linked-resource
  361. void HTMLLinkElement::process_linked_resource(bool success, Fetch::Infrastructure::Response const& response, Variant<Empty, Fetch::Infrastructure::FetchAlgorithms::ConsumeBodyFailureTag, ByteBuffer> body_bytes)
  362. {
  363. if (m_relationship & Relationship::Stylesheet)
  364. process_stylesheet_resource(success, response, body_bytes);
  365. }
  366. // https://html.spec.whatwg.org/multipage/semantics.html#linked-resource-fetch-setup-steps
  367. bool HTMLLinkElement::linked_resource_fetch_setup_steps(Fetch::Infrastructure::Request& request)
  368. {
  369. if (m_relationship & Relationship::Stylesheet)
  370. return stylesheet_linked_resource_fetch_setup_steps(request);
  371. return true;
  372. }
  373. // https://html.spec.whatwg.org/multipage/links.html#link-type-stylesheet:linked-resource-fetch-setup-steps
  374. bool HTMLLinkElement::stylesheet_linked_resource_fetch_setup_steps(Fetch::Infrastructure::Request& request)
  375. {
  376. // 1. If el's disabled attribute is set, then return false.
  377. if (has_attribute(AttributeNames::disabled))
  378. return false;
  379. // FIXME: 2. If el contributes a script-blocking style sheet, increment el's node document's script-blocking style sheet counter by 1.
  380. // 3. If el's media attribute's value matches the environment and el is potentially render-blocking, then block rendering on el.
  381. // FIXME: Check media attribute value.
  382. m_document_load_event_delayer.emplace(document());
  383. // 4. If el is currently render-blocking, then set request's render-blocking to true.
  384. // FIXME: Check if el is currently render-blocking.
  385. request.set_render_blocking(true);
  386. // 5. Return true.
  387. return true;
  388. }
  389. void HTMLLinkElement::resource_did_load_favicon()
  390. {
  391. VERIFY(m_relationship & (Relationship::Icon));
  392. if (!resource()->has_encoded_data()) {
  393. dbgln_if(SPAM_DEBUG, "Favicon downloaded, no encoded data");
  394. return;
  395. }
  396. dbgln_if(SPAM_DEBUG, "Favicon downloaded, {} bytes from {}", resource()->encoded_data().size(), resource()->url());
  397. document().check_favicon_after_loading_link_resource();
  398. }
  399. bool HTMLLinkElement::load_favicon_and_use_if_window_is_active()
  400. {
  401. if (!has_loaded_icon())
  402. return false;
  403. RefPtr<Gfx::Bitmap> favicon_bitmap;
  404. auto decoded_image = Platform::ImageCodecPlugin::the().decode_image(resource()->encoded_data());
  405. if (!decoded_image.has_value() || decoded_image->frames.is_empty()) {
  406. dbgln("Could not decode favicon {}", resource()->url());
  407. return false;
  408. }
  409. favicon_bitmap = decoded_image->frames[0].bitmap;
  410. dbgln_if(IMAGE_DECODER_DEBUG, "Decoded favicon, {}", favicon_bitmap->size());
  411. auto* page = document().page();
  412. if (!page)
  413. return favicon_bitmap;
  414. if (document().browsing_context() == &page->top_level_browsing_context())
  415. if (favicon_bitmap) {
  416. page->client().page_did_change_favicon(*favicon_bitmap);
  417. return true;
  418. }
  419. return false;
  420. }
  421. void HTMLLinkElement::visit_edges(Cell::Visitor& visitor)
  422. {
  423. Base::visit_edges(visitor);
  424. visitor.visit(m_loaded_style_sheet);
  425. }
  426. }