WindowFeatures.cpp (8242B)
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/. */ 6 7 #include "WindowFeatures.h" 8 9 #include "nsContentUtils.h" // nsContentUtils 10 #include "nsDependentSubstring.h" // Substring 11 #include "nsINode.h" // IsSpaceCharacter 12 #include "nsReadableUtils.h" // ToLowerCase 13 14 using mozilla::dom::IsSpaceCharacter; 15 using mozilla::dom::WindowFeatures; 16 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; 24 } 25 #endif 26 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 == ','; 32 } 33 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 37 // 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) 43 44 // Step 2.2. Advance `position` by 1. 45 ++aPosition; 46 } 47 } 48 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`: 56 57 // Step 1. Let `result` be the empty string. 58 auto start = aPosition; 59 60 // Step 2. 61 AdvanceWhile(aPosition, aEnd, aCondition); 62 63 // Step 3. Return `result`. 64 return Substring(start, aPosition); 65 } 66 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"; 77 } 78 } 79 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 83 // 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); 87 88 // Step 4. If `parsed` is an error, then set it to 0. 89 // 90 // https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#rules-for-parsing-integers 91 // 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` 97 // 98 // eParseHTMLInteger_DidNotConsumeAllInput is not tested given: 99 // * Step 8 collects digits and ignores remaining part 100 // 101 if (parseResult & nsContentUtils::eParseHTMLInteger_Error) { 102 parsed = 0; 103 } 104 105 return parsed; 106 } 107 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`: 112 113 // Step 1. If `value` is the empty string, then return true. 114 if (aValue.IsEmpty()) { 115 return true; 116 } 117 118 // Step 2. If `value` is "yes", then return true. 119 if (aValue == "yes") { 120 return true; 121 } 122 123 // Step 3. If `value` is "true", then return 124 if (aValue == "true") { 125 return true; 126 } 127 128 // Steps 4-5. 129 int32_t parsed = ParseIntegerWithFallback(aValue); 130 131 // Step 6. Return false if `parsed` is 0, and true otherwise. 132 return parsed != 0; 133 } 134 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: 138 139 // Step 1. Let `tokenizedFeatures` be a new ordered map. 140 // (implicit) 141 142 // Step 2. Let `position` point at the first code point of features. 143 auto position = aFeatures.BeginReading(); 144 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) 150 151 // Step 3.2. Let `value` be the empty string. 152 nsAutoCString value; 153 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. 157 // 158 // NOTE: Do not collect given this value is unused. 159 AdvanceWhile(position, end, IsFeatureSeparator); 160 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); 167 168 // Step 3.5. Set `name` to the result of normalizing the feature name 169 // `name`. 170 NormalizeName(name); 171 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 (=): 174 // 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. 177 // 178 // Step 3.6.2. Advance `position` by 1. 179 // 180 // NOTE: This skips to the first U+003D (=) but does not skip past a U+002C 181 // (,) or a non-separator. 182 // 183 // The above means skip all whitespaces. 184 AdvanceWhile(position, end, [](char c) { return IsSpaceCharacter(c); }); 185 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: 191 // 192 // Step 3.7.1.1. If the code point at `position` in `features` is 193 // U+002C (,), then break. 194 // 195 // Step 3.7.1.2. Advance `position` by 1. 196 // 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 != ','; }); 201 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); 208 } 209 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; 215 } 216 } 217 } 218 219 // Step 4. Return `tokenizedFeatures`. 220 return true; 221 } 222 223 void WindowFeatures::Stringify(nsAutoCString& aOutput) { 224 MOZ_ASSERT(aOutput.IsEmpty()); 225 226 for (auto r = tokenizedFeatures_.all(); !r.empty(); r.popFront()) { 227 if (!aOutput.IsEmpty()) { 228 aOutput.Append(','); 229 } 230 231 const nsCString& name = r.front().key(); 232 const nsCString& value = r.front().value(); 233 234 aOutput.Append(name); 235 236 if (!value.IsEmpty()) { 237 aOutput.Append('='); 238 aOutput.Append(value); 239 } 240 } 241 }