TokenizedFeatures.cpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. /*
  2. * Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/GenericLexer.h>
  7. #include <LibWeb/HTML/TokenizedFeatures.h>
  8. #include <LibWeb/Infra/CharacterTypes.h>
  9. namespace Web::HTML {
  10. // https://html.spec.whatwg.org/multipage/nav-history-apis.html#normalizing-the-feature-name
  11. static String normalize_feature_name(String const& name)
  12. {
  13. // For legacy reasons, there are some aliases of some feature names. To normalize a feature name name, switch on name:
  14. // "screenx"
  15. if (name == "screenx"sv) {
  16. // Return "left".
  17. return "left"_string;
  18. }
  19. // "screeny"
  20. else if (name == "screeny"sv) {
  21. // Return "top".
  22. return "top"_string;
  23. }
  24. // "innerwidth"
  25. else if (name == "innerwidth"sv) {
  26. // Return "width".
  27. return "width"_string;
  28. }
  29. // "innerheight"
  30. else if (name == "innerheight") {
  31. // Return "height".
  32. return "height"_string;
  33. }
  34. // Anything else
  35. else {
  36. // Return name.
  37. return name;
  38. }
  39. }
  40. // https://html.spec.whatwg.org/multipage/nav-history-apis.html#concept-window-open-features-tokenize
  41. TokenizedFeature::Map tokenize_open_features(StringView features)
  42. {
  43. // 1. Let tokenizedFeatures be a new ordered map.
  44. TokenizedFeature::Map tokenized_features;
  45. // 2. Let position point at the first code point of features.
  46. GenericLexer lexer(features);
  47. // https://html.spec.whatwg.org/multipage/nav-history-apis.html#feature-separator
  48. auto is_feature_separator = [](auto character) {
  49. return Infra::is_ascii_whitespace(character) || character == '=' || character == ',';
  50. };
  51. // 3. While position is not past the end of features:
  52. while (!lexer.is_eof()) {
  53. // 1. Let name be the empty string.
  54. String name;
  55. // 2. Let value be the empty string.
  56. String value;
  57. // 3. Collect a sequence of code points that are feature separators from features given position. This skips past leading separators before the name.
  58. lexer.ignore_while(is_feature_separator);
  59. // 4. Collect a sequence of code points that are not feature separators from features given position. Set name to the collected characters, converted to ASCII lowercase.
  60. name = MUST(String::from_byte_string(lexer.consume_until(is_feature_separator).to_lowercase_string()));
  61. // 5. Set name to the result of normalizing the feature name name.
  62. name = normalize_feature_name(name);
  63. // 6. While position is not past the end of features and the code point at position in features is not U+003D (=):
  64. // 1. If the code point at position in features is U+002C (,), or if it is not a feature separator, then break.
  65. // 2. Advance position by 1.
  66. lexer.ignore_while(Infra::is_ascii_whitespace);
  67. // 7. If the code point at position in features is a feature separator:
  68. // 1. While position is not past the end of features and the code point at position in features is a feature separator:
  69. // 1. If the code point at position in features is U+002C (,), then break.
  70. // 2. Advance position by 1.
  71. lexer.ignore_while([](auto character) { return Infra::is_ascii_whitespace(character) || character == '='; });
  72. // 2. Collect a sequence of code points that are not feature separators code points from features given position. Set value to the collected code points, converted to ASCII lowercase.
  73. value = MUST(String::from_byte_string(lexer.consume_until(is_feature_separator).to_lowercase_string()));
  74. // 8. If name is not the empty string, then set tokenizedFeatures[name] to value.
  75. if (!name.is_empty())
  76. tokenized_features.set(move(name), move(value));
  77. }
  78. // 4. Return tokenizedFeatures.
  79. return tokenized_features;
  80. }
  81. // https://html.spec.whatwg.org/multipage/nav-history-apis.html#concept-window-open-features-parse-boolean
  82. template<Enum T>
  83. T parse_boolean_feature(StringView value)
  84. {
  85. // 1. If value is the empty string, then return true.
  86. if (value.is_empty())
  87. return T::Yes;
  88. // 2. If value is "yes", then return true.
  89. if (value == "yes"sv)
  90. return T::Yes;
  91. // 3. If value is "true", then return true.
  92. if (value == "true"sv)
  93. return T::Yes;
  94. // 4. Let parsed be the result of parsing value as an integer.
  95. auto parsed = value.to_number<i64>();
  96. // 5. If parsed is an error, then set it to 0.
  97. if (!parsed.has_value())
  98. parsed = 0;
  99. // 6. Return false if parsed is 0, and true otherwise.
  100. return parsed == 0 ? T::No : T::Yes;
  101. }
  102. template TokenizedFeature::Location parse_boolean_feature<TokenizedFeature::Location>(StringView value);
  103. template TokenizedFeature::Menubar parse_boolean_feature<TokenizedFeature::Menubar>(StringView value);
  104. template TokenizedFeature::NoOpener parse_boolean_feature<TokenizedFeature::NoOpener>(StringView value);
  105. template TokenizedFeature::NoReferrer parse_boolean_feature<TokenizedFeature::NoReferrer>(StringView value);
  106. template TokenizedFeature::Popup parse_boolean_feature<TokenizedFeature::Popup>(StringView value);
  107. template TokenizedFeature::Resizable parse_boolean_feature<TokenizedFeature::Resizable>(StringView value);
  108. template TokenizedFeature::Scrollbars parse_boolean_feature<TokenizedFeature::Scrollbars>(StringView value);
  109. template TokenizedFeature::Status parse_boolean_feature<TokenizedFeature::Status>(StringView value);
  110. template TokenizedFeature::Toolbar parse_boolean_feature<TokenizedFeature::Toolbar>(StringView value);
  111. // https://html.spec.whatwg.org/multipage/window-object.html#popup-window-is-requested
  112. TokenizedFeature::Popup check_if_a_popup_window_is_requested(TokenizedFeature::Map const& tokenized_features)
  113. {
  114. // 1. If tokenizedFeatures is empty, then return false.
  115. if (tokenized_features.is_empty())
  116. return TokenizedFeature::Popup::No;
  117. // 2. If tokenizedFeatures["popup"] exists, then return the result of parsing tokenizedFeatures["popup"] as a boolean feature.
  118. if (auto popup_feature = tokenized_features.get("popup"sv); popup_feature.has_value())
  119. return parse_boolean_feature<TokenizedFeature::Popup>(*popup_feature);
  120. // https://html.spec.whatwg.org/multipage/window-object.html#window-feature-is-set
  121. auto check_if_a_window_feature_is_set = [&]<Enum T>(StringView feature_name, T default_value) {
  122. // 1. If tokenizedFeatures[featureName] exists, then return the result of parsing tokenizedFeatures[featureName] as a boolean feature.
  123. if (auto feature = tokenized_features.get(feature_name); feature.has_value())
  124. return parse_boolean_feature<T>(*feature);
  125. // 2. Return defaultValue.
  126. return default_value;
  127. };
  128. // 3. Let location be the result of checking if a window feature is set, given tokenizedFeatures, "location", and false.
  129. auto location = check_if_a_window_feature_is_set("location"sv, TokenizedFeature::Location::No);
  130. // 4. Let toolbar be the result of checking if a window feature is set, given tokenizedFeatures, "toolbar", and false.
  131. auto toolbar = check_if_a_window_feature_is_set("toolbar"sv, TokenizedFeature::Toolbar::No);
  132. // 5. If location and toolbar are both false, then return true.
  133. if (location == TokenizedFeature::Location::No && toolbar == TokenizedFeature::Toolbar::No)
  134. return TokenizedFeature::Popup::Yes;
  135. // 6. Let menubar be the result of checking if a window feature is set, given tokenizedFeatures, menubar", and false.
  136. auto menubar = check_if_a_window_feature_is_set("menubar"sv, TokenizedFeature::Menubar::No);
  137. // 7. If menubar is false, then return true.
  138. if (menubar == TokenizedFeature::Menubar::No)
  139. return TokenizedFeature::Popup::Yes;
  140. // 8. Let resizable be the result of checking if a window feature is set, given tokenizedFeatures, "resizable", and true.
  141. auto resizable = check_if_a_window_feature_is_set("resizable"sv, TokenizedFeature::Resizable::Yes);
  142. // 9. If resizable is false, then return true.
  143. if (resizable == TokenizedFeature::Resizable::No)
  144. return TokenizedFeature::Popup::Yes;
  145. // 10. Let scrollbars be the result of checking if a window feature is set, given tokenizedFeatures, "scrollbars", and false.
  146. auto scrollbars = check_if_a_window_feature_is_set("scrollbars"sv, TokenizedFeature::Scrollbars::No);
  147. // 11. If scrollbars is false, then return true.
  148. if (scrollbars == TokenizedFeature::Scrollbars::No)
  149. return TokenizedFeature::Popup::Yes;
  150. // 12. Let status be the result of checking if a window feature is set, given tokenizedFeatures, "status", and false.
  151. auto status = check_if_a_window_feature_is_set("status"sv, TokenizedFeature::Status::No);
  152. // 13. If status is false, then return true.
  153. if (status == TokenizedFeature::Status::No)
  154. return TokenizedFeature::Popup::Yes;
  155. // 14. Return false.
  156. return TokenizedFeature::Popup::No;
  157. }
  158. }