Bug 1852740: add tests for the `fetchpriority` attribute in Link headers. r=necko...
[gecko.git] / dom / base / WindowFeatures.cpp
blob5de62f8b59cb369abb667f9799ecb8de8d511e82
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "WindowFeatures.h"
9 #include "nsINode.h" // IsSpaceCharacter
10 #include "nsContentUtils.h" // nsContentUtils
11 #include "nsDependentSubstring.h" // Substring
12 #include "nsReadableUtils.h" // ToLowerCase
14 using mozilla::dom::IsSpaceCharacter;
15 using mozilla::dom::WindowFeatures;
17 #ifdef DEBUG
18 /* static */
19 bool WindowFeatures::IsLowerCase(const char* text) {
20 nsAutoCString before(text);
21 nsAutoCString after;
22 ToLowerCase(before, after);
23 return before == after;
25 #endif
27 static bool IsFeatureSeparator(char aChar) {
28 // https://html.spec.whatwg.org/multipage/window-object.html#feature-separator
29 // A code point is a feature separator if it is ASCII whitespace, U+003D (=),
30 // or U+002C (,).
31 return IsSpaceCharacter(aChar) || aChar == '=' || aChar == ',';
34 template <class IterT, class CondT>
35 void AdvanceWhile(IterT& aPosition, const IterT& aEnd, CondT aCondition) {
36 // https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points
38 // Step 2. While `position` doesn’t point past the end of `input` and the
39 // code point at `position` within `input` meets the condition condition:
40 while (aCondition(*aPosition) && aPosition < aEnd) {
41 // Step 2.1. Append that code point to the end of `result`.
42 // (done by caller)
44 // Step 2.2. Advance `position` by 1.
45 ++aPosition;
49 template <class IterT, class CondT>
50 nsTDependentSubstring<char> CollectSequence(IterT& aPosition, const IterT& aEnd,
51 CondT aCondition) {
52 // https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points
53 // To collect a sequence of code points meeting a condition `condition` from
54 // a string `input`, given a position variable `position` tracking the
55 // position of the calling algorithm within `input`:
57 // Step 1. Let `result` be the empty string.
58 auto start = aPosition;
60 // Step 2.
61 AdvanceWhile(aPosition, aEnd, aCondition);
63 // Step 3. Return `result`.
64 return Substring(start, aPosition);
67 static void NormalizeName(nsAutoCString& aName) {
68 // https://html.spec.whatwg.org/multipage/window-object.html#normalizing-the-feature-name
69 if (aName == "screenx") {
70 aName = "left";
71 } else if (aName == "screeny") {
72 aName = "top";
73 } else if (aName == "innerwidth") {
74 aName = "width";
75 } else if (aName == "innerheight") {
76 aName = "height";
80 /* static */
81 int32_t WindowFeatures::ParseIntegerWithFallback(const nsCString& aValue) {
82 // https://html.spec.whatwg.org/multipage/window-object.html#concept-window-open-features-parse-boolean
84 // Step 3. Let `parsed` be the result of parsing value as an integer.
85 nsContentUtils::ParseHTMLIntegerResultFlags parseResult;
86 int32_t parsed = nsContentUtils::ParseHTMLInteger(aValue, &parseResult);
88 // Step 4. If `parsed` is an error, then set it to 0.
90 // https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#rules-for-parsing-integers
92 // eParseHTMLInteger_NonStandard is not tested given:
93 // * Step 4 allows leading whitespaces
94 // * Step 6 allows a plus sign
95 // * Step 8 does not disallow leading zeros
96 // * Steps 6 and 9 allow `-0`
98 // eParseHTMLInteger_DidNotConsumeAllInput is not tested given:
99 // * Step 8 collects digits and ignores remaining part
101 if (parseResult & nsContentUtils::eParseHTMLInteger_Error) {
102 parsed = 0;
105 return parsed;
108 /* static */
109 bool WindowFeatures::ParseBool(const nsCString& aValue) {
110 // https://html.spec.whatwg.org/multipage/window-object.html#concept-window-open-features-parse-boolean
111 // To parse a boolean feature given a string `value`:
113 // Step 1. If `value` is the empty string, then return true.
114 if (aValue.IsEmpty()) {
115 return true;
118 // Step 2. If `value` is "yes", then return true.
119 if (aValue == "yes") {
120 return true;
123 // Step 3. If `value` is "true", then return
124 if (aValue == "true") {
125 return true;
128 // Steps 4-5.
129 int32_t parsed = ParseIntegerWithFallback(aValue);
131 // Step 6. Return false if `parsed` is 0, and true otherwise.
132 return parsed != 0;
135 bool WindowFeatures::Tokenize(const nsACString& aFeatures) {
136 // https://html.spec.whatwg.org/multipage/window-object.html#concept-window-open-features-tokenize
137 // To tokenize the `features` argument:
139 // Step 1. Let `tokenizedFeatures` be a new ordered map.
140 // (implicit)
142 // Step 2. Let `position` point at the first code point of features.
143 auto position = aFeatures.BeginReading();
145 // Step 3. While `position` is not past the end of `features`:
146 auto end = aFeatures.EndReading();
147 while (position < end) {
148 // Step 3.1. Let `name` be the empty string.
149 // (implicit)
151 // Step 3.2. Let `value` be the empty string.
152 nsAutoCString value;
154 // Step 3.3. Collect a sequence of code points that are feature separators
155 // from `features` given `position`. This skips past leading separators
156 // before the name.
158 // NOTE: Do not collect given this value is unused.
159 AdvanceWhile(position, end, IsFeatureSeparator);
161 // Step 3.4. Collect a sequence of code points that are not feature
162 // separators from `features` given `position`. Set `name` to the collected
163 // characters, converted to ASCII lowercase.
164 nsAutoCString name(CollectSequence(
165 position, end, [](char c) { return !IsFeatureSeparator(c); }));
166 ToLowerCase(name);
168 // Step 3.5. Set `name` to the result of normalizing the feature name
169 // `name`.
170 NormalizeName(name);
172 // Step 3.6. While `position` is not past the end of `features` and the
173 // code point at `position` in features is not U+003D (=):
175 // Step 3.6.1. If the code point at `position` in features is U+002C (,),
176 // or if it is not a feature separator, then break.
178 // Step 3.6.2. Advance `position` by 1.
180 // NOTE: This skips to the first U+003D (=) but does not skip past a U+002C
181 // (,) or a non-separator.
183 // The above means skip all whitespaces.
184 AdvanceWhile(position, end, [](char c) { return IsSpaceCharacter(c); });
186 // Step 3.7. If the code point at `position` in `features` is a feature
187 // separator:
188 if (position < end && IsFeatureSeparator(*position)) {
189 // Step 3.7.1. While `position` is not past the end of `features` and the
190 // code point at `position` in `features` is a feature separator:
192 // Step 3.7.1.1. If the code point at `position` in `features` is
193 // U+002C (,), then break.
195 // Step 3.7.1.2. Advance `position` by 1.
197 // NOTE: This skips to the first non-separator but does not skip past a
198 // U+002C (,).
199 AdvanceWhile(position, end,
200 [](char c) { return IsFeatureSeparator(c) && c != ','; });
202 // Step 3.7.2. Collect a sequence of code points that are not feature
203 // separators code points from `features` given `position`. Set `value` to
204 // the collected code points, converted to ASCII lowercase.
205 value = CollectSequence(position, end,
206 [](char c) { return !IsFeatureSeparator(c); });
207 ToLowerCase(value);
210 // Step 3.8. If `name` is not the empty string, then set
211 // `tokenizedFeatures[name]` to `value`.
212 if (!name.IsEmpty()) {
213 if (!tokenizedFeatures_.put(name, value)) {
214 return false;
219 // Step 4. Return `tokenizedFeatures`.
220 return true;
223 void WindowFeatures::Stringify(nsAutoCString& aOutput) {
224 MOZ_ASSERT(aOutput.IsEmpty());
226 for (auto r = tokenizedFeatures_.all(); !r.empty(); r.popFront()) {
227 if (!aOutput.IsEmpty()) {
228 aOutput.Append(',');
231 const nsCString& name = r.front().key();
232 const nsCString& value = r.front().value();
234 aOutput.Append(name);
236 if (!value.IsEmpty()) {
237 aOutput.Append('=');
238 aOutput.Append(value);