summaryrefslogtreecommitdiffstats
path: root/dom/base/WindowFeatures.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/base/WindowFeatures.cpp')
-rw-r--r--dom/base/WindowFeatures.cpp236
1 files changed, 236 insertions, 0 deletions
diff --git a/dom/base/WindowFeatures.cpp b/dom/base/WindowFeatures.cpp
new file mode 100644
index 0000000000..a41fd4bb0d
--- /dev/null
+++ b/dom/base/WindowFeatures.cpp
@@ -0,0 +1,236 @@
+/* -*- 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 <class IterT, class CondT>
+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 <class IterT, class CondT>
+nsTDependentSubstring<char> 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);
+ }
+ }
+}