Bug 1432749 - Part 2: Introduce WindowFeature class with spec-compliant tokenization, and use it both in nsGlobalWindowOuter and nsWindowWatcher. r=smaug

WindowFeature provides the tokenization and access to the map.
This changes the following behavior:
  * "*" value is removed, given it's unused.
    * Default width and default height handling is removed,
      given there's no callsites
  * Some chrome-priv feature handling becomes stricter:
    * All substring match is removed and directly checks the item in the map

Also, fixed noopener=0 and noreferrer=0 options to be handled properly.

Differential Revision: https://phabricator.services.mozilla.com/D67725

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Tooru Fujisawa 2020-04-13 15:42:19 +00:00
parent c2e8f7014c
commit ede1eed174
14 changed files with 718 additions and 414 deletions

233
dom/base/WindowFeatures.cpp Normal file
View File

@ -0,0 +1,233 @@
/* -*- 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
#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` doesnt 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);
}
}
}

131
dom/base/WindowFeatures.h Normal file
View File

@ -0,0 +1,131 @@
/* -*- 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/. */
#ifndef mozilla_dom_WindowFeatures_h
#define mozilla_dom_WindowFeatures_h
#include "mozilla/Assertions.h" // MOZ_ASSERT
#include "mozilla/HashTable.h" // mozilla::HashMap
#include "nsStringFwd.h" // nsCString, nsACString, nsAutoCString, nsLiteralCString
#include "nsTStringHasher.h" // mozilla::DefaultHasher<nsCString>
namespace mozilla {
namespace dom {
// Represents `tokenizedFeatures` in
// https://html.spec.whatwg.org/multipage/window-object.html#concept-window-open-features-tokenize
// with accessor methods for values.
class WindowFeatures {
public:
WindowFeatures() = default;
WindowFeatures(const WindowFeatures& aOther) = delete;
WindowFeatures& operator=(const WindowFeatures& aOther) = delete;
WindowFeatures(WindowFeatures&& aOther) = delete;
WindowFeatures& operator=(WindowFeatures&& aOther) = delete;
// Tokenizes `aFeatures` and stores the result map in member field.
// This should be called at the begining, only once.
//
// Returns true if successfully tokenized, false otherwise.
bool Tokenize(const nsACString& aFeatures);
// Returns true if the `aName` feature is specified.
template <size_t N>
bool Exists(const char (&aName)[N]) const {
MOZ_ASSERT(IsLowerCase(aName));
nsLiteralCString name(aName);
return tokenizedFeatures_.has(name);
}
// Returns string value of `aName` feature.
// The feature must exist.
template <size_t N>
const nsCString& Get(const char (&aName)[N]) const {
MOZ_ASSERT(IsLowerCase(aName));
nsLiteralCString name(aName);
auto p = tokenizedFeatures_.lookup(name);
MOZ_ASSERT(p.found());
return p->value();
}
// Returns integer value of `aName` feature.
// The feature must exist.
template <size_t N>
int32_t GetInt(const char (&aName)[N]) const {
const nsCString& value = Get(aName);
return ParseIntegerWithFallback(value);
}
// Returns bool value of `aName` feature.
// The feature must exist.
template <size_t N>
bool GetBool(const char (&aName)[N]) const {
const nsCString& value = Get(aName);
return ParseBool(value);
}
// Returns bool value of `aName` feature.
// If the feature doesn't exist, returns `aDefault`.
//
// If `aPresenceFlag` is provided and the feature exists, it's set to `true`.
// (note that the value isn't overwritten if the feature doesn't exist)
template <size_t N>
bool GetBoolWithDefault(const char (&aName)[N], bool aDefault,
bool* aPresenceFlag = nullptr) const {
MOZ_ASSERT(IsLowerCase(aName));
nsLiteralCString name(aName);
auto p = tokenizedFeatures_.lookup(name);
if (p.found()) {
if (aPresenceFlag) {
*aPresenceFlag = true;
}
return ParseBool(p->value());
}
return aDefault;
}
// Remove the feature from the map.
template <size_t N>
void Remove(const char (&aName)[N]) {
MOZ_ASSERT(IsLowerCase(aName));
nsLiteralCString name(aName);
tokenizedFeatures_.remove(name);
}
// Returns true if there was no feature specified, or all features are
// removed by `Remove`.
//
// Note that this can be true even if `aFeatures` parameter of `Tokenize`
// is not empty, in case it contains no feature with non-empty name.
bool IsEmpty() const { return tokenizedFeatures_.empty(); }
// Stringify the map into `aOutput`.
// The result can be parsed again with `Tokenize`.
void Stringify(nsAutoCString& aOutput);
private:
#ifdef DEBUG
// Returns true if `text` does not contain any character that gets modified by
// `ToLowerCase`.
static bool IsLowerCase(const char* text);
#endif
static int32_t ParseIntegerWithFallback(const nsCString& aValue);
static bool ParseBool(const nsCString& aValue);
// A map from feature name to feature value.
// If value is not provided, it's empty string.
mozilla::HashMap<nsCString, nsCString> tokenizedFeatures_;
};
} // namespace dom
} // namespace mozilla
#endif // #ifndef mozilla_dom_WindowFeatures_h

View File

@ -253,6 +253,7 @@ EXPORTS.mozilla.dom += [
'UserActivation.h',
'ViewportMetaData.h',
'VisualViewport.h',
'WindowFeatures.h',
'WindowOrientationObserver.h',
'WindowProxyHolder.h',
]
@ -425,6 +426,7 @@ UNIFIED_SOURCES += [
'ViewportMetaData.cpp',
'VisualViewport.cpp',
'WindowDestroyedEvent.cpp',
'WindowFeatures.cpp',
'WindowNamedPropertiesHandler.cpp',
'WindowOrientationObserver.cpp',
'XPathGenerator.cpp',

View File

@ -1181,11 +1181,14 @@ nsContentUtils::InternalSerializeAutocompleteAttribute(
}
// Parse an integer according to HTML spec
int32_t nsContentUtils::ParseHTMLInteger(const nsAString& aValue,
ParseHTMLIntegerResultFlags* aResult) {
template <class StringT>
int32_t nsContentUtils::ParseHTMLIntegerImpl(
const StringT& aValue, ParseHTMLIntegerResultFlags* aResult) {
using CharT = typename StringT::char_type;
int result = eParseHTMLInteger_NoFlags;
nsAString::const_iterator iter, end;
typename StringT::const_iterator iter, end;
aValue.BeginReading(iter);
aValue.EndReading(end);
@ -1201,11 +1204,11 @@ int32_t nsContentUtils::ParseHTMLInteger(const nsAString& aValue,
}
int sign = 1;
if (*iter == char16_t('-')) {
if (*iter == CharT('-')) {
sign = -1;
result |= eParseHTMLInteger_Negative;
++iter;
} else if (*iter == char16_t('+')) {
} else if (*iter == CharT('+')) {
result |= eParseHTMLInteger_NonStandard;
++iter;
}
@ -1216,7 +1219,7 @@ int32_t nsContentUtils::ParseHTMLInteger(const nsAString& aValue,
// Check for leading zeros first.
uint64_t leadingZeros = 0;
while (iter != end) {
if (*iter != char16_t('0')) {
if (*iter != CharT('0')) {
break;
}
@ -1226,8 +1229,8 @@ int32_t nsContentUtils::ParseHTMLInteger(const nsAString& aValue,
}
while (iter != end) {
if (*iter >= char16_t('0') && *iter <= char16_t('9')) {
value = (value * 10) + (*iter - char16_t('0')) * sign;
if (*iter >= CharT('0') && *iter <= CharT('9')) {
value = (value * 10) + (*iter - CharT('0')) * sign;
++iter;
if (!value.isValid()) {
result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorOverflow;
@ -1257,6 +1260,17 @@ int32_t nsContentUtils::ParseHTMLInteger(const nsAString& aValue,
return value.isValid() ? value.value() : 0;
}
// Parse an integer according to HTML spec
int32_t nsContentUtils::ParseHTMLInteger(const nsAString& aValue,
ParseHTMLIntegerResultFlags* aResult) {
return ParseHTMLIntegerImpl(aValue, aResult);
}
int32_t nsContentUtils::ParseHTMLInteger(const nsACString& aValue,
ParseHTMLIntegerResultFlags* aResult) {
return ParseHTMLIntegerImpl(aValue, aResult);
}
#define SKIP_WHITESPACE(iter, end_iter, end_res) \
while ((iter) != (end_iter) && nsCRT::IsAsciiSpace(*(iter))) { \
++(iter); \

View File

@ -614,7 +614,12 @@ class nsContentUtils {
enum ParseHTMLIntegerResultFlags {
eParseHTMLInteger_NoFlags = 0,
// eParseHTMLInteger_NonStandard is set if the string representation of the
// integer was not the canonical one (e.g. had extra leading '+' or '0').
// integer was not the canonical one, but matches at least one of the
// following:
// * had leading whitespaces
// * had '+' sign
// * had leading '0'
// * was '-0'
eParseHTMLInteger_NonStandard = 1 << 0,
eParseHTMLInteger_DidNotConsumeAllInput = 1 << 1,
// Set if one or more error flags were set.
@ -626,7 +631,15 @@ class nsContentUtils {
};
static int32_t ParseHTMLInteger(const nsAString& aValue,
ParseHTMLIntegerResultFlags* aResult);
static int32_t ParseHTMLInteger(const nsACString& aValue,
ParseHTMLIntegerResultFlags* aResult);
private:
template <class StringT>
static int32_t ParseHTMLIntegerImpl(const StringT& aValue,
ParseHTMLIntegerResultFlags* aResult);
public:
/**
* Parse a margin string of format 'top, right, bottom, left' into
* an nsIntMargin.

View File

@ -41,6 +41,7 @@
#include "mozilla/dom/TimeoutHandler.h"
#include "mozilla/dom/TimeoutManager.h"
#include "mozilla/dom/WindowContext.h"
#include "mozilla/dom/WindowFeatures.h" // WindowFeatures
#include "mozilla/dom/WindowProxyHolder.h"
#include "mozilla/IntegerPrintfMacros.h"
#if defined(MOZ_WIDGET_ANDROID)
@ -76,7 +77,6 @@
#include "jsfriendapi.h"
#include "js/PropertySpec.h"
#include "js/Wrapper.h"
#include "nsCharSeparatedTokenizer.h"
#include "nsReadableUtils.h"
#include "nsJSEnvironment.h"
#include "mozilla/dom/ScriptSettings.h"
@ -7019,34 +7019,32 @@ nsresult nsGlobalWindowOuter::OpenInternal(
NS_ASSERTION(mDocShell, "Must have docshell here");
nsAutoCString options;
NS_ConvertUTF16toUTF8 optionsUtf8(aOptions);
WindowFeatures features;
if (!features.Tokenize(optionsUtf8)) {
return NS_ERROR_FAILURE;
}
bool forceNoOpener = aForceNoOpener;
if (features.Exists("noopener")) {
forceNoOpener = features.GetBool("noopener");
features.Remove("noopener");
}
bool forceNoReferrer = false;
// Unlike other window flags, "noopener" comes from splitting on commas with
// HTML whitespace trimming...
nsCharSeparatedTokenizerTemplate<nsContentUtils::IsHTMLWhitespace> tok(
aOptions, ',');
while (tok.hasMoreTokens()) {
auto nextTok = tok.nextToken();
if (nextTok.EqualsLiteral("noopener")) {
forceNoOpener = true;
continue;
}
if (StaticPrefs::dom_window_open_noreferrer_enabled() &&
nextTok.LowerCaseEqualsLiteral("noreferrer")) {
forceNoReferrer = true;
if (features.Exists("noreferrer")) {
forceNoReferrer = features.GetBool("noreferrer");
if (forceNoReferrer) {
// noreferrer implies noopener
forceNoOpener = true;
continue;
}
// Want to create a copy of the options without 'noopener' because having
// 'noopener' in the options affects other window features.
if (!options.IsEmpty()) {
options.Append(',');
}
AppendUTF16toUTF8(nextTok, options);
features.Remove("noreferrer");
}
nsAutoCString options;
features.Stringify(options);
// If current's top-level browsing context's active document's
// cross-origin-opener-policy is "same-origin" or "same-origin + COEP" then
// if currentDoc's origin is not same origin with currentDoc's top-level

View File

@ -3,10 +3,14 @@
if webrender and not debug: bug 1425588
if (os == "android") and e10s: bug 1550895 (frequently fails on geckoview)
["innerwidth==401" should set width of opened window]
expected: FAIL
expected:
if (os == "android") and not e10s: FAIL
if (os == "android") and e10s: FAIL
["innerheight==402" should set height of opened window]
expected: FAIL
expected:
if (os == "android") and not e10s: FAIL
if (os == "android") and e10s: FAIL
["INNERHEIGHT=402" should set height of opened window]
expected:

View File

@ -1,20 +1,2 @@
[open-features-tokenization-noopener.html]
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1364696
[tokenization should skip window features separators before `name`]
expected: FAIL
[feature `name` should be converted to ASCII lowercase]
expected: FAIL
[after `name`, tokenization should skip window features separators that are not "=" or ","]
expected: FAIL
[Tokenizing should ignore window feature separators except "," after initial "=" and before value]
expected: FAIL
[Tokenizing should read characters until first window feature separator as `value`]
expected: FAIL
["noopener" should be based on name (key), not value]
expected: FAIL

View File

@ -1,22 +1,3 @@
[open-features-tokenization-noreferrer.html]
expected:
if webrender and (os == "linux"): ["OK", "TIMEOUT", "CRASH"]
[Tokenizing "noreferrer" should ignore window feature separators except "," after initial "=" and before value]
expected: FAIL
[Tokenizing "noreferrer" should read characters until first window feature separator as `value`]
expected: FAIL
[After "noreferrer", tokenization should skip window features separators that are not "=" or ","]
expected: FAIL
[Integer values other than 0 should activate the feature]
expected: FAIL
[Tokenization of "noreferrer" should skip window features separators before feature]
expected: FAIL
[Feature "noreferrer" should be converted to ASCII lowercase]
expected: FAIL

View File

@ -2,69 +2,5 @@
disabled:
if webrender and not debug: bug 1425588
if verify and (os == "linux") and not debug: fails in verify mode
["screenx==141" should set left position of opened window]
expected: FAIL
["screeny==142" should set top position of opened window]
expected: FAIL
["screenx=141" should set left position of opened window]
expected:
if (os == "android") and not e10s: FAIL
if (os == "android") and e10s: FAIL
["screeny=142" should set top position of opened window]
expected:
if (os == "android") and not e10s: FAIL
if (os == "android") and e10s: FAIL
["\\nscreenx= 141" should set left position of opened window]
expected:
if (os == "android") and not e10s: FAIL
if (os == "android") and e10s: FAIL
[" screeny = 142" should set top position of opened window]
expected:
if (os == "android") and not e10s: FAIL
if (os == "android") and e10s: FAIL
["screenX=141" should set left position of opened window]
expected:
if (os == "android") and not e10s: FAIL
if (os == "android") and e10s: FAIL
[",screenx=141,," should set left position of opened window]
expected:
if (os == "android") and not e10s: FAIL
if (os == "android") and e10s: FAIL
[",screeny=142,," should set top position of opened window]
expected:
if (os == "android") and not e10s: FAIL
if (os == "android") and e10s: FAIL
["screenY=142" should set top position of opened window]
expected:
if (os == "android") and not e10s: FAIL
if (os == "android") and e10s: FAIL
[" screenx = 141" should set left position of opened window]
expected:
if (os == "android") and not e10s: FAIL
if (os == "android") and e10s: FAIL
["SCREENX=141" should set left position of opened window]
expected:
if (os == "android") and not e10s: FAIL
if (os == "android") and e10s: FAIL
["SCREENY=142" should set top position of opened window]
expected:
if (os == "android") and not e10s: FAIL
if (os == "android") and e10s: FAIL
["\\nscreeny= 142" should set top position of opened window]
expected:
if (os == "android") and not e10s: FAIL
if (os == "android") and e10s: FAIL
if os == "android": frequently hits timeout

View File

@ -3,13 +3,19 @@
if webrender and not debug: bug 1425588
if verify and (os == "linux") and not debug: fails in verify mode
["left==141" should set left position of opened window]
expected: FAIL
expected:
if (os == "android") and not e10s: FAIL
if (os == "android") and e10s: FAIL
["top==142" should set top position of opened window]
expected: FAIL
expected:
if (os == "android") and not e10s: FAIL
if (os == "android") and e10s: FAIL
["top=152==left=152" should set top and left position of opened window]
expected: FAIL
expected:
if (os == "android") and not e10s: FAIL
if (os == "android") and e10s: FAIL
[",left=141,," should set left position of opened window]
expected:

View File

@ -3,16 +3,27 @@
if webrender and not debug: bug 1425588
if (os == "android") and e10s: bug 1550895 (frequently fails on geckoview)
["width==401" should set width of opened window]
expected: FAIL
expected:
if (os == "android") and not e10s: FAIL
if (os == "android") and e10s: FAIL
["height==402" should set height of opened window]
expected: FAIL
expected:
if (os == "android") and not e10s: FAIL
if (os == "android") and e10s: FAIL
if devedition and os == "win" and bits == 32: FAIL # bug 1540551
["height==402 width = 401" should set height and width of opened window]
expected: FAIL
expected:
if (os == "android") and not e10s: FAIL
if (os == "android") and e10s: FAIL
if devedition and os == "win" and bits == 32: FAIL # bug 1540551
[",height=402,,width==401" should set height and width of opened window]
expected: FAIL
expected:
if (os == "android") and not e10s: FAIL
if (os == "android") and e10s: FAIL
if devedition and os == "win" and bits == 32: FAIL # bug 1540551
["\\nheight= 402" should set height of opened window]
expected:

View File

@ -317,9 +317,7 @@ struct SizeSpec {
mOuterHeightSpecified(false),
mInnerWidthSpecified(false),
mInnerHeightSpecified(false),
mLockAspectRatio(false),
mUseDefaultWidth(false),
mUseDefaultHeight(false) {}
mLockAspectRatio(false) {}
int32_t mLeft;
int32_t mTop;
@ -336,11 +334,6 @@ struct SizeSpec {
bool mInnerHeightSpecified;
bool mLockAspectRatio;
// If these booleans are true, don't look at the corresponding width values
// even if they're specified -- they'll be bogus
bool mUseDefaultWidth;
bool mUseDefaultHeight;
bool PositionSpecified() const { return mLeftSpecified || mTopSpecified; }
bool SizeSpecified() const { return WidthSpecified() || HeightSpecified(); }
@ -411,8 +404,7 @@ static bool CheckUserContextCompatibility(nsIDocShell* aDocShell) {
return subjectPrincipal->GetUserContextId() == userContextId;
}
nsresult nsWindowWatcher::CreateChromeWindow(const nsACString& aFeatures,
nsIWebBrowserChrome* aParentChrome,
nsresult nsWindowWatcher::CreateChromeWindow(nsIWebBrowserChrome* aParentChrome,
uint32_t aChromeFlags,
nsIOpenWindowInfo* aOpenWindowInfo,
nsIWebBrowserChrome** aResult) {
@ -444,21 +436,18 @@ nsresult nsWindowWatcher::CreateChromeWindow(const nsACString& aFeatures,
* the size.
*
* @param aFeatures
* The features string that was used to open the window.
* The features that was used to open the window.
* @param aTreeOwner
* The nsIDocShellTreeOwner of the newly opened window. If null,
* this function is a no-op.
*/
void nsWindowWatcher::MaybeDisablePersistence(
const nsACString& aFeatures, nsIDocShellTreeOwner* aTreeOwner) {
const SizeSpec& sizeSpec, nsIDocShellTreeOwner* aTreeOwner) {
if (!aTreeOwner) {
return;
}
// At the moment, the strings "height=" or "width=" never happen
// outside a size specification, so we can do this the Q&D way.
if (PL_strcasestr(aFeatures.BeginReading(), "width=") ||
PL_strcasestr(aFeatures.BeginReading(), "height=")) {
if (sizeSpec.SizeSpecified()) {
aTreeOwner->SetPersistence(false, false, false);
}
}
@ -523,10 +512,13 @@ nsWindowWatcher::OpenWindowWithRemoteTab(nsIRemoteTab* aRemoteTab,
return NS_ERROR_UNEXPECTED;
}
SizeSpec sizeSpec;
CalcSizeSpec(aFeatures, sizeSpec);
WindowFeatures features;
features.Tokenize(aFeatures);
uint32_t chromeFlags = CalculateChromeFlagsForChild(aFeatures, sizeSpec);
SizeSpec sizeSpec;
CalcSizeSpec(features, sizeSpec);
uint32_t chromeFlags = CalculateChromeFlagsForChild(features, sizeSpec);
if (isPrivateBrowsingWindow) {
chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW;
@ -543,7 +535,7 @@ nsWindowWatcher::OpenWindowWithRemoteTab(nsIRemoteTab* aRemoteTab,
nsCOMPtr<nsIWebBrowserChrome> parentChrome(do_GetInterface(parentTreeOwner));
nsCOMPtr<nsIWebBrowserChrome> newWindowChrome;
CreateChromeWindow(aFeatures, parentChrome, chromeFlags, aOpenWindowInfo,
CreateChromeWindow(parentChrome, chromeFlags, aOpenWindowInfo,
getter_AddRefs(newWindowChrome));
if (NS_WARN_IF(!newWindowChrome)) {
@ -574,7 +566,7 @@ nsWindowWatcher::OpenWindowWithRemoteTab(nsIRemoteTab* aRemoteTab,
// that will also run with out-of-process tabs.
MOZ_ASSERT(chromeContext->UseRemoteTabs());
MaybeDisablePersistence(aFeatures, chromeTreeOwner);
MaybeDisablePersistence(sizeSpec, chromeTreeOwner);
SizeOpenedWindow(chromeTreeOwner, parentWindowOuter, false, sizeSpec,
Some(aOpenerFullZoom));
@ -606,7 +598,6 @@ nsresult nsWindowWatcher::OpenWindowInternal(
uint32_t chromeFlags;
nsAutoString name; // string version of aName
nsAutoCString features; // string version of aFeatures
nsCOMPtr<nsIURI> uriToLoad; // from aUrl, if any
nsCOMPtr<nsIDocShellTreeOwner>
parentTreeOwner; // from the parent window, if any
@ -644,11 +635,13 @@ nsresult nsWindowWatcher::OpenWindowInternal(
name.SetIsVoid(true);
}
WindowFeatures features;
nsAutoCString featuresStr;
if (aFeatures) {
features.Assign(aFeatures);
features.StripWhitespace();
featuresStr.Assign(aFeatures);
features.Tokenize(featuresStr);
} else {
features.SetIsVoid(true);
featuresStr.SetIsVoid(true);
}
RefPtr<BrowsingContext> parentBC(
@ -839,8 +832,8 @@ nsresult nsWindowWatcher::OpenWindowInternal(
if (provider) {
rv = provider->ProvideWindow(openWindowInfo, chromeFlags, aCalledFromJS,
sizeSpec.WidthSpecified(), uriToLoad, name,
features, aForceNoOpener, aForceNoReferrer,
aLoadState, &windowIsNew,
featuresStr, aForceNoOpener,
aForceNoReferrer, aLoadState, &windowIsNew,
getter_AddRefs(newBC));
if (NS_SUCCEEDED(rv) && newBC) {
@ -936,9 +929,8 @@ nsresult nsWindowWatcher::OpenWindowInternal(
completely honest: we clear that indicator if the opener is chrome, so
that the downstream consumer can treat the indicator to mean simply
that the new window is subject to popup control. */
rv = CreateChromeWindow(features, parentChrome, chromeFlags,
openWindowInfo, getter_AddRefs(newChrome));
rv = CreateChromeWindow(parentChrome, chromeFlags, openWindowInfo,
getter_AddRefs(newChrome));
if (parentTopInnerWindow) {
parentTopInnerWindow->Resume();
}
@ -1031,7 +1023,7 @@ nsresult nsWindowWatcher::OpenWindowInternal(
if (isNewToplevelWindow) {
nsCOMPtr<nsIDocShellTreeOwner> newTreeOwner;
newDocShell->GetTreeOwner(getter_AddRefs(newTreeOwner));
MaybeDisablePersistence(features, newTreeOwner);
MaybeDisablePersistence(sizeSpec, newTreeOwner);
}
if (aDialog && aArgv) {
@ -1649,41 +1641,43 @@ nsresult nsWindowWatcher::URIfromURL(const char* aURL,
return NS_NewURI(aURI, aURL, baseURI);
}
#define NS_CALCULATE_CHROME_FLAG_FOR(feature, flag) \
chromeFlags |= \
WinHasOption(aFeatures, (feature), 0, &presenceFlag) ? (flag) : 0;
// static
uint32_t nsWindowWatcher::CalculateChromeFlagsHelper(
uint32_t aInitialFlags, const nsACString& aFeatures,
const SizeSpec& aSizeSpec, bool& presenceFlag, bool aHasChromeParent) {
uint32_t aInitialFlags, const WindowFeatures& aFeatures,
const SizeSpec& aSizeSpec, bool* presenceFlag, bool aHasChromeParent) {
uint32_t chromeFlags = aInitialFlags;
// NS_CALCULATE_CHROME_FLAG_FOR requires aFeatures, presenceFlag, and
// chromeFlags to be in scope.
if (aFeatures.GetBoolWithDefault("titlebar", false, presenceFlag)) {
chromeFlags |= nsIWebBrowserChrome::CHROME_TITLEBAR;
}
if (aFeatures.GetBoolWithDefault("close", false, presenceFlag)) {
chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_CLOSE;
}
if (aFeatures.GetBoolWithDefault("toolbar", false, presenceFlag)) {
chromeFlags |= nsIWebBrowserChrome::CHROME_TOOLBAR;
}
if (aFeatures.GetBoolWithDefault("location", false, presenceFlag)) {
chromeFlags |= nsIWebBrowserChrome::CHROME_LOCATIONBAR;
}
if (aFeatures.GetBoolWithDefault("personalbar", false, presenceFlag)) {
chromeFlags |= nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR;
}
if (aFeatures.GetBoolWithDefault("status", false, presenceFlag)) {
chromeFlags |= nsIWebBrowserChrome::CHROME_STATUSBAR;
}
if (aFeatures.GetBoolWithDefault("menubar", false, presenceFlag)) {
chromeFlags |= nsIWebBrowserChrome::CHROME_MENUBAR;
}
if (aFeatures.GetBoolWithDefault("resizable", false, presenceFlag)) {
chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_RESIZE;
}
if (aFeatures.GetBoolWithDefault("minimizable", false, presenceFlag)) {
chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_MIN;
}
NS_CALCULATE_CHROME_FLAG_FOR("titlebar",
nsIWebBrowserChrome::CHROME_TITLEBAR);
NS_CALCULATE_CHROME_FLAG_FOR("close",
nsIWebBrowserChrome::CHROME_WINDOW_CLOSE);
NS_CALCULATE_CHROME_FLAG_FOR("toolbar", nsIWebBrowserChrome::CHROME_TOOLBAR);
NS_CALCULATE_CHROME_FLAG_FOR("location",
nsIWebBrowserChrome::CHROME_LOCATIONBAR);
NS_CALCULATE_CHROME_FLAG_FOR("personalbar",
nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR);
NS_CALCULATE_CHROME_FLAG_FOR("status", nsIWebBrowserChrome::CHROME_STATUSBAR);
NS_CALCULATE_CHROME_FLAG_FOR("menubar", nsIWebBrowserChrome::CHROME_MENUBAR);
NS_CALCULATE_CHROME_FLAG_FOR("resizable",
nsIWebBrowserChrome::CHROME_WINDOW_RESIZE);
NS_CALCULATE_CHROME_FLAG_FOR("minimizable",
nsIWebBrowserChrome::CHROME_WINDOW_MIN);
// default scrollbar to "on," unless explicitly turned off
bool scrollbarsPresent = false;
if (WinHasOption(aFeatures, "scrollbars", 1, &scrollbarsPresent) ||
!scrollbarsPresent) {
if (aFeatures.GetBoolWithDefault("scrollbars", true, presenceFlag)) {
chromeFlags |= nsIWebBrowserChrome::CHROME_SCROLLBARS;
}
presenceFlag = presenceFlag || scrollbarsPresent;
if (aHasChromeParent) {
return chromeFlags;
@ -1739,37 +1733,32 @@ uint32_t nsWindowWatcher::EnsureFlagsSafeForContent(uint32_t aChromeFlags,
}
// static
bool nsWindowWatcher::ShouldOpenPopup(const nsACString& aFeatures,
bool nsWindowWatcher::ShouldOpenPopup(const WindowFeatures& aFeatures,
const SizeSpec& aSizeSpec) {
if (aFeatures.IsVoid()) {
if (aFeatures.IsEmpty()) {
return false;
}
// Follow Google Chrome's behavior that opens a popup depending on
// the following features.
bool unused;
if (!WinHasOption(aFeatures, "location", 0, &unused) &&
!WinHasOption(aFeatures, "toolbar", 0, &unused)) {
if (!aFeatures.GetBoolWithDefault("location", false) &&
!aFeatures.GetBoolWithDefault("toolbar", false)) {
return true;
}
if (!WinHasOption(aFeatures, "menubar", 0, &unused)) {
if (!aFeatures.GetBoolWithDefault("menubar", false)) {
return true;
}
// `resizable` defaults to true.
// Should open popup only when explicitly specified to 0.
bool resizablePresent = false;
if (!WinHasOption(aFeatures, "resizable", 0, &resizablePresent) &&
resizablePresent) {
if (!aFeatures.GetBoolWithDefault("resizable", true)) {
return true;
}
if (!WinHasOption(aFeatures, "scrollbars", 0, &unused)) {
if (!aFeatures.GetBoolWithDefault("scrollbars", false)) {
return true;
}
if (!WinHasOption(aFeatures, "status", 0, &unused)) {
if (!aFeatures.GetBoolWithDefault("status", false)) {
return true;
}
@ -1791,15 +1780,13 @@ bool nsWindowWatcher::ShouldOpenPopup(const nsACString& aFeatures,
*/
// static
uint32_t nsWindowWatcher::CalculateChromeFlagsForChild(
const nsACString& aFeatures, const SizeSpec& aSizeSpec) {
if (aFeatures.IsVoid()) {
const WindowFeatures& aFeatures, const SizeSpec& aSizeSpec) {
if (aFeatures.IsEmpty()) {
return nsIWebBrowserChrome::CHROME_ALL;
}
bool presenceFlag = false;
uint32_t chromeFlags =
CalculateChromeFlagsHelper(nsIWebBrowserChrome::CHROME_WINDOW_BORDERS,
aFeatures, aSizeSpec, presenceFlag);
uint32_t chromeFlags = CalculateChromeFlagsHelper(
nsIWebBrowserChrome::CHROME_WINDOW_BORDERS, aFeatures, aSizeSpec);
return EnsureFlagsSafeForContent(chromeFlags);
}
@ -1817,7 +1804,7 @@ uint32_t nsWindowWatcher::CalculateChromeFlagsForChild(
*/
// static
uint32_t nsWindowWatcher::CalculateChromeFlagsForParent(
mozIDOMWindowProxy* aParent, const nsACString& aFeatures,
mozIDOMWindowProxy* aParent, const WindowFeatures& aFeatures,
const SizeSpec& aSizeSpec, bool aDialog, bool aChromeURL,
bool aHasChromeParent, bool aCalledFromJS) {
MOZ_ASSERT(XRE_IsParentProcess());
@ -1827,7 +1814,7 @@ uint32_t nsWindowWatcher::CalculateChromeFlagsForParent(
// The features string is made void by OpenWindowInternal
// if nullptr was originally passed as the features string.
if (aFeatures.IsVoid()) {
if (aFeatures.IsEmpty()) {
chromeFlags = nsIWebBrowserChrome::CHROME_ALL;
if (aDialog) {
chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_DIALOG |
@ -1846,29 +1833,29 @@ uint32_t nsWindowWatcher::CalculateChromeFlagsForParent(
in the standards-compliant window.(normal)open. */
bool presenceFlag = false;
if (aDialog && WinHasOption(aFeatures, "all", 0, &presenceFlag)) {
if (aDialog && aFeatures.GetBoolWithDefault("all", false, &presenceFlag)) {
chromeFlags = nsIWebBrowserChrome::CHROME_ALL;
}
/* Next, allow explicitly named options to override the initial settings */
chromeFlags = CalculateChromeFlagsHelper(chromeFlags, aFeatures, aSizeSpec,
presenceFlag, aHasChromeParent);
&presenceFlag, aHasChromeParent);
// Determine whether the window is a private browsing window
chromeFlags |= WinHasOption(aFeatures, "private", 0, &presenceFlag)
? nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW
: 0;
chromeFlags |= WinHasOption(aFeatures, "non-private", 0, &presenceFlag)
? nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW
: 0;
if (aFeatures.GetBoolWithDefault("private", false, &presenceFlag)) {
chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW;
}
if (aFeatures.GetBoolWithDefault("non-private", false, &presenceFlag)) {
chromeFlags |= nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW;
}
// Determine whether the window should have remote tabs.
bool remote = BrowserTabsRemoteAutostart();
if (remote) {
remote = !WinHasOption(aFeatures, "non-remote", 0, &presenceFlag);
remote = !aFeatures.GetBoolWithDefault("non-remote", false, &presenceFlag);
} else {
remote = WinHasOption(aFeatures, "remote", 0, &presenceFlag);
remote = aFeatures.GetBoolWithDefault("remote", false, &presenceFlag);
}
if (remote) {
@ -1879,18 +1866,19 @@ uint32_t nsWindowWatcher::CalculateChromeFlagsForParent(
bool fission = StaticPrefs::fission_autostart();
if (fission) {
fission = !WinHasOption(aFeatures, "non-fission", 0, &presenceFlag);
fission =
!aFeatures.GetBoolWithDefault("non-fission", false, &presenceFlag);
} else {
fission = WinHasOption(aFeatures, "fission", 0, &presenceFlag);
fission = aFeatures.GetBoolWithDefault("fission", false, &presenceFlag);
}
if (fission) {
chromeFlags |= nsIWebBrowserChrome::CHROME_FISSION_WINDOW;
}
chromeFlags |= WinHasOption(aFeatures, "popup", 0, &presenceFlag)
? nsIWebBrowserChrome::CHROME_WINDOW_POPUP
: 0;
if (aFeatures.GetBoolWithDefault("popup", false, &presenceFlag)) {
chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_POPUP;
}
/* OK.
Normal browser windows, in spite of a stated pattern of turning off
@ -1901,15 +1889,15 @@ uint32_t nsWindowWatcher::CalculateChromeFlagsForParent(
// default titlebar and closebox to "on," if not mentioned at all
if (!(chromeFlags & nsIWebBrowserChrome::CHROME_WINDOW_POPUP)) {
if (!PL_strcasestr(aFeatures.BeginReading(), "titlebar")) {
if (!aFeatures.Exists("titlebar")) {
chromeFlags |= nsIWebBrowserChrome::CHROME_TITLEBAR;
}
if (!PL_strcasestr(aFeatures.BeginReading(), "close")) {
if (!aFeatures.Exists("close")) {
chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_CLOSE;
}
}
if (aDialog && !aFeatures.IsVoid() && !presenceFlag) {
if (aDialog && !aFeatures.IsEmpty() && !presenceFlag) {
chromeFlags = nsIWebBrowserChrome::CHROME_DEFAULT;
}
@ -1917,35 +1905,35 @@ uint32_t nsWindowWatcher::CalculateChromeFlagsForParent(
with the features that are more operating hints than appearance
instructions. (Note modality implies dependence.) */
if (WinHasOption(aFeatures, "alwaysLowered", 0, nullptr) ||
WinHasOption(aFeatures, "z-lock", 0, nullptr)) {
if (aFeatures.GetBoolWithDefault("alwayslowered", false) ||
aFeatures.GetBoolWithDefault("z-lock", false)) {
chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_LOWERED;
} else if (WinHasOption(aFeatures, "alwaysRaised", 0, nullptr)) {
} else if (aFeatures.GetBoolWithDefault("alwaysraised", false)) {
chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_RAISED;
}
chromeFlags |= WinHasOption(aFeatures, "suppressanimation", 0, nullptr)
? nsIWebBrowserChrome::CHROME_SUPPRESS_ANIMATION
: 0;
chromeFlags |= WinHasOption(aFeatures, "alwaysontop", 0, nullptr)
? nsIWebBrowserChrome::CHROME_ALWAYS_ON_TOP
: 0;
chromeFlags |= WinHasOption(aFeatures, "chrome", 0, nullptr)
? nsIWebBrowserChrome::CHROME_OPENAS_CHROME
: 0;
chromeFlags |= WinHasOption(aFeatures, "extrachrome", 0, nullptr)
? nsIWebBrowserChrome::CHROME_EXTRA
: 0;
chromeFlags |= WinHasOption(aFeatures, "centerscreen", 0, nullptr)
? nsIWebBrowserChrome::CHROME_CENTER_SCREEN
: 0;
chromeFlags |= WinHasOption(aFeatures, "dependent", 0, nullptr)
? nsIWebBrowserChrome::CHROME_DEPENDENT
: 0;
chromeFlags |= WinHasOption(aFeatures, "modal", 0, nullptr)
? (nsIWebBrowserChrome::CHROME_MODAL |
nsIWebBrowserChrome::CHROME_DEPENDENT)
: 0;
if (aFeatures.GetBoolWithDefault("suppressanimation", false)) {
chromeFlags |= nsIWebBrowserChrome::CHROME_SUPPRESS_ANIMATION;
}
if (aFeatures.GetBoolWithDefault("alwaysontop", false)) {
chromeFlags |= nsIWebBrowserChrome::CHROME_ALWAYS_ON_TOP;
}
if (aFeatures.GetBoolWithDefault("chrome", false)) {
chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_CHROME;
}
if (aFeatures.GetBoolWithDefault("extrachrome", false)) {
chromeFlags |= nsIWebBrowserChrome::CHROME_EXTRA;
}
if (aFeatures.GetBoolWithDefault("centerscreen", false)) {
chromeFlags |= nsIWebBrowserChrome::CHROME_CENTER_SCREEN;
}
if (aFeatures.GetBoolWithDefault("dependent", false)) {
chromeFlags |= nsIWebBrowserChrome::CHROME_DEPENDENT;
}
if (aFeatures.GetBoolWithDefault("modal", false)) {
chromeFlags |= nsIWebBrowserChrome::CHROME_MODAL |
nsIWebBrowserChrome::CHROME_DEPENDENT;
}
/* On mobile we want to ignore the dialog window feature, since the mobile UI
does not provide any affordance for dialog windows. This does not interfere
@ -1957,18 +1945,18 @@ uint32_t nsWindowWatcher::CalculateChromeFlagsForParent(
&disableDialogFeature);
if (!disableDialogFeature) {
chromeFlags |= WinHasOption(aFeatures, "dialog", 0, nullptr)
? nsIWebBrowserChrome::CHROME_OPENAS_DIALOG
: 0;
if (aFeatures.GetBoolWithDefault("dialog", false)) {
chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_DIALOG;
}
}
/* and dialogs need to have the last word. assume dialogs are dialogs,
and opened as chrome, unless explicitly told otherwise. */
if (aDialog) {
if (!PL_strcasestr(aFeatures.BeginReading(), "dialog")) {
if (!aFeatures.Exists("dialog")) {
chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_DIALOG;
}
if (!PL_strcasestr(aFeatures.BeginReading(), "chrome")) {
if (!aFeatures.Exists("chrome")) {
chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_CHROME;
}
}
@ -1992,63 +1980,6 @@ uint32_t nsWindowWatcher::CalculateChromeFlagsForParent(
return chromeFlags;
}
// static
int32_t nsWindowWatcher::WinHasOption(const nsACString& aOptions,
const char* aName, int32_t aDefault,
bool* aPresenceFlag) {
if (aOptions.IsEmpty()) {
return 0;
}
const char* options = aOptions.BeginReading();
char* comma;
char* equal;
int32_t found = 0;
#ifdef DEBUG
NS_ASSERTION(nsAutoCString(aOptions).FindCharInSet(" \n\r\t") == kNotFound,
"There should be no whitespace in this string!");
#endif
while (true) {
comma = PL_strchr(options, ',');
if (comma) {
*comma = '\0';
}
equal = PL_strchr(options, '=');
if (equal) {
*equal = '\0';
}
if (nsCRT::strcasecmp(options, aName) == 0) {
if (aPresenceFlag) {
*aPresenceFlag = true;
}
if (equal)
if (*(equal + 1) == '*') {
found = aDefault;
} else if (nsCRT::strcasecmp(equal + 1, "yes") == 0) {
found = 1;
} else {
found = atoi(equal + 1);
}
else {
found = 1;
}
}
if (equal) {
*equal = '=';
}
if (comma) {
*comma = ',';
}
if (found || !comma) {
break;
}
options = comma + 1;
}
return found;
}
already_AddRefed<BrowsingContext> nsWindowWatcher::GetBrowsingContextByName(
const nsAString& aName, bool aForceNoOpener,
BrowsingContext* aCurrentContext) {
@ -2080,70 +2011,148 @@ already_AddRefed<BrowsingContext> nsWindowWatcher::GetBrowsingContextByName(
}
// static
void nsWindowWatcher::CalcSizeSpec(const nsACString& aFeatures,
void nsWindowWatcher::CalcSizeSpec(const WindowFeatures& aFeatures,
SizeSpec& aResult) {
// Parse position spec, if any, from aFeatures
bool present;
int32_t temp;
// https://drafts.csswg.org/cssom-view/#set-up-browsing-context-features
// To set up browsing context features for a browsing context `target` given
// a map `tokenizedFeatures`:
present = false;
if ((temp = WinHasOption(aFeatures, "left", 0, &present)) || present) {
aResult.mLeft = temp;
} else if ((temp = WinHasOption(aFeatures, "screenX", 0, &present)) ||
present) {
aResult.mLeft = temp;
// Step 1. Let `x` be null.
// (implicit)
// Step 2. Let `y` be null.
// (implicit)
// Step 3. Let `width` be null.
// (implicit)
// Step 4. Let `height` be null.
// (implicit)
// Step 5. If `tokenizedFeatures["left"]` exists:
if (aFeatures.Exists("left")) {
// Step 5.1. Set `x` to the result of invoking the rules for parsing
// integers on `tokenizedFeatures["left"]`.
//
// Step 5.2. If `x` is an error, set `x` to 0.
int32_t x = aFeatures.GetInt("left");
// Step 5.3. Optionally, clamp `x` in a user-agent-defined manner so that
// the window does not move outside the Web-exposed available screen area.
// (done later)
// Step 5.4. Optionally, move `target`s window such that the windows
// left edge is at the horizontal coordinate `x` relative to the left edge
// of the Web-exposed screen area, measured in CSS pixels of target.
// The positive axis is rightward.
aResult.mLeft = x;
aResult.mLeftSpecified = true;
}
aResult.mLeftSpecified = present;
present = false;
if ((temp = WinHasOption(aFeatures, "top", 0, &present)) || present) {
aResult.mTop = temp;
} else if ((temp = WinHasOption(aFeatures, "screenY", 0, &present)) ||
present) {
aResult.mTop = temp;
// Step 6. If `tokenizedFeatures["top"]` exists:
if (aFeatures.Exists("top")) {
// Step 6.1. Set `y` to the result of invoking the rules for parsing
// integers on `tokenizedFeatures["top"]`.
//
// Step 6.2. If `y` is an error, set `y` to 0.
int32_t y = aFeatures.GetInt("top");
// Step 6.3. Optionally, clamp `y` in a user-agent-defined manner so that
// the window does not move outside the Web-exposed available screen area.
// (done later)
// Step 6.4. Optionally, move `target`s window such that the windows top
// edge is at the vertical coordinate `y` relative to the top edge of the
// Web-exposed screen area, measured in CSS pixels of target. The positive
// axis is downward.
aResult.mTop = y;
aResult.mTopSpecified = true;
}
aResult.mTopSpecified = present;
// Parse size spec, if any. Chrome size overrides content size.
if ((temp = WinHasOption(aFeatures, "outerWidth", INT32_MIN, nullptr))) {
if (temp == INT32_MIN) {
aResult.mUseDefaultWidth = true;
} else {
aResult.mOuterWidth = temp;
// Non-standard extension.
// See bug 1623826
if (aFeatures.Exists("outerwidth")) {
int32_t width = aFeatures.GetInt("outerwidth");
if (width) {
aResult.mOuterWidth = width;
aResult.mOuterWidthSpecified = true;
}
aResult.mOuterWidthSpecified = true;
} else if ((temp = WinHasOption(aFeatures, "width", INT32_MIN, nullptr)) ||
(temp =
WinHasOption(aFeatures, "innerWidth", INT32_MIN, nullptr))) {
if (temp == INT32_MIN) {
aResult.mUseDefaultWidth = true;
} else {
aResult.mInnerWidth = temp;
}
aResult.mInnerWidthSpecified = true;
}
if ((temp = WinHasOption(aFeatures, "outerHeight", INT32_MIN, nullptr))) {
if (temp == INT32_MIN) {
aResult.mUseDefaultHeight = true;
} else {
aResult.mOuterHeight = temp;
if (!aResult.mOuterWidthSpecified) {
// Step 7. If `tokenizedFeatures["width"]` exists:
if (aFeatures.Exists("width")) {
// Step 7.1. Set `width` to the result of invoking the rules for parsing
// integers on `tokenizedFeatures["width"]`.
//
// Step 7.2. If `width` is an error, set `width` to 0.
int32_t width = aFeatures.GetInt("width");
// Step 7.3. If `width` is not 0:
if (width) {
// Step 7.3.1. Optionally, clamp `width` in a user-agent-defined manner
// so that the window does not get too small or bigger than the
// Web-exposed available screen area.
// (done later)
// Step 7.3.2. Optionally, size `target`s window by moving its right
// edge such that the distance between the left and right edges of the
// viewport are `width` CSS pixels of target.
aResult.mInnerWidth = width;
aResult.mInnerWidthSpecified = true;
// Step 7.3.3. Optionally, move targets window in a user-agent-defined
// manner so that it does not grow outside the Web-exposed available
// screen area.
// (done later)
}
}
aResult.mOuterHeightSpecified = true;
} else if ((temp = WinHasOption(aFeatures, "height", INT32_MIN, nullptr)) ||
(temp =
WinHasOption(aFeatures, "innerHeight", INT32_MIN, nullptr))) {
if (temp == INT32_MIN) {
aResult.mUseDefaultHeight = true;
} else {
aResult.mInnerHeight = temp;
}
aResult.mInnerHeightSpecified = true;
}
if (WinHasOption(aFeatures, "lockaspectratio", 0, nullptr)) {
aResult.mLockAspectRatio = true;
// Non-standard extension.
// See bug 1623826
if (aFeatures.Exists("outerheight")) {
int32_t height = aFeatures.GetInt("outerheight");
if (height) {
aResult.mOuterHeight = height;
aResult.mOuterHeightSpecified = true;
}
}
if (!aResult.mOuterHeightSpecified) {
// Step 8. If `tokenizedFeatures["height"]` exists:
if (aFeatures.Exists("height")) {
// Step 8.1. Set `height` to the result of invoking the rules for parsing
// integers on `tokenizedFeatures["height"]`.
//
// Step 8.2. If `height` is an error, set `height` to 0.
int32_t height = aFeatures.GetInt("height");
// Step 8.3. If `height` is not 0:
if (height) {
// Step 8.3.1. Optionally, clamp `height` in a user-agent-defined manner
// so that the window does not get too small or bigger than the
// Web-exposed available screen area.
// (done later)
// Step 8.3.2. Optionally, size `target`s window by moving its bottom
// edge such that the distance between the top and bottom edges of the
// viewport are `height` CSS pixels of target.
aResult.mInnerHeight = height;
aResult.mInnerHeightSpecified = true;
// Step 8.3.3. Optionally, move targets window in a user-agent-defined
// manner so that it does not grow outside the Web-exposed available
// screen area.
// (done later)
}
}
}
// NOTE: The value is handled only on chrome-priv code.
// See nsWindowWatcher::SizeOpenedWindow.
aResult.mLockAspectRatio =
aFeatures.GetBoolWithDefault("lockaspectratio", false);
}
/* Size and position a new window according to aSizeSpec. This method
@ -2233,30 +2242,18 @@ void nsWindowWatcher::SizeOpenedWindow(nsIDocShellTreeOwner* aTreeOwner,
// Set up width
if (aSizeSpec.mOuterWidthSpecified) {
if (!aSizeSpec.mUseDefaultWidth) {
width = NSToIntRound(aSizeSpec.mOuterWidth * openerZoom);
} // Else specified to default; just use our existing width
width = NSToIntRound(aSizeSpec.mOuterWidth * openerZoom);
} else if (aSizeSpec.mInnerWidthSpecified) {
sizeChromeWidth = false;
if (aSizeSpec.mUseDefaultWidth) {
width = width - chromeWidth;
} else {
width = NSToIntRound(aSizeSpec.mInnerWidth * openerZoom);
}
width = NSToIntRound(aSizeSpec.mInnerWidth * openerZoom);
}
// Set up height
if (aSizeSpec.mOuterHeightSpecified) {
if (!aSizeSpec.mUseDefaultHeight) {
height = NSToIntRound(aSizeSpec.mOuterHeight * openerZoom);
} // Else specified to default; just use our existing height
height = NSToIntRound(aSizeSpec.mOuterHeight * openerZoom);
} else if (aSizeSpec.mInnerHeightSpecified) {
sizeChromeHeight = false;
if (aSizeSpec.mUseDefaultHeight) {
height = height - chromeHeight;
} else {
height = NSToIntRound(aSizeSpec.mInnerHeight * openerZoom);
}
height = NSToIntRound(aSizeSpec.mInnerHeight * openerZoom);
}
bool positionSpecified = aSizeSpec.PositionSpecified();

View File

@ -25,6 +25,7 @@
#include "nsIRemoteTab.h"
#include "nsPIWindowWatcher.h"
#include "nsTArray.h"
#include "mozilla/dom/WindowFeatures.h" // mozilla::dom::WindowFeatures
class nsIURI;
class nsIDocShellTreeItem;
@ -86,23 +87,20 @@ class nsWindowWatcher : public nsIWindowWatcher,
static nsresult URIfromURL(const char* aURL, mozIDOMWindowProxy* aParent,
nsIURI** aURI);
static bool ShouldOpenPopup(const nsACString& aFeatures,
static bool ShouldOpenPopup(const mozilla::dom::WindowFeatures& aFeatures,
const SizeSpec& aSizeSpec);
static uint32_t CalculateChromeFlagsForChild(const nsACString& aFeaturesStr,
const SizeSpec& aSizeSpec);
static uint32_t CalculateChromeFlagsForChild(
const mozilla::dom::WindowFeatures& aFeatures, const SizeSpec& aSizeSpec);
static uint32_t CalculateChromeFlagsForParent(mozIDOMWindowProxy* aParent,
const nsACString& aFeaturesStr,
const SizeSpec& aSizeSpec,
bool aDialog, bool aChromeURL,
bool aHasChromeParent,
bool aCalledFromJS);
static uint32_t CalculateChromeFlagsForParent(
mozIDOMWindowProxy* aParent,
const mozilla::dom::WindowFeatures& aFeatures, const SizeSpec& aSizeSpec,
bool aDialog, bool aChromeURL, bool aHasChromeParent, bool aCalledFromJS);
static int32_t WinHasOption(const nsACString& aOptions, const char* aName,
int32_t aDefault, bool* aPresenceFlag);
/* Compute the right SizeSpec based on aFeatures */
static void CalcSizeSpec(const nsACString& aFeatures, SizeSpec& aResult);
static void CalcSizeSpec(const mozilla::dom::WindowFeatures& aFeatures,
SizeSpec& aResult);
static void SizeOpenedWindow(
nsIDocShellTreeOwner* aTreeOwner, mozIDOMWindowProxy* aParent,
bool aIsCallerChrome, const SizeSpec& aSizeSpec,
@ -113,20 +111,18 @@ class nsWindowWatcher : public nsIWindowWatcher,
nsIDocShellTreeOwner** aResult);
private:
nsresult CreateChromeWindow(const nsACString& aFeatures,
nsIWebBrowserChrome* aParentChrome,
nsresult CreateChromeWindow(nsIWebBrowserChrome* aParentChrome,
uint32_t aChromeFlags,
nsIOpenWindowInfo* aOpenWindowInfo,
nsIWebBrowserChrome** aResult);
void MaybeDisablePersistence(const nsACString& aFeatures,
void MaybeDisablePersistence(const SizeSpec& sizeSpec,
nsIDocShellTreeOwner* aTreeOwner);
static uint32_t CalculateChromeFlagsHelper(uint32_t aInitialFlags,
const nsACString& aFeatures,
const SizeSpec& aSizeSpec,
bool& presenceFlag,
bool aHasChromeParent = false);
static uint32_t CalculateChromeFlagsHelper(
uint32_t aInitialFlags, const mozilla::dom::WindowFeatures& aFeatures,
const SizeSpec& aSizeSpec, bool* presenceFlag = nullptr,
bool aHasChromeParent = false);
static uint32_t EnsureFlagsSafeForContent(uint32_t aChromeFlags,
bool aChromeURL = false);