HTMLMetaElement.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. /*
  2. * Copyright (c) 2020, the SerenityOS developers.
  3. * Copyright (c) 2023, Luke Wilde <lukew@serenityos.org>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <AK/GenericLexer.h>
  8. #include <LibWeb/Bindings/HTMLMetaElementPrototype.h>
  9. #include <LibWeb/Bindings/Intrinsics.h>
  10. #include <LibWeb/CSS/Parser/Parser.h>
  11. #include <LibWeb/CSS/Parser/ParsingContext.h>
  12. #include <LibWeb/CSS/PropertyID.h>
  13. #include <LibWeb/CSS/StyleValues/CSSColorValue.h>
  14. #include <LibWeb/DOM/Document.h>
  15. #include <LibWeb/HTML/HTMLMetaElement.h>
  16. #include <LibWeb/Infra/CharacterTypes.h>
  17. #include <LibWeb/Page/Page.h>
  18. namespace Web::HTML {
  19. GC_DEFINE_ALLOCATOR(HTMLMetaElement);
  20. HTMLMetaElement::HTMLMetaElement(DOM::Document& document, DOM::QualifiedName qualified_name)
  21. : HTMLElement(document, move(qualified_name))
  22. {
  23. }
  24. HTMLMetaElement::~HTMLMetaElement() = default;
  25. void HTMLMetaElement::initialize(JS::Realm& realm)
  26. {
  27. Base::initialize(realm);
  28. WEB_SET_PROTOTYPE_FOR_INTERFACE(HTMLMetaElement);
  29. }
  30. Optional<HTMLMetaElement::HttpEquivAttributeState> HTMLMetaElement::http_equiv_state() const
  31. {
  32. auto value = get_attribute_value(HTML::AttributeNames::http_equiv);
  33. #define __ENUMERATE_HTML_META_HTTP_EQUIV_ATTRIBUTE(keyword, state) \
  34. if (value.equals_ignoring_ascii_case(keyword##sv)) \
  35. return HTMLMetaElement::HttpEquivAttributeState::state;
  36. ENUMERATE_HTML_META_HTTP_EQUIV_ATTRIBUTES
  37. #undef __ENUMERATE_HTML_META_HTTP_EQUIV_ATTRIBUTE
  38. return OptionalNone {};
  39. }
  40. void HTMLMetaElement::inserted()
  41. {
  42. Base::inserted();
  43. // https://html.spec.whatwg.org/multipage/semantics.html#meta-theme-color
  44. // 1. To obtain a page's theme color, user agents must run the following steps:
  45. // * The element is in a document tree
  46. // * The element has a name attribute, whose value is an ASCII case-insensitive match for theme-color
  47. // * The element has a content attribute
  48. auto content = attribute(AttributeNames::content);
  49. if (name().has_value() && name()->equals_ignoring_ascii_case("theme-color"sv) && content.has_value()) {
  50. auto context = CSS::Parser::ParsingContext { document() };
  51. // 2. For each element in candidate elements:
  52. // 1. If element has a media attribute and the value of element's media attribute does not match the environment, then continue.
  53. auto media = attribute(AttributeNames::media);
  54. if (media.has_value()) {
  55. auto query = parse_media_query(context, media.value());
  56. if (document().window() && !query->evaluate(*document().window()))
  57. return;
  58. }
  59. // 2. Let value be the result of stripping leading and trailing ASCII whitespace from the value of element's content attribute.
  60. auto value = content->bytes_as_string_view().trim(Infra::ASCII_WHITESPACE);
  61. // 3. Let color be the result of parsing value.
  62. auto css_value = parse_css_value(context, value, CSS::PropertyID::Color);
  63. if (css_value.is_null() || !css_value->is_color())
  64. return;
  65. auto color = css_value->to_color({}); // TODO: Pass a layout node?
  66. // 4. If color is not failure, then return color.
  67. document().page().client().page_did_change_theme_color(color);
  68. return;
  69. }
  70. // https://html.spec.whatwg.org/multipage/semantics.html#pragma-directives
  71. // When a meta element is inserted into the document, if its http-equiv attribute is present and represents one of
  72. // the above states, then the user agent must run the algorithm appropriate for that state, as described in the
  73. // following list:
  74. auto http_equiv = http_equiv_state();
  75. if (http_equiv.has_value()) {
  76. switch (http_equiv.value()) {
  77. case HttpEquivAttributeState::EncodingDeclaration:
  78. // https://html.spec.whatwg.org/multipage/semantics.html#attr-meta-http-equiv-content-type
  79. // The Encoding declaration state is just an alternative form of setting the charset attribute: it is a character encoding declaration.
  80. // This state's user agent requirements are all handled by the parsing section of the specification.
  81. break;
  82. case HttpEquivAttributeState::Refresh: {
  83. // https://html.spec.whatwg.org/multipage/semantics.html#attr-meta-http-equiv-refresh
  84. // 1. If the meta element has no content attribute, or if that attribute's value is the empty string, then return.
  85. // 2. Let input be the value of the element's content attribute.
  86. if (!has_attribute(AttributeNames::content))
  87. break;
  88. auto input = get_attribute_value(AttributeNames::content);
  89. if (input.is_empty())
  90. break;
  91. // 3. Run the shared declarative refresh steps with the meta element's node document, input, and the meta element.
  92. document().shared_declarative_refresh_steps(input, this);
  93. break;
  94. }
  95. case HttpEquivAttributeState::SetCookie:
  96. // https://html.spec.whatwg.org/multipage/semantics.html#attr-meta-http-equiv-set-cookie
  97. // This pragma is non-conforming and has no effect.
  98. // User agents are required to ignore this pragma.
  99. break;
  100. case HttpEquivAttributeState::XUACompatible:
  101. // https://html.spec.whatwg.org/multipage/semantics.html#attr-meta-http-equiv-x-ua-compatible
  102. // In practice, this pragma encourages Internet Explorer to more closely follow the specifications.
  103. // For meta elements with an http-equiv attribute in the X-UA-Compatible state, the content attribute must have a value that is an ASCII case-insensitive match for the string "IE=edge".
  104. // User agents are required to ignore this pragma.
  105. break;
  106. case HttpEquivAttributeState::ContentLanguage: {
  107. // https://html.spec.whatwg.org/multipage/semantics.html#attr-meta-http-equiv-content-language
  108. // 1. If the meta element has no content attribute, then return.
  109. if (!has_attribute(AttributeNames::content))
  110. break;
  111. // 2. If the element's content attribute contains a U+002C COMMA character (,) then return.
  112. auto content = get_attribute_value(AttributeNames::content);
  113. if (content.contains(","sv))
  114. break;
  115. // 3. Let input be the value of the element's content attribute.
  116. // 4. Let position point at the first character of input.
  117. GenericLexer lexer { content };
  118. // 5. Skip ASCII whitespace within input given position.
  119. lexer.ignore_while(Web::Infra::is_ascii_whitespace);
  120. // 6. Collect a sequence of code points that are not ASCII whitespace from input given position.
  121. // 7. Let candidate be the string that resulted from the previous step.
  122. auto candidate = lexer.consume_until(Web::Infra::is_ascii_whitespace);
  123. // 8. If candidate is the empty string, return.
  124. if (candidate.is_empty())
  125. break;
  126. // 9. Set the pragma-set default language to candidate.
  127. auto language = String::from_utf8_without_validation(candidate.bytes());
  128. document().set_pragma_set_default_language(language);
  129. break;
  130. }
  131. default:
  132. dbgln("FIXME: Implement '{}' http-equiv state", get_attribute_value(AttributeNames::http_equiv));
  133. break;
  134. }
  135. }
  136. }
  137. }