Merge mozilla-central to autoland. a=merge CLOSED TREE
authorNarcis Beleuzu <nbeleuzu@mozilla.com>
Thu, 24 Jan 2019 23:46:04 +0200
changeset 515350 1b31a8ce863549766ab06debcffb52c1b1f51ca1
parent 515342 ec5b5e910ce1a4c77b19dbdd35e37bbfd849328d (current diff)
parent 515349 b55ddb97722a51b3365f1a72bdb416f028fd2073 (diff)
child 515351 41ac42ce006f22502c0498fffaf6228c36a424d2
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland. a=merge CLOSED TREE
--- 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