Merge inbound to mozilla-central. a=merge
authorBrindusan Cristian <cbrindusan@mozilla.com>
Thu, 26 Jul 2018 00:37:55 +0300
changeset 428383 7ba07ef0e4532b644b812942aa38af4510dbc74f
parent 428377 daacc8061a8f086dfc1511a136c5c100fb7d4e3f (current diff)
parent 428382 d6c5e93d33a1f86946480004fc19c2d4ef3984e1 (diff)
child 428397 02c4096073eda039096bb6a7aaed19887722dc81
child 428513 3ac6ae5fca25328f393a89db813cee8c127845e0
push id34334
push usercbrindusan@mozilla.com
push dateWed, 25 Jul 2018 21:38:17 +0000
treeherdermozilla-central@7ba07ef0e453 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone63.0a1
first release with
nightly linux32
7ba07ef0e453 / 63.0a1 / 20180726001822 / files
nightly linux64
7ba07ef0e453 / 63.0a1 / 20180726001822 / files
nightly mac
7ba07ef0e453 / 63.0a1 / 20180726001822 / files
nightly win32
7ba07ef0e453 / 63.0a1 / 20180726001822 / files
nightly win64
7ba07ef0e453 / 63.0a1 / 20180726001822 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
--- a/browser/components/sessionstore/nsSessionStartup.js
+++ b/browser/components/sessionstore/nsSessionStartup.js
@@ -261,16 +261,17 @@ SessionStartup.prototype = {
       Services.obs.removeObserver(this, "quit-application");
       if (this._sessionType != Ci.nsISessionStartup.NO_SESSION)
         Services.obs.removeObserver(this, "browser:purge-session-history");
       break;
     case "sessionstore-windows-restored":
       Services.obs.removeObserver(this, "sessionstore-windows-restored");
       // free _initialState after nsSessionStore is done with it
       this._initialState = null;
+      this._didRestore = true;
       break;
     case "browser:purge-session-history":
       Services.obs.removeObserver(this, "browser:purge-session-history");
       // reset all state on sanitization
       this._sessionType = Ci.nsISessionStartup.NO_SESSION;
       break;
     }
   },
@@ -334,16 +335,20 @@ SessionStartup.prototype = {
     // If the session file hasn't been read yet and resuming the session isn't
     // enabled via prefs, go ahead and load the homepage. We may still replace
     // it when recovering from a crash, which we'll only know after reading the
     // session file, but waiting for that would delay loading the homepage in
     // the non-crash case.
     if (!this._initialState && !this._resumeSessionEnabled) {
       return false;
     }
+    // If we've already restored the session, we won't override again.
+    if (this._didRestore) {
+      return false;
+    }
 
     return new Promise(resolve => {
       this.onceInitialized.then(() => {
         // If there are valid windows with not only pinned tabs, signal that we
         // will override the default homepage by restoring a session.
         resolve(this._willRestore() &&
                 this._initialState &&
                 this._initialState.windows &&
--- a/layout/style/test/test_non_content_accessible_values.html
+++ b/layout/style/test/test_non_content_accessible_values.html
@@ -1,41 +1,43 @@
 <!doctype html>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <style id="sheet"></style>
 <div></div>
 <script>
 const NON_CONTENT_ACCESSIBLE_VALUES = {
   "display": [
-    "-moz-box",
-    "-moz-inline-box",
     "-moz-grid",
     "-moz-inline-grid",
     "-moz-grid-group",
     "-moz-grid-line",
     "-moz-stack",
     "-moz-inline-stack",
     "-moz-deck",
     "-moz-popup",
     "-moz-groupbox",
   ],
 };
 
+if (!SpecialPowers.getBoolPref("layout.css.xul-box-display-values.content.enabled")) {
+  NON_CONTENT_ACCESSIBLE_VALUES.display.push("-moz-box", "-moz-inline-box");
+}
+
 const sheet = document.getElementById("sheet");
 const div = document.querySelector("div");
 
 test(function() {
   sheet.textContent = `div { color: initial }`;
   assert_equals(sheet.sheet.cssRules[0].style.length, 1);
 }, "sanity");
 
 for (const prop in NON_CONTENT_ACCESSIBLE_VALUES) {
+  const values = NON_CONTENT_ACCESSIBLE_VALUES[prop];
   test(function() {
-    const values = NON_CONTENT_ACCESSIBLE_VALUES[prop];
     for (const value of values) {
       sheet.textContent = `div { ${prop}: ${value} }`;
       const block = sheet.sheet.cssRules[0].style;
       assert_equals(
         block.length,
         0,
         `${prop}: ${value} should not be parsed in content`
       );
@@ -54,11 +56,11 @@ for (const prop in NON_CONTENT_ACCESSIBL
       assert_not_equals(
         getComputedStyle(div).getPropertyValue(prop),
         value,
         `${prop}: ${value} should not be settable via CSSOM in content (gcs)`
       );
 
       assert_false(CSS.supports(prop, value), `${prop}: ${value} should not claim to be supported`)
     }
-  }, prop + " non-accessible values")
+  }, prop + " non-accessible values: " + values.join(", "))
 }
 </script>
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java
@@ -301,17 +301,21 @@ public final class GeckoRuntimeSettings 
     /* package */ Pref<Integer> mCookieBehavior = new Pref<Integer>(
         "network.cookie.cookieBehavior", COOKIE_ACCEPT_ALL);
     /* package */ Pref<Integer> mCookieLifetime = new Pref<Integer>(
         "network.cookie.lifetimePolicy", COOKIE_LIFETIME_NORMAL);
     /* package */ Pref<Integer> mCookieLifetimeDays = new Pref<Integer>(
         "network.cookie.lifetime.days", 90);
     /* package */ Pref<String> mTrackingProtection = new Pref<String>(
         "urlclassifier.trackingTable",
-        TrackingProtection.buildPrefValue(TrackingProtectionDelegate.CATEGORY_ALL));
+        TrackingProtection.buildPrefValue(
+            TrackingProtectionDelegate.CATEGORY_TEST |
+            TrackingProtectionDelegate.CATEGORY_ANALYTIC |
+            TrackingProtectionDelegate.CATEGORY_SOCIAL |
+            TrackingProtectionDelegate.CATEGORY_AD));
     /* package */ Pref<Boolean> mConsoleOutput = new Pref<Boolean>(
         "geckoview.console.enabled", false);
 
     /* package */ boolean mNativeCrashReporting;
     /* package */ boolean mJavaCrashReporting;
     /* package */ int mCrashReportingJobId;
     /* package */ boolean mDebugPause;
 
--- a/modules/libpref/Preferences.cpp
+++ b/modules/libpref/Preferences.cpp
@@ -461,16 +461,17 @@ class PrefWrapper;
 class Pref
 {
 public:
   explicit Pref(const char* aName)
     : mName(ArenaStrdup(aName, gPrefNameArena))
     , mType(static_cast<uint32_t>(PrefType::None))
     , mIsSticky(false)
     , mIsLocked(false)
+    , mDefaultChanged(false)
     , mHasDefaultValue(false)
     , mHasUserValue(false)
     , mDefaultValue()
     , mUserValue()
   {
   }
 
   ~Pref()
@@ -496,26 +497,32 @@ public:
   bool IsTypeInt() const { return IsType(PrefType::Int); }
   bool IsTypeBool() const { return IsType(PrefType::Bool); }
 
   // Other properties.
 
   bool IsLocked() const { return mIsLocked; }
   void SetIsLocked(bool aValue) { mIsLocked = aValue; }
 
+  bool DefaultChanged() const { return mDefaultChanged; }
+
   bool IsSticky() const { return mIsSticky; }
 
   bool HasDefaultValue() const { return mHasDefaultValue; }
   bool HasUserValue() const { return mHasUserValue; }
 
   template<typename T>
   void AddToMap(SharedPrefMapBuilder& aMap)
   {
     aMap.Add(Name(),
-             { HasDefaultValue(), HasUserValue(), IsSticky(), IsLocked() },
+             { HasDefaultValue(),
+               HasUserValue(),
+               IsSticky(),
+               IsLocked(),
+               DefaultChanged() },
              HasDefaultValue() ? mDefaultValue.Get<T>() : T(),
              HasUserValue() ? mUserValue.Get<T>() : T());
   }
 
   void AddToMap(SharedPrefMapBuilder& aMap)
   {
     if (IsTypeBool()) {
       AddToMap<bool>(aMap);
@@ -708,16 +715,19 @@ public:
     // Should we set the default value? Only if the pref is not locked, and
     // doing so would change the default value.
     if (!IsLocked()) {
       if (aIsLocked) {
         SetIsLocked(true);
       }
       if (!ValueMatches(PrefValueKind::Default, aType, aValue)) {
         mDefaultValue.Replace(mHasDefaultValue, Type(), aType, aValue);
+        if (mHasDefaultValue) {
+          mDefaultChanged = true;
+        }
         mHasDefaultValue = true;
         if (aIsSticky) {
           mIsSticky = true;
         }
         if (!mHasUserValue) {
           *aValueChanged = true;
         }
         // What if we change the default to be the same as the user value?
@@ -931,16 +941,17 @@ public:
   }
 
 private:
   const char* mName; // allocated in gPrefNameArena
 
   uint32_t mType : 2;
   uint32_t mIsSticky : 1;
   uint32_t mIsLocked : 1;
+  uint32_t mDefaultChanged : 1;
   uint32_t mHasDefaultValue : 1;
   uint32_t mHasUserValue : 1;
 
   PrefValue mDefaultValue;
   PrefValue mUserValue;
 };
 
 class PrefEntry : public PLDHashEntryHdr
@@ -1003,16 +1014,17 @@ public:
     struct Matcher                                                             \
     {                                                                          \
       retType match(const Pref* aPref) { return aPref->method(); }             \
       retType match(SharedPref& aPref) { return aPref.method(); }              \
     };                                                                         \
     return match(Matcher());                                                   \
   }
 
+  FORWARD(bool, DefaultChanged)
   FORWARD(bool, IsLocked)
   FORWARD(bool, IsSticky)
   FORWARD(bool, HasDefaultValue)
   FORWARD(bool, HasUserValue)
   FORWARD(const char*, Name)
   FORWARD(nsCString, NameString)
   FORWARD(PrefType, Type)
 #undef FORWARD
@@ -4825,33 +4837,29 @@ Preferences::InitInitialObjects(bool aIs
 
   if (!XRE_IsParentProcess()) {
     MOZ_ASSERT(gSharedMap);
 
     // We got our initial preference values from the content process, so we
     // don't need to add them to the DB. For static var caches, though, the
     // current preference values may differ from their static defaults. So we
     // still need to notify callbacks for each of our shared prefs which have
-    // user values.
-    //
-    // While it is technically also possible for the default values to have
-    // changed at runtime, and therefore not match the static defaults, we don't
-    // support that for static preferences in this configuration, and therefore
-    // ignore the possibility.
+    // user values, of whose default values have changed since they were
+    // initialized.
     for (auto& pref : gSharedMap->Iter()) {
-      if (pref.HasUserValue() || pref.IsLocked()) {
+      if (pref.HasUserValue() || pref.DefaultChanged()) {
         NotifyCallbacks(pref.Name(), PrefWrapper(pref));
       }
     }
 
 #ifdef DEBUG
-    // Check that all varcache preferences match their current values. This can
-    // currently fail if the default value of a static varcache preference is
-    // changed in a preference file or at runtime, rather than in
-    // StaticPrefList.h.
+      // Check that all varcache preferences match their current values. This
+      // can currently fail if the default value of a static varcache preference
+      // is changed in a preference file or at runtime, rather than in
+      // StaticPrefList.h.
 
 #define PREF(name, cpp_type, value)
 #define VARCACHE_PREF(name, id, cpp_type, value)                               \
   MOZ_ASSERT(GetPref<StripAtomic<cpp_type>>(name, value) == StaticPrefs::id(), \
              "Incorrect cached value for " name);
 #include "mozilla/StaticPrefList.h"
 #undef PREF
 #undef VARCACHE_PREF
--- a/modules/libpref/SharedPrefMap.cpp
+++ b/modules/libpref/SharedPrefMap.cpp
@@ -93,16 +93,17 @@ SharedPrefMapBuilder::Add(const char* aK
     aKey,
     mKeyTable.Add(aKey),
     { aDefaultValue, aUserValue },
     uint8_t(PrefType::Bool),
     aFlags.mHasDefaultValue,
     aFlags.mHasUserValue,
     aFlags.mIsSticky,
     aFlags.mIsLocked,
+    aFlags.mDefaultChanged,
   });
 }
 
 void
 SharedPrefMapBuilder::Add(const char* aKey,
                           const Flags& aFlags,
                           int32_t aDefaultValue,
                           int32_t aUserValue)
@@ -118,16 +119,17 @@ SharedPrefMapBuilder::Add(const char* aK
     aKey,
     mKeyTable.Add(aKey),
     { index },
     uint8_t(PrefType::Int),
     aFlags.mHasDefaultValue,
     aFlags.mHasUserValue,
     aFlags.mIsSticky,
     aFlags.mIsLocked,
+    aFlags.mDefaultChanged,
   });
 }
 
 void
 SharedPrefMapBuilder::Add(const char* aKey,
                           const Flags& aFlags,
                           const nsCString& aDefaultValue,
                           const nsCString& aUserValue)
@@ -145,16 +147,17 @@ SharedPrefMapBuilder::Add(const char* aK
     aKey,
     mKeyTable.Add(aKey),
     { index },
     uint8_t(PrefType::String),
     aFlags.mHasDefaultValue,
     aFlags.mHasUserValue,
     aFlags.mIsSticky,
     aFlags.mIsLocked,
+    aFlags.mDefaultChanged,
   });
 }
 
 Result<Ok, nsresult>
 SharedPrefMapBuilder::Finalize(loader::AutoMemMap& aMap)
 {
   using Header = SharedPrefMap::Header;
 
@@ -211,17 +214,17 @@ SharedPrefMapBuilder::Finalize(loader::A
   headerPtr[0] = header;
 
   auto* entryPtr = reinterpret_cast<SharedPrefMap::Entry*>(&headerPtr[1]);
   for (auto* entry : entries) {
     *entryPtr = {
       entry->mKey,          GetValue(*entry),
       entry->mType,         entry->mHasDefaultValue,
       entry->mHasUserValue, entry->mIsSticky,
-      entry->mIsLocked,
+      entry->mIsLocked,     entry->mDefaultChanged,
     };
     entryPtr++;
   }
 
   auto ptr = mem.Get<uint8_t>();
 
   mKeyTable.Write(
     { &ptr[header.mKeyStrings.mOffset], header.mKeyStrings.mSize });
--- a/modules/libpref/SharedPrefMap.h
+++ b/modules/libpref/SharedPrefMap.h
@@ -313,16 +313,18 @@ class SharedPrefMap
     uint8_t mHasDefaultValue : 1;
     // True if the preference has a user value. Callers must not attempt to
     // access the entry's user value if this is false.
     uint8_t mHasUserValue : 1;
     // True if the preference is sticky, as defined by the preference service.
     uint8_t mIsSticky : 1;
     // True if the preference is locked, as defined by the preference service.
     uint8_t mIsLocked : 1;
+    // True if the preference's default value has changed since it was first set.
+    uint8_t mDefaultChanged : 1;
   };
 
 public:
   NS_INLINE_DECL_REFCOUNTING(SharedPrefMap)
 
   // A temporary wrapper class for accessing entries in the array. Instances of
   // this class are valid as long as SharedPrefMap instance is alive, but
   // generally should not be stored long term, or allocated on the heap.
@@ -340,16 +342,17 @@ public:
     nsCString NameString() const { return mMap->KeyTable().Get(mEntry->mKey); }
 
     PrefType Type() const
     {
       MOZ_ASSERT(PrefType(mEntry->mType) != PrefType::None);
       return PrefType(mEntry->mType);
     }
 
+    bool DefaultChanged() const { return mEntry->mDefaultChanged; }
     bool HasDefaultValue() const { return mEntry->mHasDefaultValue; }
     bool HasUserValue() const { return mEntry->mHasUserValue; }
     bool IsLocked() const { return mEntry->mIsLocked; }
     bool IsSticky() const { return mEntry->mIsSticky; }
 
     bool GetBoolValue(PrefValueKind aKind = PrefValueKind::User) const
     {
       MOZ_ASSERT(Type() == PrefType::Bool);
@@ -584,16 +587,17 @@ public:
 
   // The set of flags for the preference, as documented in SharedPrefMap::Entry.
   struct Flags
   {
     uint8_t mHasDefaultValue : 1;
     uint8_t mHasUserValue : 1;
     uint8_t mIsSticky : 1;
     uint8_t mIsLocked : 1;
+    uint8_t mDefaultChanged : 1;
   };
 
   void Add(const char* aKey,
            const Flags& aFlags,
            bool aDefaultValue,
            bool aUserValue);
 
   void Add(const char* aKey,
@@ -855,16 +859,17 @@ private:
     StringTableEntry mKey;
     Value mValue;
 
     uint8_t mType : 2;
     uint8_t mHasDefaultValue : 1;
     uint8_t mHasUserValue : 1;
     uint8_t mIsSticky : 1;
     uint8_t mIsLocked : 1;
+    uint8_t mDefaultChanged : 1;
   };
 
   // Converts a builder Value struct to a SharedPrefMap::Value struct for
   // serialization. This must not be called before callers have finished adding
   // entries to the value array builders.
   SharedPrefMap::Value GetValue(const Entry& aEntry) const
   {
     switch (PrefType(aEntry.mType)) {
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -144,24 +144,28 @@ VARCACHE_PREF(
 #endif
 VARCACHE_PREF(
   "dom.animations-api.timelines.enabled",
    dom_animations_api_timelines_enabled,
   bool, PREF_VALUE
 )
 #undef PREF_VALUE
 
+// NOTE: This preference is used in unit tests. If it is removed or its default
+// value changes, please update test_sharedMap_var_caches.js accordingly.
 VARCACHE_PREF(
   "dom.webcomponents.shadowdom.report_usage",
    dom_webcomponents_shadowdom_report_usage,
   bool, false
 )
 
 // Whether we disable triggering mutation events for changes to style
 // attribute via CSSOM.
+// NOTE: This preference is used in unit tests. If it is removed or its default
+// value changes, please update test_sharedMap_var_caches.js accordingly.
 VARCACHE_PREF(
   "dom.mutation-events.cssom.disabled",
    dom_mutation_events_cssom_disabled,
   bool, true
 )
 
 VARCACHE_PREF(
   "dom.performance.enable_scheduler_timing",
new file mode 100644
--- /dev/null
+++ b/modules/libpref/test/unit_ipc/test_sharedMap_var_caches.js
@@ -0,0 +1,65 @@
+/* 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/. */
+"use strict";
+
+// Tests that static preference varcaches in the content process
+// correctly handle values which are different from their
+// statically-defined defaults.
+//
+// Since we can't access varcaches values from JS, this tests relies on
+// assertions in debug builds to detect mismatches. The default and user
+// values of two preferences are changed (respectively) before a content
+// process is started. Once the content process is launched, the
+// preference service asserts that the values stored in all var caches
+// match their current values as known to the preference service. If
+// there's a mismatch, the shell will crash, and the test will fail.
+//
+// For sanity, we also check that the dynamically retrieved preference
+// values in the content process match our expectations, though this is
+// not strictly part of the test.
+
+const PREF1_NAME = "dom.webcomponents.shadowdom.report_usage";
+const PREF1_VALUE = false;
+
+const PREF2_NAME = "dom.mutation-events.cssom.disabled"
+const PREF2_VALUE = true;
+
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+ChromeUtils.import("resource://testing-common/ExtensionXPCShellUtils.jsm");
+
+ExtensionTestUtils.init(this);
+
+let contentPage;
+
+const {prefs} = Services;
+const defaultPrefs = prefs.getDefaultBranch("");
+
+add_task(async function test_sharedMap_var_caches() {
+  equal(prefs.getBoolPref(PREF1_NAME), PREF1_VALUE,
+        `Expected initial value for ${PREF1_NAME}`);
+  equal(prefs.getBoolPref(PREF2_NAME), PREF2_VALUE,
+        `Expected initial value for ${PREF2_NAME}`);
+
+  defaultPrefs.setBoolPref(PREF1_NAME, !PREF1_VALUE);
+  prefs.setBoolPref(PREF2_NAME, !PREF2_VALUE);
+
+  equal(prefs.getBoolPref(PREF1_NAME), !PREF1_VALUE,
+        `Expected updated value for ${PREF1_NAME}`);
+  equal(prefs.getBoolPref(PREF2_NAME), !PREF2_VALUE,
+        `Expected updated value for ${PREF2_NAME}`);
+
+  let contentPage = await ExtensionTestUtils.loadContentPage("about:blank", {remote: true});
+  registerCleanupFunction(() => contentPage.close());
+
+  let values = await contentPage.spawn([PREF1_NAME, PREF2_NAME], (prefs) => {
+    ChromeUtils.import("resource://gre/modules/Services.jsm");
+    return prefs.map(pref => Services.prefs.getBoolPref(pref));
+  })
+
+  equal(values[0], !PREF1_VALUE,
+        `Expected content value for ${PREF1_NAME}`);
+  equal(values[1], !PREF2_VALUE,
+        `Expected content value for ${PREF2_NAME}`);
+});
+
--- a/modules/libpref/test/unit_ipc/xpcshell.ini
+++ b/modules/libpref/test/unit_ipc/xpcshell.ini
@@ -4,9 +4,11 @@ skip-if = toolkit == 'android'
 
 [test_existing_prefs.js]
 [test_initial_prefs.js]
 [test_large_pref.js]
 [test_locked_prefs.js]
 [test_observed_prefs.js]
 [test_update_prefs.js]
 [test_sharedMap.js]
+[test_sharedMap_var_caches.js]
+skip-if = !debug # Relies on debug assertions to catch failure cases.
 [test_user_default_prefs.js]
--- a/xpfe/appshell/nsXULWindow.cpp
+++ b/xpfe/appshell/nsXULWindow.cpp
@@ -1106,19 +1106,16 @@ NS_IMETHODIMP nsXULWindow::ForceRoundedD
     &targetContentHeight
   );
 
   targetContentWidth = NSToIntRound(targetContentWidth * devicePerCSSPixels);
   targetContentHeight = NSToIntRound(targetContentHeight * devicePerCSSPixels);
 
   SetPrimaryContentSize(targetContentWidth, targetContentHeight);
 
-  mIgnoreXULSize = true;
-  mIgnoreXULSizeMode = true;
-
   return NS_OK;
 }
 
 void nsXULWindow::OnChromeLoaded()
 {
   nsresult rv = EnsureContentTreeOwner();
 
   if (NS_SUCCEEDED(rv)) {
@@ -1365,67 +1362,53 @@ nsXULWindow::SetSpecifiedSize(int32_t aS
     SetSize(aSpecWidth, aSpecHeight, false);
   }
 }
 
 /* Miscellaneous persistent attributes are attributes named in the
    |persist| attribute, other than size and position. Those are special
    because it's important to load those before one of the misc
    attributes (sizemode) and they require extra processing. */
-bool nsXULWindow::LoadMiscPersistentAttributesFromXUL()
+bool nsXULWindow::UpdateWindowStateFromMiscXULAttributes()
 {
   bool     gotState = false;
 
   /* There are no misc attributes of interest to the hidden window.
      It's especially important not to try to validate that window's
      size or position, because some platforms (Mac OS X) need to
      make it visible and offscreen. */
   if (mIsHiddenWindow)
     return false;
 
   nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
   NS_ENSURE_TRUE(windowElement, false);
 
   nsAutoString stateString;
-
-  // sizemode
-  windowElement->GetAttribute(MODE_ATTRIBUTE, stateString);
   nsSizeMode sizeMode = nsSizeMode_Normal;
-  /* ignore request to minimize, to not confuse novices
-  if (stateString.Equals(SIZEMODE_MINIMIZED))
-    sizeMode = nsSizeMode_Minimized;
-  */
-  if (!mIgnoreXULSizeMode &&
-      (stateString.Equals(SIZEMODE_MAXIMIZED) || stateString.Equals(SIZEMODE_FULLSCREEN))) {
-    /* Honor request to maximize only if the window is sizable.
-       An unsizable, unmaximizable, yet maximized window confuses
-       Windows OS and is something of a travesty, anyway. */
-    if (mChromeFlags & nsIWebBrowserChrome::CHROME_WINDOW_RESIZE) {
-      mIntrinsicallySized = false;
-
-      if (stateString.Equals(SIZEMODE_MAXIMIZED))
-        sizeMode = nsSizeMode_Maximized;
-      else
-        sizeMode = nsSizeMode_Fullscreen;
-    }
-  }
-
-  // If we are told to ignore the size mode attribute update the
-  // document so the attribute and window are in sync.
+
+  // If we are told to ignore the size mode attribute, force
+  // normal sizemode.
   if (mIgnoreXULSizeMode) {
-    nsAutoString sizeString;
-    if (sizeMode == nsSizeMode_Maximized)
-      sizeString.Assign(SIZEMODE_MAXIMIZED);
-    else if (sizeMode == nsSizeMode_Fullscreen)
-      sizeString.Assign(SIZEMODE_FULLSCREEN);
-    else if (sizeMode == nsSizeMode_Normal)
-      sizeString.Assign(SIZEMODE_NORMAL);
-    if (!sizeString.IsEmpty()) {
-      ErrorResult rv;
-      windowElement->SetAttribute(MODE_ATTRIBUTE, sizeString, rv);
+    windowElement->SetAttribute(MODE_ATTRIBUTE, NS_LITERAL_STRING("normal"), IgnoreErrors());
+  } else {
+    // Otherwise, read sizemode from DOM and, if the window is resizable,
+    // set it later.
+    windowElement->GetAttribute(MODE_ATTRIBUTE, stateString);
+    if ((stateString.Equals(SIZEMODE_MAXIMIZED) || stateString.Equals(SIZEMODE_FULLSCREEN))) {
+      /* Honor request to maximize only if the window is sizable.
+         An unsizable, unmaximizable, yet maximized window confuses
+         Windows OS and is something of a travesty, anyway. */
+      if (mChromeFlags & nsIWebBrowserChrome::CHROME_WINDOW_RESIZE) {
+        mIntrinsicallySized = false;
+
+        if (stateString.Equals(SIZEMODE_MAXIMIZED))
+          sizeMode = nsSizeMode_Maximized;
+        else
+          sizeMode = nsSizeMode_Fullscreen;
+      }
     }
   }
 
   if (sizeMode == nsSizeMode_Fullscreen) {
     nsCOMPtr<mozIDOMWindowProxy> ourWindow;
     GetWindowDOMWindow(getter_AddRefs(ourWindow));
     auto* piWindow = nsPIDOMWindowOuter::From(ourWindow);
     piWindow->SetFullScreen(true);
@@ -2511,26 +2494,36 @@ nsXULWindow::LoadPersistentWindowState()
 void
 nsXULWindow::SizeShell()
 {
   AutoRestore<bool> sizingShellFromXUL(mSizingShellFromXUL);
   mSizingShellFromXUL = true;
 
   int32_t specWidth = -1, specHeight = -1;
   bool gotSize = false;
-  bool isContent = false;
-
-  GetHasPrimaryContent(&isContent);
+
+  nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
+  nsAutoString windowType;
+  if (windowElement) {
+    windowElement->GetAttribute(WINDOWTYPE_ATTRIBUTE, windowType);
+  }
 
   CSSIntSize windowDiff = GetOuterToInnerSizeDifferenceInCSSPixels(mWindow);
 
-  // If this window has a primary content and fingerprinting resistance is
-  // enabled, we enforce this window to rounded dimensions.
-  if (isContent && nsContentUtils::ShouldResistFingerprinting()) {
-    ForceRoundedDimensions();
+  // If we're using fingerprint resistance, we're going to resize the window
+  // once we have primary content.
+  if (nsContentUtils::ShouldResistFingerprinting() &&
+      windowType.EqualsLiteral("navigator:browser")) {
+    // Once we've got primary content, force dimensions.
+    if (mPrimaryContentShell || mPrimaryTabParent) {
+      ForceRoundedDimensions();
+    }
+    // Always avoid setting size/sizemode on this window.
+    mIgnoreXULSize = true;
+    mIgnoreXULSizeMode = true;
   } else if (!mIgnoreXULSize) {
     gotSize = LoadSizeFromXUL(specWidth, specHeight);
     specWidth += windowDiff.width;
     specHeight += windowDiff.height;
   }
 
   bool positionSet = !mIgnoreXULPosition;
   nsCOMPtr<nsIXULWindow> parentWindow(do_QueryReferent(mParentWindow));
@@ -2576,17 +2569,17 @@ nsXULWindow::SizeShell()
   }
 
   // Now that we have set the window's final size, we can re-do its
   // positioning so that it is properly constrained to the screen.
   if (positionSet) {
     LoadPositionFromXUL(specWidth, specHeight);
   }
 
-  LoadMiscPersistentAttributesFromXUL();
+  UpdateWindowStateFromMiscXULAttributes();
 
   if (mChromeLoaded && mCenterAfterLoad && !positionSet &&
       mWindow->SizeMode() == nsSizeMode_Normal) {
     Center(parentWindow, parentWindow ? false : true, false);
   }
 }
 
 NS_IMETHODIMP nsXULWindow::GetXULBrowserWindow(nsIXULBrowserWindow * *aXULBrowserWindow)
--- a/xpfe/appshell/nsXULWindow.h
+++ b/xpfe/appshell/nsXULWindow.h
@@ -100,17 +100,17 @@ protected:
    void ApplyChromeFlags();
    void SizeShell();
    void OnChromeLoaded();
    void StaggerPosition(int32_t &aRequestedX, int32_t &aRequestedY,
                         int32_t aSpecWidth, int32_t aSpecHeight);
    bool       LoadPositionFromXUL(int32_t aSpecWidth, int32_t aSpecHeight);
    bool       LoadSizeFromXUL(int32_t& aSpecWidth, int32_t& aSpecHeight);
    void       SetSpecifiedSize(int32_t aSpecWidth, int32_t aSpecHeight);
-   bool       LoadMiscPersistentAttributesFromXUL();
+   bool       UpdateWindowStateFromMiscXULAttributes();
    void       SyncAttributesToWidget();
    NS_IMETHOD SavePersistentAttributes();
 
    bool NeedsTooltipListener();
    void AddTooltipSupport();
    void RemoveTooltipSupport();
 
    NS_IMETHOD GetWindowDOMWindow(mozIDOMWindowProxy** aDOMWindow);