/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "WindowFeatures.h" #include "nsINode.h" // IsSpaceCharacter #include "nsContentUtils.h" // nsContentUtils #include "nsDependentSubstring.h" // Substring #include "nsReadableUtils.h" // ToLowerCase using mozilla::dom::IsSpaceCharacter; using mozilla::dom::WindowFeatures; #ifdef DEBUG /* static */ bool WindowFeatures::IsLowerCase(const char* text) { nsAutoCString before(text); nsAutoCString after; ToLowerCase(before, after); return before == after; } #endif static bool IsFeatureSeparator(char aChar) { // https://html.spec.whatwg.org/multipage/window-object.html#feature-separator // A code point is a feature separator if it is ASCII whitespace, U+003D (=), // or U+002C (,). return IsSpaceCharacter(aChar) || aChar == '=' || aChar == ','; } template void AdvanceWhile(IterT& aPosition, const IterT& aEnd, CondT aCondition) { // https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points // // Step 2. While `position` doesn’t point past the end of `input` and the // code point at `position` within `input` meets the condition condition: while (aCondition(*aPosition) && aPosition < aEnd) { // Step 2.1. Append that code point to the end of `result`. // (done by caller) // Step 2.2. Advance `position` by 1. ++aPosition; } } template nsTDependentSubstring CollectSequence(IterT& aPosition, const IterT& aEnd, CondT aCondition) { // https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points // To collect a sequence of code points meeting a condition `condition` from // a string `input`, given a position variable `position` tracking the // position of the calling algorithm within `input`: // Step 1. Let `result` be the empty string. auto start = aPosition; // Step 2. AdvanceWhile(aPosition, aEnd, aCondition); // Step 3. Return `result`. return Substring(start, aPosition); } static void NormalizeName(nsAutoCString& aName) { // https://html.spec.whatwg.org/multipage/window-object.html#normalizing-the-feature-name if (aName == "screenx") { aName = "left"; } else if (aName == "screeny") { aName = "top"; } else if (aName == "innerwidth") { aName = "width"; } else if (aName == "innerheight") { aName = "height"; } } /* static */ int32_t WindowFeatures::ParseIntegerWithFallback(const nsCString& aValue) { // https://html.spec.whatwg.org/multipage/window-object.html#concept-window-open-features-parse-boolean // // Step 3. Let `parsed` be the result of parsing value as an integer. nsContentUtils::ParseHTMLIntegerResultFlags parseResult; int32_t parsed = nsContentUtils::ParseHTMLInteger(aValue, &parseResult); // Step 4. If `parsed` is an error, then set it to 0. // // https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#rules-for-parsing-integers // // eParseHTMLInteger_NonStandard is not tested given: // * Step 4 allows leading whitespaces // * Step 6 allows a plus sign // * Step 8 does not disallow leading zeros // * Steps 6 and 9 allow `-0` // // eParseHTMLInteger_DidNotConsumeAllInput is not tested given: // * Step 8 collects digits and ignores remaining part // if (parseResult & nsContentUtils::eParseHTMLInteger_Error) { parsed = 0; } return parsed; } /* static */ bool WindowFeatures::ParseBool(const nsCString& aValue) { // https://html.spec.whatwg.org/multipage/window-object.html#concept-window-open-features-parse-boolean // To parse a boolean feature given a string `value`: // Step 1. If `value` is the empty string, then return true. if (aValue.IsEmpty()) { return true; } // Step 2. If `value` is a case-sensitive match for "yes", then return true. if (aValue == "yes") { return true; } // Steps 3-4. int32_t parsed = ParseIntegerWithFallback(aValue); // Step 5. Return false if `parsed` is 0, and true otherwise. return parsed != 0; } bool WindowFeatures::Tokenize(const nsACString& aFeatures) { // https://html.spec.whatwg.org/multipage/window-object.html#concept-window-open-features-tokenize // To tokenize the `features` argument: // Step 1. Let `tokenizedFeatures` be a new ordered map. // (implicit) // Step 2. Let `position` point at the first code point of features. auto position = aFeatures.BeginReading(); // Step 3. While `position` is not past the end of `features`: auto end = aFeatures.EndReading(); while (position < end) { // Step 3.1. Let `name` be the empty string. // (implicit) // Step 3.2. Let `value` be the empty string. nsAutoCString value; // Step 3.3. Collect a sequence of code points that are feature separators // from `features` given `position`. This skips past leading separators // before the name. // // NOTE: Do not collect given this value is unused. AdvanceWhile(position, end, IsFeatureSeparator); // Step 3.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. nsAutoCString name(CollectSequence( position, end, [](char c) { return !IsFeatureSeparator(c); })); ToLowerCase(name); // Step 3.5. Set `name` to the result of normalizing the feature name // `name`. NormalizeName(name); // Step 3.6. While `position` is not past the end of `features` and the // code point at `position` in features is not U+003D (=): // // Step 3.6.1. If the code point at `position` in features is U+002C (,), // or if it is not a feature separator, then break. // // Step 3.6.2. Advance `position` by 1. // // NOTE: This skips to the first U+003D (=) but does not skip past a U+002C // (,) or a non-separator. // // The above means skip all whitespaces. AdvanceWhile(position, end, [](char c) { return IsSpaceCharacter(c); }); // Step 3.7. If the code point at `position` in `features` is a feature // separator: if (position < end && IsFeatureSeparator(*position)) { // Step 3.7.1. While `position` is not past the end of `features` and the // code point at `position` in `features` is a feature separator: // // Step 3.7.1.1. If the code point at `position` in `features` is // U+002C (,), then break. // // Step 3.7.1.2. Advance `position` by 1. // // NOTE: This skips to the first non-separator but does not skip past a // U+002C (,). AdvanceWhile(position, end, [](char c) { return IsFeatureSeparator(c) && c != ','; }); // Step 3.7.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. value = CollectSequence(position, end, [](char c) { return !IsFeatureSeparator(c); }); ToLowerCase(value); } // Step 3.8. If `name` is not the empty string, then set // `tokenizedFeatures[name]` to `value`. if (!name.IsEmpty()) { if (!tokenizedFeatures_.put(name, value)) { return false; } } } // Step 4. Return `tokenizedFeatures`. return true; } void WindowFeatures::Stringify(nsAutoCString& aOutput) { MOZ_ASSERT(aOutput.IsEmpty()); for (auto r = tokenizedFeatures_.all(); !r.empty(); r.popFront()) { if (!aOutput.IsEmpty()) { aOutput.Append(','); } const nsCString& name = r.front().key(); const nsCString& value = r.front().value(); aOutput.Append(name); if (!value.IsEmpty()) { aOutput.Append('='); aOutput.Append(value); } } }