diff --git a/dom/base/WindowFeatures.cpp b/dom/base/WindowFeatures.cpp deleted file mode 100644 index 0d1c3172f39f..000000000000 --- a/dom/base/WindowFeatures.cpp +++ /dev/null @@ -1,233 +0,0 @@ -/* -*- 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 -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); - } - } -} diff --git a/dom/base/WindowFeatures.h b/dom/base/WindowFeatures.h deleted file mode 100644 index 634533c9fe95..000000000000 --- a/dom/base/WindowFeatures.h +++ /dev/null @@ -1,131 +0,0 @@ -/* -*- 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 - -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 - 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 - 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 - 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 - 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 - 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 - 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 tokenizedFeatures_; -}; - -} // namespace dom -} // namespace mozilla - -#endif // #ifndef mozilla_dom_WindowFeatures_h diff --git a/dom/base/moz.build b/dom/base/moz.build index 1016f7db4496..0411561e121b 100644 --- a/dom/base/moz.build +++ b/dom/base/moz.build @@ -253,7 +253,6 @@ EXPORTS.mozilla.dom += [ 'UserActivation.h', 'ViewportMetaData.h', 'VisualViewport.h', - 'WindowFeatures.h', 'WindowOrientationObserver.h', 'WindowProxyHolder.h', ] @@ -426,7 +425,6 @@ UNIFIED_SOURCES += [ 'ViewportMetaData.cpp', 'VisualViewport.cpp', 'WindowDestroyedEvent.cpp', - 'WindowFeatures.cpp', 'WindowNamedPropertiesHandler.cpp', 'WindowOrientationObserver.cpp', 'XPathGenerator.cpp', diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index d0b32b1e720d..2c8855b05706 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -1181,14 +1181,11 @@ nsContentUtils::InternalSerializeAutocompleteAttribute( } // Parse an integer according to HTML spec -template -int32_t nsContentUtils::ParseHTMLIntegerImpl( - const StringT& aValue, ParseHTMLIntegerResultFlags* aResult) { - using CharT = typename StringT::char_type; - +int32_t nsContentUtils::ParseHTMLInteger(const nsAString& aValue, + ParseHTMLIntegerResultFlags* aResult) { int result = eParseHTMLInteger_NoFlags; - typename StringT::const_iterator iter, end; + nsAString::const_iterator iter, end; aValue.BeginReading(iter); aValue.EndReading(end); @@ -1204,11 +1201,11 @@ int32_t nsContentUtils::ParseHTMLIntegerImpl( } int sign = 1; - if (*iter == CharT('-')) { + if (*iter == char16_t('-')) { sign = -1; result |= eParseHTMLInteger_Negative; ++iter; - } else if (*iter == CharT('+')) { + } else if (*iter == char16_t('+')) { result |= eParseHTMLInteger_NonStandard; ++iter; } @@ -1219,7 +1216,7 @@ int32_t nsContentUtils::ParseHTMLIntegerImpl( // Check for leading zeros first. uint64_t leadingZeros = 0; while (iter != end) { - if (*iter != CharT('0')) { + if (*iter != char16_t('0')) { break; } @@ -1229,8 +1226,8 @@ int32_t nsContentUtils::ParseHTMLIntegerImpl( } while (iter != end) { - if (*iter >= CharT('0') && *iter <= CharT('9')) { - value = (value * 10) + (*iter - CharT('0')) * sign; + if (*iter >= char16_t('0') && *iter <= char16_t('9')) { + value = (value * 10) + (*iter - char16_t('0')) * sign; ++iter; if (!value.isValid()) { result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorOverflow; @@ -1260,17 +1257,6 @@ int32_t nsContentUtils::ParseHTMLIntegerImpl( 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); \ diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index ec0d74d66c58..285b0147d4eb 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -614,12 +614,7 @@ class nsContentUtils { enum ParseHTMLIntegerResultFlags { eParseHTMLInteger_NoFlags = 0, // eParseHTMLInteger_NonStandard is set if the string representation of the - // integer was not the canonical one, but matches at least one of the - // following: - // * had leading whitespaces - // * had '+' sign - // * had leading '0' - // * was '-0' + // integer was not the canonical one (e.g. had extra leading '+' or '0'). eParseHTMLInteger_NonStandard = 1 << 0, eParseHTMLInteger_DidNotConsumeAllInput = 1 << 1, // Set if one or more error flags were set. @@ -631,15 +626,7 @@ class nsContentUtils { }; static int32_t ParseHTMLInteger(const nsAString& aValue, ParseHTMLIntegerResultFlags* aResult); - static int32_t ParseHTMLInteger(const nsACString& aValue, - ParseHTMLIntegerResultFlags* aResult); - private: - template - static int32_t ParseHTMLIntegerImpl(const StringT& aValue, - ParseHTMLIntegerResultFlags* aResult); - - public: /** * Parse a margin string of format 'top, right, bottom, left' into * an nsIntMargin. diff --git a/dom/base/nsGlobalWindowOuter.cpp b/dom/base/nsGlobalWindowOuter.cpp index 99311b0b1d72..c160b600d288 100644 --- a/dom/base/nsGlobalWindowOuter.cpp +++ b/dom/base/nsGlobalWindowOuter.cpp @@ -41,7 +41,6 @@ #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) @@ -77,6 +76,7 @@ #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,32 +7019,34 @@ nsresult nsGlobalWindowOuter::OpenInternal( NS_ASSERTION(mDocShell, "Must have docshell here"); - NS_ConvertUTF16toUTF8 optionsUtf8(aOptions); - - WindowFeatures features; - if (!features.Tokenize(optionsUtf8)) { - return NS_ERROR_FAILURE; - } - + nsAutoCString options; bool forceNoOpener = aForceNoOpener; - if (features.Exists("noopener")) { - forceNoOpener = features.GetBool("noopener"); - features.Remove("noopener"); - } - bool forceNoReferrer = false; - if (features.Exists("noreferrer")) { - forceNoReferrer = features.GetBool("noreferrer"); - if (forceNoReferrer) { + // Unlike other window flags, "noopener" comes from splitting on commas with + // HTML whitespace trimming... + nsCharSeparatedTokenizerTemplate 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; // noreferrer implies noopener forceNoOpener = true; + continue; } - features.Remove("noreferrer"); + // 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); } - 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 diff --git a/startupcache/StartupCache.h b/startupcache/StartupCache.h index 73d0aed92d6e..153000a7330a 100644 --- a/startupcache/StartupCache.h +++ b/startupcache/StartupCache.h @@ -11,7 +11,6 @@ #include "nsClassHashtable.h" #include "nsComponentManagerUtils.h" #include "nsTArray.h" -#include "nsTStringHasher.h" // mozilla::DefaultHasher #include "nsZipArchive.h" #include "nsITimer.h" #include "nsIMemoryReporter.h" @@ -122,6 +121,19 @@ struct StartupCacheEntry { }; }; +struct nsCStringHasher { + using Key = nsCString; + using Lookup = nsCString; + + static HashNumber hash(const Lookup& aLookup) { + return HashString(aLookup.get()); + } + + static bool match(const Key& aKey, const Lookup& aLookup) { + return aKey.Equals(aLookup); + } +}; + // We don't want to refcount StartupCache, and ObserverService wants to // refcount its listeners, so we'll let it refcount this instead. class StartupCacheListener final : public nsIObserver { @@ -206,7 +218,7 @@ class StartupCache : public nsIMemoryReporter { static void ThreadedWrite(void* aClosure); static void ThreadedPrefetch(void* aClosure); - HashMap mTable; + HashMap mTable; // owns references to the contents of tables which have been invalidated. // In theory grows forever if the cache is continually filled and then // invalidated, but this should not happen in practice. diff --git a/testing/web-platform/meta/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-innerheight-innerwidth.html.ini b/testing/web-platform/meta/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-innerheight-innerwidth.html.ini index 3445ae8c8eb6..dbf12eceab84 100644 --- a/testing/web-platform/meta/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-innerheight-innerwidth.html.ini +++ b/testing/web-platform/meta/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-innerheight-innerwidth.html.ini @@ -3,14 +3,10 @@ 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: - if (os == "android") and not e10s: FAIL - if (os == "android") and e10s: FAIL + expected: FAIL ["innerheight==402" should set height of opened window] - expected: - if (os == "android") and not e10s: FAIL - if (os == "android") and e10s: FAIL + expected: FAIL ["INNERHEIGHT=402" should set height of opened window] expected: diff --git a/testing/web-platform/meta/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-noopener.html.ini b/testing/web-platform/meta/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-noopener.html.ini index 1be9eb56b198..4930c51c2112 100644 --- a/testing/web-platform/meta/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-noopener.html.ini +++ b/testing/web-platform/meta/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-noopener.html.ini @@ -1,2 +1,20 @@ [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 + diff --git a/testing/web-platform/meta/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-noreferrer.html.ini b/testing/web-platform/meta/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-noreferrer.html.ini index 7756183174fe..67a6ebaab506 100644 --- a/testing/web-platform/meta/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-noreferrer.html.ini +++ b/testing/web-platform/meta/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-noreferrer.html.ini @@ -1,3 +1,22 @@ [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 + diff --git a/testing/web-platform/meta/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-screenx-screeny.html.ini b/testing/web-platform/meta/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-screenx-screeny.html.ini index bb38619bc8a6..f34745d53747 100644 --- a/testing/web-platform/meta/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-screenx-screeny.html.ini +++ b/testing/web-platform/meta/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-screenx-screeny.html.ini @@ -2,5 +2,69 @@ disabled: if webrender and not debug: bug 1425588 if verify and (os == "linux") and not debug: fails in verify mode - if os == "android": frequently hits timeout + ["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 diff --git a/testing/web-platform/meta/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-top-left.html.ini b/testing/web-platform/meta/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-top-left.html.ini index 98e6ebb3425a..25380f83e3a5 100644 --- a/testing/web-platform/meta/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-top-left.html.ini +++ b/testing/web-platform/meta/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-top-left.html.ini @@ -3,19 +3,13 @@ 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: - if (os == "android") and not e10s: FAIL - if (os == "android") and e10s: FAIL + expected: FAIL ["top==142" should set top position of opened window] - expected: - if (os == "android") and not e10s: FAIL - if (os == "android") and e10s: FAIL + expected: FAIL ["top=152==left=152" should set top and left position of opened window] - expected: - if (os == "android") and not e10s: FAIL - if (os == "android") and e10s: FAIL + expected: FAIL [",left=141,," should set left position of opened window] expected: diff --git a/testing/web-platform/meta/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-width-height.html.ini b/testing/web-platform/meta/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-width-height.html.ini index 1d32c8c9897d..9385f41fa1c6 100644 --- a/testing/web-platform/meta/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-width-height.html.ini +++ b/testing/web-platform/meta/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-width-height.html.ini @@ -3,27 +3,16 @@ 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: - if (os == "android") and not e10s: FAIL - if (os == "android") and e10s: FAIL + expected: FAIL ["height==402" should set height of opened window] - 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 + expected: FAIL ["height==402 width = 401" should set height and width of opened window] - 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 + expected: FAIL [",height=402,,width==401" should set height and width of opened window] - 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 + expected: FAIL ["\\nheight= 402" should set height of opened window] expected: diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 55d0c87b13ff..796983490cc1 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -14949,16 +14949,6 @@ "bug_numbers": [1597956, 1614905], "description": "milliseconds to complete a TLS session resumption with external cache" }, - "WINDOW_OPEN_OUTER_SIZE": { - "record_in_processes": ["main", "content"], - "products": ["firefox"], - "alert_emails": ["tfujisawa.birchill@mozilla.com"], - "expires_in_version": "80", - "kind": "boolean", - "bug_numbers": [1624150], - "releaseChannelCollection": "opt-out", - "description": "Flag indicating consumer of non-standard outerWidth/outerHeight features in window.open" - }, "WINDOW_REMOTE_SUBFRAMES_ENABLED_STATUS": { "record_in_processes": ["main"], "products": ["firefox"], diff --git a/toolkit/components/windowwatcher/nsWindowWatcher.cpp b/toolkit/components/windowwatcher/nsWindowWatcher.cpp index 4479a018c628..b6054a02b968 100644 --- a/toolkit/components/windowwatcher/nsWindowWatcher.cpp +++ b/toolkit/components/windowwatcher/nsWindowWatcher.cpp @@ -63,7 +63,6 @@ #include "mozilla/ResultExtensions.h" #include "mozilla/StaticPrefs_fission.h" #include "mozilla/StaticPrefs_full_screen_api.h" -#include "mozilla/Telemetry.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/Storage.h" #include "mozilla/dom/ScriptSettings.h" @@ -318,7 +317,9 @@ struct SizeSpec { mOuterHeightSpecified(false), mInnerWidthSpecified(false), mInnerHeightSpecified(false), - mLockAspectRatio(false) {} + mLockAspectRatio(false), + mUseDefaultWidth(false), + mUseDefaultHeight(false) {} int32_t mLeft; int32_t mTop; @@ -335,6 +336,11 @@ 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(); } @@ -405,7 +411,8 @@ static bool CheckUserContextCompatibility(nsIDocShell* aDocShell) { return subjectPrincipal->GetUserContextId() == userContextId; } -nsresult nsWindowWatcher::CreateChromeWindow(nsIWebBrowserChrome* aParentChrome, +nsresult nsWindowWatcher::CreateChromeWindow(const nsACString& aFeatures, + nsIWebBrowserChrome* aParentChrome, uint32_t aChromeFlags, nsIOpenWindowInfo* aOpenWindowInfo, nsIWebBrowserChrome** aResult) { @@ -437,18 +444,21 @@ nsresult nsWindowWatcher::CreateChromeWindow(nsIWebBrowserChrome* aParentChrome, * the size. * * @param aFeatures - * The features that was used to open the window. + * The features string 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 SizeSpec& sizeSpec, nsIDocShellTreeOwner* aTreeOwner) { + const nsACString& aFeatures, nsIDocShellTreeOwner* aTreeOwner) { if (!aTreeOwner) { return; } - if (sizeSpec.SizeSpecified()) { + // 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=")) { aTreeOwner->SetPersistence(false, false, false); } } @@ -513,13 +523,10 @@ nsWindowWatcher::OpenWindowWithRemoteTab(nsIRemoteTab* aRemoteTab, return NS_ERROR_UNEXPECTED; } - WindowFeatures features; - features.Tokenize(aFeatures); - SizeSpec sizeSpec; - CalcSizeSpec(features, sizeSpec); + CalcSizeSpec(aFeatures, sizeSpec); - uint32_t chromeFlags = CalculateChromeFlagsForChild(features, sizeSpec); + uint32_t chromeFlags = CalculateChromeFlagsForChild(aFeatures, sizeSpec); if (isPrivateBrowsingWindow) { chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW; @@ -536,7 +543,7 @@ nsWindowWatcher::OpenWindowWithRemoteTab(nsIRemoteTab* aRemoteTab, nsCOMPtr parentChrome(do_GetInterface(parentTreeOwner)); nsCOMPtr newWindowChrome; - CreateChromeWindow(parentChrome, chromeFlags, aOpenWindowInfo, + CreateChromeWindow(aFeatures, parentChrome, chromeFlags, aOpenWindowInfo, getter_AddRefs(newWindowChrome)); if (NS_WARN_IF(!newWindowChrome)) { @@ -567,7 +574,7 @@ nsWindowWatcher::OpenWindowWithRemoteTab(nsIRemoteTab* aRemoteTab, // that will also run with out-of-process tabs. MOZ_ASSERT(chromeContext->UseRemoteTabs()); - MaybeDisablePersistence(sizeSpec, chromeTreeOwner); + MaybeDisablePersistence(aFeatures, chromeTreeOwner); SizeOpenedWindow(chromeTreeOwner, parentWindowOuter, false, sizeSpec, Some(aOpenerFullZoom)); @@ -599,6 +606,7 @@ nsresult nsWindowWatcher::OpenWindowInternal( uint32_t chromeFlags; nsAutoString name; // string version of aName + nsAutoCString features; // string version of aFeatures nsCOMPtr uriToLoad; // from aUrl, if any nsCOMPtr parentTreeOwner; // from the parent window, if any @@ -636,13 +644,11 @@ nsresult nsWindowWatcher::OpenWindowInternal( name.SetIsVoid(true); } - WindowFeatures features; - nsAutoCString featuresStr; if (aFeatures) { - featuresStr.Assign(aFeatures); - features.Tokenize(featuresStr); + features.Assign(aFeatures); + features.StripWhitespace(); } else { - featuresStr.SetIsVoid(true); + features.SetIsVoid(true); } RefPtr parentBC( @@ -685,13 +691,6 @@ nsresult nsWindowWatcher::OpenWindowInternal( bool isCallerChrome = nsContentUtils::LegacyIsCallerChromeOrNativeCode(); - if (!hasChromeParent) { - bool outerSizeUsed = - features.Exists("outerwidth") || features.Exists("outerheight"); - mozilla::Telemetry::Accumulate(mozilla::Telemetry::WINDOW_OPEN_OUTER_SIZE, - outerSizeUsed); - } - SizeSpec sizeSpec; CalcSizeSpec(features, sizeSpec); @@ -840,8 +839,8 @@ nsresult nsWindowWatcher::OpenWindowInternal( if (provider) { rv = provider->ProvideWindow(openWindowInfo, chromeFlags, aCalledFromJS, sizeSpec.WidthSpecified(), uriToLoad, name, - featuresStr, aForceNoOpener, - aForceNoReferrer, aLoadState, &windowIsNew, + features, aForceNoOpener, aForceNoReferrer, + aLoadState, &windowIsNew, getter_AddRefs(newBC)); if (NS_SUCCEEDED(rv) && newBC) { @@ -937,8 +936,9 @@ 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(parentChrome, chromeFlags, openWindowInfo, - getter_AddRefs(newChrome)); + rv = CreateChromeWindow(features, parentChrome, chromeFlags, + openWindowInfo, getter_AddRefs(newChrome)); + if (parentTopInnerWindow) { parentTopInnerWindow->Resume(); } @@ -1031,7 +1031,7 @@ nsresult nsWindowWatcher::OpenWindowInternal( if (isNewToplevelWindow) { nsCOMPtr newTreeOwner; newDocShell->GetTreeOwner(getter_AddRefs(newTreeOwner)); - MaybeDisablePersistence(sizeSpec, newTreeOwner); + MaybeDisablePersistence(features, newTreeOwner); } if (aDialog && aArgv) { @@ -1649,43 +1649,41 @@ 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 WindowFeatures& aFeatures, - const SizeSpec& aSizeSpec, bool* presenceFlag, bool aHasChromeParent) { + uint32_t aInitialFlags, const nsACString& aFeatures, + const SizeSpec& aSizeSpec, bool& presenceFlag, bool aHasChromeParent) { uint32_t chromeFlags = aInitialFlags; - 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 requires aFeatures, presenceFlag, and + // chromeFlags to be in scope. - if (aFeatures.GetBoolWithDefault("scrollbars", true, presenceFlag)) { + 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) { chromeFlags |= nsIWebBrowserChrome::CHROME_SCROLLBARS; } + presenceFlag = presenceFlag || scrollbarsPresent; if (aHasChromeParent) { return chromeFlags; @@ -1741,32 +1739,37 @@ uint32_t nsWindowWatcher::EnsureFlagsSafeForContent(uint32_t aChromeFlags, } // static -bool nsWindowWatcher::ShouldOpenPopup(const WindowFeatures& aFeatures, +bool nsWindowWatcher::ShouldOpenPopup(const nsACString& aFeatures, const SizeSpec& aSizeSpec) { - if (aFeatures.IsEmpty()) { + if (aFeatures.IsVoid()) { return false; } // Follow Google Chrome's behavior that opens a popup depending on // the following features. - if (!aFeatures.GetBoolWithDefault("location", false) && - !aFeatures.GetBoolWithDefault("toolbar", false)) { + bool unused; + if (!WinHasOption(aFeatures, "location", 0, &unused) && + !WinHasOption(aFeatures, "toolbar", 0, &unused)) { return true; } - if (!aFeatures.GetBoolWithDefault("menubar", false)) { + if (!WinHasOption(aFeatures, "menubar", 0, &unused)) { return true; } - if (!aFeatures.GetBoolWithDefault("resizable", true)) { + // `resizable` defaults to true. + // Should open popup only when explicitly specified to 0. + bool resizablePresent = false; + if (!WinHasOption(aFeatures, "resizable", 0, &resizablePresent) && + resizablePresent) { return true; } - if (!aFeatures.GetBoolWithDefault("scrollbars", false)) { + if (!WinHasOption(aFeatures, "scrollbars", 0, &unused)) { return true; } - if (!aFeatures.GetBoolWithDefault("status", false)) { + if (!WinHasOption(aFeatures, "status", 0, &unused)) { return true; } @@ -1788,13 +1791,15 @@ bool nsWindowWatcher::ShouldOpenPopup(const WindowFeatures& aFeatures, */ // static uint32_t nsWindowWatcher::CalculateChromeFlagsForChild( - const WindowFeatures& aFeatures, const SizeSpec& aSizeSpec) { - if (aFeatures.IsEmpty()) { + const nsACString& aFeatures, const SizeSpec& aSizeSpec) { + if (aFeatures.IsVoid()) { return nsIWebBrowserChrome::CHROME_ALL; } - uint32_t chromeFlags = CalculateChromeFlagsHelper( - nsIWebBrowserChrome::CHROME_WINDOW_BORDERS, aFeatures, aSizeSpec); + bool presenceFlag = false; + uint32_t chromeFlags = + CalculateChromeFlagsHelper(nsIWebBrowserChrome::CHROME_WINDOW_BORDERS, + aFeatures, aSizeSpec, presenceFlag); return EnsureFlagsSafeForContent(chromeFlags); } @@ -1812,7 +1817,7 @@ uint32_t nsWindowWatcher::CalculateChromeFlagsForChild( */ // static uint32_t nsWindowWatcher::CalculateChromeFlagsForParent( - mozIDOMWindowProxy* aParent, const WindowFeatures& aFeatures, + mozIDOMWindowProxy* aParent, const nsACString& aFeatures, const SizeSpec& aSizeSpec, bool aDialog, bool aChromeURL, bool aHasChromeParent, bool aCalledFromJS) { MOZ_ASSERT(XRE_IsParentProcess()); @@ -1822,7 +1827,7 @@ uint32_t nsWindowWatcher::CalculateChromeFlagsForParent( // The features string is made void by OpenWindowInternal // if nullptr was originally passed as the features string. - if (aFeatures.IsEmpty()) { + if (aFeatures.IsVoid()) { chromeFlags = nsIWebBrowserChrome::CHROME_ALL; if (aDialog) { chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_DIALOG | @@ -1841,29 +1846,29 @@ uint32_t nsWindowWatcher::CalculateChromeFlagsForParent( in the standards-compliant window.(normal)open. */ bool presenceFlag = false; - if (aDialog && aFeatures.GetBoolWithDefault("all", false, &presenceFlag)) { + if (aDialog && WinHasOption(aFeatures, "all", 0, &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 - if (aFeatures.GetBoolWithDefault("private", false, &presenceFlag)) { - chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW; - } - if (aFeatures.GetBoolWithDefault("non-private", false, &presenceFlag)) { - chromeFlags |= nsIWebBrowserChrome::CHROME_NON_PRIVATE_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; // Determine whether the window should have remote tabs. bool remote = BrowserTabsRemoteAutostart(); if (remote) { - remote = !aFeatures.GetBoolWithDefault("non-remote", false, &presenceFlag); + remote = !WinHasOption(aFeatures, "non-remote", 0, &presenceFlag); } else { - remote = aFeatures.GetBoolWithDefault("remote", false, &presenceFlag); + remote = WinHasOption(aFeatures, "remote", 0, &presenceFlag); } if (remote) { @@ -1874,19 +1879,18 @@ uint32_t nsWindowWatcher::CalculateChromeFlagsForParent( bool fission = StaticPrefs::fission_autostart(); if (fission) { - fission = - !aFeatures.GetBoolWithDefault("non-fission", false, &presenceFlag); + fission = !WinHasOption(aFeatures, "non-fission", 0, &presenceFlag); } else { - fission = aFeatures.GetBoolWithDefault("fission", false, &presenceFlag); + fission = WinHasOption(aFeatures, "fission", 0, &presenceFlag); } if (fission) { chromeFlags |= nsIWebBrowserChrome::CHROME_FISSION_WINDOW; } - if (aFeatures.GetBoolWithDefault("popup", false, &presenceFlag)) { - chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_POPUP; - } + chromeFlags |= WinHasOption(aFeatures, "popup", 0, &presenceFlag) + ? nsIWebBrowserChrome::CHROME_WINDOW_POPUP + : 0; /* OK. Normal browser windows, in spite of a stated pattern of turning off @@ -1897,15 +1901,15 @@ uint32_t nsWindowWatcher::CalculateChromeFlagsForParent( // default titlebar and closebox to "on," if not mentioned at all if (!(chromeFlags & nsIWebBrowserChrome::CHROME_WINDOW_POPUP)) { - if (!aFeatures.Exists("titlebar")) { + if (!PL_strcasestr(aFeatures.BeginReading(), "titlebar")) { chromeFlags |= nsIWebBrowserChrome::CHROME_TITLEBAR; } - if (!aFeatures.Exists("close")) { + if (!PL_strcasestr(aFeatures.BeginReading(), "close")) { chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_CLOSE; } } - if (aDialog && !aFeatures.IsEmpty() && !presenceFlag) { + if (aDialog && !aFeatures.IsVoid() && !presenceFlag) { chromeFlags = nsIWebBrowserChrome::CHROME_DEFAULT; } @@ -1913,35 +1917,35 @@ uint32_t nsWindowWatcher::CalculateChromeFlagsForParent( with the features that are more operating hints than appearance instructions. (Note modality implies dependence.) */ - if (aFeatures.GetBoolWithDefault("alwayslowered", false) || - aFeatures.GetBoolWithDefault("z-lock", false)) { + if (WinHasOption(aFeatures, "alwaysLowered", 0, nullptr) || + WinHasOption(aFeatures, "z-lock", 0, nullptr)) { chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_LOWERED; - } else if (aFeatures.GetBoolWithDefault("alwaysraised", false)) { + } else if (WinHasOption(aFeatures, "alwaysRaised", 0, nullptr)) { chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_RAISED; } - 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; - } + 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; /* 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 @@ -1953,18 +1957,18 @@ uint32_t nsWindowWatcher::CalculateChromeFlagsForParent( &disableDialogFeature); if (!disableDialogFeature) { - if (aFeatures.GetBoolWithDefault("dialog", false)) { - chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_DIALOG; - } + chromeFlags |= WinHasOption(aFeatures, "dialog", 0, nullptr) + ? nsIWebBrowserChrome::CHROME_OPENAS_DIALOG + : 0; } /* and dialogs need to have the last word. assume dialogs are dialogs, and opened as chrome, unless explicitly told otherwise. */ if (aDialog) { - if (!aFeatures.Exists("dialog")) { + if (!PL_strcasestr(aFeatures.BeginReading(), "dialog")) { chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_DIALOG; } - if (!aFeatures.Exists("chrome")) { + if (!PL_strcasestr(aFeatures.BeginReading(), "chrome")) { chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_CHROME; } } @@ -1988,6 +1992,63 @@ 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 nsWindowWatcher::GetBrowsingContextByName( const nsAString& aName, bool aForceNoOpener, BrowsingContext* aCurrentContext) { @@ -2019,148 +2080,70 @@ already_AddRefed nsWindowWatcher::GetBrowsingContextByName( } // static -void nsWindowWatcher::CalcSizeSpec(const WindowFeatures& aFeatures, +void nsWindowWatcher::CalcSizeSpec(const nsACString& aFeatures, SizeSpec& aResult) { - // 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`: + // Parse position spec, if any, from aFeatures + bool present; + int32_t 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 window’s - // 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; + 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; } + aResult.mLeftSpecified = present; - // 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 window’s 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; + 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; } + aResult.mTopSpecified = present; - // Non-standard extension. - // See bug 1623826 - if (aFeatures.Exists("outerwidth")) { - int32_t width = aFeatures.GetInt("outerwidth"); - if (width) { - aResult.mOuterWidth = width; - aResult.mOuterWidthSpecified = true; + // 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; } - } - - 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 target’s window in a user-agent-defined - // manner so that it does not grow outside the Web-exposed available - // screen area. - // (done later) - } + 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; } - // 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 ((temp = WinHasOption(aFeatures, "outerHeight", INT32_MIN, nullptr))) { + if (temp == INT32_MIN) { + aResult.mUseDefaultHeight = true; + } else { + aResult.mOuterHeight = temp; } - } - - 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 target’s 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; } - // NOTE: The value is handled only on chrome-priv code. - // See nsWindowWatcher::SizeOpenedWindow. - aResult.mLockAspectRatio = - aFeatures.GetBoolWithDefault("lockaspectratio", false); + if (WinHasOption(aFeatures, "lockaspectratio", 0, nullptr)) { + aResult.mLockAspectRatio = true; + } } /* Size and position a new window according to aSizeSpec. This method @@ -2250,18 +2233,30 @@ void nsWindowWatcher::SizeOpenedWindow(nsIDocShellTreeOwner* aTreeOwner, // Set up width if (aSizeSpec.mOuterWidthSpecified) { - width = NSToIntRound(aSizeSpec.mOuterWidth * openerZoom); + if (!aSizeSpec.mUseDefaultWidth) { + width = NSToIntRound(aSizeSpec.mOuterWidth * openerZoom); + } // Else specified to default; just use our existing width } else if (aSizeSpec.mInnerWidthSpecified) { sizeChromeWidth = false; - width = NSToIntRound(aSizeSpec.mInnerWidth * openerZoom); + if (aSizeSpec.mUseDefaultWidth) { + width = width - chromeWidth; + } else { + width = NSToIntRound(aSizeSpec.mInnerWidth * openerZoom); + } } // Set up height if (aSizeSpec.mOuterHeightSpecified) { - height = NSToIntRound(aSizeSpec.mOuterHeight * openerZoom); + if (!aSizeSpec.mUseDefaultHeight) { + height = NSToIntRound(aSizeSpec.mOuterHeight * openerZoom); + } // Else specified to default; just use our existing height } else if (aSizeSpec.mInnerHeightSpecified) { sizeChromeHeight = false; - height = NSToIntRound(aSizeSpec.mInnerHeight * openerZoom); + if (aSizeSpec.mUseDefaultHeight) { + height = height - chromeHeight; + } else { + height = NSToIntRound(aSizeSpec.mInnerHeight * openerZoom); + } } bool positionSpecified = aSizeSpec.PositionSpecified(); diff --git a/toolkit/components/windowwatcher/nsWindowWatcher.h b/toolkit/components/windowwatcher/nsWindowWatcher.h index ce864f014310..44d5237df1d1 100644 --- a/toolkit/components/windowwatcher/nsWindowWatcher.h +++ b/toolkit/components/windowwatcher/nsWindowWatcher.h @@ -25,7 +25,6 @@ #include "nsIRemoteTab.h" #include "nsPIWindowWatcher.h" #include "nsTArray.h" -#include "mozilla/dom/WindowFeatures.h" // mozilla::dom::WindowFeatures class nsIURI; class nsIDocShellTreeItem; @@ -87,20 +86,23 @@ class nsWindowWatcher : public nsIWindowWatcher, static nsresult URIfromURL(const char* aURL, mozIDOMWindowProxy* aParent, nsIURI** aURI); - static bool ShouldOpenPopup(const mozilla::dom::WindowFeatures& aFeatures, + static bool ShouldOpenPopup(const nsACString& aFeatures, const SizeSpec& aSizeSpec); - static uint32_t CalculateChromeFlagsForChild( - const mozilla::dom::WindowFeatures& aFeatures, const SizeSpec& aSizeSpec); + static uint32_t CalculateChromeFlagsForChild(const nsACString& aFeaturesStr, + const SizeSpec& aSizeSpec); - static uint32_t CalculateChromeFlagsForParent( - mozIDOMWindowProxy* aParent, - const mozilla::dom::WindowFeatures& aFeatures, const SizeSpec& aSizeSpec, - bool aDialog, bool aChromeURL, bool aHasChromeParent, bool aCalledFromJS); + static uint32_t CalculateChromeFlagsForParent(mozIDOMWindowProxy* aParent, + const nsACString& aFeaturesStr, + 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 mozilla::dom::WindowFeatures& aFeatures, - SizeSpec& aResult); + static void CalcSizeSpec(const nsACString& aFeatures, SizeSpec& aResult); static void SizeOpenedWindow( nsIDocShellTreeOwner* aTreeOwner, mozIDOMWindowProxy* aParent, bool aIsCallerChrome, const SizeSpec& aSizeSpec, @@ -111,18 +113,20 @@ class nsWindowWatcher : public nsIWindowWatcher, nsIDocShellTreeOwner** aResult); private: - nsresult CreateChromeWindow(nsIWebBrowserChrome* aParentChrome, + nsresult CreateChromeWindow(const nsACString& aFeatures, + nsIWebBrowserChrome* aParentChrome, uint32_t aChromeFlags, nsIOpenWindowInfo* aOpenWindowInfo, nsIWebBrowserChrome** aResult); - void MaybeDisablePersistence(const SizeSpec& sizeSpec, + void MaybeDisablePersistence(const nsACString& aFeatures, nsIDocShellTreeOwner* aTreeOwner); - static uint32_t CalculateChromeFlagsHelper( - uint32_t aInitialFlags, const mozilla::dom::WindowFeatures& aFeatures, - const SizeSpec& aSizeSpec, bool* presenceFlag = nullptr, - bool aHasChromeParent = false); + static uint32_t CalculateChromeFlagsHelper(uint32_t aInitialFlags, + const nsACString& aFeatures, + const SizeSpec& aSizeSpec, + bool& presenceFlag, + bool aHasChromeParent = false); static uint32_t EnsureFlagsSafeForContent(uint32_t aChromeFlags, bool aChromeURL = false); diff --git a/xpcom/string/moz.build b/xpcom/string/moz.build index 3b0e44803492..c8da7234c1cc 100644 --- a/xpcom/string/moz.build +++ b/xpcom/string/moz.build @@ -28,7 +28,6 @@ EXPORTS += [ 'nsTLiteralString.h', 'nsTPromiseFlatString.h', 'nsTString.h', - 'nsTStringHasher.h', 'nsTStringRepr.h', 'nsTSubstring.h', 'nsTSubstringTuple.h', diff --git a/xpcom/string/nsTStringHasher.h b/xpcom/string/nsTStringHasher.h deleted file mode 100644 index 7b3f42ba5883..000000000000 --- a/xpcom/string/nsTStringHasher.h +++ /dev/null @@ -1,30 +0,0 @@ -/* -*- 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 nsTStringHasher_h___ -#define nsTStringHasher_h___ - -#include "mozilla/HashTable.h" // mozilla::{DefaultHasher, HashNumber, HashString} - -namespace mozilla { - -template -struct DefaultHasher> { - using Key = nsTString; - using Lookup = nsTString; - - static mozilla::HashNumber hash(const Lookup& aLookup) { - return mozilla::HashString(aLookup.get()); - } - - static bool match(const Key& aKey, const Lookup& aLookup) { - return aKey.Equals(aLookup); - } -}; - -} // namespace mozilla - -#endif // !defined(nsTStringHasher_h___)