Merge inbound to mozilla-central. a=merge
authorNarcis Beleuzu <nbeleuzu@mozilla.com>
Thu, 24 Jan 2019 23:43:35 +0200
changeset 455321 b55ddb97722a51b3365f1a72bdb416f028fd2073
parent 455320 6b7bc40023111979a4755dfeca82629c9c022cd5 (current diff)
parent 455268 6cee5c4042e9f1ab067f3f815b3d250a4ef7bdab (diff)
child 455322 2d8247673f5d837ef5a5203c62c6f008389317f3
child 455323 2d056ed15129ea779eda14a04615906b3b809e97
child 455362 1b31a8ce863549766ab06debcffb52c1b1f51ca1
push id111440
push usernbeleuzu@mozilla.com
push dateThu, 24 Jan 2019 21:48:07 +0000
treeherdermozilla-inbound@2d8247673f5d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone66.0a1
first release with
nightly linux32
b55ddb97722a / 66.0a1 / 20190124214415 / files
nightly linux64
b55ddb97722a / 66.0a1 / 20190124214415 / files
nightly mac
b55ddb97722a / 66.0a1 / 20190124214415 / files
nightly win32
b55ddb97722a / 66.0a1 / 20190124214415 / files
nightly win64
b55ddb97722a / 66.0a1 / 20190124214415 / 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
testing/mozharness/configs/merge_day/bump_central.py
testing/mozharness/configs/merge_day/bump_esr.py
testing/mozharness/scripts/merge_day/gecko_migration.py
--- a/browser/components/aboutconfig/content/aboutconfig.js
+++ b/browser/components/aboutconfig/content/aboutconfig.js
@@ -3,16 +3,22 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 ChromeUtils.import("resource://gre/modules/DeferredTask.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/Preferences.jsm");
 
 const SEARCH_TIMEOUT_MS = 500;
 
+const GETTERS_BY_PREF_TYPE = {
+  [Ci.nsIPrefBranch.PREF_BOOL]: "getBoolPref",
+  [Ci.nsIPrefBranch.PREF_INT]: "getIntPref",
+  [Ci.nsIPrefBranch.PREF_STRING]: "getStringPref",
+};
+
 let gDefaultBranch = Services.prefs.getDefaultBranch("");
 let gFilterPrefsTask = new DeferredTask(() => filterPrefs(), SEARCH_TIMEOUT_MS);
 
 /**
  * Maps the name of each preference in the back-end to its PrefRow object,
  * separating the preferences that actually exist. This is as an optimization to
  * avoid querying the preferences service each time the list is filtered.
  */
@@ -33,45 +39,52 @@ let gPrefInEdit = null;
  * Lowercase substring that should be contained in the preference name.
  */
 let gFilterString = null;
 
 class PrefRow {
   constructor(name) {
     this.name = name;
     this.value = true;
+    this.editing = false;
     this.refreshValue();
-
-    this.editing = false;
-    this.element = document.createElement("tr");
-    this._setupElement();
-    gElementToPrefMap.set(this.element, this);
   }
 
   refreshValue() {
-    this.hasDefaultValue = prefHasDefaultValue(this.name);
-    this.hasUserValue = Services.prefs.prefHasUserValue(this.name);
-    this.isLocked = Services.prefs.prefIsLocked(this.name);
+    let prefType = Services.prefs.getPrefType(this.name);
 
     // If this preference has been deleted, we keep its last known value.
-    if (!this.exists) {
+    if (prefType == Ci.nsIPrefBranch.PREF_INVALID) {
+      this.hasDefaultValue = false;
+      this.hasUserValue = false;
+      this.isLocked = false;
       gExistingPrefs.delete(this.name);
       gDeletedPrefs.set(this.name, this);
       return;
     }
+
     gExistingPrefs.set(this.name, this);
     gDeletedPrefs.delete(this.name);
 
     try {
-      // This can throw for locked preferences without a default value.
-      this.value = Preferences.get(this.name);
-      // We don't know which preferences should be read using getComplexValue,
-      // so we use a heuristic to determine if this is a localized preference.
-      if (!this.hasUserValue &&
-          /^chrome:\/\/.+\/locale\/.+\.properties/.test(this.value)) {
+      this.value = gDefaultBranch[GETTERS_BY_PREF_TYPE[prefType]](this.name);
+      this.hasDefaultValue = true;
+    } catch (ex) {
+      this.hasDefaultValue = false;
+    }
+    this.hasUserValue = Services.prefs.prefHasUserValue(this.name);
+    this.isLocked = Services.prefs.prefIsLocked(this.name);
+
+    try {
+      if (this.hasUserValue) {
+        // This can throw for locked preferences without a default value.
+        this.value = Services.prefs[GETTERS_BY_PREF_TYPE[prefType]](this.name);
+      } else if (/^chrome:\/\/.+\/locale\/.+\.properties/.test(this.value)) {
+        // We don't know which preferences should be read using getComplexValue,
+        // so we use a heuristic to determine if this is a localized preference.
         // This can throw if there is no value in the localized files.
         this.value = Services.prefs.getComplexValue(this.name,
           Ci.nsIPrefLocalizedString).data;
       }
     } catch (ex) {
       this.value = "";
     }
   }
@@ -83,20 +96,30 @@ class PrefRow {
   get exists() {
     return this.hasDefaultValue || this.hasUserValue;
   }
 
   get matchesFilter() {
     return !gFilterString || this.name.toLowerCase().includes(gFilterString);
   }
 
-  _setupElement() {
-    this.element.textContent = "";
+  /**
+   * Returns a reference to the table row element to be added to the document,
+   * constructing and initializing it the first time this method is called.
+   */
+  getElement() {
+    if (this._element) {
+      return this._element;
+    }
+
+    this._element = document.createElement("tr");
+    gElementToPrefMap.set(this._element, this);
+
     let nameCell = document.createElement("th");
-    this.element.append(
+    this._element.append(
       nameCell,
       this.valueCell = document.createElement("td"),
       this.editCell = document.createElement("td"),
       this.resetCell = document.createElement("td")
     );
     this.editCell.appendChild(
       this.editButton = document.createElement("button")
     );
@@ -109,22 +132,29 @@ class PrefRow {
     // Add <wbr> behind dots to prevent line breaking in random mid-word places.
     let parts = this.name.split(".");
     for (let i = 0; i < parts.length - 1; i++) {
       nameCell.append(parts[i] + ".", document.createElement("wbr"));
     }
     nameCell.append(parts[parts.length - 1]);
 
     this.refreshElement();
+
+    return this._element;
   }
 
   refreshElement() {
-    this.element.classList.toggle("has-user-value", !!this.hasUserValue);
-    this.element.classList.toggle("locked", !!this.isLocked);
-    this.element.classList.toggle("deleted", !this.exists);
+    if (!this._element) {
+      // No need to update if this preference was never added to the table.
+      return;
+    }
+
+    this._element.classList.toggle("has-user-value", !!this.hasUserValue);
+    this._element.classList.toggle("locked", !!this.isLocked);
+    this._element.classList.toggle("deleted", !this.exists);
     if (this.exists && !this.editing) {
       // We need to place the text inside a "span" element to ensure that the
       // text copied to the clipboard includes all whitespace.
       let span = document.createElement("span");
       span.textContent = this.value;
       // We additionally need to wrap this with another "span" element to convey
       // the state to screen readers without affecting the visual presentation.
       span.setAttribute("aria-hidden", "true");
@@ -262,17 +292,17 @@ let gPrefObserver = {
       if (!pref.editing) {
         pref.refreshElement();
       }
       return;
     }
 
     let newPref = new PrefRow(data);
     if (newPref.matchesFilter) {
-      document.getElementById("prefs").appendChild(newPref.element);
+      document.getElementById("prefs").appendChild(newPref.getElement());
     }
   },
 };
 
 if (!Preferences.get("browser.aboutConfig.showWarning")) {
   // When showing the filtered preferences directly, remove the warning elements
   // immediately to prevent flickering, but wait to filter the preferences until
   // the value of the textbox has been restored from previous sessions.
@@ -369,42 +399,25 @@ function filterPrefs() {
   if (searchName && !gExistingPrefs.has(searchName)) {
     prefArray.push(new PrefRow(searchName));
   }
 
   let prefsElement = document.getElementById("prefs");
   prefsElement.textContent = "";
   let fragment = document.createDocumentFragment();
   for (let pref of prefArray) {
-    fragment.appendChild(pref.element);
+    fragment.appendChild(pref.getElement());
   }
   prefsElement.appendChild(fragment);
 
   // We only start observing preference changes after the first search is done,
   // so that newly added preferences won't appear while the page is still empty.
   if (!gPrefObserverRegistered) {
     gPrefObserverRegistered = true;
     Services.prefs.addObserver("", gPrefObserver);
     window.addEventListener("unload", () => {
       Services.prefs.removeObserver("", gPrefObserver);
     }, { once: true });
   }
 
   document.body.classList.toggle("config-warning",
     location.href.split(":").every(l => gFilterString.includes(l)));
 }
-
-function prefHasDefaultValue(name) {
-  try {
-    switch (Services.prefs.getPrefType(name)) {
-      case Ci.nsIPrefBranch.PREF_STRING:
-        gDefaultBranch.getStringPref(name);
-        return true;
-      case Ci.nsIPrefBranch.PREF_INT:
-        gDefaultBranch.getIntPref(name);
-        return true;
-      case Ci.nsIPrefBranch.PREF_BOOL:
-        gDefaultBranch.getBoolPref(name);
-        return true;
-    }
-  } catch (ex) {}
-  return false;
-}
--- a/browser/components/aboutconfig/test/browser/browser.ini
+++ b/browser/components/aboutconfig/test/browser/browser.ini
@@ -1,18 +1,14 @@
 [DEFAULT]
-skip-if = asan # Bug 1520398
+skip-if = debug || asan # Bug 1507747 and bug 1520398
 support-files =
   head.js
 
 [browser_accessibility.js]
 [browser_basic.js]
 [browser_clipboard.js]
-skip-if = debug # Bug 1507747
 subsuite = clipboard
 [browser_edit.js]
-skip-if = debug # Bug 1507747
 [browser_locked.js]
 [browser_observe.js]
-skip-if = debug # Bug 1507747
 [browser_search.js]
-skip-if = debug # Bug 1507747
 [browser_warning.js]
--- a/dom/script/ModuleLoadRequest.cpp
+++ b/dom/script/ModuleLoadRequest.cpp
@@ -151,27 +151,35 @@ void ModuleLoadRequest::ModuleLoaded() {
     ModuleErrored();
     return;
   }
 
   mLoader->StartFetchingModuleDependencies(this);
 }
 
 void ModuleLoadRequest::ModuleErrored() {
+  if (IsCanceled()) {
+    return;
+  }
+
   LOG(("ScriptLoadRequest (%p): Module errored", this));
 
   mLoader->CheckModuleDependenciesLoaded(this);
   MOZ_ASSERT(!mModuleScript || mModuleScript->HasParseError());
 
   CancelImports();
   SetReady();
   LoadFinished();
 }
 
 void ModuleLoadRequest::DependenciesLoaded() {
+  if (IsCanceled()) {
+    return;
+  }
+
   // The module and all of its dependencies have been successfully fetched and
   // compiled.
 
   LOG(("ScriptLoadRequest (%p): Module dependencies loaded", this));
 
   MOZ_ASSERT(mModuleScript);
 
   mLoader->CheckModuleDependenciesLoaded(this);
--- a/dom/script/ScriptLoadRequest.cpp
+++ b/dom/script/ScriptLoadRequest.cpp
@@ -86,17 +86,18 @@ ScriptLoadRequest::ScriptLoadRequest(Scr
       mFetchOptions(aFetchOptions),
       mOffThreadToken(nullptr),
       mScriptTextLength(0),
       mScriptBytecode(),
       mBytecodeOffset(0),
       mURI(aURI),
       mLineNo(1),
       mIntegrity(aIntegrity),
-      mReferrer(aReferrer) {
+      mReferrer(aReferrer),
+      mUnreportedPreloadError(NS_OK) {
   MOZ_ASSERT(mFetchOptions);
 }
 
 ScriptLoadRequest::~ScriptLoadRequest() {
   // We should always clean up any off-thread script parsing resources.
   MOZ_ASSERT(!mOffThreadToken);
 
   // But play it safe in release builds and try to clean them up here
--- a/dom/script/ScriptLoadRequest.h
+++ b/dom/script/ScriptLoadRequest.h
@@ -234,36 +234,36 @@ class ScriptLoadRequest
   void SetScript(JSScript* aScript);
 
   void MaybeCancelOffThreadScript();
   void DropBytecodeCacheReferences();
 
   using super::getNext;
   using super::isInList;
 
-  const ScriptKind
-      mKind;  // Whether this is a classic script or a module script.
+  const ScriptKind mKind;  // Whether this is a classic script or a module
+                           // script.
   ScriptMode mScriptMode;  // Whether this is a blocking, defer or async script.
   Progress mProgress;      // Are we still waiting for a load to complete?
   DataType mDataType;      // Does this contain Source or Bytecode?
   bool mScriptFromHead;    // Synchronous head script block loading of other non
                            // js/css content.
   bool mIsInline;          // Is the script inline or loaded?
   bool mHasSourceMapURL;   // Does the HTTP header have a source map url?
   bool mInDeferList;       // True if we live in mDeferRequests.
   bool mInAsyncList;       // True if we live in mLoadingAsyncRequests or
                            // mLoadedAsyncRequests.
   bool mIsNonAsyncScriptInserted;  // True if we live in
                                    // mNonAsyncExternalScriptInsertedRequests
   bool mIsXSLT;                    // True if we live in mXSLTRequests.
   bool mIsCanceled;                // True if we have been explicitly canceled.
-  bool
-      mWasCompiledOMT;  // True if the script has been compiled off main thread.
-  bool mIsTracking;  // True if the script comes from a source on our tracking
-                     // protection list.
+  bool mWasCompiledOMT;    // True if the script has been compiled off main
+                           // thread.
+  bool mIsTracking;        // True if the script comes from a source on our
+                           // tracking protection list.
 
   RefPtr<ScriptFetchOptions> mFetchOptions;
 
   JS::OffThreadToken* mOffThreadToken;  // Off-thread parsing token.
   nsString mSourceMapURL;  // Holds source map url for loaded scripts
 
   // Holds the top-level JSScript that corresponds to the current source, once
   // it is parsed, and planned to be saved in the bytecode cache.
@@ -292,16 +292,20 @@ class ScriptLoadRequest
   const nsCOMPtr<nsIURI> mReferrer;
 
   // Holds the Cache information, which is used to register the bytecode
   // on the cache entry, such that we can load it the next time.
   nsCOMPtr<nsICacheInfoChannel> mCacheInfo;
 
   // The base URL used for resolving relative module imports.
   nsCOMPtr<nsIURI> mBaseURL;
+
+  // For preload requests, we defer reporting errors to the console until the
+  // request is used.
+  nsresult mUnreportedPreloadError;
 };
 
 class ScriptLoadRequestList : private mozilla::LinkedList<ScriptLoadRequest> {
   typedef mozilla::LinkedList<ScriptLoadRequest> super;
 
  public:
   ~ScriptLoadRequestList();
 
--- a/dom/script/ScriptLoader.cpp
+++ b/dom/script/ScriptLoader.cpp
@@ -1798,16 +1798,19 @@ ScriptLoadRequest* ScriptLoader::LookupP
       referrerPolicy != request->ReferrerPolicy() ||
       aScriptKind != request->mKind) {
     // Drop the preload.
     request->Cancel();
     AccumulateCategorical(LABELS_DOM_SCRIPT_PRELOAD_RESULT::RequestMismatch);
     return nullptr;
   }
 
+  // Report any errors that we skipped while preloading.
+  ReportPreloadErrorsToConsole(request);
+
   return request;
 }
 
 void ScriptLoader::GetSRIMetadata(const nsAString& aIntegrityAttr,
                                   SRIMetadata* aMetadataOut) {
   MOZ_ASSERT(aMetadataOut->IsEmpty());
 
   if (aIntegrityAttr.IsEmpty()) {
@@ -3191,16 +3194,19 @@ nsresult ScriptLoader::SaveSRIHash(
   return NS_OK;
 }
 
 void ScriptLoader::ReportErrorToConsole(ScriptLoadRequest* aRequest,
                                         nsresult aResult) const {
   MOZ_ASSERT(aRequest);
 
   if (aRequest->IsPreload()) {
+    // Skip reporting errors in preload requests. If the request is actually
+    // used then we will report the error in ReportPreloadErrorsToConsole below.
+    aRequest->mUnreportedPreloadError = aResult;
     return;
   }
 
   bool isScript = !aRequest->IsModuleRequest();
   const char* message;
   if (aResult == NS_ERROR_MALFORMED_URI) {
     message = isScript ? "ScriptSourceMalformed" : "ModuleSourceMalformed";
   } else if (aResult == NS_ERROR_DOM_BAD_URI) {
@@ -3220,16 +3226,29 @@ void ScriptLoader::ReportErrorToConsole(
   uint32_t columnNo = element ? element->GetScriptColumnNumber() : 0;
 
   nsContentUtils::ReportToConsole(
       nsIScriptError::warningFlag, NS_LITERAL_CSTRING("Script Loader"),
       mDocument, nsContentUtils::eDOM_PROPERTIES, message, params,
       ArrayLength(params), nullptr, EmptyString(), lineNo, columnNo);
 }
 
+void ScriptLoader::ReportPreloadErrorsToConsole(ScriptLoadRequest* aRequest) {
+  if (NS_FAILED(aRequest->mUnreportedPreloadError)) {
+    ReportErrorToConsole(aRequest, aRequest->mUnreportedPreloadError);
+    aRequest->mUnreportedPreloadError = NS_OK;
+  }
+
+  if (aRequest->IsModuleRequest()) {
+    for (auto childRequest : aRequest->AsModuleRequest()->mImports) {
+      ReportPreloadErrorsToConsole(childRequest);
+    }
+  }
+}
+
 void ScriptLoader::HandleLoadError(ScriptLoadRequest* aRequest,
                                    nsresult aResult) {
   /*
    * Handle script not loading error because source was a tracking URL.
    * We make a note of this script node by including it in a dedicated
    * array of blocked tracking nodes under its parent document.
    */
   if (aResult == NS_ERROR_TRACKING_URI) {
--- a/dom/script/ScriptLoader.h
+++ b/dom/script/ScriptLoader.h
@@ -443,16 +443,17 @@ class ScriptLoader final : public nsISup
                      nsIIncrementalStreamLoader* aLoader, nsresult aSRIStatus,
                      SRICheckDataVerifier* aSRIDataVerifier) const;
 
   nsresult SaveSRIHash(ScriptLoadRequest* aRequest,
                        SRICheckDataVerifier* aSRIDataVerifier) const;
 
   void ReportErrorToConsole(ScriptLoadRequest* aRequest,
                             nsresult aResult) const;
+  void ReportPreloadErrorsToConsole(ScriptLoadRequest* aRequest);
 
   nsresult AttemptAsyncScriptCompile(ScriptLoadRequest* aRequest,
                                      bool* aCouldCompileOut);
   nsresult ProcessRequest(ScriptLoadRequest* aRequest);
   void ProcessDynamicImport(ModuleLoadRequest* aRequest);
   nsresult CompileOffThreadOrProcessRequest(ScriptLoadRequest* aRequest);
   void FireScriptAvailable(nsresult aResult, ScriptLoadRequest* aRequest);
   void FireScriptEvaluated(nsresult aResult, ScriptLoadRequest* aRequest);
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -2344,17 +2344,17 @@ WorkerThreadPrimaryRunnable::Run() {
     // strong reference to the debugger global scope. These runnables are not
     // visible to the cycle collector, so we need to make sure to clear the
     // debugger event queue before we try to destroy the context. If we don't,
     // the garbage collector will crash.
     mWorkerPrivate->ClearDebuggerEventQueue();
 
     // Perform a full GC. This will collect the main worker global and CC,
     // which should break all cycles that touch JS.
-    JS_GC(cx);
+    JS_GC(cx, JS::GCReason::WORKER_SHUTDOWN);
 
     // Before shutting down the cycle collector we need to do one more pass
     // through the event loop to clean up any C++ objects that need deferred
     // cleanup.
     mWorkerPrivate->ClearMainEventQueue(WorkerPrivate::WorkerRan);
 
     // Now WorkerJSContext goes out of scope and its destructor will shut
     // down the cycle collector. This breaks any remaining cycles and collects
--- a/js/public/GCAPI.h
+++ b/js/public/GCAPI.h
@@ -364,39 +364,39 @@ namespace JS {
   D(ABORT_GC, 16)                          \
   D(FULL_WHOLE_CELL_BUFFER, 17)            \
   D(FULL_GENERIC_BUFFER, 18)               \
   D(FULL_VALUE_BUFFER, 19)                 \
   D(FULL_CELL_PTR_BUFFER, 20)              \
   D(FULL_SLOT_BUFFER, 21)                  \
   D(FULL_SHAPE_BUFFER, 22)                 \
   D(TOO_MUCH_WASM_MEMORY, 23)              \
+  D(DISABLE_GENERATIONAL_GC, 24)           \
+  D(FINISH_GC, 25)                         \
+  D(PREPARE_FOR_TRACING, 26)               \
                                            \
   /* These are reserved for future use. */ \
-  D(RESERVED0, 24)                         \
-  D(RESERVED1, 25)                         \
-  D(RESERVED2, 26)                         \
   D(RESERVED3, 27)                         \
   D(RESERVED4, 28)                         \
   D(RESERVED5, 29)                         \
   D(RESERVED6, 30)                         \
   D(RESERVED7, 31)                         \
   D(RESERVED8, 32)                         \
                                            \
   /* Reasons from Firefox */               \
   D(DOM_WINDOW_UTILS, 33)                  \
   D(COMPONENT_UTILS, 34)                   \
   D(MEM_PRESSURE, 35)                      \
   D(CC_WAITING, 36)                        \
   D(CC_FORCED, 37)                         \
   D(LOAD_END, 38)                          \
-  D(POST_COMPARTMENT, 39)                  \
+  D(UNUSED3, 39)                           \
   D(PAGE_HIDE, 40)                         \
   D(NSJSCONTEXT_DESTROY, 41)               \
-  D(SET_NEW_DOCUMENT, 42)                  \
+  D(WORKER_SHUTDOWN, 42)                   \
   D(SET_DOC_SHELL, 43)                     \
   D(DOM_UTILS, 44)                         \
   D(DOM_IPC, 45)                           \
   D(DOM_WORKER, 46)                        \
   D(INTER_SLICE_GC, 47)                    \
   D(UNUSED1, 48)                           \
   D(FULL_GC_TIMER, 49)                     \
   D(SHUTDOWN_CC, 50)                       \
@@ -870,17 +870,18 @@ extern JS_PUBLIC_API bool JS_AddExtraGCR
                                                    JSTraceDataOp traceOp,
                                                    void* data);
 
 /** Undo a call to JS_AddExtraGCRootsTracer. */
 extern JS_PUBLIC_API void JS_RemoveExtraGCRootsTracer(JSContext* cx,
                                                       JSTraceDataOp traceOp,
                                                       void* data);
 
-extern JS_PUBLIC_API void JS_GC(JSContext* cx);
+extern JS_PUBLIC_API void JS_GC(JSContext* cx,
+                                JS::GCReason reason = JS::GCReason::API);
 
 extern JS_PUBLIC_API void JS_MaybeGC(JSContext* cx);
 
 extern JS_PUBLIC_API void JS_SetGCCallback(JSContext* cx, JSGCCallback cb,
                                            void* data);
 
 extern JS_PUBLIC_API void JS_SetObjectsTenuredCallback(
     JSContext* cx, JSObjectsTenuredCallback cb, void* data);
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -6934,18 +6934,16 @@ static bool ShouldSweepOnBackgroundThrea
 }
 
 void GCRuntime::incrementalSlice(SliceBudget& budget, JS::GCReason reason,
                                  AutoGCSession& session) {
   AutoDisableBarriers disableBarriers(rt);
 
   bool destroyingRuntime = (reason == JS::GCReason::DESTROY_RUNTIME);
 
-  number++;
-
   initialState = incrementalState;
 
 #ifdef JS_GC_ZEAL
   /*
    * Do the incremental collection type specified by zeal mode if the
    * collection was triggered by runDebugGC() and incremental GC has not been
    * cancelled by resetIncrementalGC().
    */
@@ -7386,16 +7384,18 @@ MOZ_NEVER_INLINE GCRuntime::IncrementalR
 
   auto result = budgetIncrementalGC(nonincrementalByAPI, reason, budget);
   if (result == IncrementalResult::ResetIncremental) {
     reason = JS::GCReason::RESET;
   }
 
   if (shouldCollectNurseryForSlice(nonincrementalByAPI, budget)) {
     minorGC(reason, gcstats::PhaseKind::EVICT_NURSERY_FOR_MAJOR_GC);
+  } else {
+    ++number; // This otherwise happens in minorGC().
   }
 
   AutoGCSession session(rt, JS::HeapState::MajorCollecting);
 
   majorGCTriggerReason = JS::GCReason::NO_REASON;
 
   {
     gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::WAIT_BACKGROUND_THREAD);
@@ -7475,16 +7475,19 @@ static bool IsDeterministicGCReason(JS::
     case JS::GCReason::LAST_DITCH:
     case JS::GCReason::TOO_MUCH_MALLOC:
     case JS::GCReason::TOO_MUCH_WASM_MEMORY:
     case JS::GCReason::ALLOC_TRIGGER:
     case JS::GCReason::DEBUG_GC:
     case JS::GCReason::CC_FORCED:
     case JS::GCReason::SHUTDOWN_CC:
     case JS::GCReason::ABORT_GC:
+    case JS::GCReason::DISABLE_GENERATIONAL_GC:
+    case JS::GCReason::FINISH_GC:
+    case JS::GCReason::PREPARE_FOR_TRACING:
       return true;
 
     default:
       return false;
   }
 }
 #endif
 
@@ -7845,17 +7848,17 @@ void GCRuntime::startBackgroundFreeAfter
   }
 
   startBackgroundFree();
 }
 
 JS::AutoDisableGenerationalGC::AutoDisableGenerationalGC(JSContext* cx)
     : cx(cx) {
   if (!cx->generationalDisabled) {
-    cx->runtime()->gc.evictNursery(JS::GCReason::API);
+    cx->runtime()->gc.evictNursery(JS::GCReason::DISABLE_GENERATIONAL_GC);
     cx->nursery().disable();
   }
   ++cx->generationalDisabled;
 }
 
 JS::AutoDisableGenerationalGC::~AutoDisableGenerationalGC() {
   if (--cx->generationalDisabled == 0) {
     cx->nursery().enable();
@@ -7888,20 +7891,20 @@ bool GCRuntime::gcIfRequested() {
       gcSlice(majorGCTriggerReason);
     }
     return true;
   }
 
   return false;
 }
 
-void js::gc::FinishGC(JSContext* cx) {
+void js::gc::FinishGC(JSContext* cx, JS::GCReason reason) {
   if (JS::IsIncrementalGCInProgress(cx)) {
     JS::PrepareForIncrementalGC(cx);
-    JS::FinishIncrementalGC(cx, JS::GCReason::API);
+    JS::FinishIncrementalGC(cx, reason);
   }
 
   cx->runtime()->gc.waitBackgroundFreeEnd();
 }
 
 Realm* js::NewRealm(JSContext* cx, JSPrincipals* principals,
                     const JS::RealmOptions& options) {
   JSRuntime* rt = cx->runtime();
--- a/js/src/gc/GC.h
+++ b/js/src/gc/GC.h
@@ -122,17 +122,17 @@ extern void IterateScripts(JSContext* cx
 extern void IterateLazyScripts(JSContext* cx, JS::Realm* realm, void* data,
                                IterateLazyScriptCallback lazyScriptCallback);
 
 JS::Realm* NewRealm(JSContext* cx, JSPrincipals* principals,
                     const JS::RealmOptions& options);
 
 namespace gc {
 
-void FinishGC(JSContext* cx);
+void FinishGC(JSContext* cx, JS::GCReason = JS::GCReason::FINISH_GC);
 
 /*
  * Merge all contents of source into target. This can only be used if source is
  * the only realm in its zone.
  */
 void MergeRealms(JS::Realm* source, JS::Realm* target);
 
 enum VerifierType { PreBarrierVerifier };
--- a/js/src/gc/GCInternals.h
+++ b/js/src/gc/GCInternals.h
@@ -80,26 +80,29 @@ class MOZ_RAII AutoGCSession : public Au
 class MOZ_RAII AutoTraceSession : public AutoLockAllAtoms,
                                   public AutoHeapSession {
  public:
   explicit AutoTraceSession(JSRuntime* rt)
       : AutoLockAllAtoms(rt), AutoHeapSession(rt, JS::HeapState::Tracing) {}
 };
 
 struct MOZ_RAII AutoFinishGC {
-  explicit AutoFinishGC(JSContext* cx) { FinishGC(cx); }
+  explicit AutoFinishGC(JSContext* cx, JS::GCReason reason) {
+    FinishGC(cx, reason);
+  }
 };
 
 // This class should be used by any code that needs exclusive access to the heap
 // in order to trace through it.
 class MOZ_RAII AutoPrepareForTracing : private AutoFinishGC,
                                        public AutoTraceSession {
  public:
   explicit AutoPrepareForTracing(JSContext* cx)
-      : AutoFinishGC(cx), AutoTraceSession(cx->runtime()) {}
+      : AutoFinishGC(cx, JS::GCReason::PREPARE_FOR_TRACING),
+        AutoTraceSession(cx->runtime()) {}
 };
 
 AbortReason IsIncrementalGCUnsafe(JSRuntime* rt);
 
 #ifdef JS_GC_ZEAL
 
 class MOZ_RAII AutoStopVerifyingBarriers {
   GCRuntime* gc;
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -402,17 +402,16 @@ class GCRuntime {
   void incMinorGcNumber() {
     ++minorGCNumber;
     ++number;
   }
 
   uint64_t majorGCCount() const { return majorGCNumber; }
   void incMajorGcNumber() {
     ++majorGCNumber;
-    ++number;
   }
 
   int64_t defaultSliceBudget() const { return defaultTimeBudget_; }
 
   bool isIncrementalGc() const { return isIncremental; }
   bool isFullGc() const { return isFull; }
   bool isCompactingGc() const { return isCompacting; }
 
@@ -811,17 +810,17 @@ class GCRuntime {
   MainThreadData<bool> fullGCForAtomsRequested_;
 
   /* Incremented at the start of every minor GC. */
   MainThreadData<uint64_t> minorGCNumber;
 
   /* Incremented at the start of every major GC. */
   MainThreadData<uint64_t> majorGCNumber;
 
-  /* Incremented on every GC slice. */
+  /* Incremented on every GC slice or minor collection. */
   MainThreadData<uint64_t> number;
 
   /* Whether the currently running GC can finish in multiple slices. */
   MainThreadData<bool> isIncremental;
 
   /* Whether all zones are being collected in first GC slice. */
   MainThreadData<bool> isFull;
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -699,16 +699,20 @@ JS_PUBLIC_API JSObject* JS_TransplantObj
   if (origobj->compartment() == destination) {
     // If the original object is in the same compartment as the
     // destination, then we know that we won't find a wrapper in the
     // destination's cross compartment map and that the same
     // object will continue to work.
     AutoRealmUnchecked ar(cx, origobj->nonCCWRealm());
     JSObject::swap(cx, origobj, target);
     newIdentity = origobj;
+
+    // |origobj| might be gray so unmark it to avoid returning a possibly-gray
+    // object.
+    JS::ExposeObjectToActiveJS(newIdentity);
   } else if (WrapperMap::Ptr p = destination->lookupWrapper(origv)) {
     // There might already be a wrapper for the original object in
     // the new compartment. If there is, we use its identity and swap
     // in the contents of |target|.
     newIdentity = &p->value().get().toObject();
 
     // When we remove origv from the wrapper map, its wrapper, newIdentity,
     // must immediately cease to be a cross-compartment wrapper. Nuke it.
@@ -1166,20 +1170,20 @@ JS_PUBLIC_API bool JS::IsIdleGCTaskNeede
 
 JS_PUBLIC_API void JS::RunIdleTimeGCTask(JSRuntime* rt) {
   gc::GCRuntime& gc = rt->gc;
   if (gc.nursery().needIdleTimeCollection()) {
     gc.minorGC(JS::GCReason::IDLE_TIME_COLLECTION);
   }
 }
 
-JS_PUBLIC_API void JS_GC(JSContext* cx) {
+JS_PUBLIC_API void JS_GC(JSContext* cx, JS::GCReason reason) {
   AssertHeapIsIdle();
   JS::PrepareForFullGC(cx);
-  cx->runtime()->gc.gc(GC_NORMAL, JS::GCReason::API);
+  cx->runtime()->gc.gc(GC_NORMAL, reason);
 }
 
 JS_PUBLIC_API void JS_MaybeGC(JSContext* cx) {
   gc::GCRuntime& gc = cx->runtime()->gc;
   gc.maybeGC(cx->zone());
 }
 
 JS_PUBLIC_API void JS_SetGCCallback(JSContext* cx, JSGCCallback cb,
new file mode 100644
--- /dev/null
+++ b/layout/reftests/invalidation/paintedlayer-recycling-8-ref.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta http-equiv="content-type" content="text/html; charset=utf-8">
+<title>PaintedLayer recycling should use the right translation</title>
+<style>
+body {
+  overflow: hidden;
+  background-color: grey;
+}
+
+.fixed {
+  position: fixed;
+  width: 800px;
+  height: 800px;
+}
+
+.container {
+  position: relative;
+  top: 50px;
+  left: 50px;
+  width: 400px;
+  height: 400px;
+  z-index: 1;
+  pointer-events: none;
+  transform: scale(1.0);
+}
+
+.not-transformed {
+  background-color: lightblue;
+  width: 200px;
+  height: 200px;
+  position: absolute;
+  top: 0px;
+  left: 0px;
+  z-index: 1;
+}
+
+.transformed {
+  position: relative;
+  top: 50px;
+  left: 50px;
+  width: 200px;
+  height: 200px;
+  background: red;
+  transform: scale(1.5);
+}
+</style>
+</head>
+<body>
+  <div class="fixed">
+    <div class="container">
+      <div class="not-transformed"></div>
+      <div class="transformed"></div>
+    </div>
+  </div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/invalidation/paintedlayer-recycling-8.html
@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<html lang="en" class="reftest-wait">
+<head>
+<meta http-equiv="content-type" content="text/html; charset=utf-8">
+<title>PaintedLayer recycling should use the right translation</title>
+<style>
+body {
+  overflow: hidden;
+  background-color: grey;
+}
+
+.fixed {
+  position: fixed;
+  width: 800px;
+  height: 800px;
+}
+
+.container {
+  position: relative;
+  top: 50px;
+  left: 50px;
+  width: 400px;
+  height: 400px;
+  z-index: 1;
+  pointer-events: none;
+}
+
+.not-transformed {
+  background-color: blue;
+  width: 200px;
+  height: 200px;
+  position: absolute;
+  top: 0px;
+  left: 0px;
+  z-index: 1;
+}
+
+.transformed {
+  position: relative;
+  top: 50px;
+  left: 50px;
+  width: 200px;
+  height: 200px;
+  background: red;
+  transform: scale(1.5);
+}
+</style>
+</head>
+<body>
+  <div class="fixed">
+    <div class="container">
+      <div class="not-transformed"></div>
+      <div class="transformed"></div>
+    </div>
+  </div>
+</body>
+<script type="text/javascript">
+function end() {
+  document.documentElement.removeAttribute("class");
+}
+
+function runAfterNextPaint(cb) {
+  requestAnimationFrame(() => requestAnimationFrame(cb))
+}
+
+function change() {
+  document.querySelector(".not-transformed").style["background-color"] = "lightblue";
+  runAfterNextPaint(end);
+}
+
+function doTest() {
+  document.querySelector(".container").style.transform = "scale(1.0)";
+  runAfterNextPaint(change);
+}
+
+document.addEventListener("MozReftestInvalidate", doTest);
+
+//setTimeout(doTest, 5000);
+</script>
+</html>
--- a/layout/reftests/invalidation/reftest.list
+++ b/layout/reftests/invalidation/reftest.list
@@ -96,8 +96,10 @@ pref(layers.single-tile.enabled,false) !
 == clip-path-invalidation-1c.html mask-invalidation-2-ref.html
 == clip-path-invalidation-1d.html mask-invalidation-2-ref.html
 
 != fractional-transform-1.html about:blank
 skip-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu)) != fractional-transform-2.html about:blank
 != fractional-transform-3.html about:blank
 
 == partially-scrolled-svg-group.html partially-scrolled-svg-group-ref.html
+
+== paintedlayer-recycling-8.html paintedlayer-recycling-8-ref.html
--- a/testing/mozharness/configs/merge_day/bump_central.py
+++ b/testing/mozharness/configs/merge_day/bump_central.py
@@ -21,11 +21,12 @@ config = {
 
     "require_remove_locales": False,
     "requires_head_merge": False,
 
     "migration_behavior": "bump_and_tag_central", # like esr_bump.py, needed for key validation
     "default_actions": [
         "clean-repos",
         "pull",
+        "set_push_to_ssh",
         "bump_and_tag_central"
     ],
 }
--- a/testing/mozharness/configs/merge_day/bump_esr.py
+++ b/testing/mozharness/configs/merge_day/bump_esr.py
@@ -12,11 +12,12 @@ config = {
     "to_repo_url": "https://hg.mozilla.org/releases/mozilla-esr60",
 
     "migration_behavior": "bump_second_digit",
     "require_remove_locales": False,
     "requires_head_merge": False,
     "default_actions": [
         "clean-repos",
         "pull",
+        "set_push_to_ssh",
         "bump_second_digit"
     ],
 }
--- a/testing/mozharness/scripts/merge_day/gecko_migration.py
+++ b/testing/mozharness/scripts/merge_day/gecko_migration.py
@@ -165,17 +165,18 @@ class GeckoMigration(MercurialScript, Vi
     def query_push_args(self, cwd):
         if cwd == self.query_abs_dirs()['abs_to_dir'] and \
                 self.config['migration_behavior'] == 'beta_to_release':
             return ['--new-branch', '-r', '.']
         else:
             return ['-r', '.']
 
     def set_push_to_ssh(self):
-        for cwd in self.query_push_dirs():
+        push_dirs = [d for d in self.query_push_dirs() if d is not None]
+        for cwd in push_dirs:
             repo_url = self.read_repo_hg_rc(cwd).get('paths', 'default')
             username = self.config.get('ssh_user', '')
             # Add a trailing @ to the username if it exists, otherwise it gets
             # mushed up with the hostname.
             if username:
                 username += '@'
             push_dest = repo_url.replace('https://', 'ssh://' + username)
 
--- a/testing/web-platform/meta/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/abort-while-navigating.window.js.ini
+++ b/testing/web-platform/meta/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/abort-while-navigating.window.js.ini
@@ -1,9 +1,10 @@
 [abort-while-navigating.window.html]
+  disabled:  https://bugzilla.mozilla.org/show_bug.cgi?id=1490978
   [document.open() does NOT abort documents that are queued for navigation through Refresh header with 1-sec timeout (fetch())]
     expected: TIMEOUT
 
   [document.open() aborts documents that are queued for navigation through Refresh header with timeout 0 (fetch())]
     expected: TIMEOUT
 
   [document.open() does NOT abort documents that are queued for navigation through Refresh header with 1-sec timeout (XMLHttpRequest)]
     expected: TIMEOUT