Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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) {
// 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) {
//
// 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) {
// 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) {
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) {
//
// 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.
//
//
// 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) {
// 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 "yes", then return true.
if (aValue == "yes") {
return true;
}
// Step 3. If `value` is "true", then return
if (aValue == "true") {
return true;
}
// Steps 4-5.
int32_t parsed = ParseIntegerWithFallback(aValue);
// Step 6. Return false if `parsed` is 0, and true otherwise.
return parsed != 0;
}
bool WindowFeatures::Tokenize(const nsACString& aFeatures) {
// 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);
}
}
}