merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 12 Jul 2016 10:58:33 +0200
changeset 344611 aac8ff1024c553d9c92b85b8b6ba90f65de2ed08
parent 344569 4e2084c39d2be1824042666e2fd05d2b26bab543 (current diff)
parent 344610 5ff20b7b76518c631cdb3fea95dfbab61afedf30 (diff)
child 344626 722bbf56e5da51370ef420212e23e96399857335
child 344629 efc08ad3e8afd42499a1cd20ade4e7c0cd05e3a8
child 344655 da8948e3c26d39932ce8f133a423187262676006
push id6389
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:38:22 +0000
treeherdermozilla-beta@01d67bfe6c81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone50.0a1
first release with
nightly linux32
aac8ff1024c5 / 50.0a1 / 20160712030234 / files
nightly linux64
aac8ff1024c5 / 50.0a1 / 20160712030234 / files
nightly mac
aac8ff1024c5 / 50.0a1 / 20160712030202 / files
nightly win32
aac8ff1024c5 / 50.0a1 / 20160712030234 / files
nightly win64
aac8ff1024c5 / 50.0a1 / 20160712030234 / 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 mozilla-inbound to mozilla-central a=merge
gfx/src/TiledRegion.cpp
toolkit/locales/en-US/chrome/global-region/region.properties
toolkit/mozapps/extensions/test/browser/browser_globalinformations.js
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -620,19 +620,16 @@ pref("network.protocol-handler.expose.nn
 pref("accessibility.typeaheadfind", false);
 pref("accessibility.typeaheadfind.timeout", 5000);
 pref("accessibility.typeaheadfind.linksonly", false);
 pref("accessibility.typeaheadfind.flashBar", 1);
 
 // Tracks when accessibility is loaded into the previous session.
 pref("accessibility.loadedInLastSession", false);
 
-pref("plugins.update.url", "https://www.mozilla.org/%LOCALE%/plugincheck/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_campaign=plugincheck-update");
-pref("plugins.update.notifyUser", false);
-
 pref("plugins.click_to_play", true);
 pref("plugins.testmode", false);
 
 pref("plugin.default.state", 1);
 
 // Plugins bundled in XPIs are enabled by default.
 pref("plugin.defaultXpi.state", 2);
 
--- a/browser/base/content/browser-plugins.js
+++ b/browser/base/content/browser-plugins.js
@@ -67,35 +67,39 @@ var gPluginHandler = {
           this.submitReport(msg.data.runID, msg.data.keyVals, msg.data.submitURLOptIn);
         }
         break;
       case "PluginContent:LinkClickCallback":
         switch (msg.data.name) {
           case "managePlugins":
           case "openHelpPage":
           case "openPluginUpdatePage":
-            this[msg.data.name].apply(this);
+            this[msg.data.name].call(this, msg.data.pluginTag);
             break;
         }
         break;
       default:
         Cu.reportError("gPluginHandler did not expect to handle message " + msg.name);
         break;
     }
   },
 
   // Callback for user clicking on a disabled plugin
   managePlugins: function () {
     BrowserOpenAddonsMgr("addons://list/plugin");
   },
 
   // Callback for user clicking on the link in a click-to-play plugin
   // (where the plugin has an update)
-  openPluginUpdatePage: function () {
-    openUILinkIn(Services.urlFormatter.formatURLPref("plugins.update.url"), "tab");
+  openPluginUpdatePage: function(pluginTag) {
+    let url = Services.blocklist.getPluginInfoURL(pluginTag);
+    if (!url) {
+      url = Services.blocklist.getPluginBlocklistURL(pluginTag);
+    }
+    openUILinkIn(url, "tab");
   },
 
   submitReport: function submitReport(runID, keyVals, submitURLOptIn) {
     if (!AppConstants.MOZ_CRASHREPORTER) {
       return;
     }
     Services.prefs.setBoolPref("dom.ipc.plugins.reportCrashURL", submitURLOptIn);
     PluginCrashReporter.submitCrashReport(runID, keyVals);
@@ -239,22 +243,17 @@ var gPluginHandler = {
       if (pluginData.has(pluginInfo.permissionString)) {
         continue;
       }
 
       // If a block contains an infoURL, we should always prefer that to the default
       // URL that we construct in-product, even for other blocklist types.
       let url = Services.blocklist.getPluginInfoURL(pluginInfo.pluginTag);
 
-      if (pluginInfo.blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE) {
-        if (!url) {
-          url = Services.urlFormatter.formatURLPref("plugins.update.url");
-        }
-      }
-      else if (pluginInfo.blocklistState != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) {
+      if (pluginInfo.blocklistState != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) {
         if (!url) {
           url = Services.blocklist.getPluginBlocklistURL(pluginInfo.pluginTag);
         }
       }
       else {
         url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "clicktoplay";
       }
       pluginInfo.detailsLink = url;
--- a/browser/base/content/test/plugins/browser_blocking.js
+++ b/browser/base/content/test/plugins/browser_blocking.js
@@ -91,18 +91,16 @@ add_task(function* () {
       "Test 18a, Plugin overlay should exist, not be hidden");
 
     let updateLink = doc.getAnonymousElementByAttribute(plugin, "anonid", "checkForUpdatesLink");
     Assert.ok(updateLink.style.visibility != "hidden",
       "Test 18a, Plugin should have an update link");
   });
 
   let promise = waitForEvent(gBrowser.tabContainer, "TabOpen", null, true);
-  let pluginUpdateURL = Services.urlFormatter.formatURLPref("plugins.update.url");
-  info(pluginUpdateURL);
 
   yield ContentTask.spawn(gTestBrowser, {}, function* () {
     let doc = content.document;
     let plugin = doc.getElementById("test");
     let updateLink = doc.getAnonymousElementByAttribute(plugin, "anonid", "checkForUpdatesLink");
     let bounds = updateLink.getBoundingClientRect();
     let left = (bounds.left + bounds.right) / 2;
     let top = (bounds.top + bounds.bottom) / 2;
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -166,19 +166,16 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource://gre/modules/WindowsRegistry.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "WindowsUIUtils",
                                    "@mozilla.org/windows-ui-utils;1", "nsIWindowsUIUtils");
 
 XPCOMUtils.defineLazyServiceGetter(this, "AlertsService",
                                    "@mozilla.org/alerts-service;1", "nsIAlertsService");
 
-const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser";
-const PREF_PLUGINS_UPDATEURL  = "plugins.update.url";
-
 // Seconds of idle before trying to create a bookmarks backup.
 const BOOKMARKS_BACKUP_IDLE_TIME_SEC = 8 * 60;
 // Minimum interval between backups.  We try to not create more than one backup
 // per interval.
 const BOOKMARKS_BACKUP_MIN_INTERVAL_DAYS = 1;
 // Maximum interval between backups.  If the last backup is older than these
 // days we will try to create a new one more aggressively.
 const BOOKMARKS_BACKUP_MAX_INTERVAL_DAYS = 3;
@@ -1225,21 +1222,16 @@ BrowserGlue.prototype = {
       this._showUpdateNotification();
 
     // Load the "more info" page for a locked places.sqlite
     // This property is set earlier by places-database-locked topic.
     if (this._isPlacesDatabaseLocked) {
       this._showPlacesLockedNotificationBox();
     }
 
-    // If there are plugins installed that are outdated, and the user hasn't
-    // been warned about them yet, open the plugins update page.
-    if (Services.prefs.getBoolPref(PREF_PLUGINS_NOTIFYUSER))
-      this._showPluginUpdatePage();
-
     // For any add-ons that were installed disabled and can be enabled offer
     // them to the user.
     let win = RecentWindow.getMostRecentBrowserWindow();
     AddonManager.getAllAddons(addons => {
       for (let addon of addons) {
         // If this add-on has already seen (or seen is undefined for non-XPI
         // add-ons) then skip it.
         if (addon.seen !== false) {
@@ -1633,27 +1625,16 @@ BrowserGlue.prototype = {
       AlertsService.showAlertNotification(null, title, text,
                                           true, url, clickCallback);
     }
     catch (e) {
       Cu.reportError(e);
     }
   },
 
-  _showPluginUpdatePage: function BG__showPluginUpdatePage() {
-    Services.prefs.setBoolPref(PREF_PLUGINS_NOTIFYUSER, false);
-
-    var formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].
-                    getService(Ci.nsIURLFormatter);
-    var updateUrl = formatter.formatURLPref(PREF_PLUGINS_UPDATEURL);
-
-    var win = RecentWindow.getMostRecentBrowserWindow();
-    win.openUILinkIn(updateUrl, "tab");
-  },
-
   /**
    * Initialize Places
    * - imports the bookmarks html file if bookmarks database is empty, try to
    *   restore bookmarks from a JSON backup if the backend indicates that the
    *   database was corrupt.
    *
    * These prefs can be set up by the frontend:
    *
--- a/browser/modules/PluginContent.jsm
+++ b/browser/modules/PluginContent.jsm
@@ -390,17 +390,19 @@ PluginContent.prototype = {
 
       case "PluginBlocklisted":
       case "PluginOutdated":
         shouldShowNotification = true;
         break;
 
       case "PluginVulnerableUpdatable":
         let updateLink = this.getPluginUI(plugin, "checkForUpdatesLink");
-        this.addLinkClickCallback(updateLink, "forwardCallback", "openPluginUpdatePage");
+        let { pluginTag } = this._getPluginInfo(plugin);
+        this.addLinkClickCallback(updateLink, "forwardCallback",
+                                  "openPluginUpdatePage", pluginTag);
         /* FALLTHRU */
 
       case "PluginVulnerableNoUpdate":
       case "PluginClickToPlay":
         this._handleClickToPlayEvent(plugin);
         let overlay = this.getPluginUI(plugin, "main");
         let pluginName = this._getPluginInfo(plugin).pluginName;
         let messageString = gNavigatorBundle.formatStringFromName("PluginClickToActivate", [pluginName], 1);
@@ -539,18 +541,19 @@ PluginContent.prototype = {
   hideClickToPlayOverlay: function (plugin) {
     let overlay = this.getPluginUI(plugin, "main");
     if (overlay) {
       overlay.classList.remove("visible");
     }
   },
 
   // Forward a link click callback to the chrome process.
-  forwardCallback: function (name) {
-    this.global.sendAsyncMessage("PluginContent:LinkClickCallback", { name: name });
+  forwardCallback: function (name, pluginTag) {
+    this.global.sendAsyncMessage("PluginContent:LinkClickCallback",
+      { name, pluginTag });
   },
 
   submitReport: function submitReport(plugin) {
     if (!AppConstants.MOZ_CRASHREPORTER) {
       return;
     }
     if (!plugin) {
       Cu.reportError("Attempted to submit crash report without an associated plugin.");
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -899,19 +899,16 @@ public:
   static void PreTransactionCallback(void* aData)
   {
     CanvasRenderingContext2DUserData* self =
       static_cast<CanvasRenderingContext2DUserData*>(aData);
     CanvasRenderingContext2D* context = self->mContext;
     if (!context || !context->mTarget)
       return;
 
-    // Since SkiaGL default to store drawing command until flush
-    // We will have to flush it before present.
-    context->mTarget->Flush();
     context->ReturnTarget();
   }
 
   static void DidTransactionCallback(void* aData)
   {
     CanvasRenderingContext2DUserData* self =
       static_cast<CanvasRenderingContext2DUserData*>(aData);
     if (self->mContext) {
@@ -1616,16 +1613,17 @@ CanvasRenderingContext2D::EnsureTarget(c
             gfxCriticalNote << "Failed to create a SkiaGL DrawTarget, falling back to software\n";
             mode = RenderingMode::SoftwareBackendMode;
           }
         }
 #endif
       }
 
       if (!mBufferProvider) {
+        mTarget = nullptr;
         mBufferProvider = layerManager->CreatePersistentBufferProvider(size, format);
       }
     }
 
     if (mBufferProvider) {
       mTarget = mBufferProvider->BorrowDrawTarget(IntRect(IntPoint(), IntSize(mWidth, mHeight)));
     } else if (!mTarget) {
       mTarget = gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(size, format);
@@ -1662,16 +1660,17 @@ CanvasRenderingContext2D::EnsureTarget(c
       mCanvasElement->InvalidateCanvas();
     }
     // Calling Redraw() tells our invalidation machinery that the entire
     // canvas is already invalid, which can speed up future drawing.
     Redraw();
   } else {
     EnsureErrorTarget();
     mTarget = sErrorTarget;
+    mBufferProvider = nullptr;
   }
 
   // Drop a note in the debug builds if we ever use accelerated Skia canvas.
   if (mIsSkiaGL && mTarget && mTarget->GetType() == DrawTargetType::HARDWARE_RASTER) {
     gfxWarningOnce() << "Using SkiaGL canvas.";
   }
   return mode;
 }
@@ -1768,19 +1767,21 @@ CanvasRenderingContext2D::InitializeWith
                                                    gfx::DrawTarget* aTarget)
 {
   RemovePostRefreshObserver();
   mDocShell = aShell;
   AddPostRefreshObserverIfNecessary();
 
   IntSize size = aTarget->GetSize();
   SetDimensions(size.width, size.height);
-  mTarget = aTarget;
-
-  if (!mTarget) {
+
+  if (aTarget) {
+    mTarget = aTarget;
+    mBufferProvider = new PersistentBufferProviderBasic(aTarget);
+  } else {
     EnsureErrorTarget();
     mTarget = sErrorTarget;
   }
 
   if (mTarget->GetBackendType() == gfx::BackendType::CAIRO) {
     // Cf comment in EnsureTarget
     mTarget->PushClipRect(gfx::Rect(Point(0, 0), Size(mWidth, mHeight)));
   }
@@ -5787,29 +5788,26 @@ CanvasRenderingContext2D::GetBufferProvi
   if (AlreadyShutDown()) {
     return nullptr;
   }
 
   if (mBufferProvider) {
     return mBufferProvider;
   }
 
-  if (!mTarget) {
-    return nullptr;
+  if (mTarget) {
+    mBufferProvider = new PersistentBufferProviderBasic(mTarget);
+    return mBufferProvider;
   }
 
   if (aManager) {
     mBufferProvider = aManager->CreatePersistentBufferProvider(gfx::IntSize(mWidth, mHeight),
                                                                GetSurfaceFormat());
   }
 
-  if (!mBufferProvider) {
-    mBufferProvider = new PersistentBufferProviderBasic(mTarget);
-  }
-
   return mBufferProvider;
 }
 
 already_AddRefed<Layer>
 CanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
                                          Layer *aOldLayer,
                                          LayerManager *aManager,
                                          bool aMirror /* = false */)
--- a/dom/canvas/WebGL2Context.cpp
+++ b/dom/canvas/WebGL2Context.cpp
@@ -157,12 +157,32 @@ WebGLContext::InitWebGL2(FailureReason* 
 
     if (!gl->IsGLES()) {
         // Desktop OpenGL requires the following to be enabled in order to
         // support sRGB operations on framebuffers.
         gl->MakeCurrent();
         gl->fEnable(LOCAL_GL_FRAMEBUFFER_SRGB_EXT);
     }
 
+    //////
+
+    static const GLenum kWebGL2_CompressedFormats[] = {
+        LOCAL_GL_COMPRESSED_R11_EAC,
+        LOCAL_GL_COMPRESSED_SIGNED_R11_EAC,
+        LOCAL_GL_COMPRESSED_RG11_EAC,
+        LOCAL_GL_COMPRESSED_SIGNED_RG11_EAC,
+        LOCAL_GL_COMPRESSED_RGB8_ETC2,
+        LOCAL_GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2,
+        LOCAL_GL_COMPRESSED_RGBA8_ETC2_EAC,
+        LOCAL_GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC,
+        LOCAL_GL_COMPRESSED_SRGB8_ETC2,
+        LOCAL_GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2
+    };
+
+    mCompressedTextureFormats.AppendElements(kWebGL2_CompressedFormats,
+                                             MOZ_ARRAY_LENGTH(kWebGL2_CompressedFormats));
+
+    //////
+
     return true;
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGLActiveInfo.cpp
+++ b/dom/canvas/WebGLActiveInfo.cpp
@@ -4,17 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLActiveInfo.h"
 
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 
 namespace mozilla {
 
-static uint8_t
+uint8_t
 ElemSizeFromType(GLenum elemType)
 {
     switch (elemType) {
     case LOCAL_GL_BOOL:
     case LOCAL_GL_FLOAT:
     case LOCAL_GL_INT:
     case LOCAL_GL_UNSIGNED_INT:
     case LOCAL_GL_SAMPLER_2D:
--- a/dom/canvas/WebGLActiveInfo.h
+++ b/dom/canvas/WebGLActiveInfo.h
@@ -83,11 +83,15 @@ private:
         , mElemSize(0)
         , mBaseMappedName("")
     { }
 
     // Private destructor, to discourage deletion outside of Release():
     ~WebGLActiveInfo() { }
 };
 
+//////////
+
+uint8_t ElemSizeFromType(GLenum elemType);
+
 } // namespace mozilla
 
 #endif // WEBGL_ACTIVE_INFO_H_
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -2100,29 +2100,29 @@ ZeroTexImageWithClear(WebGLContext* webg
     ScopedFramebuffer scopedFB(gl);
     ScopedBindFramebuffer scopedBindFB(gl, scopedFB.FB());
 
     const auto format = usage->format;
 
     GLenum attachPoint = 0;
     GLbitfield clearBits = 0;
 
-    if (format->isColorFormat) {
+    if (format->IsColorFormat()) {
         attachPoint = LOCAL_GL_COLOR_ATTACHMENT0;
         clearBits = LOCAL_GL_COLOR_BUFFER_BIT;
     }
 
-    if (format->hasDepth) {
+    if (format->d) {
         attachPoint = LOCAL_GL_DEPTH_ATTACHMENT;
         clearBits |= LOCAL_GL_DEPTH_BUFFER_BIT;
     }
 
-    if (format->hasStencil) {
-        attachPoint = (format->hasDepth ? LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
-                                        : LOCAL_GL_STENCIL_ATTACHMENT);
+    if (format->s) {
+        attachPoint = (format->d ? LOCAL_GL_DEPTH_STENCIL_ATTACHMENT
+                                 : LOCAL_GL_STENCIL_ATTACHMENT);
         clearBits |= LOCAL_GL_STENCIL_BUFFER_BIT;
     }
 
     MOZ_RELEASE_ASSERT(attachPoint && clearBits, "GFX: No bits cleared.");
 
     {
         gl::GLContext::LocalErrorScope errorScope(*gl);
         gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, attachPoint, target.get(), tex,
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -1040,16 +1040,17 @@ public:
 
 private:
     // Cache the max number of vertices and instances that can be read from
     // bound VBOs (result of ValidateBuffers).
     bool mBufferFetchingIsVerified;
     bool mBufferFetchingHasPerVertex;
     uint32_t mMaxFetchedVertices;
     uint32_t mMaxFetchedInstances;
+    bool mBufferFetch_IsAttrib0Active;
 
     bool DrawArrays_check(GLint first, GLsizei count, GLsizei primcount,
                           const char* info);
     bool DrawElements_check(GLsizei count, GLenum type, WebGLintptr byteOffset,
                             GLsizei primcount, const char* info,
                             GLuint* out_upperBound);
     bool DrawInstanced_check(const char* info);
     void Draw_cleanup(const char* funcName);
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -578,57 +578,75 @@ WebGLContext::ValidateBufferFetching(con
 #endif
 
     if (mBufferFetchingIsVerified)
         return true;
 
     bool hasPerVertex = false;
     uint32_t maxVertices = UINT32_MAX;
     uint32_t maxInstances = UINT32_MAX;
-    uint32_t attribs = mBoundVertexArray->mAttribs.Length();
+    const uint32_t attribCount = mBoundVertexArray->mAttribs.Length();
 
-    for (uint32_t i = 0; i < attribs; ++i) {
-        const WebGLVertexAttribData& vd = mBoundVertexArray->mAttribs[i];
-
+    uint32_t i = 0;
+    for (const auto& vd : mBoundVertexArray->mAttribs) {
         // If the attrib array isn't enabled, there's nothing to check;
         // it's a static value.
         if (!vd.enabled)
             continue;
 
         if (vd.buf == nullptr) {
-            ErrorInvalidOperation("%s: no VBO bound to enabled vertex attrib index %d!", info, i);
+            ErrorInvalidOperation("%s: no VBO bound to enabled vertex attrib index %du!",
+                                  info, i);
             return false;
         }
 
-        // If the attrib is not in use, then we don't have to validate
-        // it, just need to make sure that the binding is non-null.
-        if (!mActiveProgramLinkInfo->HasActiveAttrib(i))
+        ++i;
+    }
+
+    mBufferFetch_IsAttrib0Active = false;
+
+    for (const auto& pair : mActiveProgramLinkInfo->activeAttribLocs) {
+        const uint32_t attribLoc = pair.second;
+
+        if (attribLoc >= attribCount)
+            continue;
+
+        if (attribLoc == 0) {
+            mBufferFetch_IsAttrib0Active = true;
+        }
+
+        const auto& vd = mBoundVertexArray->mAttribs[attribLoc];
+        if (!vd.enabled)
             continue;
 
         // the base offset
         CheckedUint32 checked_byteLength = CheckedUint32(vd.buf->ByteLength()) - vd.byteOffset;
         CheckedUint32 checked_sizeOfLastElement = CheckedUint32(vd.componentSize()) * vd.size;
 
         if (!checked_byteLength.isValid() ||
             !checked_sizeOfLastElement.isValid())
         {
-            ErrorInvalidOperation("%s: integer overflow occured while checking vertex attrib %d", info, i);
+            ErrorInvalidOperation("%s: Integer overflow occured while checking vertex"
+                                  " attrib %u.",
+                                  info, attribLoc);
             return false;
         }
 
         if (checked_byteLength.value() < checked_sizeOfLastElement.value()) {
             maxVertices = 0;
             maxInstances = 0;
             break;
         }
 
         CheckedUint32 checked_maxAllowedCount = ((checked_byteLength - checked_sizeOfLastElement) / vd.actualStride()) + 1;
 
         if (!checked_maxAllowedCount.isValid()) {
-            ErrorInvalidOperation("%s: integer overflow occured while checking vertex attrib %d", info, i);
+            ErrorInvalidOperation("%s: Integer overflow occured while checking vertex"
+                                  " attrib %u.",
+                                  info, attribLoc);
             return false;
         }
 
         if (vd.divisor == 0) {
             maxVertices = std::min(maxVertices, checked_maxAllowedCount.value());
             hasPerVertex = true;
         } else {
             CheckedUint32 checked_curMaxInstances = checked_maxAllowedCount * vd.divisor;
@@ -658,29 +676,29 @@ WebGLContext::WhatDoesVertexAttrib0Need(
 {
     MOZ_ASSERT(mCurrentProgram);
     MOZ_ASSERT(mActiveProgramLinkInfo);
 
     // work around Mac OSX crash, see bug 631420
 #ifdef XP_MACOSX
     if (gl->WorkAroundDriverBugs() &&
         mBoundVertexArray->IsAttribArrayEnabled(0) &&
-        !mActiveProgramLinkInfo->HasActiveAttrib(0))
+        !mBufferFetch_IsAttrib0Active)
     {
         return WebGLVertexAttrib0Status::EmulatedUninitializedArray;
     }
 #endif
 
     if (MOZ_LIKELY(gl->IsGLES() ||
                    mBoundVertexArray->IsAttribArrayEnabled(0)))
     {
         return WebGLVertexAttrib0Status::Default;
     }
 
-    return mActiveProgramLinkInfo->HasActiveAttrib(0)
+    return mBufferFetch_IsAttrib0Active
            ? WebGLVertexAttrib0Status::EmulatedInitializedArray
            : WebGLVertexAttrib0Status::EmulatedUninitializedArray;
 }
 
 bool
 WebGLContext::DoFakeVertexAttrib0(GLuint vertexCount)
 {
     WebGLVertexAttrib0Status whatDoesAttrib0Need = WhatDoesVertexAttrib0Need();
--- a/dom/canvas/WebGLFormats.cpp
+++ b/dom/canvas/WebGLFormats.cpp
@@ -124,67 +124,77 @@ InitCompressedFormatInfo()
 //////////////////////////////////////////////////////////////////////////////////////////
 
 static void
 AddFormatInfo(EffectiveFormat format, const char* name, GLenum sizedFormat,
               uint8_t bytesPerPixel, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
               uint8_t d, uint8_t s, UnsizedFormat unsizedFormat, bool isSRGB,
               ComponentType componentType)
 {
+    switch (unsizedFormat) {
+    case UnsizedFormat::R:
+        MOZ_ASSERT(r && !g && !b && !a && !d && !s);
+        break;
+
+    case UnsizedFormat::RG:
+        MOZ_ASSERT(r && g && !b && !a && !d && !s);
+        break;
+
+    case UnsizedFormat::RGB:
+        MOZ_ASSERT(r && g && b && !a && !d && !s);
+        break;
+
+    case UnsizedFormat::RGBA:
+        MOZ_ASSERT(r && g && b && a && !d && !s);
+        break;
+
+    case UnsizedFormat::L:
+        MOZ_ASSERT(r && !g && !b && !a && !d && !s);
+        break;
+
+    case UnsizedFormat::A:
+        MOZ_ASSERT(!r && !g && !b && a && !d && !s);
+        break;
+
+    case UnsizedFormat::LA:
+        MOZ_ASSERT(r && !g && !b && a && !d && !s);
+        break;
+
+    case UnsizedFormat::D:
+        MOZ_ASSERT(!r && !g && !b && !a && d && !s);
+        break;
+
+    case UnsizedFormat::S:
+        MOZ_ASSERT(!r && !g && !b && !a && !d && s);
+        break;
+
+    case UnsizedFormat::DS:
+        MOZ_ASSERT(!r && !g && !b && !a && d && s);
+        break;
+    }
+
+    const CompressedFormatInfo* compressedFormatInfo = GetCompressedFormatInfo(format);
+    MOZ_ASSERT(!bytesPerPixel == bool(compressedFormatInfo));
+
 #ifdef DEBUG
     uint8_t totalBits = r + g + b + a + d + s;
     if (format == EffectiveFormat::RGB9_E5) {
         totalBits = 9 + 9 + 9 + 5;
     }
-    MOZ_ASSERT(totalBits == bytesPerPixel*8);
+
+    if (compressedFormatInfo) {
+        MOZ_ASSERT(totalBits);
+        MOZ_ASSERT(!bytesPerPixel);
+    } else {
+        MOZ_ASSERT(totalBits == bytesPerPixel*8);
+    }
 #endif
 
-    bool isColorFormat = false;
-    bool hasAlpha = false;
-    bool hasDepth = false;
-    bool hasStencil = false;
-
-    switch (unsizedFormat) {
-    case UnsizedFormat::L:
-    case UnsizedFormat::R:
-    case UnsizedFormat::RG:
-    case UnsizedFormat::RGB:
-        isColorFormat = true;
-        break;
-
-    case UnsizedFormat::A: // Alpha is a 'color format' since it's 'color-attachable'.
-    case UnsizedFormat::LA:
-    case UnsizedFormat::RGBA:
-        isColorFormat = true;
-        hasAlpha = true;
-        break;
-
-    case UnsizedFormat::D:
-        hasDepth = true;
-        break;
-
-    case UnsizedFormat::S:
-        hasStencil = true;
-        break;
-
-    case UnsizedFormat::DS:
-        hasDepth = true;
-        hasStencil = true;
-        break;
-
-    default:
-        MOZ_CRASH("GFX: Missing UnsizedFormat case.");
-    }
-
-    const CompressedFormatInfo* compressedFormatInfo = GetCompressedFormatInfo(format);
-    MOZ_ASSERT(!bytesPerPixel == bool(compressedFormatInfo));
-
     const FormatInfo info = { format, name, sizedFormat, unsizedFormat, componentType,
-                              bytesPerPixel, r,g,b,a, isColorFormat, isSRGB, hasAlpha,
-                              hasDepth, hasStencil, compressedFormatInfo };
+                              isSRGB, compressedFormatInfo, bytesPerPixel, r,g,b,a,d,s };
     AlwaysInsert(gFormatInfoMap, format, info);
 }
 
 static void
 InitFormatInfo()
 {
 #define FOO(x) EffectiveFormat::x, #x, LOCAL_GL_ ## x
 
@@ -253,46 +263,46 @@ InitFormatInfo()
     AddFormatInfo(FOO(DEPTH24_STENCIL8  ), 4, 0,0,0,0, 24,8, UnsizedFormat::DS, false, ComponentType::Special);
     AddFormatInfo(FOO(DEPTH32F_STENCIL8 ), 5, 0,0,0,0, 32,8, UnsizedFormat::DS, false, ComponentType::Special);
 
     // GLES 3.0.4, p205-206, "Required Renderbuffer Formats"
     AddFormatInfo(FOO(STENCIL_INDEX8), 1, 0,0,0,0, 0,8, UnsizedFormat::S, false, ComponentType::UInt);
 
     // GLES 3.0.4, p147, table 3.19
     // GLES 3.0.4  p286+  $C.1 "ETC Compressed Texture Image Formats"
-    AddFormatInfo(FOO(COMPRESSED_RGB8_ETC2                     ), 0, 0,0,0,0, 0,0, UnsizedFormat::RGB , false, ComponentType::NormUInt);
-    AddFormatInfo(FOO(COMPRESSED_SRGB8_ETC2                    ), 0, 0,0,0,0, 0,0, UnsizedFormat::RGB , true , ComponentType::NormUInt);
-    AddFormatInfo(FOO(COMPRESSED_RGBA8_ETC2_EAC                ), 0, 0,0,0,0, 0,0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
-    AddFormatInfo(FOO(COMPRESSED_SRGB8_ALPHA8_ETC2_EAC         ), 0, 0,0,0,0, 0,0, UnsizedFormat::RGBA, true , ComponentType::NormUInt);
-    AddFormatInfo(FOO(COMPRESSED_R11_EAC                       ), 0, 0,0,0,0, 0,0, UnsizedFormat::R   , false, ComponentType::NormUInt);
-    AddFormatInfo(FOO(COMPRESSED_RG11_EAC                      ), 0, 0,0,0,0, 0,0, UnsizedFormat::RG  , false, ComponentType::NormUInt);
-    AddFormatInfo(FOO(COMPRESSED_SIGNED_R11_EAC                ), 0, 0,0,0,0, 0,0, UnsizedFormat::R   , false, ComponentType::NormInt );
-    AddFormatInfo(FOO(COMPRESSED_SIGNED_RG11_EAC               ), 0, 0,0,0,0, 0,0, UnsizedFormat::RG  , false, ComponentType::NormInt );
-    AddFormatInfo(FOO(COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 ), 0, 0,0,0,0, 0,0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
-    AddFormatInfo(FOO(COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2), 0, 0,0,0,0, 0,0, UnsizedFormat::RGBA, true , ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_RGB8_ETC2                     ), 0, 1,1,1,0, 0,0, UnsizedFormat::RGB , false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_SRGB8_ETC2                    ), 0, 1,1,1,0, 0,0, UnsizedFormat::RGB , true , ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_RGBA8_ETC2_EAC                ), 0, 1,1,1,1, 0,0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_SRGB8_ALPHA8_ETC2_EAC         ), 0, 1,1,1,1, 0,0, UnsizedFormat::RGBA, true , ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_R11_EAC                       ), 0, 1,0,0,0, 0,0, UnsizedFormat::R   , false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_RG11_EAC                      ), 0, 1,1,0,0, 0,0, UnsizedFormat::RG  , false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_SIGNED_R11_EAC                ), 0, 1,0,0,0, 0,0, UnsizedFormat::R   , false, ComponentType::NormInt );
+    AddFormatInfo(FOO(COMPRESSED_SIGNED_RG11_EAC               ), 0, 1,1,0,0, 0,0, UnsizedFormat::RG  , false, ComponentType::NormInt );
+    AddFormatInfo(FOO(COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 ), 0, 1,1,1,1, 0,0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2), 0, 1,1,1,1, 0,0, UnsizedFormat::RGBA, true , ComponentType::NormUInt);
 
     // AMD_compressed_ATC_texture
-    AddFormatInfo(FOO(ATC_RGB_AMD                    ), 0, 0,0,0,0, 0,0, UnsizedFormat::RGB , false, ComponentType::NormUInt);
-    AddFormatInfo(FOO(ATC_RGBA_EXPLICIT_ALPHA_AMD    ), 0, 0,0,0,0, 0,0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
-    AddFormatInfo(FOO(ATC_RGBA_INTERPOLATED_ALPHA_AMD), 0, 0,0,0,0, 0,0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(ATC_RGB_AMD                    ), 0, 1,1,1,0, 0,0, UnsizedFormat::RGB , false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(ATC_RGBA_EXPLICIT_ALPHA_AMD    ), 0, 1,1,1,1, 0,0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(ATC_RGBA_INTERPOLATED_ALPHA_AMD), 0, 1,1,1,1, 0,0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
 
     // EXT_texture_compression_s3tc
-    AddFormatInfo(FOO(COMPRESSED_RGB_S3TC_DXT1_EXT ), 0, 0,0,0,0, 0,0, UnsizedFormat::RGB , false, ComponentType::NormUInt);
-    AddFormatInfo(FOO(COMPRESSED_RGBA_S3TC_DXT1_EXT), 0, 0,0,0,0, 0,0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
-    AddFormatInfo(FOO(COMPRESSED_RGBA_S3TC_DXT3_EXT), 0, 0,0,0,0, 0,0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
-    AddFormatInfo(FOO(COMPRESSED_RGBA_S3TC_DXT5_EXT), 0, 0,0,0,0, 0,0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_RGB_S3TC_DXT1_EXT ), 0, 1,1,1,0, 0,0, UnsizedFormat::RGB , false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_RGBA_S3TC_DXT1_EXT), 0, 1,1,1,1, 0,0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_RGBA_S3TC_DXT3_EXT), 0, 1,1,1,1, 0,0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_RGBA_S3TC_DXT5_EXT), 0, 1,1,1,1, 0,0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
 
     // IMG_texture_compression_pvrtc
-    AddFormatInfo(FOO(COMPRESSED_RGB_PVRTC_4BPPV1 ), 0, 0,0,0,0, 0,0, UnsizedFormat::RGB , false, ComponentType::NormUInt);
-    AddFormatInfo(FOO(COMPRESSED_RGBA_PVRTC_4BPPV1), 0, 0,0,0,0, 0,0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
-    AddFormatInfo(FOO(COMPRESSED_RGB_PVRTC_2BPPV1 ), 0, 0,0,0,0, 0,0, UnsizedFormat::RGB , false, ComponentType::NormUInt);
-    AddFormatInfo(FOO(COMPRESSED_RGBA_PVRTC_2BPPV1), 0, 0,0,0,0, 0,0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_RGB_PVRTC_4BPPV1 ), 0, 1,1,1,0, 0,0, UnsizedFormat::RGB , false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_RGBA_PVRTC_4BPPV1), 0, 1,1,1,1, 0,0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_RGB_PVRTC_2BPPV1 ), 0, 1,1,1,0, 0,0, UnsizedFormat::RGB , false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_RGBA_PVRTC_2BPPV1), 0, 1,1,1,1, 0,0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
 
     // OES_compressed_ETC1_RGB8_texture
-    AddFormatInfo(FOO(ETC1_RGB8_OES), 0, 0,0,0,0, 0,0, UnsizedFormat::RGB, false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(ETC1_RGB8_OES), 0, 1,1,1,0, 0,0, UnsizedFormat::RGB, false, ComponentType::NormUInt);
 
 #undef FOO
 
     // 'Virtual' effective formats have no sizedFormat.
 #define FOO(x) EffectiveFormat::x, #x, 0
 
     // GLES 3.0.4, p128, table 3.12.
     AddFormatInfo(FOO(Luminance8Alpha8), 2, 8,0,0,8, 0,0, UnsizedFormat::LA, false, ComponentType::NormUInt);
@@ -634,17 +644,17 @@ AddUnsizedFormats(FormatUsageAuthority* 
 
 void
 FormatUsageInfo::SetRenderable()
 {
     this->isRenderable = true;
 
 #ifdef DEBUG
     const auto format = this->format;
-    if (format->isColorFormat) {
+    if (format->IsColorFormat()) {
         const auto& map = format->copyDecayFormats;
         const auto itr = map.find(format->unsizedFormat);
         MOZ_ASSERT(itr != map.end(), "Renderable formats must be in copyDecayFormats.");
         MOZ_ASSERT(itr->second == format);
     }
 #endif
 }
 
@@ -918,17 +928,16 @@ FormatUsageAuthority::CreateForWebGL2(gl
     fnAllowES3TexFormat(FOO(DEPTH_COMPONENT16 ), true, false);
     fnAllowES3TexFormat(FOO(DEPTH_COMPONENT24 ), true, false);
     fnAllowES3TexFormat(FOO(DEPTH_COMPONENT32F), true, false);
     fnAllowES3TexFormat(FOO(DEPTH24_STENCIL8  ), true, false);
     fnAllowES3TexFormat(FOO(DEPTH32F_STENCIL8 ), true, false);
 
     // GLES 3.0.4, p147, table 3.19
     // GLES 3.0.4, p286+, $C.1 "ETC Compressed Texture Image Formats"
-#if ALLOW_ES3_FORMATS
     // Note that all compressed texture formats are filterable:
     // GLES 3.0.4 p161:
     // "[A] texture is complete unless any of the following conditions hold true:
     //  [...]
     //  * The effective internal format specified for the texture arrays is a sized
     //    internal color format that is not texture-filterable (see table 3.13) and [the
     //    mag filter requires filtering]."
     // Compressed formats are not sized internal color formats, and indeed they are not
@@ -938,17 +947,17 @@ FormatUsageAuthority::CreateForWebGL2(gl
     fnAllowES3TexFormat(FOO(COMPRESSED_RGBA8_ETC2_EAC                ), false, true);
     fnAllowES3TexFormat(FOO(COMPRESSED_SRGB8_ALPHA8_ETC2_EAC         ), false, true);
     fnAllowES3TexFormat(FOO(COMPRESSED_R11_EAC                       ), false, true);
     fnAllowES3TexFormat(FOO(COMPRESSED_RG11_EAC                      ), false, true);
     fnAllowES3TexFormat(FOO(COMPRESSED_SIGNED_R11_EAC                ), false, true);
     fnAllowES3TexFormat(FOO(COMPRESSED_SIGNED_RG11_EAC               ), false, true);
     fnAllowES3TexFormat(FOO(COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 ), false, true);
     fnAllowES3TexFormat(FOO(COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2), false, true);
-#endif
+
 #undef FOO
 
     // GLES 3.0.4, p206, "Required Renderbuffer Formats":
     // "Implementations are also required to support STENCIL_INDEX8. Requesting this
     //  internal format for a renderbuffer will allocate at least 8 stencil bit planes."
 
     auto usage = ptr->EditUsage(EffectiveFormat::STENCIL_INDEX8);
     usage->SetRenderable();
--- a/dom/canvas/WebGLFormats.h
+++ b/dom/canvas/WebGLFormats.h
@@ -193,32 +193,41 @@ struct CompressedFormatInfo
 
 struct FormatInfo
 {
     const EffectiveFormat effectiveFormat;
     const char* const name;
     const GLenum sizedFormat;
     const UnsizedFormat unsizedFormat;
     const ComponentType componentType;
-    const uint8_t estimatedBytesPerPixel; // 0 iff `!!compression`. Only use this for
+    const bool isSRGB;
+
+    const CompressedFormatInfo* const compression;
+
+    const uint8_t estimatedBytesPerPixel; // 0 iff bool(compression).
+
+    // In bits. Iff bool(compression), active channels are 1.
     const uint8_t r;
     const uint8_t g;
     const uint8_t b;
     const uint8_t a;
-    const bool isColorFormat;             // memory usage estimation. Use
-    const bool isSRGB;                    // BytesPerPixel(packingFormat, packingType) for
-    const bool hasAlpha;                  // calculating pack/unpack byte count.
-    const bool hasDepth;
-    const bool hasStencil;
+    const uint8_t d;
+    const uint8_t s;
 
-    const CompressedFormatInfo* const compression;
+    //////
 
     std::map<UnsizedFormat, const FormatInfo*> copyDecayFormats;
 
     const FormatInfo* GetCopyDecayFormat(UnsizedFormat) const;
+
+    bool IsColorFormat() const {
+         // Alpha is a 'color format' since it's 'color-attachable'.
+        return bool(compression) ||
+               bool(r | g | b | a);
+    }
 };
 
 struct PackingInfo
 {
     GLenum format;
     GLenum type;
 
     bool operator <(const PackingInfo& x) const
--- a/dom/canvas/WebGLFramebuffer.cpp
+++ b/dom/canvas/WebGLFramebuffer.cpp
@@ -78,17 +78,17 @@ WebGLFBAttachPoint::Samples() const
         return mRenderbufferPtr->Samples();
 
     return 0;
 }
 
 bool
 WebGLFBAttachPoint::HasAlpha() const
 {
-    return Format()->format->hasAlpha;
+    return Format()->format->a;
 }
 
 const webgl::FormatUsageInfo*
 WebGLFramebuffer::GetFormatForAttachment(const WebGLFBAttachPoint& attachment) const
 {
     MOZ_ASSERT(attachment.IsDefined());
     MOZ_ASSERT(attachment.Texture() || attachment.Renderbuffer());
 
@@ -97,17 +97,17 @@ WebGLFramebuffer::GetFormatForAttachment
 
 bool
 WebGLFBAttachPoint::IsReadableFloat() const
 {
     auto formatUsage = Format();
     MOZ_ASSERT(formatUsage);
 
     auto format = formatUsage->format;
-    if (!format->isColorFormat)
+    if (!format->IsColorFormat())
         return false;
 
     return format->componentType == webgl::ComponentType::Float;
 }
 
 void
 WebGLFBAttachPoint::Clear()
 {
@@ -289,50 +289,50 @@ WebGLFBAttachPoint::IsComplete(WebGLCont
     }
 
     const auto format = formatUsage->format;
 
     bool hasRequiredBits;
 
     switch (mAttachmentPoint) {
     case LOCAL_GL_DEPTH_ATTACHMENT:
-        hasRequiredBits = format->hasDepth;
+        hasRequiredBits = format->d;
         break;
 
     case LOCAL_GL_STENCIL_ATTACHMENT:
-        hasRequiredBits = format->hasStencil;
+        hasRequiredBits = format->s;
         break;
 
     case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
         MOZ_ASSERT(!webgl->IsWebGL2());
-        hasRequiredBits = (format->hasDepth && format->hasStencil);
+        hasRequiredBits = (format->d && format->s);
         break;
 
     default:
         MOZ_ASSERT(mAttachmentPoint >= LOCAL_GL_COLOR_ATTACHMENT0);
-        hasRequiredBits = format->isColorFormat;
+        hasRequiredBits = format->IsColorFormat();
         break;
     }
 
     if (!hasRequiredBits) {
         AttachmentName(out_info);
         out_info->AppendLiteral("'s format is missing required color/depth/stencil bits");
         return false;
     }
 
     if (!webgl->IsWebGL2()) {
         bool hasSurplusPlanes = false;
 
         switch (mAttachmentPoint) {
         case LOCAL_GL_DEPTH_ATTACHMENT:
-            hasSurplusPlanes = format->hasStencil;
+            hasSurplusPlanes = format->s;
             break;
 
         case LOCAL_GL_STENCIL_ATTACHMENT:
-            hasSurplusPlanes = format->hasDepth;
+            hasSurplusPlanes = format->d;
             break;
         }
 
         if (hasSurplusPlanes) {
             AttachmentName(out_info);
             out_info->AppendLiteral("'s format has depth or stencil bits when it"
                                     " shouldn't");
             return false;
@@ -464,84 +464,136 @@ WebGLFBAttachPoint::GetParameter(const c
         return JS::Int32Value(mTexturePtr ? LOCAL_GL_TEXTURE
                                           : LOCAL_GL_RENDERBUFFER);
 
     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
         return (mTexturePtr ? webgl->WebGLObjectAsJSValue(cx, mTexturePtr.get(),
                                                           *out_error)
                             : webgl->WebGLObjectAsJSValue(cx, mRenderbufferPtr.get(),
                                                           *out_error));
-    ////////////////
 
-    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE:
-    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE:
-    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE:
-    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE:
-    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE:
-    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE:
-        isPNameValid = true;
-        break;
-
-    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE:
-        MOZ_ASSERT(attachment != LOCAL_GL_DEPTH_STENCIL_ATTACHMENT);
-        isPNameValid = true;
-        break;
-
-    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING:
-        if (webgl->IsWebGL2() ||
-            webgl->IsExtensionEnabled(WebGLExtensionID::EXT_sRGB))
-        {
-            const auto format = Format();
-            if (!format)
-                return JS::NullValue();
-            return JS::Int32Value(format->format->isSRGB ? LOCAL_GL_SRGB
-                                                         : LOCAL_GL_LINEAR);
-        }
-        break;
-
-    ////////////////
+        //////
 
     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL:
         if (mTexturePtr)
             return JS::Int32Value(MipLevel());
         break;
 
     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE:
         if (mTexturePtr) {
             GLenum face = 0;
             if (mTexturePtr->Target() == LOCAL_GL_TEXTURE_CUBE_MAP) {
                 face = ImageTarget().get();
             }
             return JS::Int32Value(face);
         }
         break;
 
+        //////
+
     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER:
-        if (mTexturePtr) {
+        if (webgl->IsWebGL2() && mTexturePtr) {
             int32_t layer = 0;
             if (ImageTarget() == LOCAL_GL_TEXTURE_2D_ARRAY ||
                 ImageTarget() == LOCAL_GL_TEXTURE_3D)
             {
                 layer = Layer();
             }
             return JS::Int32Value(layer);
         }
         break;
+
+        //////
+
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE:
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE:
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE:
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE:
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE:
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE:
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE:
+        isPNameValid = webgl->IsWebGL2();
+        break;
+
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING:
+        isPNameValid = (webgl->IsWebGL2() ||
+                        webgl->IsExtensionEnabled(WebGLExtensionID::EXT_sRGB));
+        break;
     }
 
     if (!isPNameValid) {
         webgl->ErrorInvalidEnum("%s: Invalid pname: 0x%04x", funcName, pname);
         return JS::NullValue();
     }
 
-    gl::GLContext* gl = webgl->GL();
-    gl->MakeCurrent();
+    const auto usage = Format();
+    if (!usage)
+        return JS::NullValue();
+
+    const auto format = usage->format;
 
     GLint ret = 0;
-    gl->fGetFramebufferAttachmentParameteriv(target, attachment, pname, &ret);
+    switch (pname) {
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE:
+        ret = format->r;
+        break;
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE:
+        ret = format->g;
+        break;
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE:
+        ret = format->b;
+        break;
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE:
+        ret = format->a;
+        break;
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE:
+        ret = format->d;
+        break;
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE:
+        ret = format->s;
+        break;
+
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING:
+        ret = (format->isSRGB ? LOCAL_GL_SRGB
+                              : LOCAL_GL_LINEAR);
+        break;
+
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE:
+        MOZ_ASSERT(attachment != LOCAL_GL_DEPTH_STENCIL_ATTACHMENT);
+
+        switch (format->componentType) {
+        case webgl::ComponentType::Special:
+            MOZ_ASSERT(false, "Should never happen.");
+            break;
+        case webgl::ComponentType::None:
+            ret = LOCAL_GL_NONE;
+            break;
+        case webgl::ComponentType::Int:
+            ret = LOCAL_GL_INT;
+            break;
+        case webgl::ComponentType::UInt:
+            ret = LOCAL_GL_UNSIGNED_INT;
+            break;
+        case webgl::ComponentType::NormInt:
+            ret = LOCAL_GL_SIGNED_NORMALIZED;
+            break;
+        case webgl::ComponentType::NormUInt:
+            ret = LOCAL_GL_UNSIGNED_NORMALIZED;
+            break;
+        case webgl::ComponentType::Float:
+            ret = LOCAL_GL_FLOAT;
+            break;
+        }
+        break;
+
+    default:
+        MOZ_ASSERT(false, "Missing case.");
+        break;
+    }
+
     return JS::Int32Value(ret);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // WebGLFramebuffer
 
 WebGLFramebuffer::WebGLFramebuffer(WebGLContext* webgl, GLuint fbo)
     : WebGLContextBoundObject(webgl)
--- a/dom/canvas/WebGLProgram.cpp
+++ b/dom/canvas/WebGLProgram.cpp
@@ -64,28 +64,29 @@ ParseName(const nsCString& name, nsCStri
         return false;
 
     *out_baseName = StringHead(name, indexOpenBracket);
     *out_isArray = true;
     *out_arrayIndex = indexNum;
     return true;
 }
 
-static void
+static WebGLActiveInfo*
 AddActiveInfo(WebGLContext* webgl, GLint elemCount, GLenum elemType, bool isArray,
               const nsACString& baseUserName, const nsACString& baseMappedName,
               std::vector<RefPtr<WebGLActiveInfo>>* activeInfoList,
               std::map<nsCString, const WebGLActiveInfo*>* infoLocMap)
 {
     RefPtr<WebGLActiveInfo> info = new WebGLActiveInfo(webgl, elemCount, elemType,
                                                        isArray, baseUserName,
                                                        baseMappedName);
     activeInfoList->push_back(info);
 
     infoLocMap->insert(std::make_pair(info->mBaseUserName, info.get()));
+    return info.get();
 }
 
 static void
 AddActiveBlockInfo(const nsACString& baseUserName,
                    const nsACString& baseMappedName,
                    std::vector<RefPtr<webgl::UniformBlockInfo>>* activeInfoList)
 {
     RefPtr<webgl::UniformBlockInfo> info = new webgl::UniformBlockInfo(baseUserName, baseMappedName);
@@ -163,26 +164,27 @@ QueryProgramInfo(WebGLProgram* prog, gl:
 
 #ifdef DUMP_SHADERVAR_MAPPINGS
         printf_stderr("[attrib %i] %s/%s\n", i, mappedName.BeginReading(),
                       userName.BeginReading());
         printf_stderr("    lengthWithoutNull: %d\n", lengthWithoutNull);
 #endif
 
         const bool isArray = false;
-        AddActiveInfo(prog->mContext, elemCount, elemType, isArray, userName, mappedName,
-                      &info->activeAttribs, &info->attribMap);
+        const auto attrib = AddActiveInfo(prog->mContext, elemCount, elemType, isArray,
+                                          userName, mappedName, &info->activeAttribs,
+                                          &info->attribMap);
 
         // Collect active locations:
         GLint loc = gl->fGetAttribLocation(prog->mGLName, mappedName.BeginReading());
         if (loc == -1) {
             if (mappedName != "gl_InstanceID")
                 MOZ_CRASH("GFX: Active attrib has no location.");
         } else {
-            info->activeAttribLocs.insert(loc);
+            info->activeAttribLocs.insert({attrib, (GLuint)loc});
         }
     }
 
     // Uniforms
 
     const bool needsCheckForArrays = gl->WorkAroundDriverBugs();
 
     GLuint numActiveUniforms = 0;
@@ -958,47 +960,93 @@ WebGLProgram::LinkProgram()
         // This can't be done trivially, because we have to deal with mapped names too.
         mVertShader->ApplyTransformFeedbackVaryings(mGLName,
                                                     mTransformFeedbackVaryings,
                                                     mTransformFeedbackBufferMode,
                                                     &mTempMappedVaryings);
     }
 
     LinkAndUpdate();
-    if (IsLinked()) {
-        // Check if the attrib name conflicting to uniform name
-        for (const auto& uniform : mMostRecentLinkInfo->uniformMap) {
-            if (mMostRecentLinkInfo->attribMap.find(uniform.first) != mMostRecentLinkInfo->attribMap.end()) {
-                mLinkLog = nsPrintfCString("The uniform name (%s) conflicts with attribute name.",
-                                           uniform.first.get());
-                mMostRecentLinkInfo = nullptr;
-                break;
-            }
-        }
+
+    if (mMostRecentLinkInfo) {
+        nsCString postLinkLog;
+        if (ValidateAfterTentativeLink(&postLinkLog))
+            return;
+
+        mMostRecentLinkInfo = nullptr;
+        mLinkLog = postLinkLog;
     }
 
-    if (mMostRecentLinkInfo)
-        return;
-
     // Failed link.
     if (mContext->ShouldGenerateWarnings()) {
         // report shader/program infoLogs as warnings.
         // note that shader compilation errors can be deferred to linkProgram,
         // which is why we can't do anything in compileShader. In practice we could
         // report in compileShader the translation errors generated by ANGLE,
         // but it seems saner to keep a single way of obtaining shader infologs.
         if (!mLinkLog.IsEmpty()) {
             mContext->GenerateWarning("linkProgram: Failed to link, leaving the following"
                                       " log:\n%s\n",
                                       mLinkLog.BeginReading());
         }
     }
 }
 
 bool
+WebGLProgram::ValidateAfterTentativeLink(nsCString* const out_linkLog) const
+{
+    const auto& linkInfo = mMostRecentLinkInfo;
+
+    // Check if the attrib name conflicting to uniform name
+    for (const auto& uniform : linkInfo->uniformMap) {
+        if (linkInfo->attribMap.find(uniform.first) != linkInfo->attribMap.end()) {
+            *out_linkLog = nsPrintfCString("The uniform name (%s) conflicts with"
+                                           " attribute name.",
+                                           uniform.first.get());
+            return false;
+        }
+    }
+
+    std::map<GLuint, const WebGLActiveInfo*> attribsByLoc;
+    for (const auto& pair : linkInfo->activeAttribLocs) {
+        const auto dupe = attribsByLoc.find(pair.second);
+        if (dupe != attribsByLoc.end()) {
+            *out_linkLog = nsPrintfCString("Aliased location between active attribs"
+                                           " \"%s\" and \"%s\".",
+                                           dupe->second->mBaseUserName.BeginReading(),
+                                           pair.first->mBaseUserName.BeginReading());
+            return false;
+        }
+    }
+
+    for (const auto& pair : attribsByLoc) {
+        const GLuint attribLoc = pair.first;
+        const auto attrib = pair.second;
+
+        const auto elemSize = ElemSizeFromType(attrib->mElemType);
+        const GLuint locationsUsed = (elemSize + 3) / 4;
+        for (GLuint i = 1; i < locationsUsed; i++) {
+            const GLuint usedLoc = attribLoc + i;
+
+            const auto dupe = attribsByLoc.find(usedLoc);
+            if (dupe != attribsByLoc.end()) {
+                *out_linkLog = nsPrintfCString("Attrib \"%s\" of type \"0x%04x\" aliases"
+                                               " \"%s\" by overhanging its location.",
+                                               attrib->mBaseUserName.BeginReading(),
+                                               attrib->mElemType,
+                                               dupe->second->mBaseUserName.BeginReading());
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
+bool
 WebGLProgram::UseProgram() const
 {
     if (!mMostRecentLinkInfo) {
         mContext->ErrorInvalidOperation("useProgram: Program has not been successfully"
                                         " linked.");
         return false;
     }
 
@@ -1056,17 +1104,17 @@ WebGLProgram::LinkAndUpdate()
     empty.swap(mTempMappedVaryings);
 
     GLint ok = 0;
     gl->fGetProgramiv(mGLName, LOCAL_GL_LINK_STATUS, &ok);
     if (!ok)
         return;
 
     mMostRecentLinkInfo = QueryProgramInfo(this, gl);
-    MOZ_RELEASE_ASSERT(mMostRecentLinkInfo, "GFX: most rent link info not set.");
+    MOZ_RELEASE_ASSERT(mMostRecentLinkInfo, "GFX: most recent link info not set.");
 }
 
 bool
 WebGLProgram::FindActiveOutputMappedNameByUserName(const nsACString& userName,
                                                    nsCString* const out_mappedName) const
 {
     if (mFragShader->FindActiveOutputMappedNameByUserName(userName, out_mappedName)) {
         return true;
--- a/dom/canvas/WebGLProgram.h
+++ b/dom/canvas/WebGLProgram.h
@@ -66,17 +66,19 @@ struct LinkedProgramInfo final
     // user-facing `GLActiveInfo::name`s, without any final "[0]".
     std::map<nsCString, const WebGLActiveInfo*> attribMap;
     std::map<nsCString, const WebGLActiveInfo*> uniformMap;
     std::map<nsCString, const WebGLActiveInfo*> transformFeedbackVaryingsMap;
 
     std::vector<RefPtr<UniformBlockInfo>> uniformBlocks;
 
     // Needed for draw call validation.
-    std::set<GLuint> activeAttribLocs;
+    std::map<const WebGLActiveInfo*, GLuint> activeAttribLocs;
+
+    //////
 
     explicit LinkedProgramInfo(WebGLProgram* prog);
 
     bool FindAttrib(const nsCString& baseUserName,
                     const WebGLActiveInfo** const out_activeInfo) const
     {
         auto itr = attribMap.find(baseUserName);
         if (itr == attribMap.end())
@@ -105,21 +107,16 @@ struct LinkedProgramInfo final
             if (baseUserName == uniformBlocks[i]->mBaseUserName) {
                 *out_info = uniformBlocks[i].get();
                 return true;
             }
         }
 
         return false;
     }
-
-    bool HasActiveAttrib(GLuint loc) const {
-        auto itr = activeAttribLocs.find(loc);
-        return itr != activeAttribLocs.end();
-    }
 };
 
 } // namespace webgl
 
 class WebGLProgram final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLProgram>
     , public LinkedListElement<WebGLProgram>
@@ -191,16 +188,17 @@ public:
     }
 
     virtual JSObject* WrapObject(JSContext* js, JS::Handle<JSObject*> givenProto) override;
 
 private:
     ~WebGLProgram();
 
     void LinkAndUpdate();
+    bool ValidateAfterTentativeLink(nsCString* const out_linkLog) const;
 
 public:
     const GLuint mGLName;
 
 private:
     WebGLRefPtr<WebGLShader> mVertShader;
     WebGLRefPtr<WebGLShader> mFragShader;
     std::map<nsCString, GLuint> mBoundAttribLocs;
--- a/dom/canvas/WebGLRenderbuffer.cpp
+++ b/dom/canvas/WebGLRenderbuffer.cpp
@@ -253,17 +253,17 @@ WebGLRenderbuffer::GetRenderbufferParame
 {
     gl::GLContext* gl = mContext->gl;
 
     switch (pname.get()) {
     case LOCAL_GL_RENDERBUFFER_STENCIL_SIZE:
         if (!mFormat)
             return 0;
 
-        if (!mFormat->format->hasStencil)
+        if (!mFormat->format->s)
             return 0;
 
         return 8;
 
     case LOCAL_GL_RENDERBUFFER_SAMPLES:
     case LOCAL_GL_RENDERBUFFER_WIDTH:
     case LOCAL_GL_RENDERBUFFER_HEIGHT:
     case LOCAL_GL_RENDERBUFFER_RED_SIZE:
--- a/dom/canvas/WebGLTexture.cpp
+++ b/dom/canvas/WebGLTexture.cpp
@@ -338,17 +338,17 @@ WebGLTexture::IsComplete(uint32_t texUni
         auto format = formatUsage->format;
 
         // "* The effective internal format specified for the texture arrays is a sized
         //    internal color format that is not texture-filterable, and either the
         //    magnification filter is not NEAREST or the minification filter is neither
         //    NEAREST nor NEAREST_MIPMAP_NEAREST."
         // Since all (GLES3) unsized color formats are filterable just like their sized
         // equivalents, we don't have to care whether its sized or not.
-        if (format->isColorFormat && !formatUsage->isFilterable) {
+        if (format->IsColorFormat() && !formatUsage->isFilterable) {
             *out_reason = "Because minification or magnification filtering is not NEAREST"
                           " or NEAREST_MIPMAP_NEAREST, and the texture's format is a"
                           " color format, its format must be \"texture-filterable\".";
             return false;
         }
 
         // "* The effective internal format specified for the texture arrays is a sized
         //    internal depth or depth and stencil format, the value of
@@ -358,17 +358,17 @@ WebGLTexture::IsComplete(uint32_t texUni
         // [1]: This sounds suspect, but is explicitly noted in the change log for GLES
         //      3.0.1:
         //      "* Clarify that a texture is incomplete if it has a depth component, no
         //         shadow comparison, and linear filtering (also Bug 9481)."
         // As of OES_packed_depth_stencil rev #3, the sample code explicitly samples from
         // a DEPTH_STENCIL_OES texture with a min-filter of LINEAR. Therefore we relax
         // this restriction if WEBGL_depth_texture is enabled.
         if (!mContext->IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture)) {
-            if (format->hasDepth && mTexCompareMode != LOCAL_GL_NONE) {
+            if (format->d && mTexCompareMode != LOCAL_GL_NONE) {
                 *out_reason = "A depth or depth-stencil format with TEXTURE_COMPARE_MODE"
                               " of NONE must have minification or magnification filtering"
                               " of NEAREST or NEAREST_MIPMAP_NEAREST.";
                 return false;
             }
         }
     }
 
@@ -481,19 +481,19 @@ WebGLTexture::GetFakeBlackType(const cha
 
     if (!hasUninitializedData) {
         *out_fakeBlack = FakeBlackType::None;
         return true;
     }
 
     if (!hasInitializedData) {
         const auto format = ImageInfoAtFace(0, mBaseMipmapLevel).mFormat->format;
-        if (format->isColorFormat) {
-            *out_fakeBlack = (format->hasAlpha ? FakeBlackType::RGBA0000
-                                               : FakeBlackType::RGBA0001);
+        if (format->IsColorFormat()) {
+            *out_fakeBlack = (format->a ? FakeBlackType::RGBA0000
+                                        : FakeBlackType::RGBA0001);
             return true;
         }
 
         mContext->GenerateWarning("%s: Active texture %u for target 0x%04x is"
                                   " uninitialized, and will be (perhaps slowly) cleared"
                                   " by the implementation.",
                                   funcName, texUnit, mTarget.get());
     } else {
@@ -731,17 +731,17 @@ WebGLTexture::GenerateMipmap(TexTarget t
 
     auto format = baseImageInfo.mFormat->format;
     if (format->compression) {
         mContext->ErrorInvalidOperation("generateMipmap: Texture data at base level is"
                                         " compressed.");
         return;
     }
 
-    if (format->hasDepth) {
+    if (format->d) {
         mContext->ErrorInvalidOperation("generateMipmap: Depth textures are not"
                                         " supported.");
         return;
     }
 
     // OpenGL ES 3.0.4 p160:
     // If the level base array was not specified with an unsized internal format from
     // table 3.3 or a sized internal format that is both color-renderable and
--- a/dom/canvas/WebGLTextureUpload.cpp
+++ b/dom/canvas/WebGLTextureUpload.cpp
@@ -1256,17 +1256,17 @@ WebGLTexture::TexImage(const char* funcN
 
     ////////////////////////////////////
     // Check that source and dest info are compatible
     auto dstFormat = dstUsage->format;
 
     if (!ValidateTargetForFormat(funcName, mContext, target, dstFormat))
         return;
 
-    if (!mContext->IsWebGL2() && dstFormat->hasDepth) {
+    if (!mContext->IsWebGL2() && dstFormat->d) {
         if (target != LOCAL_GL_TEXTURE_2D ||
             blob->mHasData ||
             level != 0)
         {
             mContext->ErrorInvalidOperation("%s: With format %s, this function may only"
                                             " be called with target=TEXTURE_2D,"
                                             " data=null, and level=0.",
                                             funcName, dstFormat->name);
@@ -1340,17 +1340,17 @@ WebGLTexture::TexSubImage(const char* fu
     auto dstFormat = dstUsage->format;
 
     if (dstFormat->compression) {
         mContext->ErrorInvalidEnum("%s: Specified TexImage must not be compressed.",
                                    funcName);
         return;
     }
 
-    if (!mContext->IsWebGL2() && dstFormat->hasDepth) {
+    if (!mContext->IsWebGL2() && dstFormat->d) {
         mContext->ErrorInvalidOperation("%s: Function may not be called on a texture of"
                                         " format %s.",
                                         funcName, dstFormat->name);
         return;
     }
 
     ////////////////////////////////////
     // Get source info
@@ -1990,17 +1990,17 @@ WebGLTexture::CopyTexImage2D(TexImageTar
     if (!dstUsage)
         return;
 
     const auto dstFormat = dstUsage->format;
 
     if (!ValidateTargetForFormat(funcName, mContext, target, dstFormat))
         return;
 
-    if (!mContext->IsWebGL2() && dstFormat->hasDepth) {
+    if (!mContext->IsWebGL2() && dstFormat->d) {
         mContext->ErrorInvalidOperation("%s: Function may not be called with format %s.",
                                         funcName, dstFormat->name);
         return;
     }
 
     if (!ValidateCopyTexImageFormats(mContext, funcName, srcFormat, dstFormat))
         return;
 
@@ -2090,17 +2090,17 @@ WebGLTexture::CopyTexSubImage(const char
         return;
     }
     MOZ_ASSERT(imageInfo);
 
     auto dstUsage = imageInfo->mFormat;
     MOZ_ASSERT(dstUsage);
     auto dstFormat = dstUsage->format;
 
-    if (!mContext->IsWebGL2() && dstFormat->hasDepth) {
+    if (!mContext->IsWebGL2() && dstFormat->d) {
         mContext->ErrorInvalidOperation("%s: Function may not be called on a texture of"
                                         " format %s.",
                                         funcName, dstFormat->name);
         return;
     }
 
     ////////////////////////////////////
     // Get source info
--- a/dom/fetch/InternalRequest.cpp
+++ b/dom/fetch/InternalRequest.cpp
@@ -33,17 +33,17 @@ InternalRequest::GetRequestConstructorCo
 
   copy->mBodyStream = mBodyStream;
   copy->mForceOriginHeader = true;
   // The "client" is not stored in our implementation. Fetch API users should
   // use the appropriate window/document/principal and other Gecko security
   // mechanisms as appropriate.
   copy->mSameOriginDataURL = true;
   copy->mPreserveContentCodings = true;
-  // The default referrer is already about:client.
+  copy->mReferrer = mReferrer;
   copy->mReferrerPolicy = mReferrerPolicy;
   copy->mEnvironmentReferrerPolicy = mEnvironmentReferrerPolicy;
 
   copy->mContentPolicyType = mContentPolicyTypeOverridden ?
                              mContentPolicyType :
                              nsIContentPolicy::TYPE_FETCH;
   copy->mMode = mMode;
   copy->mCredentialsMode = mCredentialsMode;
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -2138,18 +2138,16 @@ nsresult nsPluginHost::ScanPluginsDirect
     }
   }
 
   pluginFiles.Sort(CompareFilesByTime());
 
   nsCOMArray<nsIFile> extensionDirs;
   GetExtensionDirectories(extensionDirs);
 
-  bool warnOutdated = false;
-
   for (int32_t i = (pluginFiles.Length() - 1); i >= 0; i--) {
     nsCOMPtr<nsIFile>& localfile = pluginFiles[i];
 
     nsString utf16FilePath;
     rv = localfile->GetPath(utf16FilePath);
     if (NS_FAILED(rv))
       continue;
 
@@ -2239,24 +2237,19 @@ nsresult nsPluginHost::ScanPluginsDirect
       pluginFile.FreePluginInfo(info);
       pluginTag->mLibrary = library;
       uint32_t state;
       rv = pluginTag->GetBlocklistState(&state);
       NS_ENSURE_SUCCESS(rv, rv);
 
       // If the blocklist says it is risky and we have never seen this
       // plugin before, then disable it.
-      // If the blocklist says this is an outdated plugin, warn about
-      // outdated plugins.
       if (state == nsIBlocklistService::STATE_SOFTBLOCKED && !seenBefore) {
         pluginTag->SetEnabledState(nsIPluginTag::STATE_DISABLED);
       }
-      if (state == nsIBlocklistService::STATE_OUTDATED && !seenBefore) {
-        warnOutdated = true;
-      }
 
       // Plugin unloading is tag-based. If we created a new tag and loaded
       // the library in the process then we want to attempt to unload it here.
       // Only do this if the pref is set for aggressive unloading.
       if (UnloadPluginsASAP()) {
         pluginTag->TryUnloadPlugin(false);
       }
     }
@@ -2286,20 +2279,16 @@ nsresult nsPluginHost::ScanPluginsDirect
     // then we're done.
     if (!aCreatePluginList) {
       return NS_OK;
     }
 
     AddPluginTag(pluginTag);
   }
 
-  if (warnOutdated) {
-    Preferences::SetBool("plugins.update.notifyUser", true);
-  }
-
   return NS_OK;
 }
 
 nsresult nsPluginHost::ScanPluginsDirectoryList(nsISimpleEnumerator *dirEnum,
                                                 bool aCreatePluginList,
                                                 bool *aPluginsChanged)
 {
   MOZ_ASSERT(XRE_IsParentProcess());
--- a/gfx/layers/PersistentBufferProvider.cpp
+++ b/gfx/layers/PersistentBufferProvider.cpp
@@ -35,16 +35,21 @@ PersistentBufferProviderBasic::BorrowDra
   return dt.forget();
 }
 
 bool
 PersistentBufferProviderBasic::ReturnDrawTarget(already_AddRefed<gfx::DrawTarget> aDT)
 {
   RefPtr<gfx::DrawTarget> dt(aDT);
   MOZ_ASSERT(mDrawTarget == dt);
+  if (dt) {
+    // Since SkiaGL default to storing drawing command until flush
+    // we have to flush it before present.
+    dt->Flush();
+  }
   return true;
 }
 
 already_AddRefed<gfx::SourceSurface>
 PersistentBufferProviderBasic::BorrowSnapshot()
 {
   mSnapshot = mDrawTarget->Snapshot();
   RefPtr<SourceSurface> snapshot = mSnapshot;
--- a/gfx/layers/apz/test/gtest/TestBasic.cpp
+++ b/gfx/layers/apz/test/gtest/TestBasic.cpp
@@ -294,17 +294,21 @@ TEST_F(APZCBasicTester, OverScroll_Bug11
   // the overscroll animation once, to get it to initialize the first overscroll
   // sample.
   SampleAnimationOnce();
 
   // Do a touch-down to cancel the overscroll animation, and then a touch-up
   // to schedule a new one since we're still overscrolled. We don't pan because
   // panning can trigger functions that clear the overscroll animation state
   // in other ways.
-  TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time(), nullptr);
+  uint64_t blockId;
+  nsEventStatus status = TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time(), &blockId);
+  if (gfxPrefs::TouchActionEnabled() && status != nsEventStatus_eConsumeNoDefault) {
+    SetDefaultAllowedTouchBehavior(apzc, blockId);
+  }
   TouchUp(apzc, ScreenIntPoint(10, 10), mcc->Time());
 
   // Sample the second overscroll animation to its end.
   // If the ending of the first overscroll animation fails to clear state
   // properly, this will assert.
   ParentLayerPoint expectedScrollOffset(0, GetScrollRange().YMost());
   SampleAnimationUntilRecoveredFromOverscroll(expectedScrollOffset);
 }
--- a/gfx/layers/apz/test/gtest/TestGestureDetector.cpp
+++ b/gfx/layers/apz/test/gtest/TestGestureDetector.cpp
@@ -599,17 +599,20 @@ TEST_F(APZCGestureDetectorTester, TapFol
 TEST_F(APZCGestureDetectorTester, LongPressInterruptedByWheel) {
   // Since the wheel block interrupted the long-press, we don't expect
   // any long-press notifications. However, this also shouldn't crash, which
   // is what it used to do.
   EXPECT_CALL(*mcc, HandleTap(TapType::eLongTap, _, _, _, _)).Times(0);
 
   uint64_t touchBlockId = 0;
   uint64_t wheelBlockId = 0;
-  TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time(), &touchBlockId);
+  nsEventStatus status = TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time(), &touchBlockId);
+  if (gfxPrefs::TouchActionEnabled() && status != nsEventStatus_eConsumeNoDefault) {
+    SetDefaultAllowedTouchBehavior(apzc, touchBlockId);
+  }
   mcc->AdvanceByMillis(10);
   Wheel(apzc, ScreenIntPoint(10, 10), ScreenPoint(0, -10), mcc->Time(), &wheelBlockId);
   EXPECT_NE(touchBlockId, wheelBlockId);
   mcc->AdvanceByMillis(1000);
 }
 
 TEST_F(APZCGestureDetectorTester, TapTimeoutInterruptedByWheel) {
   // In this test, even though the wheel block comes right after the tap, the
--- a/gfx/layers/apz/util/TouchActionHelper.cpp
+++ b/gfx/layers/apz/util/TouchActionHelper.cpp
@@ -55,16 +55,19 @@ TouchActionHelper::GetAllowedTouchBehavi
   }
 
   nsIFrame *viewFrame = view->GetFrame();
 
   nsPoint relativePoint =
     nsLayoutUtils::GetEventCoordinatesRelativeTo(aWidget, aPoint, viewFrame);
 
   nsIFrame *target = nsLayoutUtils::GetFrameForPoint(viewFrame, relativePoint, nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME);
+  if (!target) {
+    return behavior;
+  }
   nsIScrollableFrame *nearestScrollableParent = nsLayoutUtils::GetNearestScrollableFrame(target, 0);
   nsIFrame* nearestScrollableFrame = do_QueryFrame(nearestScrollableParent);
 
   // We're walking up the DOM tree until we meet the element with touch behavior and accumulating
   // touch-action restrictions of all elements in this chain.
   // The exact quote from the spec, that clarifies more:
   // To determine the effect of a touch, find the nearest ancestor (starting from the element itself)
   // that has a default touch behavior. Then examine the touch-action property of each element between
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -848,17 +848,25 @@ ClientLayerManager::DependsOnStaleDevice
   return gfxPlatform::GetPlatform()->GetDeviceCounter() != mDeviceCounter;
 }
 
 
 already_AddRefed<PersistentBufferProvider>
 ClientLayerManager::CreatePersistentBufferProvider(const gfx::IntSize& aSize,
                                                    gfx::SurfaceFormat aFormat)
 {
-  return PersistentBufferProviderShared::Create(aSize, aFormat, AsShadowForwarder());
+  if (gfxPrefs::PersistentBufferProviderSharedEnabled()) {
+    RefPtr<PersistentBufferProvider> provider
+      = PersistentBufferProviderShared::Create(aSize, aFormat, AsShadowForwarder());
+    if (provider) {
+      return provider.forget();
+    }
+  }
+
+  return LayerManager::CreatePersistentBufferProvider(aSize, aFormat);
 }
 
 
 ClientLayer::~ClientLayer()
 {
   if (HasShadow()) {
     PLayerChild::Send__delete__(GetShadow());
   }
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -423,16 +423,17 @@ private:
   DECL_GFX_PREF(Live, "layers.acceleration.draw-fps.write-to-file", WriteFPSToFile, bool, false);
   DECL_GFX_PREF(Once, "layers.acceleration.force-enabled",     LayersAccelerationForceEnabledDoNotUseDirectly, bool, false);
   DECL_GFX_PREF(Once, "layers.allow-d3d9-fallback",            LayersAllowD3D9Fallback, bool, false);
   DECL_GFX_PREF(Once, "layers.amd-switchable-gfx.enabled",     LayersAMDSwitchableGfxEnabled, bool, false);
   DECL_GFX_PREF(Once, "layers.async-pan-zoom.enabled",         AsyncPanZoomEnabledDoNotUseDirectly, bool, true);
   DECL_GFX_PREF(Once, "layers.async-pan-zoom.separate-event-thread", AsyncPanZoomSeparateEventThread, bool, false);
   DECL_GFX_PREF(Live, "layers.bench.enabled",                  LayersBenchEnabled, bool, false);
   DECL_GFX_PREF(Once, "layers.bufferrotation.enabled",         BufferRotationEnabled, bool, true);
+  DECL_GFX_PREF(Live, "layers.shared-buffer-provider.enabled", PersistentBufferProviderSharedEnabled, bool, false);
 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
   // If MOZ_GFX_OPTIMIZE_MOBILE is defined, we force component alpha off
   // and ignore the preference.
   DECL_GFX_PREF(Skip, "layers.componentalpha.enabled",         ComponentAlphaEnabled, bool, false);
 #else
   // If MOZ_GFX_OPTIMIZE_MOBILE is not defined, we actually take the
   // preference value, defaulting to true.
   DECL_GFX_PREF(Once, "layers.componentalpha.enabled",         ComponentAlphaEnabled, bool, true);
--- a/gfx/thebes/gfxTextRun.cpp
+++ b/gfx/thebes/gfxTextRun.cpp
@@ -2854,21 +2854,16 @@ gfxFontGroup::FindFontForChar(uint32_t a
     // 3. use fallback fonts
     // -- before searching for something else check the font used for the previous character
     if (aPrevMatchedFont && aPrevMatchedFont->HasCharacter(aCh)) {
         *aMatchType = gfxTextRange::kSystemFallback;
         RefPtr<gfxFont> ret = aPrevMatchedFont;
         return ret.forget();
     }
 
-    // never fall back for characters from unknown scripts
-    if (aRunScript == Script::UNKNOWN) {
-        return nullptr;
-    }
-
     // for known "space" characters, don't do a full system-fallback search;
     // we'll synthesize appropriate-width spaces instead of missing-glyph boxes
     if (GetGeneralCategory(aCh) ==
             HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR &&
         GetFirstValidFont()->SynthesizeSpaceWidth(aCh) >= 0.0)
     {
         return nullptr;
     }
--- a/image/Decoder.cpp
+++ b/image/Decoder.cpp
@@ -17,16 +17,44 @@
 #include "mozilla/Telemetry.h"
 
 using mozilla::gfx::IntSize;
 using mozilla::gfx::SurfaceFormat;
 
 namespace mozilla {
 namespace image {
 
+class MOZ_STACK_CLASS AutoRecordDecoderTelemetry final
+{
+public:
+  AutoRecordDecoderTelemetry(Decoder* aDecoder, uint32_t aByteCount)
+    : mDecoder(aDecoder)
+  {
+    MOZ_ASSERT(mDecoder);
+
+    // Begin recording telemetry data.
+    mStartTime = TimeStamp::Now();
+    mDecoder->mChunkCount++;
+
+    // Keep track of the total number of bytes written.
+    mDecoder->mBytesDecoded += aByteCount;
+
+  }
+
+  ~AutoRecordDecoderTelemetry()
+  {
+    // Finish telemetry.
+    mDecoder->mDecodeTime += (TimeStamp::Now() - mStartTime);
+  }
+
+private:
+  Decoder* mDecoder;
+  TimeStamp mStartTime;
+};
+
 Decoder::Decoder(RasterImage* aImage)
   : mImageData(nullptr)
   , mImageDataLength(0)
   , mColormap(nullptr)
   , mColormapSize(0)
   , mImage(aImage)
   , mProgress(NoProgress)
   , mFrameCount(0)
@@ -112,70 +140,45 @@ Decoder::Decode(NotNull<IResumable*> aOn
       }
 
       CompleteDecode();
       return finalStatus;
     }
 
     MOZ_ASSERT(newState == SourceBufferIterator::READY);
 
-    Write(mIterator->Data(), mIterator->Length());
+    {
+      PROFILER_LABEL("ImageDecoder", "Write",
+        js::ProfileEntry::Category::GRAPHICS);
+
+      AutoRecordDecoderTelemetry telemetry(this, mIterator->Length());
+
+      // Pass the data along to the implementation.
+      Maybe<TerminalState> terminalState = DoDecode(*mIterator);
+
+      if (terminalState == Some(TerminalState::FAILURE)) {
+        PostDataError();
+      }
+    }
   }
 
   CompleteDecode();
   return HasError() ? NS_ERROR_FAILURE : NS_OK;
 }
 
 bool
 Decoder::ShouldSyncDecode(size_t aByteLimit)
 {
   MOZ_ASSERT(aByteLimit > 0);
   MOZ_ASSERT(mIterator, "Should have a SourceBufferIterator");
 
   return mIterator->RemainingBytesIsNoMoreThan(aByteLimit);
 }
 
 void
-Decoder::Write(const char* aBuffer, uint32_t aCount)
-{
-  PROFILER_LABEL("ImageDecoder", "Write",
-    js::ProfileEntry::Category::GRAPHICS);
-
-  MOZ_ASSERT(aBuffer);
-  MOZ_ASSERT(aCount > 0);
-
-  // We're strict about decoder errors
-  MOZ_ASSERT(!HasDecoderError(),
-             "Not allowed to make more decoder calls after error!");
-
-  // Begin recording telemetry data.
-  TimeStamp start = TimeStamp::Now();
-  mChunkCount++;
-
-  // Keep track of the total number of bytes written.
-  mBytesDecoded += aCount;
-
-  // If a data error occured, just ignore future data.
-  if (HasDataError()) {
-    return;
-  }
-
-  if (IsMetadataDecode() && HasSize()) {
-    // More data came in since we found the size. We have nothing to do here.
-    return;
-  }
-
-  // Pass the data along to the implementation.
-  WriteInternal(aBuffer, aCount);
-
-  // Finish telemetry.
-  mDecodeTime += (TimeStamp::Now() - start);
-}
-
-void
 Decoder::CompleteDecode()
 {
   // Implementation-specific finalization
   BeforeFinishInternal();
   if (!HasError()) {
     FinishInternal();
   } else {
     FinishWithErrorInternal();
--- a/image/Decoder.h
+++ b/image/Decoder.h
@@ -3,24 +3,26 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_image_Decoder_h
 #define mozilla_image_Decoder_h
 
 #include "FrameAnimator.h"
 #include "RasterImage.h"
+#include "mozilla/Maybe.h"
 #include "mozilla/NotNull.h"
 #include "mozilla/RefPtr.h"
 #include "DecodePool.h"
 #include "DecoderFlags.h"
 #include "Downscaler.h"
 #include "ImageMetadata.h"
 #include "Orientation.h"
 #include "SourceBuffer.h"
+#include "StreamingLexer.h"
 #include "SurfaceFlags.h"
 
 namespace mozilla {
 
 namespace Telemetry {
   enum ID : uint32_t;
 } // namespace Telemetry
 
@@ -157,20 +159,20 @@ public:
    */
   bool IsFirstFrameDecode() const
   {
     return bool(mDecoderFlags & DecoderFlags::FIRST_FRAME_ONLY);
   }
 
   size_t BytesDecoded() const { return mBytesDecoded; }
 
-  // The amount of time we've spent inside Write() so far for this decoder.
+  // The amount of time we've spent inside DoDecode() so far for this decoder.
   TimeDuration DecodeTime() const { return mDecodeTime; }
 
-  // The number of times Write() has been called so far for this decoder.
+  // The number of chunks this decoder's input was divided into.
   uint32_t ChunkCount() const { return mChunkCount; }
 
   // The number of frames we have, including anything in-progress. Thus, this
   // is only 0 if we haven't begun any frames.
   uint32_t GetFrameCount() { return mFrameCount; }
 
   // The number of complete frames we have (ie, not including anything
   // in-progress).
@@ -267,32 +269,33 @@ public:
   RawAccessFrameRef GetCurrentFrameRef()
   {
     return mCurrentFrame ? mCurrentFrame->RawAccessRef()
                          : RawAccessFrameRef();
   }
 
 
 protected:
+  friend class AutoRecordDecoderTelemetry;
   friend class nsICODecoder;
   friend class PalettedSurfaceSink;
   friend class SurfaceSink;
 
   virtual ~Decoder();
 
   /*
    * Internal hooks. Decoder implementations may override these and
    * only these methods.
    *
    * BeforeFinishInternal() can be used to detect if decoding is in an
    * incomplete state, e.g. due to file truncation, in which case it should
    * call PostDataError().
    */
   virtual void InitInternal();
-  virtual void WriteInternal(const char* aBuffer, uint32_t aCount) = 0;
+  virtual Maybe<TerminalState> DoDecode(SourceBufferIterator& aIterator) = 0;
   virtual void BeforeFinishInternal();
   virtual void FinishInternal();
   virtual void FinishWithErrorInternal();
 
   /*
    * Progress notifications.
    */
 
@@ -355,26 +358,16 @@ protected:
   // means a single iteration, stopping on the last frame.
   void PostDecodeDone(int32_t aLoopCount = 0);
 
   // Data errors are the fault of the source data, decoder errors are our fault
   void PostDataError();
   void PostDecoderError(nsresult aFailCode);
 
   /**
-   * Called by Decode() to write data to the decoder.
-   *
-   * @param aBuffer A buffer containing the data to be written.
-   * @param aCount The number of bytes to write.
-   *
-   * Any errors are reported by setting the appropriate state on the decoder.
-   */
-  void Write(const char* aBuffer, uint32_t aCount);
-
-  /**
    * CompleteDecode() finishes up the decoding process after Decode() determines
    * that we're finished. It records final progress and does all the cleanup
    * that's possible off-main-thread.
    */
   void CompleteDecode();
 
   /**
    * Allocates a new frame, making it our current frame if successful.
--- a/image/StreamingLexer.h
+++ b/image/StreamingLexer.h
@@ -206,34 +206,34 @@ private:
  *
  *  - Create a State type. This should be an |enum class| listing all of the
  *    states that you can be in while lexing the image format you're trying to
  *    read.
  *
  *  - Add an instance of StreamingLexer<State> to your decoder class. Initialize
  *    it with a Transition::To() the state that you want to start lexing in.
  *
- *  - In your decoder's WriteInternal method(), call Lex(), passing in the input
- *    data and length that are passed to WriteInternal(). You also need to pass
+ *  - In your decoder's DoDecode() method, call Lex(), passing in the input
+ *    data and length that are passed to DoDecode(). You also need to pass
  *    a lambda which dispatches to lexing code for each state based on the State
  *    value that's passed in. The lambda generally should just continue a
  *    |switch| statement that calls different methods for each State value. Each
  *    method should return a LexerTransition<State>, which the lambda should
  *    return in turn.
  *
  *  - Write the methods that actually implement lexing for your image format.
  *    These methods should return either Transition::To(), to move on to another
  *    state, or Transition::Terminate{Success,Failure}(), if lexing has
  *    terminated in either success or failure. (There are also additional
  *    transitions for unbuffered reads; see below.)
  *
  * That's all there is to it. The StreamingLexer will track your position in the
  * input and buffer enough data so that your lexing methods can process
  * everything in one pass. Lex() returns Nothing() if more data is needed, in
- * which case you should just return from WriteInternal(). If lexing reaches a
+ * which case you should just return from DoDecode(). If lexing reaches a
  * terminal state, Lex() returns Some(State::SUCCESS) or Some(State::FAILURE),
  * and you can check which one to determine if lexing succeeded or failed and do
  * any necessary cleanup.
  *
  * There's one more wrinkle: some lexers may want to *avoid* buffering in some
  * cases, and just process the data as it comes in. This is useful if, for
  * example, you just want to skip over a large section of data; there's no point
  * in buffering data you're just going to ignore.
--- a/image/decoders/nsBMPDecoder.cpp
+++ b/image/decoders/nsBMPDecoder.cpp
@@ -430,46 +430,41 @@ nsBMPDecoder::FinishRow()
                        Some(invalidRect.mTargetSizeRect));
     }
   } else {
     PostInvalidation(IntRect(0, mCurrentRow, mH.mWidth, 1));
   }
   mCurrentRow--;
 }
 
-void
-nsBMPDecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
+Maybe<TerminalState>
+nsBMPDecoder::DoDecode(SourceBufferIterator& aIterator)
 {
-  MOZ_ASSERT(!HasError(), "Shouldn't call WriteInternal after error!");
-  MOZ_ASSERT(aBuffer);
-  MOZ_ASSERT(aCount > 0);
+  MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!");
+  MOZ_ASSERT(aIterator.Data());
+  MOZ_ASSERT(aIterator.Length() > 0);
 
-  Maybe<TerminalState> terminalState =
-    mLexer.Lex(aBuffer, aCount, [=](State aState,
-                                    const char* aData, size_t aLength) {
-      switch (aState) {
-        case State::FILE_HEADER:      return ReadFileHeader(aData, aLength);
-        case State::INFO_HEADER_SIZE: return ReadInfoHeaderSize(aData, aLength);
-        case State::INFO_HEADER_REST: return ReadInfoHeaderRest(aData, aLength);
-        case State::BITFIELDS:        return ReadBitfields(aData, aLength);
-        case State::COLOR_TABLE:      return ReadColorTable(aData, aLength);
-        case State::GAP:              return SkipGap();
-        case State::AFTER_GAP:        return AfterGap();
-        case State::PIXEL_ROW:        return ReadPixelRow(aData);
-        case State::RLE_SEGMENT:      return ReadRLESegment(aData);
-        case State::RLE_DELTA:        return ReadRLEDelta(aData);
-        case State::RLE_ABSOLUTE:     return ReadRLEAbsolute(aData, aLength);
-        default:
-          MOZ_CRASH("Unknown State");
-      }
-    });
-
-  if (terminalState == Some(TerminalState::FAILURE)) {
-    PostDataError();
-  }
+  return mLexer.Lex(aIterator.Data(), aIterator.Length(),
+                    [=](State aState, const char* aData, size_t aLength) {
+    switch (aState) {
+      case State::FILE_HEADER:      return ReadFileHeader(aData, aLength);
+      case State::INFO_HEADER_SIZE: return ReadInfoHeaderSize(aData, aLength);
+      case State::INFO_HEADER_REST: return ReadInfoHeaderRest(aData, aLength);
+      case State::BITFIELDS:        return ReadBitfields(aData, aLength);
+      case State::COLOR_TABLE:      return ReadColorTable(aData, aLength);
+      case State::GAP:              return SkipGap();
+      case State::AFTER_GAP:        return AfterGap();
+      case State::PIXEL_ROW:        return ReadPixelRow(aData);
+      case State::RLE_SEGMENT:      return ReadRLESegment(aData);
+      case State::RLE_DELTA:        return ReadRLEDelta(aData);
+      case State::RLE_ABSOLUTE:     return ReadRLEAbsolute(aData, aLength);
+      default:
+        MOZ_CRASH("Unknown State");
+    }
+  });
 }
 
 LexerTransition<nsBMPDecoder::State>
 nsBMPDecoder::ReadFileHeader(const char* aData, size_t aLength)
 {
   mPreGapLength += aLength;
 
   bool signatureOk = aData[0] == 'B' && aData[1] == 'M';
--- a/image/decoders/nsBMPDecoder.h
+++ b/image/decoders/nsBMPDecoder.h
@@ -144,18 +144,17 @@ public:
 
   /// Force transparency from outside. (Used by the ICO decoder.)
   void SetHasTransparency()
   {
     mMayHaveTransparency = true;
     mDoesHaveTransparency = true;
   }
 
-  virtual void WriteInternal(const char* aBuffer,
-                             uint32_t aCount) override;
+  Maybe<TerminalState> DoDecode(SourceBufferIterator& aIterator) override;
   virtual void BeforeFinishInternal() override;
   virtual void FinishInternal() override;
 
 private:
   friend class DecoderFactory;
 
   enum class State {
     FILE_HEADER,
--- a/image/decoders/nsGIFDecoder2.cpp
+++ b/image/decoders/nsGIFDecoder2.cpp
@@ -449,77 +449,72 @@ ConvertColormap(uint32_t* aColormap, uin
   // copy remaining pixel(s)
   // NB: can't use 32-bit reads, they might read off the end of the buffer
   while (c--) {
     from -= 3;
     *--to = gfxPackedPixel(0xFF, from[0], from[1], from[2]);
   }
 }
 
-void
-nsGIFDecoder2::WriteInternal(const char* aBuffer, uint32_t aCount)
+Maybe<TerminalState>
+nsGIFDecoder2::DoDecode(SourceBufferIterator& aIterator)
 {
-  MOZ_ASSERT(!HasError(), "Shouldn't call WriteInternal after error!");
-  MOZ_ASSERT(aBuffer);
-  MOZ_ASSERT(aCount > 0);
+  MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!");
+  MOZ_ASSERT(aIterator.Data());
+  MOZ_ASSERT(aIterator.Length() > 0);
 
-  Maybe<TerminalState> terminalState =
-    mLexer.Lex(aBuffer, aCount, [=](State aState,
-                                    const char* aData, size_t aLength) {
-        switch(aState) {
-          case State::GIF_HEADER:
-            return ReadGIFHeader(aData);
-          case State::SCREEN_DESCRIPTOR:
-            return ReadScreenDescriptor(aData);
-          case State::GLOBAL_COLOR_TABLE:
-            return ReadGlobalColorTable(aData, aLength);
-          case State::FINISHED_GLOBAL_COLOR_TABLE:
-            return FinishedGlobalColorTable();
-          case State::BLOCK_HEADER:
-            return ReadBlockHeader(aData);
-          case State::EXTENSION_HEADER:
-            return ReadExtensionHeader(aData);
-          case State::GRAPHIC_CONTROL_EXTENSION:
-            return ReadGraphicControlExtension(aData);
-          case State::APPLICATION_IDENTIFIER:
-            return ReadApplicationIdentifier(aData);
-          case State::NETSCAPE_EXTENSION_SUB_BLOCK:
-            return ReadNetscapeExtensionSubBlock(aData);
-          case State::NETSCAPE_EXTENSION_DATA:
-            return ReadNetscapeExtensionData(aData);
-          case State::IMAGE_DESCRIPTOR:
-            return ReadImageDescriptor(aData);
-          case State::LOCAL_COLOR_TABLE:
-            return ReadLocalColorTable(aData, aLength);
-          case State::FINISHED_LOCAL_COLOR_TABLE:
-            return FinishedLocalColorTable();
-          case State::IMAGE_DATA_BLOCK:
-            return ReadImageDataBlock(aData);
-          case State::IMAGE_DATA_SUB_BLOCK:
-            return ReadImageDataSubBlock(aData);
-          case State::LZW_DATA:
-            return ReadLZWData(aData, aLength);
-          case State::SKIP_LZW_DATA:
-            return Transition::ContinueUnbuffered(State::SKIP_LZW_DATA);
-          case State::FINISHED_LZW_DATA:
-            return Transition::To(State::IMAGE_DATA_SUB_BLOCK, SUB_BLOCK_HEADER_LEN);
-          case State::SKIP_SUB_BLOCKS:
-            return SkipSubBlocks(aData);
-          case State::SKIP_DATA_THEN_SKIP_SUB_BLOCKS:
-            return Transition::ContinueUnbuffered(State::SKIP_DATA_THEN_SKIP_SUB_BLOCKS);
-          case State::FINISHED_SKIPPING_DATA:
-            return Transition::To(State::SKIP_SUB_BLOCKS, SUB_BLOCK_HEADER_LEN);
-          default:
-            MOZ_CRASH("Unknown State");
-        }
-      });
-
-  if (terminalState == Some(TerminalState::FAILURE)) {
-    PostDataError();
-  }
+  return mLexer.Lex(aIterator.Data(), aIterator.Length(),
+                    [=](State aState, const char* aData, size_t aLength) {
+    switch(aState) {
+      case State::GIF_HEADER:
+        return ReadGIFHeader(aData);
+      case State::SCREEN_DESCRIPTOR:
+        return ReadScreenDescriptor(aData);
+      case State::GLOBAL_COLOR_TABLE:
+        return ReadGlobalColorTable(aData, aLength);
+      case State::FINISHED_GLOBAL_COLOR_TABLE:
+        return FinishedGlobalColorTable();
+      case State::BLOCK_HEADER:
+        return ReadBlockHeader(aData);
+      case State::EXTENSION_HEADER:
+        return ReadExtensionHeader(aData);
+      case State::GRAPHIC_CONTROL_EXTENSION:
+        return ReadGraphicControlExtension(aData);
+      case State::APPLICATION_IDENTIFIER:
+        return ReadApplicationIdentifier(aData);
+      case State::NETSCAPE_EXTENSION_SUB_BLOCK:
+        return ReadNetscapeExtensionSubBlock(aData);
+      case State::NETSCAPE_EXTENSION_DATA:
+        return ReadNetscapeExtensionData(aData);
+      case State::IMAGE_DESCRIPTOR:
+        return ReadImageDescriptor(aData);
+      case State::LOCAL_COLOR_TABLE:
+        return ReadLocalColorTable(aData, aLength);
+      case State::FINISHED_LOCAL_COLOR_TABLE:
+        return FinishedLocalColorTable();
+      case State::IMAGE_DATA_BLOCK:
+        return ReadImageDataBlock(aData);
+      case State::IMAGE_DATA_SUB_BLOCK:
+        return ReadImageDataSubBlock(aData);
+      case State::LZW_DATA:
+        return ReadLZWData(aData, aLength);
+      case State::SKIP_LZW_DATA:
+        return Transition::ContinueUnbuffered(State::SKIP_LZW_DATA);
+      case State::FINISHED_LZW_DATA:
+        return Transition::To(State::IMAGE_DATA_SUB_BLOCK, SUB_BLOCK_HEADER_LEN);
+      case State::SKIP_SUB_BLOCKS:
+        return SkipSubBlocks(aData);
+      case State::SKIP_DATA_THEN_SKIP_SUB_BLOCKS:
+        return Transition::ContinueUnbuffered(State::SKIP_DATA_THEN_SKIP_SUB_BLOCKS);
+      case State::FINISHED_SKIPPING_DATA:
+        return Transition::To(State::SKIP_SUB_BLOCKS, SUB_BLOCK_HEADER_LEN);
+      default:
+        MOZ_CRASH("Unknown State");
+    }
+  });
 }
 
 LexerTransition<nsGIFDecoder2::State>
 nsGIFDecoder2::ReadGIFHeader(const char* aData)
 {
   // We retrieve the version here but because many GIF encoders set header
   // fields incorrectly, we barely use it; features which should only appear in
   // GIF89a are always accepted.
--- a/image/decoders/nsGIFDecoder2.h
+++ b/image/decoders/nsGIFDecoder2.h
@@ -19,17 +19,17 @@ class RasterImage;
 //////////////////////////////////////////////////////////////////////
 // nsGIFDecoder2 Definition
 
 class nsGIFDecoder2 : public Decoder
 {
 public:
   ~nsGIFDecoder2();
 
-  virtual void WriteInternal(const char* aBuffer, uint32_t aCount) override;
+  Maybe<TerminalState> DoDecode(SourceBufferIterator& aIterator) override;
   virtual void FinishInternal() override;
   virtual Telemetry::ID SpeedHistogram() override;
 
 private:
   friend class DecoderFactory;
 
   // Decoders should only be instantiated via DecoderFactory.
   explicit nsGIFDecoder2(RasterImage* aImage);
--- a/image/decoders/nsICODecoder.cpp
+++ b/image/decoders/nsICODecoder.cpp
@@ -592,61 +592,56 @@ nsICODecoder::FinishResource()
   if (mContainedDecoder->HasSize() &&
       mContainedDecoder->GetSize() != GetRealSize()) {
     return Transition::TerminateFailure();
   }
 
   return Transition::TerminateSuccess();
 }
 
-void
-nsICODecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
+Maybe<TerminalState>
+nsICODecoder::DoDecode(SourceBufferIterator& aIterator)
 {
-  MOZ_ASSERT(!HasError(), "Shouldn't call WriteInternal after error!");
-  MOZ_ASSERT(aBuffer);
-  MOZ_ASSERT(aCount > 0);
+  MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!");
+  MOZ_ASSERT(aIterator.Data());
+  MOZ_ASSERT(aIterator.Length() > 0);
 
-  Maybe<TerminalState> terminalState =
-    mLexer.Lex(aBuffer, aCount,
-               [=](ICOState aState, const char* aData, size_t aLength) {
-      switch (aState) {
-        case ICOState::HEADER:
-          return ReadHeader(aData);
-        case ICOState::DIR_ENTRY:
-          return ReadDirEntry(aData);
-        case ICOState::SKIP_TO_RESOURCE:
-          return Transition::ContinueUnbuffered(ICOState::SKIP_TO_RESOURCE);
-        case ICOState::FOUND_RESOURCE:
-          return Transition::To(ICOState::SNIFF_RESOURCE, PNGSIGNATURESIZE);
-        case ICOState::SNIFF_RESOURCE:
-          return SniffResource(aData);
-        case ICOState::READ_PNG:
-          return ReadPNG(aData, aLength);
-        case ICOState::READ_BIH:
-          return ReadBIH(aData);
-        case ICOState::READ_BMP:
-          return ReadBMP(aData, aLength);
-        case ICOState::PREPARE_FOR_MASK:
-          return PrepareForMask();
-        case ICOState::READ_MASK_ROW:
-          return ReadMaskRow(aData);
-        case ICOState::FINISH_MASK:
-          return FinishMask();
-        case ICOState::SKIP_MASK:
-          return Transition::ContinueUnbuffered(ICOState::SKIP_MASK);
-        case ICOState::FINISHED_RESOURCE:
-          return FinishResource();
-        default:
-          MOZ_CRASH("Unknown ICOState");
-      }
-    });
-
-  if (terminalState == Some(TerminalState::FAILURE)) {
-    PostDataError();
-  }
+  return mLexer.Lex(aIterator.Data(), aIterator.Length(),
+                    [=](ICOState aState, const char* aData, size_t aLength) {
+    switch (aState) {
+      case ICOState::HEADER:
+        return ReadHeader(aData);
+      case ICOState::DIR_ENTRY:
+        return ReadDirEntry(aData);
+      case ICOState::SKIP_TO_RESOURCE:
+        return Transition::ContinueUnbuffered(ICOState::SKIP_TO_RESOURCE);
+      case ICOState::FOUND_RESOURCE:
+        return Transition::To(ICOState::SNIFF_RESOURCE, PNGSIGNATURESIZE);
+      case ICOState::SNIFF_RESOURCE:
+        return SniffResource(aData);
+      case ICOState::READ_PNG:
+        return ReadPNG(aData, aLength);
+      case ICOState::READ_BIH:
+        return ReadBIH(aData);
+      case ICOState::READ_BMP:
+        return ReadBMP(aData, aLength);
+      case ICOState::PREPARE_FOR_MASK:
+        return PrepareForMask();
+      case ICOState::READ_MASK_ROW:
+        return ReadMaskRow(aData);
+      case ICOState::FINISH_MASK:
+        return FinishMask();
+      case ICOState::SKIP_MASK:
+        return Transition::ContinueUnbuffered(ICOState::SKIP_MASK);
+      case ICOState::FINISHED_RESOURCE:
+        return FinishResource();
+      default:
+        MOZ_CRASH("Unknown ICOState");
+    }
+  });
 }
 
 bool
 nsICODecoder::WriteToContainedDecoder(const char* aBuffer, uint32_t aCount)
 {
   MOZ_ASSERT(mContainedDecoder);
   MOZ_ASSERT(mContainedSourceBuffer);
 
--- a/image/decoders/nsICODecoder.h
+++ b/image/decoders/nsICODecoder.h
@@ -65,17 +65,17 @@ public:
   gfx::IntSize GetRealSize() const
   {
     return gfx::IntSize(GetRealWidth(), GetRealHeight());
   }
 
   /// @return The offset from the beginning of the ICO to the first resource.
   size_t FirstResourceOffset() const;
 
-  virtual void WriteInternal(const char* aBuffer, uint32_t aCount) override;
+  Maybe<TerminalState> DoDecode(SourceBufferIterator& aIterator) override;
   virtual void FinishInternal() override;
   virtual void FinishWithErrorInternal() override;
 
 private:
   friend class DecoderFactory;
 
   // Decoders should only be instantiated via DecoderFactory.
   explicit nsICODecoder(RasterImage* aImage);
--- a/image/decoders/nsIconDecoder.cpp
+++ b/image/decoders/nsIconDecoder.cpp
@@ -21,41 +21,36 @@ nsIconDecoder::nsIconDecoder(RasterImage
  , mBytesPerRow()   // set by ReadHeader()
 {
   // Nothing to do
 }
 
 nsIconDecoder::~nsIconDecoder()
 { }
 
-void
-nsIconDecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
+Maybe<TerminalState>
+nsIconDecoder::DoDecode(SourceBufferIterator& aIterator)
 {
-  MOZ_ASSERT(!HasError(), "Shouldn't call WriteInternal after error!");
-  MOZ_ASSERT(aBuffer);
-  MOZ_ASSERT(aCount > 0);
+  MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!");
+  MOZ_ASSERT(aIterator.Data());
+  MOZ_ASSERT(aIterator.Length() > 0);
 
-  Maybe<TerminalState> terminalState =
-    mLexer.Lex(aBuffer, aCount, [=](State aState,
-                                    const char* aData, size_t aLength) {
-      switch (aState) {
-        case State::HEADER:
-          return ReadHeader(aData);
-        case State::ROW_OF_PIXELS:
-          return ReadRowOfPixels(aData, aLength);
-        case State::FINISH:
-          return Finish();
-        default:
-          MOZ_CRASH("Unknown State");
-      }
-    });
-
-  if (terminalState == Some(TerminalState::FAILURE)) {
-    PostDataError();
-  }
+  return mLexer.Lex(aIterator.Data(), aIterator.Length(),
+                    [=](State aState, const char* aData, size_t aLength) {
+    switch (aState) {
+      case State::HEADER:
+        return ReadHeader(aData);
+      case State::ROW_OF_PIXELS:
+        return ReadRowOfPixels(aData, aLength);
+      case State::FINISH:
+        return Finish();
+      default:
+        MOZ_CRASH("Unknown State");
+    }
+  });
 }
 
 LexerTransition<nsIconDecoder::State>
 nsIconDecoder::ReadHeader(const char* aData)
 {
   // Grab the width and height.
   uint8_t width  = uint8_t(aData[0]);
   uint8_t height = uint8_t(aData[1]);
--- a/image/decoders/nsIconDecoder.h
+++ b/image/decoders/nsIconDecoder.h
@@ -32,17 +32,17 @@ class RasterImage;
 //
 ////////////////////////////////////////////////////////////////////////////////
 
 class nsIconDecoder : public Decoder
 {
 public:
   virtual ~nsIconDecoder();
 
-  virtual void WriteInternal(const char* aBuffer, uint32_t aCount) override;
+  Maybe<TerminalState> DoDecode(SourceBufferIterator& aIterator) override;
 
 private:
   friend class DecoderFactory;
 
   // Decoders should only be instantiated via DecoderFactory.
   explicit nsIconDecoder(RasterImage* aImage);
 
   enum class State {
--- a/image/decoders/nsJPEGDecoder.cpp
+++ b/image/decoders/nsJPEGDecoder.cpp
@@ -174,38 +174,33 @@ nsJPEGDecoder::FinishInternal()
   // If we're not in any sort of error case, force our state to JPEG_DONE.
   if ((mState != JPEG_DONE && mState != JPEG_SINK_NON_JPEG_TRAILER) &&
       (mState != JPEG_ERROR) &&
       !IsMetadataDecode()) {
     mState = JPEG_DONE;
   }
 }
 
-void
-nsJPEGDecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
+Maybe<TerminalState>
+nsJPEGDecoder::DoDecode(SourceBufferIterator& aIterator)
 {
-  MOZ_ASSERT(!HasError(), "Shouldn't call WriteInternal after error!");
-  MOZ_ASSERT(aBuffer);
-  MOZ_ASSERT(aCount > 0);
+  MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!");
+  MOZ_ASSERT(aIterator.Data());
+  MOZ_ASSERT(aIterator.Length() > 0);
 
-  Maybe<TerminalState> terminalState =
-    mLexer.Lex(aBuffer, aCount, [=](State aState,
-                                    const char* aData, size_t aLength) {
-      switch (aState) {
-        case State::JPEG_DATA:
-          return ReadJPEGData(aData, aLength);
-        case State::FINISHED_JPEG_DATA:
-          return FinishedJPEGData();
-      }
-      MOZ_CRASH("Unknown State");
-    });
-
-  if (terminalState == Some(TerminalState::FAILURE)) {
-    PostDataError();
-  }
+  return mLexer.Lex(aIterator.Data(), aIterator.Length(),
+                    [=](State aState, const char* aData, size_t aLength) {
+    switch (aState) {
+      case State::JPEG_DATA:
+        return ReadJPEGData(aData, aLength);
+      case State::FINISHED_JPEG_DATA:
+        return FinishedJPEGData();
+    }
+    MOZ_CRASH("Unknown State");
+  });
 }
 
 LexerTransition<nsJPEGDecoder::State>
 nsJPEGDecoder::ReadJPEGData(const char* aData, size_t aLength)
 {
   mSegment = reinterpret_cast<const JOCTET*>(aData);
   mSegmentLen = aLength;
 
--- a/image/decoders/nsJPEGDecoder.h
+++ b/image/decoders/nsJPEGDecoder.h
@@ -53,17 +53,17 @@ public:
   virtual ~nsJPEGDecoder();
 
   virtual void SetSampleSize(int aSampleSize) override
   {
     mSampleSize = aSampleSize;
   }
 
   virtual void InitInternal() override;
-  virtual void WriteInternal(const char* aBuffer, uint32_t aCount) override;
+  Maybe<TerminalState> DoDecode(SourceBufferIterator& aIterator) override;
   virtual void FinishInternal() override;
 
   virtual Telemetry::ID SpeedHistogram() override;
   void NotifyDone();
 
 protected:
   Orientation ReadOrientationFromEXIF();
   void OutputScanlines(bool* suspend);
--- a/image/decoders/nsPNGDecoder.cpp
+++ b/image/decoders/nsPNGDecoder.cpp
@@ -343,38 +343,33 @@ nsPNGDecoder::InitInternal()
   // use this as libpng "progressive pointer" (retrieve in callbacks)
   png_set_progressive_read_fn(mPNG, static_cast<png_voidp>(this),
                               nsPNGDecoder::info_callback,
                               nsPNGDecoder::row_callback,
                               nsPNGDecoder::end_callback);
 
 }
 
-void
-nsPNGDecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
+Maybe<TerminalState>
+nsPNGDecoder::DoDecode(SourceBufferIterator& aIterator)
 {
-  MOZ_ASSERT(!HasError(), "Shouldn't call WriteInternal after error!");
-  MOZ_ASSERT(aBuffer);
-  MOZ_ASSERT(aCount > 0);
+  MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!");
+  MOZ_ASSERT(aIterator.Data());
+  MOZ_ASSERT(aIterator.Length() > 0);
 
-  Maybe<TerminalState> terminalState =
-    mLexer.Lex(aBuffer, aCount, [=](State aState,
-                                    const char* aData, size_t aLength) {
-      switch (aState) {
-        case State::PNG_DATA:
-          return ReadPNGData(aData, aLength);
-        case State::FINISHED_PNG_DATA:
-          return FinishedPNGData();
-      }
-      MOZ_CRASH("Unknown State");
-    });
-
-  if (terminalState == Some(TerminalState::FAILURE)) {
-    PostDataError();
-  }
+  return mLexer.Lex(aIterator.Data(), aIterator.Length(),
+                    [=](State aState, const char* aData, size_t aLength) {
+    switch (aState) {
+      case State::PNG_DATA:
+        return ReadPNGData(aData, aLength);
+      case State::FINISHED_PNG_DATA:
+        return FinishedPNGData();
+    }
+    MOZ_CRASH("Unknown State");
+  });
 }
 
 LexerTransition<nsPNGDecoder::State>
 nsPNGDecoder::ReadPNGData(const char* aData, size_t aLength)
 {
   // libpng uses setjmp/longjmp for error handling.
   if (setjmp(png_jmpbuf(mPNG))) {
     return Transition::TerminateFailure();
--- a/image/decoders/nsPNGDecoder.h
+++ b/image/decoders/nsPNGDecoder.h
@@ -18,17 +18,17 @@ namespace image {
 class RasterImage;
 
 class nsPNGDecoder : public Decoder
 {
 public:
   virtual ~nsPNGDecoder();
 
   virtual void InitInternal() override;
-  virtual void WriteInternal(const char* aBuffer, uint32_t aCount) override;
+  Maybe<TerminalState> DoDecode(SourceBufferIterator& aIterator) override;
   virtual Telemetry::ID SpeedHistogram() override;
 
   /// @return true if this PNG is a valid ICO resource.
   bool IsValidICO() const;
 
 private:
   friend class DecoderFactory;
 
--- a/js/public/TracingAPI.h
+++ b/js/public/TracingAPI.h
@@ -22,23 +22,25 @@ template <typename T> class TenuredHeap;
 
 /** Returns a static string equivalent of |kind|. */
 JS_FRIEND_API(const char*)
 GCTraceKindToAscii(JS::TraceKind kind);
 
 } // namespace JS
 
 enum WeakMapTraceKind {
-    /** Do true ephemeron marking with an iterative weak marking phase. */
+    /**
+     * Do not trace into weak map keys or values during traversal. Users must
+     * handle weak maps manually.
+     */
     DoNotTraceWeakMaps,
 
     /**
      * Do true ephemeron marking with a weak key lookup marking phase. This is
-     * expected to be constant for the lifetime of a JSTracer; it does not
-     * change when switching from "plain" marking to weak marking.
+     * the default for GCMarker.
      */
     ExpandWeakMaps,
 
     /**
      * Trace through to all values, irrespective of whether the keys are live
      * or not. Used for non-marking tracers.
      */
     TraceWeakMapValues,
@@ -54,21 +56,34 @@ class JS_PUBLIC_API(JSTracer)
 {
   public:
     // Return the runtime set on the tracer.
     JSRuntime* runtime() const { return runtime_; }
 
     // Return the weak map tracing behavior currently set on this tracer.
     WeakMapTraceKind weakMapAction() const { return weakMapAction_; }
 
-    // An intermediate state on the road from C to C++ style dispatch.
     enum class TracerKindTag {
+        // Marking path: a tracer used only for marking liveness of cells, not
+        // for moving them. The kind will transition to WeakMarking after
+        // everything reachable by regular edges has been marked.
         Marking,
-        WeakMarking, // In weak marking phase: looking up every marked obj/script.
+
+        // Same as Marking, except we have now moved on to the "weak marking
+        // phase", in which every marked obj/script is immediately looked up to
+        // see if it is a weak map key (and therefore might require marking its
+        // weak map value).
+        WeakMarking,
+
+        // A tracer that traverses the graph for the purposes of moving objects
+        // from the nursery to the tenured area.
         Tenuring,
+
+        // General-purpose traversal that invokes a callback on each cell.
+        // Traversing children is the responsibility of the callback.
         Callback
     };
     bool isMarkingTracer() const { return tag_ == TracerKindTag::Marking || tag_ == TracerKindTag::WeakMarking; }
     bool isWeakMarkingTracer() const { return tag_ == TracerKindTag::WeakMarking; }
     bool isTenuringTracer() const { return tag_ == TracerKindTag::Tenuring; }
     bool isCallbackTracer() const { return tag_ == TracerKindTag::Callback; }
     inline JS::CallbackTracer* asCallbackTracer();
 #ifdef DEBUG
new file mode 100755
--- /dev/null
+++ b/js/src/devtools/automation/autospider.py
@@ -0,0 +1,299 @@
+#!/usr/bin/env python
+
+import argparse
+import json
+import logging
+import re
+import os
+import platform
+import posixpath
+import shutil
+import subprocess
+import sys
+
+from collections import namedtuple
+from os import environ as env
+from subprocess import Popen
+from threading import Timer
+
+Dirs = namedtuple('Dirs', ['scripts', 'js_src', 'source'])
+
+
+def directories(pathmodule, cwd, fixup=lambda s: s):
+    scripts = pathmodule.join(fixup(cwd), fixup(pathmodule.dirname(__file__)))
+    js_src = pathmodule.abspath(pathmodule.join(scripts, "..", ".."))
+    source = pathmodule.abspath(pathmodule.join(js_src, "..", ".."))
+    return Dirs(scripts, js_src, source)
+
+# Some scripts will be called with sh, which cannot use backslashed
+# paths. So for direct subprocess.* invocation, use normal paths from
+# DIR, but when running under the shell, use POSIX style paths.
+DIR = directories(os.path, os.getcwd())
+PDIR = directories(posixpath, os.environ["PWD"],
+                   fixup=lambda s: re.sub(r'^(\w):', r'/\1', s))
+
+parser = argparse.ArgumentParser(
+    description='Run a spidermonkey shell build job')
+parser.add_argument('--dep', action='store_true',
+                    help='do not clobber the objdir before building')
+parser.add_argument('--platform', '-p', type=str, metavar='PLATFORM',
+                    default='', help='build platform')
+parser.add_argument('--timeout', '-t', type=int, metavar='TIMEOUT',
+                    default=10800,
+                    help='kill job after TIMEOUT seconds')
+parser.add_argument('--objdir', type=str, metavar='DIR',
+                    default=env.get('OBJDIR', 'obj-spider'),
+                    help='object directory')
+parser.add_argument('--skip-tests', '--skip', type=str, metavar='TESTSUITE',
+                    default='',
+                    help="comma-separated set of test suites to remove from the variant's default set")
+parser.add_argument('--run-tests', '--tests', type=str, metavar='TESTSUITE',
+                    default='',
+                    help="comma-separated set of test suites to add to the variant's default set")
+parser.add_argument('variant', type=str,
+                    help='type of job requested, see variants/ subdir')
+args = parser.parse_args()
+
+
+def set_vars_from_script(script, vars):
+    '''Run a shell script, then dump out chosen environment variables. The build
+       system uses shell scripts to do some configuration that we need to
+       borrow. On Windows, the script itself must output the variable settings
+       (in the form "export FOO=<value>"), since otherwise there will be
+       problems with mismatched Windows/POSIX formats.
+    '''
+    script_text = 'source %s' % script
+    if platform.system() == 'Windows':
+        parse_state = 'parsing exports'
+    else:
+        script_text += '; echo VAR SETTINGS:; '
+        script_text += '; '.join('echo $' + var for var in vars)
+        parse_state = 'scanning'
+    stdout = subprocess.check_output(['sh', '-x', '-c', script_text])
+    tograb = vars[:]
+    originals = {}
+    for line in stdout.splitlines():
+        if parse_state == 'scanning':
+            if line == 'VAR SETTINGS:':
+                parse_state = 'grabbing'
+        elif parse_state == 'grabbing':
+            var = tograb.pop(0)
+            env[var] = line
+        elif parse_state == 'parsing exports':
+            m = re.match(r'export (\w+)=(.*)', line)
+            if m:
+                var, value = m.groups()
+                if var in tograb:
+                    env[var] = value
+                    print("Setting %s = %s" % (var, value))
+                if var.startswith("ORIGINAL_"):
+                    originals[var[9:]] = value
+
+    # An added wrinkle: on Windows developer systems, the sourced script will
+    # blow away current settings for eg LIBS, to point to the ones that would
+    # be installed via automation. So we will append the original settings. (On
+    # an automation system, the original settings will be empty or point to
+    # nonexistent stuff.)
+    if platform.system() == 'Windows':
+        for var in vars:
+            if var in originals and len(originals[var]) > 0:
+                env[var] = "%s;%s" % (env[var], originals[var])
+                print("orig appended, %s = %s" % (var, env[var]))
+
+
+def call_alternates(binaries, command_args, *args, **kwargs):
+    last_exception = None
+    for binary in binaries:
+        try:
+            return subprocess.call(['sh', '-c', binary] + command_args, *args, **kwargs)
+        except OSError as e:
+            # Assume the binary was not found.
+            last_exception = e
+    raise last_exception
+
+
+def ensure_dir_exists(name, clobber=True):
+    if clobber:
+        shutil.rmtree(name, ignore_errors=True)
+    try:
+        os.mkdir(name)
+    except OSError:
+        if clobber:
+            raise
+
+with open(os.path.join(DIR.scripts, "variants", args.variant)) as fh:
+    variant = json.load(fh)
+
+if args.variant == 'nonunified':
+    # Rewrite js/src/**/moz.build to replace UNIFIED_SOURCES to SOURCES.
+    # Note that this modifies the current checkout.
+    for dirpath, dirnames, filenames in os.walk(DIR.js_src):
+        if 'moz.build' in filenames:
+            subprocess.check_call(['sed', '-i', 's/UNIFIED_SOURCES/SOURCES/',
+                                   os.path.join(dirpath, 'moz.build')])
+
+autoconfs = ['autoconf-2.13', 'autoconf2.13', 'autoconf213']
+if call_alternates(autoconfs, [], cwd=DIR.js_src) != 0:
+    logging.error('autoconf failed')
+    sys.exit(1)
+
+OBJDIR = os.path.join(DIR.source, args.objdir)
+POBJDIR = posixpath.join(PDIR.source, args.objdir)
+AUTOMATION = env.get('AUTOMATION', False)
+MAKE = env.get('MAKE', 'make')
+MAKEFLAGS = '-j6'
+CONFIGURE_ARGS = variant['configure-args']
+UNAME_M = subprocess.check_output(['uname', '-m']).strip()
+
+# Some of the variants request a particular word size (eg ARM simulators).
+word_bits = variant.get('bits')
+
+# On Linux and Windows, we build 32- and 64-bit versions on a 64 bit
+# host, so the caller has to specify what is desired.
+if word_bits is None and args.platform:
+    platform_arch = args.platform.split('-')[0]
+    if platform_arch in ('win32', 'linux'):
+        word_bits = 32
+    elif platform_arch in ('win64', 'linux64'):
+        word_bits = 64
+
+# Fall back to the word size of the host.
+if word_bits is None:
+    word_bits = 64 if UNAME_M == 'x86_64' else 32
+
+if platform.system() == 'Darwin':
+    set_vars_from_script(os.path.join(DIR.scripts, 'macbuildenv.sh'),
+                         ['CC', 'CXX'])
+elif platform.system() == 'Linux':
+    if AUTOMATION:
+        GCCDIR = env.get('GCCDIR', os.path.join(DIR.source, '..', 'gcc'))
+        CONFIGURE_ARGS += ' --with-ccache'
+        env.setdefault('CC', os.path.join(GCCDIR, 'bin', 'gcc'))
+        env.setdefault('CXX', os.path.join(GCCDIR, 'bin', 'g++'))
+        platlib = 'lib64' if word_bits == 64 else 'lib'
+        env.setdefault('LD_LIBRARY_PATH', os.path.join(GCCDIR, platlib))
+elif platform.system() == 'Windows':
+    MAKE = env.get('MAKE', 'mozmake')
+    os.environ['SOURCE'] = DIR.source
+    if word_bits == 64:
+        os.environ['USE_64BIT'] = '1'
+    set_vars_from_script(posixpath.join(PDIR.scripts, 'winbuildenv.sh'),
+                         ['PATH', 'INCLUDE', 'LIB', 'LIBPATH', 'CC', 'CXX'])
+
+if word_bits == 64:
+    if platform.system() == 'Windows':
+        CONFIGURE_ARGS += ' --target=x86_64-pc-mingw32 --host=x86_64-pc-mingw32'
+else:
+    if platform.system() == 'Darwin':
+        env['CC'] = '{CC} -arch i386'.format(**env)
+        env['CXX'] = '{CXX} -arch i386'.format(**env)
+    elif platform.system() == 'Windows':
+        CONFIGURE_ARGS += ' --target=i686-pc-mingw32 --host=i686-pc-mingw32'
+    else:
+        env.setdefault('CC', 'gcc')
+        env.setdefault('CXX', 'g++')
+        env['CC'] = '{CC} -m32'.format(**env)
+        env['CXX'] = '{CXX} -m32'.format(**env)
+        env['AR'] = 'ar'
+
+    if platform.system() == 'Linux':
+        if AUTOMATION and UNAME_M != 'arm':
+            CONFIGURE_ARGS += ' --target=i686-pc-linux --host=i686-pc-linux'
+
+# Timeouts.
+ACTIVE_PROCESSES = set()
+
+
+def killall():
+    for proc in ACTIVE_PROCESSES:
+        proc.kill()
+    ACTIVE_PROCESSES.clear()
+
+timer = Timer(args.timeout, killall)
+timer.daemon = True
+timer.start()
+
+ensure_dir_exists(OBJDIR, clobber=not args.dep)
+
+
+def run_command(command, check=False, **kwargs):
+    proc = Popen(command, cwd=OBJDIR, **kwargs)
+    ACTIVE_PROCESSES.add(proc)
+    stdout, stderr = None, None
+    try:
+        stdout, stderr = proc.communicate()
+    finally:
+        ACTIVE_PROCESSES.discard(proc)
+    status = proc.wait()
+    if check and status != 0:
+        raise subprocess.CalledProcessError(status, command, output=stderr)
+    return stdout, stderr, status
+
+CONFIGURE_ARGS += ' --enable-nspr-build'
+CONFIGURE_ARGS += ' --prefix={OBJDIR}/dist'.format(OBJDIR=POBJDIR)
+run_command(['sh', '-c', posixpath.join(PDIR.js_src, 'configure') + ' ' + CONFIGURE_ARGS], check=True)
+
+run_command('%s -s -w %s' % (MAKE, MAKEFLAGS), shell=True, check=True)
+
+COMMAND_PREFIX = []
+# On Linux, disable ASLR to make shell builds a bit more reproducible.
+if subprocess.call("type setarch >/dev/null 2>&1", shell=True) == 0:
+    COMMAND_PREFIX.extend(['setarch', UNAME_M, '-R'])
+
+
+def run_test_command(command, **kwargs):
+    _, _, status = run_command(COMMAND_PREFIX + command, check=False, **kwargs)
+    return status
+
+test_suites = set(['jstests', 'jittest', 'jsapitests', 'checks'])
+
+# Add in environment variable settings for this variant. Normally used to
+# modify the flags passed to the shell or to set the GC zeal mode.
+for k, v in variant.get('env', {}).items():
+    env[k] = v.format(DIR=DIR.scripts)
+
+# Need a platform name to use as a key in variant files.
+if args.platform:
+    variant_platform = args.platform.split("-")[0]
+elif platform.system() == 'Windows':
+    variant_platform = 'win64' if word_bits == 64 else 'win32'
+elif platform.system() == 'Linux':
+    variant_platform = 'linux64' if word_bits == 64 else 'linux'
+elif platform.system() == 'Darwin':
+    variant_platform = 'macosx64'
+else:
+    variant_platform = 'other'
+
+# Skip any tests that are not run on this platform.
+test_suites -= set(variant.get('skip-tests', {}).get(variant_platform, []))
+test_suites -= set(variant.get('skip-tests', {}).get('all', []))
+
+# Add in additional tests for this platform.
+test_suites |= set(variant.get('extra-tests', {}).get(variant_platform, []))
+test_suites |= set(variant.get('extra-tests', {}).get('all', []))
+
+# Now adjust the variant's default test list with command-line arguments.
+test_suites |= set(args.run_tests.split(","))
+test_suites -= set(args.skip_tests.split(","))
+
+# Always run all enabled tests, even if earlier ones failed. But return the
+# first failed status.
+results = []
+
+# 'checks' is a superset of 'check-style'.
+if 'checks' in test_suites:
+    results.append(run_test_command([MAKE, 'check']))
+elif 'check-style' in test_suites:
+    results.append(run_test_command([MAKE, 'check-style']))
+
+if 'jittest' in test_suites:
+    results.append(run_test_command([MAKE, 'check-jit-test']))
+if 'jsapitests' in test_suites:
+    jsapi_test_binary = os.path.join(OBJDIR, 'dist', 'bin', 'jsapi-tests')
+    results.append(run_test_command([jsapi_test_binary]))
+if 'jstests' in test_suites:
+    results.append(run_test_command([MAKE, 'check-jstests']))
+
+for st in results:
+    if st != 0:
+        sys.exit(st)
--- a/js/src/devtools/automation/autospider.sh
+++ b/js/src/devtools/automation/autospider.sh
@@ -1,257 +1,3 @@
 #!/bin/bash
 
-# Note that the -x will be temporarily cancelled and reinstated below, so if
-# you want to eliminate this, you'll need to eliminate it there too.
-set -x
-set -e
-
-DIR="$(dirname $0)"
-ABSDIR="$(cd $DIR; pwd)"
-SOURCE="$(cd $DIR/../../../..; pwd)"
-
-function usage() {
-  echo "Usage: $0 [--dep] <variant>"
-}
-
-clean=1
-platform=""
-# 3 hours. OS X doesn't support the "sleep 3h" syntax.
-TIMEOUT=10800
-while [ $# -gt 1 ]; do
-    case "$1" in
-        --dep)
-            shift
-            clean=""
-            ;;
-        --platform)
-            shift
-            platform="$1"
-            shift
-            ;;
-        --timeout)
-            shift
-            TIMEOUT="$1"
-            shift
-            ;;
-        *)
-            echo "Invalid arguments" >&2
-            usage
-            exit 1
-            ;;
-    esac
-done
-
-VARIANT=$1
-
-# 'generational' is being retired in favor of 'compacting', but we need to
-# decouple the landings.
-if [[ "$VARIANT" = "generational" ]]; then
-    VARIANT=compacting
-fi
-
-if [ ! -f "$ABSDIR/variants/$VARIANT" ]; then
-    echo "Could not find variant '$VARIANT'"
-    usage
-    exit 1
-fi
-
-if [[ "$VARIANT" = "nonunified" ]]; then
-    # Hack the moz.build files to turn off unified compilation.
-    find "$SOURCE/js/src" -name moz.build -exec sed -i 's/UNIFIED_SOURCES/SOURCES/' '{}' ';'
-fi
-
-(cd "$SOURCE/js/src"; autoconf-2.13 || autoconf2.13 || autoconf213)
-
-TRY_OVERRIDE=$SOURCE/js/src/config.try
-if [ -r $TRY_OVERRIDE ]; then
-  CONFIGURE_ARGS="$(cat "$TRY_OVERRIDE")"
-else
-  CONFIGURE_ARGS="$(cat "$ABSDIR/variants/$VARIANT")"
-fi
-
-OBJDIR="${OBJDIR:-$SOURCE/obj-spider}"
-
-if [ -n "$clean" ]; then
-  [ -d "$OBJDIR" ] && rm -rf "$OBJDIR"
-  mkdir "$OBJDIR"
-else
-  [ -d "$OBJDIR" ] || mkdir "$OBJDIR"
-fi
-cd "$OBJDIR"
-
-echo "OBJDIR is $OBJDIR"
-
-USE_64BIT=false
-
-if [[ "$OSTYPE" == darwin* ]]; then
-  USE_64BIT=true
-  if [ "$VARIANT" = "arm-sim-osx" ]; then
-    USE_64BIT=false
-  fi
-  source "$ABSDIR/macbuildenv.sh"
-elif [ "$OSTYPE" = "linux-gnu" ]; then
-  if [ -n "$AUTOMATION" ]; then
-      GCCDIR="${GCCDIR:-$SOURCE/../gcc}"
-      CONFIGURE_ARGS="$CONFIGURE_ARGS --with-ccache"
-  fi
-  UNAME_M=$(uname -m)
-  MAKEFLAGS=-j4
-  if [ "$VARIANT" = "arm-sim" ]; then
-    USE_64BIT=false
-  elif [ "$VARIANT" = "arm64-sim" ]; then
-    USE_64BIT=true
-  else
-    case "$platform" in
-    linux64)
-      USE_64BIT=true
-      ;;
-    linux64-debug)
-      USE_64BIT=true
-      ;;
-    linux)
-      USE_64BIT=false
-      ;;
-    linux-debug)
-      USE_64BIT=false
-      ;;
-    *)
-      if [ "$UNAME_M" = "x86_64" ]; then
-        USE_64BIT=true
-      fi
-      ;;
-    esac
-  fi
-
-  if [ -n "$AUTOMATION" ]; then
-    export CC=$GCCDIR/bin/gcc
-    export CXX=$GCCDIR/bin/g++
-    if $USE_64BIT; then
-      export LD_LIBRARY_PATH=$GCCDIR/lib64
-    else
-      export LD_LIBRARY_PATH=$GCCDIR/lib
-    fi
-  fi
-elif [ "$OSTYPE" = "msys" ]; then
-  case "$platform" in
-  win64*)
-    USE_64BIT=true
-    ;;
-  *)
-    USE_64BIT=false
-    ;;
-  esac
-  MAKE=${MAKE:-mozmake}
-  source "$ABSDIR/winbuildenv.sh"
-fi
-
-MAKE=${MAKE:-make}
-
-if $USE_64BIT; then
-  NSPR64="--enable-64bit"
-  if [ "$OSTYPE" = "msys" ]; then
-    CONFIGURE_ARGS="$CONFIGURE_ARGS --target=x86_64-pc-mingw32 --host=x86_64-pc-mingw32"
-  fi
-else
-  NSPR64=""
-  if [ "$OSTYPE" == darwin* ]; then
-    export CC="${CC:-/usr/bin/clang} -arch i386"
-    export CXX="${CXX:-/usr/bin/clang++} -arch i386"
-  elif [ "$OSTYPE" != "msys" ]; then
-    export CC="${CC:-/usr/bin/gcc} -m32"
-    export CXX="${CXX:-/usr/bin/g++} -m32"
-    export AR=ar
-  fi
-  if [ "$OSTYPE" = "linux-gnu" ]; then
-    if [ "$UNAME_M" != "arm" ] && [ -n "$AUTOMATION" ]; then
-      CONFIGURE_ARGS="$CONFIGURE_ARGS --target=i686-pc-linux --host=i686-pc-linux"
-    fi
-  fi
-fi
-
-$SOURCE/js/src/configure $CONFIGURE_ARGS --enable-nspr-build --prefix=$OBJDIR/dist || exit 2
-$MAKE -s -w -j4 || exit 2
-cp -p $SOURCE/build/unix/run-mozilla.sh $OBJDIR/dist/bin
-
-COMMAND_PREFIX=''
-
-# On Linux, disable ASLR to make shell builds a bit more reproducible.
-if type setarch >/dev/null 2>&1; then
-    COMMAND_PREFIX="setarch $(uname -m) -R "
-fi
-
-RUN_JSTESTS=true
-RUN_JITTEST=true
-RUN_JSAPITESTS=true
-: ${RUN_CHECK_STYLE_ONLY:=false}
-: ${RUN_MAKE_CHECKS:=true}
-
-PARENT=$$
-
-# Spawn off a child process, detached from any of our fds, that will kill us after a timeout.
-# To report the timeout, catch the signal in the parent before exiting.
-sh -c "sleep $TIMEOUT; kill -INT $PARENT" <&- >&- 2>&- &
-KILLER=$!
-disown %1
-set +x
-trap "echo 'TEST-UNEXPECTED-FAIL | autospider.sh $TIMEOUT timeout | ignore later failures' >&2; exit 1" INT
-set -x
-
-# If we do *not* hit that timeout, kill off the spawned process on a regular exit.
-trap "kill $KILLER" EXIT
-
-if [[ "$VARIANT" = "rootanalysis" ]]; then
-    export JS_GC_ZEAL=7
-    export JSTESTS_EXTRA_ARGS=--jitflags=debug
-elif [[ "$VARIANT" = "compacting" ]]; then
-    export JS_GC_ZEAL=14
-
-    # Ignore timeouts from tests that are known to take too long with this zeal mode.
-    # Run jittests with reduced jitflags option (3 configurations).
-    # Run jstests with default jitflags option (1 configuration).
-    export JITTEST_EXTRA_ARGS="--jitflags=debug --ignore-timeouts=$ABSDIR/cgc-jittest-timeouts.txt"
-    export JSTESTS_EXTRA_ARGS="--exclude-file=$ABSDIR/cgc-jstests-slow.txt"
-
-    case "$platform" in
-    win*)
-        RUN_JSTESTS=false
-    esac
-elif [[ "$VARIANT" = "warnaserr" ||
-        "$VARIANT" = "warnaserrdebug" ||
-        "$VARIANT" = "plain" ]]; then
-    export JSTESTS_EXTRA_ARGS=--jitflags=all
-elif [[ "$VARIANT" = "arm-sim" ||
-        "$VARIANT" = "arm-sim-osx" ||
-        "$VARIANT" = "plaindebug" ]]; then
-    export JSTESTS_EXTRA_ARGS=--jitflags=debug
-elif [[ "$VARIANT" = "nonunified" ]]; then
-    RUN_JSTESTS=false
-    RUN_JITTEST=false
-    RUN_CHECK_STYLE_ONLY=true
-elif [[ "$VARIANT" = arm64* ]]; then
-    # The ARM64 simulator is slow, so some tests are timing out.
-    # Run a reduced set of test cases so this doesn't take hours.
-    export JSTESTS_EXTRA_ARGS="--exclude-file=$ABSDIR/arm64-jstests-slow.txt"
-    export JITTEST_EXTRA_ARGS="--jitflags=none --args=--baseline-eager -x ion/ -x asm.js/"
-fi
-
-if $RUN_MAKE_CHECKS; then
-    if $RUN_CHECK_STYLE_ONLY; then
-        $COMMAND_PREFIX $MAKE check-style || exit 1
-    else
-        $COMMAND_PREFIX $MAKE check || exit 1
-    fi
-fi
-
-RESULT=0
-
-if $RUN_JITTEST; then
-    $COMMAND_PREFIX $MAKE check-jit-test || RESULT=$?
-fi
-if $RUN_JSAPITESTS; then
-    $COMMAND_PREFIX $OBJDIR/dist/bin/jsapi-tests || RESULT=$?
-fi
-if $RUN_JSTESTS; then
-    $COMMAND_PREFIX $MAKE check-jstests || RESULT=$?
-fi
-
-exit $RESULT
+exec python2.7 "$(dirname $0)/autospider.py" "$@"
--- a/js/src/devtools/automation/variants/arm-sim
+++ b/js/src/devtools/automation/variants/arm-sim
@@ -1,6 +1,4 @@
---enable-optimize
---enable-debug
---enable-stdcxx-compat
---enable-simulator=arm
---target=i686-pc-linux
---host=i686-pc-linux
+{
+    "configure-args": "--enable-optimize --enable-debug --enable-stdcxx-compat --enable-simulator=arm --target=i686-pc-linux --host=i686-pc-linux",
+    "bits": 32
+}
--- a/js/src/devtools/automation/variants/arm-sim-osx
+++ b/js/src/devtools/automation/variants/arm-sim-osx
@@ -1,6 +1,4 @@
---enable-optimize
---enable-debug
---enable-stdcxx-compat
---enable-simulator=arm
---target=i686-apple-darwin10.0.0
---host=i686-apple-darwin10.0.0
+{
+    "configure-args": "--enable-optimize --enable-debug --enable-stdcxx-compat --enable-simulator=arm --target=i686-apple-darwin10.0.0 --host=i686-apple-darwin10.0.0",
+    "bits": 32
+}
--- a/js/src/devtools/automation/variants/arm64-sim
+++ b/js/src/devtools/automation/variants/arm64-sim
@@ -1,4 +1,8 @@
---enable-optimize
---enable-debug
---enable-stdcxx-compat
---enable-simulator=arm64
+{
+    "configure-args": "--enable-optimize --enable-debug --enable-stdcxx-compat --enable-simulator=arm64",
+    "env": {
+        "JSTESTS_EXTRA_ARGS": "--exclude-file={DIR}/arm64-jstests-slow.txt",
+        "JITTEST_EXTRA_ARGS": "--jitflags=none --args=--baseline-eager -x ion/ -x asm.js/"
+    },
+    "bits": 64
+}
--- a/js/src/devtools/automation/variants/compacting
+++ b/js/src/devtools/automation/variants/compacting
@@ -1,4 +1,12 @@
---enable-optimize
---enable-debug
---enable-stdcxx-compat
---enable-ctypes
+{
+    "configure-args": "--enable-optimize --enable-debug --enable-stdcxx-compat --enable-ctypes",
+    "env": {
+        "JS_GC_ZEAL": "Compact",
+        "JITTEST_EXTRA_ARGS": "--jitflags=debug --ignore-timeouts={DIR}/cgc-jittest-timeouts.txt",
+        "JSTESTS_EXTRA_ARGS": "--exclude-file={DIR}/cgc-jstests-slow.txt"
+    },
+    "skip-tests": {
+        "win32": ["jstests"],
+        "win64": ["jstests"]
+    }
+}
--- a/js/src/devtools/automation/variants/dtrace
+++ b/js/src/devtools/automation/variants/dtrace
@@ -1,4 +1,3 @@
---enable-optimize
---enable-debug
---enable-dtrace
---enable-debug-symbols
+{
+    "configure-args": "--enable-optimize --enable-debug --enable-dtrace --enable-debug-symbols"
+}
--- a/js/src/devtools/automation/variants/nonunified
+++ b/js/src/devtools/automation/variants/nonunified
@@ -1,1 +1,9 @@
---enable-debug
+{
+    "configure-args": "--enable-debug",
+    "skip-tests": {
+        "all": ["jstests", "jittest", "checks"]
+    },
+    "extra-tests": {
+        "all": ["check-style"]
+    }
+}
--- a/js/src/devtools/automation/variants/plain
+++ b/js/src/devtools/automation/variants/plain
@@ -1,1 +1,6 @@
---enable-optimize
+{
+    "configure-args": "--enable-optimize",
+    "env": {
+        "JSTESTS_EXTRA_ARGS": "--jitflags=all"
+    }
+}
--- a/js/src/devtools/automation/variants/plaindebug
+++ b/js/src/devtools/automation/variants/plaindebug
@@ -1,1 +1,6 @@
---enable-debug
+{
+    "configure-args": "--enable-debug",
+    "env": {
+        "JSTESTS_EXTRA_ARGS": "--jitflags=debug"
+    }
+}
--- a/js/src/devtools/automation/variants/rootanalysis
+++ b/js/src/devtools/automation/variants/rootanalysis
@@ -1,4 +1,7 @@
---enable-optimize
---enable-debug
---enable-stdcxx-compat
---enable-ctypes
+{
+    "configure-args": "--enable-optimize --enable-debug --enable-stdcxx-compat --enable-ctypes",
+    "env": {
+        "JS_GC_ZEAL": "GenerationalGC",
+        "JSTESTS_EXTRA_ARGS": "--jitflags=debug"
+    }
+}
--- a/js/src/devtools/automation/variants/warnaserr
+++ b/js/src/devtools/automation/variants/warnaserr
@@ -1,3 +1,3 @@
---enable-optimize
---enable-warnings-as-errors
-
+{
+    "configure-args": "--enable-optimize --enable-warnings-as-errors"
+}
--- a/js/src/devtools/automation/variants/warnaserrdebug
+++ b/js/src/devtools/automation/variants/warnaserrdebug
@@ -1,2 +1,4 @@
---enable-debug
---enable-warnings-as-errors
+{
+    "configure-args": "--enable-debug --enable-warnings-as-errors"
+}
+
--- a/js/src/devtools/automation/winbuildenv.sh
+++ b/js/src/devtools/automation/winbuildenv.sh
@@ -1,30 +1,30 @@
 # We will be sourcing mozconfig files, which end up calling mk_add_options with
 # various settings. We only need the variable settings they create along the
-# way.
+# way. Print them out, to be sucked up when running this file.
 mk_add_options() {
-  : do nothing
+  echo "$@"
 }
 
 topsrcdir="$SOURCE"
 
 # Tooltool installs in parent of topsrcdir for spidermonkey builds.
 # Resolve that path since the mozconfigs assume tooltool installs in
 # topsrcdir.
 VSPATH="$(cd ${topsrcdir}/.. && pwd)/vs2015u2"
 
 # When running on a developer machine, several variables will already
 # have the right settings and we will need to keep them since the
 # Windows mozconfigs overwrite them.
-export OLD_INCLUDE=$(IFS=';'; for d in $INCLUDE; do ( cd "$d" && echo -n $(pwd): ); done)
-export OLD_LIB=$(IFS=';'; for d in $LIB; do ( cd "$d" && echo -n $(pwd): ); done)
-export OLD_LIBPATH=$(IFS=';'; for d in $LIBPATH; do ( cd "$d" && echo -n $(pwd): ); done)
+echo "export ORIGINAL_INCLUDE=$INCLUDE"
+echo "export ORIGINAL_LIB=$LIB"
+echo "export ORIGINAL_LIBPATH=$LIBPATH"
 
-if $USE_64BIT; then
+if [ -n "$USE_64BIT" ]; then
   . $topsrcdir/build/win64/mozconfig.vs-latest
 else
   . $topsrcdir/build/win32/mozconfig.vs-latest
 fi
 
 # PATH also needs to point to mozmake.exe, which can come from either
 # newer mozilla-build or tooltool.
 if ! which mozmake 2>/dev/null; then
--- a/js/src/devtools/rootAnalysis/annotations.js
+++ b/js/src/devtools/rootAnalysis/annotations.js
@@ -187,28 +187,16 @@ var ignoreFunctions = {
 
     // FIXME!
     "NS_DebugBreak": true,
 
     // These are a little overzealous -- these destructors *can* GC if they end
     // up wrapping a pending exception. See bug 898815 for the heavyweight fix.
     "void js::AutoCompartment::~AutoCompartment(int32)" : true,
     "void JSAutoCompartment::~JSAutoCompartment(int32)" : true,
-    "void js::AutoClearTypeInferenceStateOnOOM::~AutoClearTypeInferenceStateOnOOM()" : true,
-
-    // Bug 948646 - the only thing AutoJSContext's constructor calls
-    // is an Init() routine whose entire body is covered with an
-    // AutoSuppressGCAnalysis. AutoSafeJSContext is the same thing, just with
-    // a different value for the 'aSafe' parameter.
-    "void mozilla::AutoJSContext::AutoJSContext(mozilla::detail::GuardObjectNotifier*)" : true,
-    "void mozilla::AutoSafeJSContext::~AutoSafeJSContext(int32)" : true,
-
-    // And these are workarounds to avoid even more analysis work,
-    // which would sadly still be needed even with bug 898815.
-    "void js::AutoCompartment::AutoCompartment(js::ExclusiveContext*, JSCompartment*)": true,
 
     // The nsScriptNameSpaceManager functions can't actually GC.  They
     // just use a PLDHashTable which has function pointers, which makes the
     // analysis think maybe they can.
     "nsGlobalNameStruct* nsScriptNameSpaceManager::LookupNavigatorName(nsAString_internal*)": true,
     "nsGlobalNameStruct* nsScriptNameSpaceManager::LookupName(nsAString_internal*, uint16**)": true,
 
     // Similar to heap snapshot mock classes, and GTests below. This posts a
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1877,17 +1877,21 @@ GCMarker::reset()
 void
 GCMarker::enterWeakMarkingMode()
 {
     MOZ_ASSERT(tag_ == TracerKindTag::Marking);
     if (linearWeakMarkingDisabled_)
         return;
 
     // During weak marking mode, we maintain a table mapping weak keys to
-    // entries in known-live weakmaps.
+    // entries in known-live weakmaps. Initialize it with the keys of marked
+    // weakmaps -- or more precisely, the keys of marked weakmaps that are
+    // mapped to not yet live values. (Once bug 1167452 implements incremental
+    // weakmap marking, this initialization step will become unnecessary, as
+    // the table will already hold all such keys.)
     if (weakMapAction() == ExpandWeakMaps) {
         tag_ = TracerKindTag::WeakMarking;
 
         for (GCZoneGroupIter zone(runtime()); !zone.done(); zone.next()) {
             for (WeakMapBase* m : zone->gcWeakMapList) {
                 if (m->marked)
                     (void) m->traceEntries(this);
             }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug1285490.js
@@ -0,0 +1,4 @@
+if (helperThreadCount() === 0)
+    quit();
+gczeal(4);
+offThreadCompileScript("let x = 1;");
--- a/js/src/jsapi-tests/testDateToLocaleString.cpp
+++ b/js/src/jsapi-tests/testDateToLocaleString.cpp
@@ -17,38 +17,38 @@ BEGIN_TEST(testDateToLocaleString)
     if (!haveIntl.toBoolean())
         return true;
 
     // Pervasive assumption: our Intl support includes "de" (German) and
     // "en" (English) and treats them differently for purposes of
     // Date.prototype.toLocale{,Date,Time}String behavior.
 
     // Start with German.
-    CHECK(JS_SetDefaultLocale(rt, "de"));
+    CHECK(JS_SetDefaultLocale(cx, "de"));
 
     // The (constrained) Date object we'll use to test behavior.
     EXEC("var d = new Date(Date.UTC(2015, 9 - 1, 17));");
 
     // Test that toLocaleString behavior changes with default locale changes.
     EXEC("var deAll = d.toLocaleString();");
 
-    CHECK(JS_SetDefaultLocale(rt, "en"));
+    CHECK(JS_SetDefaultLocale(cx, "en"));
     EXEC("if (d.toLocaleString() === deAll) \n"
          "  throw 'toLocaleString results should have changed with system locale change';");
 
     // Test that toLocaleDateString behavior changes with default locale changes.
     EXEC("var enDate = d.toLocaleDateString();");
 
-    CHECK(JS_SetDefaultLocale(rt, "de"));
+    CHECK(JS_SetDefaultLocale(cx, "de"));
     EXEC("if (d.toLocaleDateString() === enDate) \n"
          "  throw 'toLocaleDateString results should have changed with system locale change';");
 
     // Test that toLocaleTimeString behavior changes with default locale changes.
     EXEC("var deTime = d.toLocaleTimeString();");
 
-    CHECK(JS_SetDefaultLocale(rt, "en"));
+    CHECK(JS_SetDefaultLocale(cx, "en"));
     EXEC("if (d.toLocaleTimeString() === deTime) \n"
          "  throw 'toLocaleTimeString results should have changed with system locale change';");
 
-    JS_ResetDefaultLocale(rt);
+    JS_ResetDefaultLocale(cx);
     return true;
 }
 END_TEST(testDateToLocaleString)
--- a/js/src/jsapi-tests/testIntlAvailableLocales.cpp
+++ b/js/src/jsapi-tests/testIntlAvailableLocales.cpp
@@ -12,17 +12,17 @@ BEGIN_TEST(testIntlAvailableLocales)
     // This test should only attempt to run if we have Intl support.
     JS::Rooted<JS::Value> haveIntl(cx);
     EVAL("typeof Intl !== 'undefined'", &haveIntl);
     if (!haveIntl.toBoolean())
         return true;
 
     // Assumption: our Intl support always includes "de" (German) support,
     // and our Intl support *does not* natively support de-ZA-ghijk.  :-)
-    CHECK(JS_SetDefaultLocale(rt, "de-ZA-abcde-x-private"));
+    CHECK(JS_SetDefaultLocale(cx, "de-ZA-abcde-x-private"));
 
     EXEC("if (Intl.Collator().resolvedOptions().locale !== 'de-ZA-abcde-x-private') \n"
          "    throw 'unexpected default locale';");
     EXEC("var used = Intl.Collator('de-ZA-abcde').resolvedOptions().locale; \n"
          "if (used !== 'de-ZA-abcde') \n"
          "    throw 'bad locale when using truncated default: ' + used;");
     EXEC("if (Intl.Collator('de-ZA').resolvedOptions().locale !== 'de-ZA') \n"
          "    throw 'bad locale when using more-truncated default';");
@@ -36,23 +36,23 @@ BEGIN_TEST(testIntlAvailableLocales)
          "}");
     EXEC("if (Intl.Collator('de-ZA-abcde').resolvedOptions().locale !== 'de-ZA-abcde') \n"
          "    throw 'bad locale when using truncated default';");
     EXEC("if (Intl.Collator('de-ZA').resolvedOptions().locale !== 'de-ZA') \n"
          "    throw 'bad locale when using more-truncated default';");
     EXEC("if (Intl.Collator('de').resolvedOptions().locale !== 'de') \n"
          "    throw 'bad locale when using most-truncated default';");
 
-    CHECK(JS_SetDefaultLocale(rt, "en-US-u-co-phonebk"));
+    CHECK(JS_SetDefaultLocale(cx, "en-US-u-co-phonebk"));
     EXEC("if (Intl.Collator().resolvedOptions().locale !== 'en-US') \n"
          "    throw 'unexpected default locale where proposed default included a Unicode extension';");
 
-    CHECK(JS_SetDefaultLocale(rt, "this is not a language tag at all, yo"));
+    CHECK(JS_SetDefaultLocale(cx, "this is not a language tag at all, yo"));
 
     EXEC("if (Intl.Collator().resolvedOptions().locale !== 'en-GB') \n"
          "    throw 'unexpected last-ditch locale';");
     EXEC("if (Intl.Collator('en-GB').resolvedOptions().locale !== 'en-GB') \n"
          "    throw 'unexpected used locale when specified, with last-ditch locale as default';");
 
-    JS_ResetDefaultLocale(rt);
+    JS_ResetDefaultLocale(cx);
     return true;
 }
 END_TEST(testIntlAvailableLocales)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -5808,41 +5808,41 @@ JS_GetRegExpSource(JSContext* cx, Handle
     if (!RegExpToShared(cx, obj, &shared))
         return nullptr;
     return shared.re()->getSource();
 }
 
 /************************************************************************/
 
 JS_PUBLIC_API(bool)
-JS_SetDefaultLocale(JSRuntime* rt, const char* locale)
-{
-    AssertHeapIsIdle(rt);
-    return rt->setDefaultLocale(locale);
+JS_SetDefaultLocale(JSContext* cx, const char* locale)
+{
+    AssertHeapIsIdle(cx);
+    return cx->setDefaultLocale(locale);
 }
 
 JS_PUBLIC_API(void)
-JS_ResetDefaultLocale(JSRuntime* rt)
-{
-    AssertHeapIsIdle(rt);
-    rt->resetDefaultLocale();
+JS_ResetDefaultLocale(JSContext* cx)
+{
+    AssertHeapIsIdle(cx);
+    cx->resetDefaultLocale();
 }
 
 JS_PUBLIC_API(void)
-JS_SetLocaleCallbacks(JSRuntime* rt, const JSLocaleCallbacks* callbacks)
-{
-    AssertHeapIsIdle(rt);
-    rt->localeCallbacks = callbacks;
+JS_SetLocaleCallbacks(JSContext* cx, const JSLocaleCallbacks* callbacks)
+{
+    AssertHeapIsIdle(cx);
+    cx->localeCallbacks = callbacks;
 }
 
 JS_PUBLIC_API(const JSLocaleCallbacks*)
-JS_GetLocaleCallbacks(JSRuntime* rt)
+JS_GetLocaleCallbacks(JSContext* cx)
 {
     /* This function can be called by a finalizer. */
-    return rt->localeCallbacks;
+    return cx->localeCallbacks;
 }
 
 /************************************************************************/
 
 JS_PUBLIC_API(bool)
 JS_IsExceptionPending(JSContext* cx)
 {
     /* This function can be called by a finalizer. */
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -5031,47 +5031,47 @@ JS_ParseJSONWithReviver(JSContext* cx, J
 /**
  * The default locale for the ECMAScript Internationalization API
  * (Intl.Collator, Intl.NumberFormat, Intl.DateTimeFormat).
  * Note that the Internationalization API encourages clients to
  * specify their own locales.
  * The locale string remains owned by the caller.
  */
 extern JS_PUBLIC_API(bool)
-JS_SetDefaultLocale(JSRuntime* rt, const char* locale);
+JS_SetDefaultLocale(JSContext* cx, const char* locale);
 
 /**
  * Reset the default locale to OS defaults.
  */
 extern JS_PUBLIC_API(void)
-JS_ResetDefaultLocale(JSRuntime* rt);
+JS_ResetDefaultLocale(JSContext* cx);
 
 /**
  * Locale specific string conversion and error message callbacks.
  */
 struct JSLocaleCallbacks {
     JSLocaleToUpperCase     localeToUpperCase;
     JSLocaleToLowerCase     localeToLowerCase;
     JSLocaleCompare         localeCompare; // not used #if EXPOSE_INTL_API
     JSLocaleToUnicode       localeToUnicode;
 };
 
 /**
  * Establish locale callbacks. The pointer must persist as long as the
- * JSRuntime.  Passing nullptr restores the default behaviour.
+ * JSContext.  Passing nullptr restores the default behaviour.
  */
 extern JS_PUBLIC_API(void)
-JS_SetLocaleCallbacks(JSRuntime* rt, const JSLocaleCallbacks* callbacks);
+JS_SetLocaleCallbacks(JSContext* cx, const JSLocaleCallbacks* callbacks);
 
 /**
  * Return the address of the current locale callbacks struct, which may
  * be nullptr.
  */
 extern JS_PUBLIC_API(const JSLocaleCallbacks*)
-JS_GetLocaleCallbacks(JSRuntime* rt);
+JS_GetLocaleCallbacks(JSContext* cx);
 
 /************************************************************************/
 
 /*
  * Error reporting.
  */
 
 namespace JS {
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -858,17 +858,19 @@ JSRuntime::onOutOfMemoryCanGC(AllocFunct
         largeAllocationFailureCallback(largeAllocationFailureCallbackData);
     return onOutOfMemory(allocFunc, bytes, reallocPtr);
 }
 
 bool
 JSRuntime::activeGCInAtomsZone()
 {
     Zone* zone = atomsCompartment_->zone();
-    return zone->needsIncrementalBarrier() || zone->isGCScheduled() || zone->wasGCStarted();
+    return (zone->needsIncrementalBarrier() && !gc.isVerifyPreBarriersEnabled()) ||
+           zone->isGCScheduled() ||
+           zone->wasGCStarted();
 }
 
 void
 JSRuntime::setUsedByExclusiveThread(Zone* zone)
 {
     MOZ_ASSERT(!zone->usedByExclusiveThread);
     zone->usedByExclusiveThread = true;
     numExclusiveThreads++;
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -1652,17 +1652,17 @@ XPCJSRuntime::~XPCJSRuntime()
     JS_RemoveWeakPointerCompartmentCallback(Context(), WeakPointerCompartmentCallback);
 
     // Clear any pending exception.  It might be an XPCWrappedJS, and if we try
     // to destroy it later we will crash.
     SetPendingException(nullptr);
 
     JS::SetGCSliceCallback(Context(), mPrevGCSliceCallback);
 
-    xpc_DelocalizeRuntime(Runtime());
+    xpc_DelocalizeContext(Context());
 
     if (mWatchdogManager->GetWatchdog())
         mWatchdogManager->StopWatchdog();
 
     if (mCallContext)
         mCallContext->SystemIsBeingShutDown();
 
     auto rtPrivate = static_cast<PerThreadAtomCache*>(JS_GetRuntimePrivate(Runtime()));
@@ -3588,21 +3588,21 @@ XPCJSRuntime::Initialize()
     ///
     // Note we do have to retain the source code in memory for scripts compiled in
     // isRunOnce mode and compiled function bodies (from
     // JS::CompileFunction). In practice, this means content scripts and event
     // handlers.
     UniquePtr<XPCJSSourceHook> hook(new XPCJSSourceHook);
     js::SetSourceHook(cx, Move(hook));
 
-    // Set up locale information and callbacks for the newly-created runtime so
+    // Set up locale information and callbacks for the newly-created context so
     // that the various toLocaleString() methods, localeCompare(), and other
     // internationalization APIs work as desired.
-    if (!xpc_LocalizeRuntime(runtime))
-        NS_RUNTIMEABORT("xpc_LocalizeRuntime failed.");
+    if (!xpc_LocalizeContext(cx))
+        NS_RUNTIMEABORT("xpc_LocalizeContext failed.");
 
     // Register memory reporters and distinguished amount functions.
     RegisterStrongMemoryReporter(new JSMainRuntimeCompartmentsReporter());
     RegisterStrongMemoryReporter(new JSMainRuntimeTemporaryPeakReporter());
     RegisterJSMainRuntimeGCHeapDistinguishedAmount(JSMainRuntimeGCHeapDistinguishedAmount);
     RegisterJSMainRuntimeTemporaryPeakDistinguishedAmount(JSMainRuntimeTemporaryPeakDistinguishedAmount);
     RegisterJSMainRuntimeCompartmentsSystemDistinguishedAmount(JSMainRuntimeCompartmentsSystemDistinguishedAmount);
     RegisterJSMainRuntimeCompartmentsUserDistinguishedAmount(JSMainRuntimeCompartmentsUserDistinguishedAmount);
--- a/js/xpconnect/src/XPCLocale.cpp
+++ b/js/xpconnect/src/XPCLocale.cpp
@@ -48,25 +48,25 @@ struct XPCLocaleCallbacks : public JSLoc
 
   ~XPCLocaleCallbacks()
   {
     AssertThreadSafety();
     MOZ_COUNT_DTOR(XPCLocaleCallbacks);
   }
 
   /**
-   * Return the XPCLocaleCallbacks that's hidden away in |rt|. (This impl uses
-   * the locale callbacks struct to store away its per-runtime data.)
+   * Return the XPCLocaleCallbacks that's hidden away in |cx|. (This impl uses
+   * the locale callbacks struct to store away its per-context data.)
    */
   static XPCLocaleCallbacks*
-  This(JSRuntime* rt)
+  This(JSContext* cx)
   {
-    // Locale information for |rt| was associated using xpc_LocalizeRuntime;
+    // Locale information for |cx| was associated using xpc_LocalizeContext;
     // assert and double-check this.
-    const JSLocaleCallbacks* lc = JS_GetLocaleCallbacks(rt);
+    const JSLocaleCallbacks* lc = JS_GetLocaleCallbacks(cx);
     MOZ_ASSERT(lc);
     MOZ_ASSERT(lc->localeToUpperCase == LocaleToUpperCase);
     MOZ_ASSERT(lc->localeToLowerCase == LocaleToLowerCase);
     MOZ_ASSERT(lc->localeCompare == LocaleCompare);
     MOZ_ASSERT(lc->localeToUnicode == LocaleToUnicode);
 
     const XPCLocaleCallbacks* ths = static_cast<const XPCLocaleCallbacks*>(lc);
     ths->AssertThreadSafety();
@@ -83,23 +83,23 @@ struct XPCLocaleCallbacks : public JSLoc
   LocaleToLowerCase(JSContext* cx, HandleString src, MutableHandleValue rval)
   {
     return ChangeCase(cx, src, rval, ToLowerCase);
   }
 
   static bool
   LocaleToUnicode(JSContext* cx, const char* src, MutableHandleValue rval)
   {
-    return This(JS_GetRuntime(cx))->ToUnicode(cx, src, rval);
+    return This(cx)->ToUnicode(cx, src, rval);
   }
 
   static bool
   LocaleCompare(JSContext* cx, HandleString src1, HandleString src2, MutableHandleValue rval)
   {
-    return This(JS_GetRuntime(cx))->Compare(cx, src1, src2, rval);
+    return This(cx)->Compare(cx, src1, src2, rval);
   }
 
 private:
   static bool
   ChangeCase(JSContext* cx, HandleString src, MutableHandleValue rval,
              void(*changeCaseFnc)(const nsAString&, nsAString&))
   {
     nsAutoJSString autoStr;
@@ -243,26 +243,26 @@ private:
   nsCOMPtr<nsICollation> mCollation;
   nsCOMPtr<nsIUnicodeDecoder> mDecoder;
 #ifdef DEBUG
   PRThread* mThread;
 #endif
 };
 
 bool
-xpc_LocalizeRuntime(JSRuntime* rt)
+xpc_LocalizeContext(JSContext* cx)
 {
-  JS_SetLocaleCallbacks(rt, new XPCLocaleCallbacks());
+  JS_SetLocaleCallbacks(cx, new XPCLocaleCallbacks());
 
   // Set the default locale.
 
   // Check a pref to see if we should use US English locale regardless
   // of the system locale.
   if (Preferences::GetBool("javascript.use_us_english_locale", false)) {
-    return JS_SetDefaultLocale(rt, "en-US");
+    return JS_SetDefaultLocale(cx, "en-US");
   }
 
   // No pref has been found, so get the default locale from the
   // application's locale.
   nsCOMPtr<nsILocaleService> localeService =
     do_GetService(NS_LOCALESERVICE_CONTRACTID);
   if (!localeService)
     return false;
@@ -272,18 +272,18 @@ xpc_LocalizeRuntime(JSRuntime* rt)
   if (NS_FAILED(rv))
     return false;
 
   nsAutoString localeStr;
   rv = appLocale->GetCategory(NS_LITERAL_STRING(NSILOCALE_TIME), localeStr);
   MOZ_ASSERT(NS_SUCCEEDED(rv), "failed to get app locale info");
   NS_LossyConvertUTF16toASCII locale(localeStr);
 
-  return !!JS_SetDefaultLocale(rt, locale.get());
+  return JS_SetDefaultLocale(cx, locale.get());
 }
 
 void
-xpc_DelocalizeRuntime(JSRuntime* rt)
+xpc_DelocalizeContext(JSContext* cx)
 {
-  const XPCLocaleCallbacks* lc = XPCLocaleCallbacks::This(rt);
-  JS_SetLocaleCallbacks(rt, nullptr);
+  const XPCLocaleCallbacks* lc = XPCLocaleCallbacks::This(cx);
+  JS_SetLocaleCallbacks(cx, nullptr);
   delete lc;
 }
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -3649,19 +3649,19 @@ nsIPrincipal* GetObjectPrincipal(JSObjec
 namespace mozilla {
 namespace dom {
 extern bool
 DefineStaticJSVals(JSContext* cx);
 } // namespace dom
 } // namespace mozilla
 
 bool
-xpc_LocalizeRuntime(JSRuntime* rt);
+xpc_LocalizeContext(JSContext* cx);
 void
-xpc_DelocalizeRuntime(JSRuntime* rt);
+xpc_DelocalizeContext(JSContext* cx);
 
 /***************************************************************************/
 // Inlines use the above - include last.
 
 #include "XPCInlines.h"
 
 /***************************************************************************/
 // Maps have inlines that use the above - include last.
--- a/layout/tools/reftest/remotereftest.py
+++ b/layout/tools/reftest/remotereftest.py
@@ -249,18 +249,16 @@ class RemoteReftest(RefTest):
         # Make sure opening about:addons won't hit the network
         prefs["extensions.webservice.discoverURL"] = "http://127.0.0.1:8888/extensions-dummy/discoveryURL"
         # Make sure AddonRepository won't hit the network
         prefs["extensions.getAddons.maxResults"] = 0
         prefs["extensions.getAddons.get.url"] = "http://127.0.0.1:8888/extensions-dummy/repositoryGetURL"
         prefs["extensions.getAddons.getWithPerformance.url"] = "http://127.0.0.1:8888/extensions-dummy/repositoryGetWithPerformanceURL"
         prefs["extensions.getAddons.search.browseURL"] = "http://127.0.0.1:8888/extensions-dummy/repositoryBrowseURL"
         prefs["extensions.getAddons.search.url"] = "http://127.0.0.1:8888/extensions-dummy/repositorySearchURL"
-        # Make sure that opening the plugins check page won't hit the network
-        prefs["plugins.update.url"] = "http://127.0.0.1:8888/plugins-dummy/updateCheckURL"
         # Make sure the GMPInstallManager won't hit the network
         prefs["media.gmp-manager.url.override"] = "http://127.0.0.1:8888/dummy-gmp-manager.xml";
         prefs["layout.css.devPixelsPerPx"] = "1.0"
         # Because Fennec is a little wacky (see bug 1156817) we need to load the
         # reftest pages at 1.0 zoom, rather than zooming to fit the CSS viewport.
         prefs["apz.allow_zooming"] = False
 
         # Disable skia-gl: see bug 907351
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoAppShell.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoAppShell.java
@@ -244,19 +244,18 @@ public class GeckoAppShell
     public static void removeObserver(String observerKey) {
         sendEventToGecko(GeckoEvent.createRemoveObserverEvent(observerKey));
     }
     public static native void onSurfaceTextureFrameAvailable(Object surfaceTexture, int id);
     public static native void dispatchMemoryPressure();
 
     private static native void reportJavaCrash(String stackTrace);
 
-    public static void notifyUriVisited(String uri) {
-        sendEventToGecko(GeckoEvent.createVisitedEvent(uri));
-    }
+    @WrapForJNI
+    public static native void notifyUriVisited(String uri);
 
     public static native void notifyBatteryChange(double aLevel, boolean aCharging, double aRemainingTime);
 
     public static native void invalidateAndScheduleComposite();
 
     public static native float computeRenderIntegrity();
 
     public static native SurfaceBits getSurfaceBits(Surface surface);
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoEvent.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoEvent.java
@@ -64,17 +64,16 @@ public class GeckoEvent {
     // AndroidGeckoEvent in widget/android/AndroidJavaWrappers.h
     @JNITarget
     public enum NativeGeckoEvent {
         NATIVE_POKE(0),
         MOTION_EVENT(2),
         LOAD_URI(12),
         NOOP(15),
         VIEWPORT(20),
-        VISITED(21),
         NETWORK_CHANGED(22),
         THUMBNAIL(25),
         SCREENORIENTATION_CHANGED(27),
         NATIVE_GESTURE_EVENT(31),
         CALL_OBSERVER(33),
         REMOVE_OBSERVER(34),
         LOW_MEMORY(35),
         NETWORK_LINK_CHANGE(36),
@@ -361,22 +360,16 @@ public class GeckoEvent {
 
     public static GeckoEvent createBookmarkLoadEvent(String uri) {
         GeckoEvent event = GeckoEvent.get(NativeGeckoEvent.LOAD_URI);
         event.mCharacters = uri;
         event.mCharactersExtra = "-bookmark";
         return event;
     }
 
-    public static GeckoEvent createVisitedEvent(String data) {
-        GeckoEvent event = GeckoEvent.get(NativeGeckoEvent.VISITED);
-        event.mCharacters = data;
-        return event;
-    }
-
     public static GeckoEvent createNetworkEvent(int connectionType, boolean isWifi, int DHCPGateway, String status) {
         GeckoEvent event = GeckoEvent.get(NativeGeckoEvent.NETWORK_CHANGED);
         event.mConnectionType = connectionType;
         event.mIsWifi = isWifi;
         event.mDHCPGateway = DHCPGateway;
         event.mCharacters = status;
         return event;
     }
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoInputConnection.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoInputConnection.java
@@ -67,16 +67,19 @@ class GeckoInputConnection
     private final GeckoEditableClient mEditableClient;
     protected int mBatchEditCount;
     private ExtractedTextRequest mUpdateRequest;
     private final ExtractedText mUpdateExtract = new ExtractedText();
     private boolean mBatchSelectionChanged;
     private boolean mBatchTextChanged;
     private final InputConnection mKeyInputConnection;
 
+    // Prevent showSoftInput and hideSoftInput from causing reentrant calls on some devices.
+    private volatile boolean mSoftInputReentrancyGuard;
+
     public static GeckoEditableListener create(View targetView,
                                                GeckoEditableClient editable) {
         if (DEBUG)
             return DebugGeckoInputConnection.create(targetView, editable);
         else
             return new GeckoInputConnection(targetView, editable);
     }
 
@@ -229,41 +232,51 @@ class GeckoInputConnection
         if (view == null) {
             return null;
         }
         Context context = view.getContext();
         return InputMethods.getInputMethodManager(context);
     }
 
     private void showSoftInput() {
+        if (mSoftInputReentrancyGuard) {
+            return;
+        }
         final View v = getView();
         final InputMethodManager imm = getInputMethodManager();
         if (v == null || imm == null) {
             return;
         }
 
         v.post(new Runnable() {
             @Override
             public void run() {
                 if (v.hasFocus() && !imm.isActive(v)) {
                     // Marshmallow workaround: The view has focus but it is not the active
                     // view for the input method. (Bug 1211848)
                     v.clearFocus();
                     v.requestFocus();
                 }
+                mSoftInputReentrancyGuard = true;
                 imm.showSoftInput(v, 0);
+                mSoftInputReentrancyGuard = false;
             }
         });
     }
 
     private void hideSoftInput() {
+        if (mSoftInputReentrancyGuard) {
+            return;
+        }
         final InputMethodManager imm = getInputMethodManager();
         if (imm != null) {
             final View v = getView();
+            mSoftInputReentrancyGuard = true;
             imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
+            mSoftInputReentrancyGuard = false;
         }
     }
 
     private void restartInput() {
 
         final InputMethodManager imm = getInputMethodManager();
         if (imm == null) {
             return;
--- a/mobile/android/base/java/org/mozilla/gecko/push/PushService.java
+++ b/mobile/android/base/java/org/mozilla/gecko/push/PushService.java
@@ -71,17 +71,17 @@ public class PushService implements Bund
             onCreate(context);
         }
         return sInstance;
     }
 
     @ReflectionTarget
     public static synchronized void onCreate(Context context) {
         if (sInstance != null) {
-            throw new IllegalStateException("PushService already created!");
+            return;
         }
         sInstance = new PushService(context);
 
         sInstance.registerGeckoEventListener();
         sInstance.onStartup();
     }
 
     protected final PushManager pushManager;
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4590,16 +4590,19 @@ pref("gfx.direct2d.force-enabled", false
 pref("layers.prefer-opengl", false);
 pref("layers.prefer-d3d9", false);
 pref("layers.allow-d3d9-fallback", true);
 pref("layers.d3d11.force-warp", false);
 pref("layers.d3d11.disable-warp", true);
 
 #endif
 
+// Copy-on-write canvas
+pref("layers.shared-buffer-provider.enabled", false);
+
 // Force all possible layers to be always active layers
 pref("layers.force-active", false);
 
 // Never use gralloc surfaces, even when they're available on this
 // platform and are the optimal surface type.
 pref("layers.gralloc.disable", false);
 
 // Don't use compositor-lru on this platform
@@ -4782,17 +4785,25 @@ pref("dom.w3c_touch_events.enabled", 2);
 
 // W3C draft pointer events
 pref("dom.w3c_pointer_events.enabled", false);
 
 // W3C draft ImageCapture API
 pref("dom.imagecapture.enabled", false);
 
 // W3C touch-action css property (related to touch and pointer events)
+// Note that we turn this on even on platforms/configurations where touch
+// events are not supported (e.g. OS X, or Windows with e10s disabled). For
+// those platforms we don't handle touch events anyway so it's conceptually
+// a no-op.
+#ifdef NIGHTLY_BUILD
+pref("layout.css.touch_action.enabled", true);
+#else
 pref("layout.css.touch_action.enabled", false);
+#endif
 
 // Enables some assertions in nsStyleContext that are too expensive
 // for general use, but might be useful to enable for specific tests.
 // This only has an effect in DEBUG-builds.
 pref("layout.css.expensive-style-struct-assertions.enabled", false);
 
 // enable JS dump() function.
 pref("browser.dom.window.dump.enabled", false);
--- a/mozglue/android/jni-stubs.inc
+++ b/mozglue/android/jni-stubs.inc
@@ -72,16 +72,35 @@ Java_org_mozilla_gecko_GeckoAppShell_rep
 #endif
 
 #ifdef JNI_BINDINGS
   xul_dlsym("Java_org_mozilla_gecko_GeckoAppShell_reportJavaCrash", &f_Java_org_mozilla_gecko_GeckoAppShell_reportJavaCrash);
 #endif
 
 #ifdef JNI_STUBS
 
+typedef void (*Java_org_mozilla_gecko_GeckoAppShell_notifyUriVisited_t)(JNIEnv *, jclass, jstring);
+static Java_org_mozilla_gecko_GeckoAppShell_notifyUriVisited_t f_Java_org_mozilla_gecko_GeckoAppShell_notifyUriVisited;
+extern "C" NS_EXPORT void MOZ_JNICALL
+Java_org_mozilla_gecko_GeckoAppShell_notifyUriVisited(JNIEnv * arg0, jclass arg1, jstring arg2) {
+    if (!f_Java_org_mozilla_gecko_GeckoAppShell_notifyUriVisited) {
+        arg0->ThrowNew(arg0->FindClass("java/lang/UnsupportedOperationException"),
+                       "JNI Function called before it was loaded");
+        return ;
+    }
+     f_Java_org_mozilla_gecko_GeckoAppShell_notifyUriVisited(arg0, arg1, arg2);
+}
+#endif
+
+#ifdef JNI_BINDINGS
+  xul_dlsym("Java_org_mozilla_gecko_GeckoAppShell_notifyUriVisited", &f_Java_org_mozilla_gecko_GeckoAppShell_notifyUriVisited);
+#endif
+
+#ifdef JNI_STUBS
+
 typedef void (*Java_org_mozilla_gecko_GeckoAppShell_notifyBatteryChange_t)(JNIEnv *, jclass, jdouble, jboolean, jdouble);
 static Java_org_mozilla_gecko_GeckoAppShell_notifyBatteryChange_t f_Java_org_mozilla_gecko_GeckoAppShell_notifyBatteryChange;
 extern "C" NS_EXPORT void MOZ_JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_notifyBatteryChange(JNIEnv * arg0, jclass arg1, jdouble arg2, jboolean arg3, jdouble arg4) {
     if (!f_Java_org_mozilla_gecko_GeckoAppShell_notifyBatteryChange) {
         arg0->ThrowNew(arg0->FindClass("java/lang/UnsupportedOperationException"),
                        "JNI Function called before it was loaded");
         return ;
--- a/taskcluster/scripts/builder/build-haz-linux.sh
+++ b/taskcluster/scripts/builder/build-haz-linux.sh
@@ -1,26 +1,39 @@
 #!/bin/bash -ex
 
+function usage() {
+    echo "Usage: $0 [--project <shell|browser>] <workspace-dir> flags..."
+    echo "flags are treated the same way as a commit message would be"
+    echo "(as in, they are scanned for directives just like a try: ... line)"
+}
+
 PROJECT=shell
-if [[ "$1" == "--project" ]]; then
-    shift
-    PROJECT="$1"
-    shift
-fi
+WORKSPACE=
+while [[ $# -gt 0 ]]; do
+    if [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]]; then
+        usage
+        exit 0
+    elif [[ "$1" == "--project" ]]; then
+        shift
+        PROJECT="$1"
+        shift
+    elif [[ -z "$WORKSPACE" ]]; then
+        WORKSPACE=$( cd "$1" && pwd )
+        shift
+        break
+    fi
+done
+
+SCRIPT_FLAGS="$@"
 
 # Ensure all the scripts in this dir are on the path....
 DIRNAME=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
 PATH=$DIRNAME:$PATH
 
-WORKSPACE=$( cd "$1" && pwd )
-shift
-
-SCRIPT_FLAGS="$*"
-
 # Use GECKO_BASE_REPOSITORY as a signal for whether we are running in automation.
 export AUTOMATION=${GECKO_BASE_REPOSITORY:+1}
 
 : ${GECKO_DIR:=$WORKSPACE/gecko}
 : ${TOOLTOOL_MANIFEST:=browser/config/tooltool-manifests/linux64/hazard.manifest}
 : ${TOOLTOOL_CACHE:=$WORKSPACE/tt-cache}
 : ${TOOLTOOL_REPO:=https://github.com/mozilla/build-tooltool}
 
@@ -35,16 +48,18 @@ export TOOLTOOL_DIR="$WORKSPACE"
 
 # Directory to hold the (useless) object files generated by the analysis.
 export MOZ_OBJDIR="$WORKSPACE/obj-analyzed"
 mkdir -p "$MOZ_OBJDIR"
 
 tc-vcs checkout --force-clone $WORKSPACE/tooltool $TOOLTOOL_REPO $TOOLTOOL_REPO $TOOLTOOL_REV
 ( cd $TOOLTOOL_DIR; python $WORKSPACE/tooltool/tooltool.py --url https://api.pub.build.mozilla.org/tooltool/ -m $GECKO_DIR/$TOOLTOOL_MANIFEST fetch -c $TOOLTOOL_CACHE )
 
+export NO_MERCURIAL_SETUP_CHECK=1
+
 if [[ "$PROJECT" = "browser" ]]; then (
     cd "$WORKSPACE"
     . setup-ccache.sh
     # Mozbuild config:
     export MOZBUILD_STATE_PATH=$WORKSPACE/mozbuild/
     # Create .mozbuild so mach doesn't complain about this
     mkdir -p $MOZBUILD_STATE_PATH
 ) fi
--- a/taskcluster/scripts/builder/build-sm-package.sh
+++ b/taskcluster/scripts/builder/build-sm-package.sh
@@ -12,15 +12,17 @@ AUTOMATION=1 DIST=$UPLOAD_DIR $SRCDIR/js
 # Extract the tarball into a new directory in the workspace.
 
 PACKAGE_DIR=$WORK/sm-package
 mkdir -p $PACKAGE_DIR
 pushd $PACKAGE_DIR
 
 tar -xjvf $UPLOAD_DIR/mozjs-*.tar.bz2
 
+: ${PYTHON:=python2.7}
+
 # Build the freshly extracted, packaged SpiderMonkey.
 pushd ./mozjs-*/js/src
-RUN_MAKE_CHECKS=false AUTOMATION=1 ./devtools/automation/autospider.sh $SPIDERMONKEY_VARIANT
+AUTOMATION=1 $PYTHON ./devtools/automation/autospider.py --skip-tests=checks $SPIDERMONKEY_VARIANT
 popd
 
 # Copy artifacts for upload by TaskCluster
 cp -rL ./mozjs-*/obj-spider/dist/bin/{js,jsapi-tests,js-gdb.py,libmozjs*} $UPLOAD_DIR
--- a/taskcluster/scripts/builder/build-sm.sh
+++ b/taskcluster/scripts/builder/build-sm.sh
@@ -1,16 +1,18 @@
 #!/bin/bash
 
 set -x
 
 source $(dirname $0)/sm-tooltool-config.sh
 
+: ${PYTHON:=python2.7}
+
 # Run the script
-AUTOMATION=1 $SRCDIR/js/src/devtools/automation/autospider.sh $SPIDERMONKEY_VARIANT
+AUTOMATION=1 $PYTHON $SRCDIR/js/src/devtools/automation/autospider.py $SPIDERMONKEY_VARIANT
 BUILD_STATUS=$?
 
 # Ensure upload dir exists
 mkdir -p $UPLOAD_DIR
 
 # Copy artifacts for upload by TaskCluster
 cp -rL $SRCDIR/obj-spider/dist/bin/{js,jsapi-tests,js-gdb.py} $UPLOAD_DIR
 
--- a/testing/profiles/prefs_general.js
+++ b/testing/profiles/prefs_general.js
@@ -109,18 +109,16 @@ user_pref("extensions.webservice.discove
 // Make sure AddonRepository won't hit the network
 user_pref("extensions.getAddons.maxResults", 0);
 user_pref("extensions.getAddons.get.url", "http://%(server)s/extensions-dummy/repositoryGetURL");
 user_pref("extensions.getAddons.getWithPerformance.url", "http://%(server)s/extensions-dummy/repositoryGetWithPerformanceURL");
 user_pref("extensions.getAddons.search.browseURL", "http://%(server)s/extensions-dummy/repositoryBrowseURL");
 user_pref("extensions.getAddons.search.url", "http://%(server)s/extensions-dummy/repositorySearchURL");
 // Ensure blocklist updates don't hit the network
 user_pref("services.settings.server", "http://%(server)s/dummy-kinto/v1");
-// Make sure that opening the plugins check page won't hit the network
-user_pref("plugins.update.url", "http://%(server)s/plugins-dummy/updateCheckURL");
 // Make sure SNTP requests don't hit the network
 user_pref("network.sntp.pools", "%(server)s");
 // We know the SNTP request will fail, since localhost isn't listening on
 // port 135. The default number of retries (10) is excessive, but retrying
 // at least once will mean that codepath is still tested in automation.
 user_pref("network.sntp.maxRetryCount", 1);
 
 // Make sure the notification permission migration test doesn't hit the network.
--- a/testing/talos/talos/config.py
+++ b/testing/talos/talos/config.py
@@ -133,18 +133,16 @@ DEFAULTS = dict(
             'http://127.0.0.1/extensions-dummy/repositoryGetURL',
         'extensions.getAddons.getWithPerformance.url':
             'http://127.0.0.1/extensions-dummy'
             '/repositoryGetWithPerformanceURL',
         'extensions.getAddons.search.browseURL':
             'http://127.0.0.1/extensions-dummy/repositoryBrowseURL',
         'extensions.getAddons.search.url':
             'http://127.0.0.1/extensions-dummy/repositorySearchURL',
-        'plugins.update.url':
-            'http://127.0.0.1/plugins-dummy/updateCheckURL',
         'media.gmp-manager.url':
             'http://127.0.0.1/gmpmanager-dummy/update.xml',
         'extensions.systemAddon.update.url':
             'http://127.0.0.1/dummy-system-addons.xml',
         'media.navigator.enabled': True,
         'media.peerconnection.enabled': True,
         'media.navigator.permission.disabled': True,
         'media.capturestream_hints.enabled': True,
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -36386,34 +36386,40 @@
         ],
         "editing/run/multitest.html": [
           {
             "path": "editing/run/multitest.html",
             "timeout": "long",
             "url": "/editing/run/multitest.html"
           }
         ],
+        "fetch/api/basic/request-referrer.html": [
+          {
+            "path": "fetch/api/basic/request-referrer.html",
+            "url": "/fetch/api/basic/request-referrer.html"
+          }
+        ],
+        "service-workers/service-worker/client-navigate.https.html": [
+          {
+            "path": "service-workers/service-worker/client-navigate.https.html",
+            "url": "/service-workers/service-worker/client-navigate.https.html"
+          }
+        ],
         "service-workers/service-worker/controller-on-disconnect.https.html": [
           {
             "path": "service-workers/service-worker/controller-on-disconnect.https.html",
             "url": "/service-workers/service-worker/controller-on-disconnect.https.html"
           }
         ],
         "web-animations/animation-model/keyframe-effects/spacing-keyframes.html": [
           {
             "path": "web-animations/animation-model/keyframe-effects/spacing-keyframes.html",
             "url": "/web-animations/animation-model/keyframe-effects/spacing-keyframes.html"
           }
         ],
-        "service-workers/service-worker/client-navigate.https.html": [
-          {
-            "path": "service-workers/service-worker/client-navigate.https.html",
-            "url": "/service-workers/service-worker/client-navigate.https.html"
-          }
-        ],
         "web-animations/interfaces/DocumentTimeline/constructor.html": [
           {
             "path": "web-animations/interfaces/DocumentTimeline/constructor.html",
             "url": "/web-animations/interfaces/DocumentTimeline/constructor.html"
           }
         ],
         "web-animations/interfaces/DocumentTimeline/idlharness.html": [
           {
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/fetch/api/basic/request-referrer.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Fetch: fetch() respects Request referrer value</title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+  </head>
+  <body>
+    <script src="../resources/utils.js"></script>
+    <script src="request-referrer.js"></script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/fetch/api/basic/request-referrer.js
@@ -0,0 +1,28 @@
+if (this.document === undefined) {
+  importScripts("/resources/testharness.js");
+  importScripts("../resources/utils.js");
+}
+
+function testReferrer(referrer, expected) {
+  promise_test(function(test) {
+    var url = RESOURCES_DIR + "inspect-headers.py?headers=referer"
+    var req = new Request(url, { referrer: referrer });
+    return fetch(req).then(function(resp) {
+      var actual = resp.headers.get("x-request-referer");
+      if (expected) {
+        assert_equals(actual, expected, "request's referer should be: " + expected);
+        return;
+      }
+      if (actual) {
+        assert_equals(actual, "", "request's referer should be empty");
+      }
+    });
+  });
+}
+
+testReferrer("about:client", window.location.href);
+
+var fooURL = new URL("./foo", window.location).href;
+testReferrer(fooURL, fooURL);
+
+done();
--- a/testing/web-platform/tests/service-workers/service-worker/resources/referer-iframe.html
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/referer-iframe.html
@@ -20,17 +20,17 @@ window.addEventListener('message', funct
     var port = evt.ports[0];
     check_referer('request-headers.py?ignore=true',
                   host_info['HTTPS_ORIGIN'] +
                   base_path() + 'referer-iframe.html')
       .then(function() {
           return check_referer(
               'request-headers.py',
               host_info['HTTPS_ORIGIN'] +
-              base_path() + 'fetch-rewrite-worker.js');
+              base_path() + 'referer-iframe.html');
         })
       .then(function() {
           return check_referer(
               'request-headers.py?url=request-headers.py',
               host_info['HTTPS_ORIGIN'] +
               base_path() + 'fetch-rewrite-worker.js');
         })
       .then(function() { port.postMessage({results: 'finish'}); })
--- a/toolkit/content/aboutSupport.js
+++ b/toolkit/content/aboutSupport.js
@@ -361,16 +361,17 @@ var snapshotFormatters = {
     delete data.numAcceleratedWindows;
     delete data.numAcceleratedWindowsMessage;
 
     addRow("features", "asyncPanZoom",
            apzInfo.length
            ? apzInfo.join("; ")
            : localizedMsg(["apzNone"]));
     addRowFromKey("features", "webglRenderer");
+    addRowFromKey("features", "webgl2Renderer");
     addRowFromKey("features", "supportsHardwareH264", "hardwareH264");
     addRowFromKey("features", "direct2DEnabled", "#Direct2D");
 
     if ("directWriteEnabled" in data) {
       let message = data.directWriteEnabled;
       if ("directWriteVersion" in data)
         message += " (" + data.directWriteVersion + ")";
       addRow("features", "#DirectWrite", message);
--- a/toolkit/content/jar.mn
+++ b/toolkit/content/jar.mn
@@ -1,12 +1,11 @@
 toolkit.jar:
 %  content global %content/global/ contentaccessible=yes
 %  content global-platform %content/global-platform/ platform
-%  content global-region %content/global-region/
 # provide the nsTransferable in nsDragAndDrop.js to extensions that have to
 # work with Geckos from before 1.9, when there was a separate file
 %  override chrome://global/content/nsTransferable.js chrome://global/content/nsDragAndDrop.js
 *  content/global/license.html
    content/global/XPCNativeWrapper.js
    content/global/minimal-xul.css
 *  content/global/xul.css
    content/global/textbox.css
--- a/toolkit/content/plugins.css
+++ b/toolkit/content/plugins.css
@@ -25,20 +25,16 @@ div#outside {
   font-weight: bold;
 }
 
 #noplugs {
   font-size: x-large;
   font-weight: bold;
 }
 
-div#findpluginupdates {
-  margin-top: 2em;
-}
-
 .plugname {
   margin-top: 2em;
   margin-bottom: 1em;
   font-size: large;
   text-align: start;
   font-weight: bold;
 }
 
--- a/toolkit/content/plugins.html
+++ b/toolkit/content/plugins.html
@@ -7,17 +7,16 @@
 <html>
 <head>
 <script type="application/javascript">
   "use strict";
 
   var Ci = Components.interfaces;
   var strBundleService = Components.classes["@mozilla.org/intl/stringbundle;1"].getService(Ci.nsIStringBundleService);
   var pluginsbundle = strBundleService.createBundle("chrome://global/locale/plugins.properties");
-  var regionbundle = strBundleService.createBundle("chrome://global-region/locale/region.properties");
 
   document.writeln("<title>" + pluginsbundle.GetStringFromName("title_label") + "<\/title>");
 </script>
 <link rel="stylesheet" type="text/css" href="chrome://global/content/plugins.css">
 <link rel="stylesheet" type="text/css" href="chrome://global/skin/plugins.css">
 </head>
 <body>
 <div id="outside">
@@ -68,30 +67,16 @@
       id = "noplugs";
       label = "nopluginsareinstalled_label";
     }
     var enabledplugins = document.createElement("h1");
     enabledplugins.setAttribute("id", id);
     enabledplugins.appendChild(document.createTextNode(pluginsbundle.GetStringFromName(label)));
     fragment.appendChild(enabledplugins);
 
-    // "Find updates for installed plugins at " ...
-    var findpluginupdates = document.createElement("div");
-    findpluginupdates.setAttribute("id", "findpluginupdates");
-    findpluginupdates.appendChild(document.createTextNode(pluginsbundle.GetStringFromName("findpluginupdates_label") + " "));
-    fragment.appendChild(findpluginupdates);
-
-    // ... "mozilla.com/plugincheck"
-    var pluginupdates = document.createElement("a");
-    pluginupdates.setAttribute("href", regionbundle.GetStringFromName("pluginupdates_url"));
-    pluginupdates.appendChild(document.createTextNode(regionbundle.GetStringFromName("pluginupdates_label")));
-    findpluginupdates.appendChild(pluginupdates);
-
-    fragment.appendChild(document.createElement("hr"));
-
     var stateNames = {};
     ["STATE_SOFTBLOCKED",
      "STATE_BLOCKED",
      "STATE_OUTDATED",
      "STATE_VULNERABLE_UPDATE_AVAILABLE",
      "STATE_VULNERABLE_NO_UPDATE"].forEach(function(label) {
       stateNames[Ci.nsIBlocklistService[label]] = label;
     });
deleted file mode 100644
--- a/toolkit/locales/en-US/chrome/global-region/region.properties
+++ /dev/null
@@ -1,13 +0,0 @@
-# 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/.
-
-#
-#   Localizable URLs
-#
-pluginStartupMessage=Starting Plugin for type
-
-# plug-ins URLs
-pluginupdates_label=mozilla.com/plugincheck
-pluginupdates_url=http://www.mozilla.com/plugincheck/
-
--- a/toolkit/locales/en-US/chrome/global/aboutSupport.properties
+++ b/toolkit/locales/en-US/chrome/global/aboutSupport.properties
@@ -45,18 +45,18 @@ tryNewerDriver = Blocked for your graphi
 blockedGfxCard = Blocked for your graphics card because of unresolved driver issues.
 
 # LOCALIZATION NOTE The verb "blocked" here refers to a graphics feature such as "Direct2D" or "OpenGL layers".
 blockedOSVersion = Blocked for your operating system version.
 
 # LOCALIZATION NOTE The verb "blocked" here refers to a graphics feature such as "Direct2D" or "OpenGL layers".
 blockedMismatchedVersion = Blocked for your graphics driver version mismatch between registry and DLL.
 
-# LOCALIZATION NOTE In the following strings, "Direct2D", "DirectWrite" and "ClearType" 
-# are proper nouns and should not be translated. Feel free to leave english strings if 
+# LOCALIZATION NOTE In the following strings, "Direct2D", "DirectWrite" and "ClearType"
+# are proper nouns and should not be translated. Feel free to leave english strings if
 # there are no good translations, these are only used in about:support
 clearTypeParameters = ClearType Parameters
 
 compositing = Compositing
 hardwareH264 = Hardware H264 Decoding
 mainThreadNoOMTC = main thread, no OMTC
 yes = Yes
 no = No
@@ -66,16 +66,17 @@ gpuVendorID = Vendor ID
 gpuDeviceID = Device ID
 gpuSubsysID = Subsys ID
 gpuDrivers = Drivers
 gpuRAM = RAM
 gpuDriverVersion = Driver Version
 gpuDriverDate = Driver Date
 gpuActive = Active
 webglRenderer = WebGL Renderer
+webgl2Renderer = WebGL2 Renderer
 GPU1 = GPU #1
 GPU2 = GPU #2
 blocklistedBug = Blocklisted due to known issues
 # LOCALIZATION NOTE %1$S will be replaced with a bug number string.
 bugLink = bug %1$S
 # LOCALIZATION NOTE %1$S will be replaced with an arbitrary identifier
 # string that can be searched on DXR/MXR or grepped in the source tree.
 unknownFailure = Blocklisted; failure code %1$S
--- a/toolkit/locales/en-US/chrome/mozapps/extensions/extensions.dtd
+++ b/toolkit/locales/en-US/chrome/mozapps/extensions/extensions.dtd
@@ -32,20 +32,16 @@
 <!ENTITY warning.safemode.label                    "All add-ons have been disabled by safe mode.">
 <!ENTITY warning.checkcompatibility.label          "Add-on compatibility checking is disabled. You may have incompatible add-ons.">
 <!ENTITY warning.checkcompatibility.enable.label   "Enable">
 <!ENTITY warning.checkcompatibility.enable.tooltip "Enable add-on compatibility checking">
 <!ENTITY warning.updatesecurity.label              "Add-on update security checking is disabled. You may be compromised by updates.">
 <!ENTITY warning.updatesecurity.enable.label       "Enable">
 <!ENTITY warning.updatesecurity.enable.tooltip     "Enable add-on update security checking">
 
-<!-- global informations -->
-<!ENTITY info.plugincheck.label                 "Check to see if your plugins are up to date">
-<!ENTITY info.plugincheck.tooltip               "Check to see if your plugins are up to date">
-
 <!-- categories / views -->
 <!ENTITY view.search.label                    "Search">
 <!ENTITY view.discover.label                  "Get Add-ons">
 <!ENTITY view.recentUpdates.label             "Recent Updates">
 <!ENTITY view.availableUpdates.label          "Available Updates">
 
 <!-- addon updates -->
 <!ENTITY updates.checkForUpdates.label        "Check for Updates">
--- a/toolkit/locales/en-US/chrome/mozapps/update/updates.dtd
+++ b/toolkit/locales/en-US/chrome/mozapps/update/updates.dtd
@@ -1,20 +1,16 @@
 <!-- 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/. -->
 <!ENTITY  updateWizard.title              "Software Update">
 
 <!ENTITY  checking.title                  "Checking for Updates">
 <!ENTITY  updateCheck.label               "Looking for newer versions of &brandShortName;…">
 
-<!ENTITY  pluginupdatesfound.title        "Found Updates for Plugins">
-<!ENTITY  pluginupdatesfound.label        "Newer versions of one or more of your plugins were found.">
-<!ENTITY  pluginupdateslink.label         "See how to upgrade your plugins.">
-
 <!ENTITY  noupdatesfound.title            "No Updates Found">
 <!ENTITY  noupdatesautoenabled.intro      "There are no updates available. &brandShortName; will check
                                            periodically for updates.">
 <!ENTITY  noupdatesautodisabled.intro     "There are no updates available. Please check again later or enable
                                            &brandShortName;'s automatic update checking.">
 
 <!ENTITY  manualUpdate.title              "Unable to Update">
 <!ENTITY  manualUpdate.desc               "A recommended security and stability update is available, but you do
--- a/toolkit/locales/jar.mn
+++ b/toolkit/locales/jar.mn
@@ -80,18 +80,16 @@
   locale/@AB_CD@/global/videocontrols.dtd               (%chrome/global/videocontrols.dtd)
   locale/@AB_CD@/global/viewSource.dtd                  (%chrome/global/viewSource.dtd)
   locale/@AB_CD@/global/viewSource.properties           (%chrome/global/viewSource.properties)
   locale/@AB_CD@/global/webapps.properties              (%chrome/global/webapps.properties)
   locale/@AB_CD@/global/wizard.dtd                      (%chrome/global/wizard.dtd)
   locale/@AB_CD@/global/wizard.properties               (%chrome/global/wizard.properties)
   locale/@AB_CD@/global/crashes.dtd                     (%crashreporter/crashes.dtd)
   locale/@AB_CD@/global/crashes.properties              (%crashreporter/crashes.properties)
-% locale global-region @AB_CD@ %locale/@AB_CD@/global-region/
-  locale/@AB_CD@/global-region/region.properties        (%chrome/global-region/region.properties)
 % locale global-platform @AB_CD@ %locale/@AB_CD@/global-platform/
   locale/@AB_CD@/global-platform/mac/platformKeys.properties  (%chrome/global-platform/mac/platformKeys.properties)
   locale/@AB_CD@/global-platform/unix/platformKeys.properties (%chrome/global-platform/unix/platformKeys.properties)
   locale/@AB_CD@/global-platform/win/platformKeys.properties  (%chrome/global-platform/win/platformKeys.properties)
   locale/@AB_CD@/global-platform/mac/intl.properties          (%chrome/global-platform/mac/intl.properties)
   locale/@AB_CD@/global-platform/unix/intl.properties         (%chrome/global-platform/unix/intl.properties)
   locale/@AB_CD@/global-platform/win/intl.properties          (%chrome/global-platform/win/intl.properties)
 % locale mozapps @AB_CD@ %locale/@AB_CD@/mozapps/
--- a/toolkit/modules/Troubleshoot.jsm
+++ b/toolkit/modules/Troubleshoot.jsm
@@ -415,54 +415,66 @@ var dataProviders = {
       }
       catch (e) {}
     }
 
     if (("direct2DEnabled" in data) && !data.direct2DEnabled)
       data.direct2DEnabledMessage =
         statusMsgForFeature(Ci.nsIGfxInfo.FEATURE_DIRECT2D);
 
+
     let doc =
       Cc["@mozilla.org/xmlextras/domparser;1"]
       .createInstance(Ci.nsIDOMParser)
       .parseFromString("<html/>", "text/html");
 
-    let canvas = doc.createElement("canvas");
-    canvas.width = 1;
-    canvas.height = 1;
+    function GetWebGLInfo(contextType) {
+        let canvas = doc.createElement("canvas");
+        canvas.width = 1;
+        canvas.height = 1;
+
+
+        let creationError = "(no info)";
+
+        canvas.addEventListener(
+            "webglcontextcreationerror",
 
-    let gl;
-    try {
-      gl = canvas.getContext("experimental-webgl");
-    } catch(e) {}
+            function(e) {
+                creationError = e.statusMessage;
+            },
+
+            false
+        );
+
+        let gl = canvas.getContext(contextType);
+        if (!gl)
+            return creationError;
 
-    if (gl) {
-      let ext = gl.getExtension("WEBGL_debug_renderer_info");
-      // this extension is unconditionally available to chrome. No need to check.
-      data.webglRenderer = gl.getParameter(ext.UNMASKED_VENDOR_WEBGL)
-                           + " -- "
-                           + gl.getParameter(ext.UNMASKED_RENDERER_WEBGL);
-    } else {
-      let feature;
-      if (AppConstants.platform == "win") {
-        // If ANGLE is not available but OpenGL is, we want to report on the
-        // OpenGL feature, because that's what's going to get used.  In all
-        // other cases we want to report on the ANGLE feature.
-        let angle = gfxInfo.getFeatureStatus(gfxInfo.FEATURE_WEBGL_ANGLE) ==
-                    gfxInfo.FEATURE_STATUS_OK;
-        let opengl = gfxInfo.getFeatureStatus(gfxInfo.FEATURE_WEBGL_OPENGL) ==
-                     gfxInfo.FEATURE_STATUS_OK;
-        feature = !angle && opengl ? gfxInfo.FEATURE_WEBGL_OPENGL :
-                                     gfxInfo.FEATURE_WEBGL_ANGLE;
-      } else {
-        feature = gfxInfo.FEATURE_WEBGL_OPENGL;
-      }
-      data.webglRendererMessage = statusMsgForFeature(feature);
+
+        let infoExt = gl.getExtension("WEBGL_debug_renderer_info");
+        // This extension is unconditionally available to chrome. No need to check.
+        let vendor = gl.getParameter(infoExt.UNMASKED_VENDOR_WEBGL);
+        let renderer = gl.getParameter(infoExt.UNMASKED_RENDERER_WEBGL);
+
+        let contextInfo = vendor + " -- " + renderer;
+
+
+        // Eagerly free resources.
+        let loseExt = gl.getExtension("WEBGL_lose_context");
+        loseExt.loseContext();
+
+
+        return contextInfo;
     }
 
+
+    data.webglRenderer = GetWebGLInfo("webgl");
+    data.webgl2Renderer = GetWebGLInfo("webgl2");
+
+
     let infoInfo = gfxInfo.getInfo();
     if (infoInfo)
       data.info = infoInfo;
 
     let failureCount = {};
     let failureIndices = {};
 
     let failures = gfxInfo.getFailures(failureCount, failureIndices);
--- a/toolkit/modules/tests/browser/browser_Troubleshoot.js
+++ b/toolkit/modules/tests/browser/browser_Troubleshoot.js
@@ -292,16 +292,19 @@ const SNAPSHOT_SCHEMA = {
           type: "string",
         },
         clearTypeParameters: {
           type: "string",
         },
         webglRenderer: {
           type: "string",
         },
+        webgl2Renderer: {
+          type: "string",
+        },
         info: {
           type: "object",
         },
         failures: {
           type: "array",
           items: {
             type: "string",
           },
@@ -310,19 +313,16 @@ const SNAPSHOT_SCHEMA = {
           type: "object",
         },
         crashGuards: {
           type: "array",
         },
         direct2DEnabledMessage: {
           type: "array",
         },
-        webglRendererMessage: {
-          type: "array",
-        },
       },
     },
     javaScript: {
       required: true,
       type: "object",
       properties: {
         incrementalGCEnabled: {
           type: "boolean",
--- a/toolkit/mozapps/extensions/content/extensions.css
+++ b/toolkit/mozapps/extensions/content/extensions.css
@@ -193,20 +193,16 @@ setting[type="menulist"] {
 
 /* Plugins aren't yet disabled by safemode (bug 342333),
    so don't show that warning when viewing plugins. */
 #addons-page[warning="safemode"] .view-pane[type="plugin"] .global-warning-container,
 #addons-page[warning="safemode"] #detail-view[loading="true"] .global-warning {
   display: none;
 }
 
-#addons-page .view-pane:not([type="plugin"]) .plugin-info-container {
-  display: none;
-}
-
 #addons-page .view-pane:not([type="experiment"]) .experiment-info-container {
   display: none;
 }
 
 .addon .relnotes {
   -moz-user-select: text;
 }
 #detail-name, #detail-desc, #detail-fulldesc {
--- a/toolkit/mozapps/extensions/content/extensions.js
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -884,25 +884,16 @@ var gViewController = {
       isEnabled: function() {
         return true;
       },
       doCommand: function() {
         AddonManager.checkUpdateSecurity = true;
       }
     },
 
-    cmd_pluginCheck: {
-      isEnabled: function() {
-        return true;
-      },
-      doCommand: function() {
-        openURL(Services.urlFormatter.formatURLPref("plugins.update.url"));
-      }
-    },
-
     cmd_toggleAutoUpdateDefault: {
       isEnabled: function() {
         return true;
       },
       doCommand: function() {
         if (!AddonManager.updateEnabled || !AddonManager.autoUpdateDefault) {
           // One or both of the prefs is false, i.e. the checkbox is not checked.
           // Now toggle both to true. If the user wants us to auto-update
@@ -3246,17 +3237,17 @@ var gDetailView = {
       } else if (this._addon.blocklistState == Ci.nsIBlocklistService.STATE_OUTDATED) {
         this.node.setAttribute("notification", "warning");
         document.getElementById("detail-warning").textContent = gStrings.ext.formatStringFromName(
           "details.notification.outdated",
           [this._addon.name], 1
         );
         let warningLink = document.getElementById("detail-warning-link");
         warningLink.value = gStrings.ext.GetStringFromName("details.notification.outdated.link");
-        warningLink.href = Services.urlFormatter.formatURLPref("plugins.update.url");
+        warningLink.href = this._addon.blocklistURL;
         warningLink.hidden = false;
       } else if (this._addon.blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE) {
         this.node.setAttribute("notification", "error");
         document.getElementById("detail-error").textContent = gStrings.ext.formatStringFromName(
           "details.notification.vulnerableUpdatable",
           [this._addon.name], 1
         );
         let errorLink = document.getElementById("detail-error-link");
--- a/toolkit/mozapps/extensions/content/extensions.xml
+++ b/toolkit/mozapps/extensions/content/extensions.xml
@@ -1278,27 +1278,27 @@
               this._warningBtn.hidden = true;
             } else if (!isUpgrade && this.mAddon.blocklistState == Ci.nsIBlocklistService.STATE_OUTDATED) {
               this.setAttribute("notification", "warning");
               this._warning.textContent = gStrings.ext.formatStringFromName(
                 "notification.outdated",
                 [this.mAddon.name], 1
               );
               this._warningLink.value = gStrings.ext.GetStringFromName("notification.outdated.link");
-              this._warningLink.href = Services.urlFormatter.formatURLPref("plugins.update.url");
+              this._warningLink.href = this.mAddon.blocklistURL;
               this._warningLink.hidden = false;
               this._warningBtn.hidden = true;
             } else if (!isUpgrade && this.mAddon.blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE) {
               this.setAttribute("notification", "error");
               this._error.textContent = gStrings.ext.formatStringFromName(
                 "notification.vulnerableUpdatable",
                 [this.mAddon.name], 1
               );
               this._errorLink.value = gStrings.ext.GetStringFromName("notification.vulnerableUpdatable.link");
-              this._errorLink.href = Services.urlFormatter.formatURLPref("plugins.update.url");
+              this._errorLink.href = this.mAddon.blocklistURL;
               this._errorLink.hidden = false;
             } else if (!isUpgrade && this.mAddon.blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE) {
               this.setAttribute("notification", "error");
               this._error.textContent = gStrings.ext.formatStringFromName(
                 "notification.vulnerableNoUpdate",
                 [this.mAddon.name], 1
               );
               this._errorLink.value = gStrings.ext.GetStringFromName("notification.vulnerableNoUpdate.link");
--- a/toolkit/mozapps/extensions/content/extensions.xul
+++ b/toolkit/mozapps/extensions/content/extensions.xul
@@ -82,17 +82,16 @@
     <command id="cmd_goToDiscoverPane"/>
     <command id="cmd_goToRecentUpdates"/>
     <command id="cmd_goToAvailableUpdates"/>
     <command id="cmd_installFromFile"/>
     <command id="cmd_debugAddons"/>
     <command id="cmd_back"/>
     <command id="cmd_forward"/>
     <command id="cmd_enableCheckCompatibility"/>
-    <command id="cmd_pluginCheck"/>
     <command id="cmd_enableUpdateSecurity"/>
     <command id="cmd_toggleAutoUpdateDefault"/>
     <command id="cmd_resetAddonAutoUpdate"/>
     <command id="cmd_experimentsLearnMore"/>
     <command id="cmd_experimentsOpenTelemetryPreferences"/>
     <command id="cmd_showUnsignedExtensions"/>
     <command id="cmd_showAllExtensions"/>
   </commandset>
@@ -381,25 +380,16 @@
                   </hbox>
                   <button class="button-link global-warning-updatesecurity"
                           label="&warning.updatesecurity.enable.label;"
                           tooltiptext="&warning.updatesecurity.enable.tooltip;"
                           command="cmd_enableUpdateSecurity"/>
                   <spacer flex="5000"/> <!-- Necessary to allow the message to wrap -->
                 </hbox>
               </hbox>
-              <hbox class="view-header global-info-container plugin-info-container">
-                <hbox class="global-info" flex="1" align="center">
-                  <button class="button-link global-info-plugincheck"
-                          label="&info.plugincheck.label;"
-                          tooltiptext="&info.plugincheck.tooltip;"
-                          command="cmd_pluginCheck"/>
-                  <spacer flex="5000"/> <!-- Necessary to allow the message to wrap -->
-                </hbox>
-              </hbox>
               <hbox class="view-header global-info-container experiment-info-container">
                 <hbox class="global-info" flex="1" align="center">
                   <label value="&experiment.info.label;"/>
                   <button id="experiments-learn-more"
                           label="&experiment.info.learnmore;"
                           tooltiptext="&experiment.info.learnmore;"
                           accesskey="&experiment.info.learnmore.accesskey;"
                           command="cmd_experimentsLearnMore"/>
--- a/toolkit/mozapps/extensions/nsBlocklistService.js
+++ b/toolkit/mozapps/extensions/nsBlocklistService.js
@@ -44,17 +44,16 @@ const PREF_BLOCKLIST_ITEM_URL         = 
 const PREF_BLOCKLIST_ENABLED          = "extensions.blocklist.enabled";
 const PREF_BLOCKLIST_INTERVAL         = "extensions.blocklist.interval";
 const PREF_BLOCKLIST_LEVEL            = "extensions.blocklist.level";
 const PREF_BLOCKLIST_PINGCOUNTTOTAL   = "extensions.blocklist.pingCountTotal";
 const PREF_BLOCKLIST_PINGCOUNTVERSION = "extensions.blocklist.pingCountVersion";
 const PREF_BLOCKLIST_SUPPRESSUI       = "extensions.blocklist.suppressUI";
 const PREF_ONECRL_VIA_AMO             = "security.onecrl.via.amo";
 const PREF_BLOCKLIST_UPDATE_ENABLED   = "services.blocklist.update_enabled";
-const PREF_PLUGINS_NOTIFYUSER         = "plugins.update.notifyUser";
 const PREF_GENERAL_USERAGENT_LOCALE   = "general.useragent.locale";
 const PREF_APP_DISTRIBUTION           = "distribution.id";
 const PREF_APP_DISTRIBUTION_VERSION   = "distribution.version";
 const PREF_EM_LOGGING_ENABLED         = "extensions.logging.enabled";
 const XMLURI_BLOCKLIST                = "http://www.mozilla.org/2006/addons-blocklist";
 const XMLURI_PARSE_ERROR              = "http://www.mozilla.org/newlayout/xml/parsererror.xml"
 const UNKNOWN_XPCOM_ABI               = "unknownABI";
 const URI_BLOCKLIST_DIALOG            = "chrome://mozapps/content/extensions/blocklist.xul"
@@ -1416,21 +1415,19 @@ Blocklist.prototype = {
         if (state == oldState)
           continue;
 
         if (oldState == Ci.nsIBlocklistService.STATE_BLOCKED) {
           if (state == Ci.nsIBlocklistService.STATE_SOFTBLOCKED)
             plugin.enabledState = Ci.nsIPluginTag.STATE_DISABLED;
         }
         else if (!plugin.disabled && state != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) {
-          if (state == Ci.nsIBlocklistService.STATE_OUTDATED) {
-            gPref.setBoolPref(PREF_PLUGINS_NOTIFYUSER, true);
-          }
-          else if (state != Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE &&
-                   state != Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE) {
+          if (state != Ci.nsIBlocklistService.STATE_OUTDATED &&
+              state != Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE &&
+              state != Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE) {
             addonList.push({
               name: plugin.name,
               version: plugin.version,
               icon: "chrome://mozapps/skin/plugins/pluginGeneric.png",
               disable: false,
               blocked: state == Ci.nsIBlocklistService.STATE_BLOCKED,
               item: plugin,
               url: this.getPluginBlocklistURL(plugin),
--- a/toolkit/mozapps/extensions/test/browser/browser-common.ini
+++ b/toolkit/mozapps/extensions/test/browser/browser-common.ini
@@ -90,17 +90,16 @@ skip-if = buildapp == 'mulet'
 [browser_sorting.js]
 [browser_sorting_plugins.js]
 [browser_plugin_enabled_state_locked.js]
 [browser_uninstalling.js]
 [browser_install.js]
 [browser_recentupdates.js]
 [browser_manualupdates.js]
 [browser_globalwarnings.js]
-[browser_globalinformations.js]
 [browser_eula.js]
 skip-if = buildapp == 'mulet'
 [browser_updateid.js]
 [browser_purchase.js]
 [browser_openDialog.js]
 tags = openwindow
 skip-if = os == 'win' # Disabled on Windows due to intermittent failures (bug 1135866)
 [browser_types.js]
--- a/toolkit/mozapps/extensions/test/browser/browser_details.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_details.js
@@ -1,29 +1,27 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 // Tests various aspects of the details view
 
 const { REQUIRE_SIGNING } = Components.utils.import("resource://gre/modules/addons/AddonConstants.jsm", {});
 
-const PREF_AUTOUPDATE_DEFAULT = "extensions.update.autoUpdateDefault"
+const PREF_AUTOUPDATE_DEFAULT = "extensions.update.autoUpdateDefault";
 const PREF_GETADDONS_GETSEARCHRESULTS = "extensions.getAddons.search.url";
 const SEARCH_URL = TESTROOT + "browser_details.xml";
 const PREF_EM_HOTFIX_ID = "extensions.hotfix.id";
 
 var gManagerWindow;
 var gCategoryUtilities;
 var gProvider;
 
 var gApp = document.getElementById("bundle_brand").getString("brandShortName");
 var gVersion = Services.appinfo.version;
-var gBlocklistURL = Services.urlFormatter.formatURLPref("extensions.blocklist.detailsURL");
-var gPluginURL = Services.urlFormatter.formatURLPref("plugins.update.url");
 var gDate = new Date(2010, 7, 1);
 var infoURL = Services.urlFormatter.formatURLPref("app.support.baseURL") + "unsigned-addons";
 
 function open_details(aId, aType, aCallback) {
   requestLongerTimeout(2);
 
   gCategoryUtilities.openType(aType, function() {
     var list = gManagerWindow.document.getElementById("addon-list");
@@ -654,17 +652,17 @@ add_test(function() {
     is_element_hidden(get("detail-enable-btn"), "Enable button should be hidden");
     is_element_visible(get("detail-disable-btn"), "Disable button should be visible");
     is_element_visible(get("detail-uninstall-btn"), "Remove button should be visible");
 
     is_element_visible(get("detail-warning"), "Warning message should be visible");
     is(get("detail-warning").textContent, "An important update is available for Test add-on 8.", "Warning message should be correct");
     is_element_visible(get("detail-warning-link"), "Warning link should be visible");
     is(get("detail-warning-link").value, "Update Now", "Warning link text should be correct");
-    is(get("detail-warning-link").href, gPluginURL, "Warning link should be correct");
+    is(get("detail-warning-link").href, "http://example.com/addon8@tests.mozilla.org", "Warning link should be correct");
     is_element_hidden(get("detail-error"), "Error message should be hidden");
     is_element_hidden(get("detail-error-link"), "Error link should be hidden");
     is_element_hidden(get("detail-pending"), "Pending message should be hidden");
 
     // Disable it
     EventUtils.synthesizeMouseAtCenter(get("detail-disable-btn"), {}, gManagerWindow);
     is_element_hidden(get("detail-prefs-btn"), "Preferences button should be hidden");
     is_element_visible(get("detail-enable-btn"), "Enable button should be visible");
@@ -698,17 +696,17 @@ add_test(function() {
       is_element_hidden(get("detail-enable-btn"), "Enable button should be hidden");
       is_element_visible(get("detail-disable-btn"), "Disable button should be visible");
       is_element_visible(get("detail-uninstall-btn"), "Remove button should be visible");
 
       is_element_visible(get("detail-warning"), "Warning message should be visible");
       is(get("detail-warning").textContent, "An important update is available for Test add-on 8.", "Warning message should be correct");
       is_element_visible(get("detail-warning-link"), "Warning link should be visible");
       is(get("detail-warning-link").value, "Update Now", "Warning link text should be correct");
-      is(get("detail-warning-link").href, gPluginURL, "Warning link should be correct");
+      is(get("detail-warning-link").href, "http://example.com/addon8@tests.mozilla.org", "Warning link should be correct");
       is_element_hidden(get("detail-error"), "Error message should be hidden");
       is_element_hidden(get("detail-error-link"), "Error link should be hidden");
       is_element_hidden(get("detail-pending"), "Pending message should be hidden");
 
       run_next_test();
     });
   });
 });
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/browser/browser_globalinformations.js
+++ /dev/null
@@ -1,53 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-// Bug 656269 - Add link to Mozilla plugin check from Add-ons Manager
-
-const MAIN_URL = "https://example.com/" + RELATIVE_DIR + "discovery.html";
-const PREF_PLUGINCHECKURL = "plugins.update.url";
-
-function test() {
-  waitForExplicitFinish();
-
-  Services.prefs.setCharPref(PREF_PLUGINCHECKURL, MAIN_URL);
-  registerCleanupFunction(function() {
-    Services.prefs.clearUserPref(PREF_PLUGINCHECKURL);
-  });
-
-  run_next_test();
-}
-
-function end_test() {
-  finish();
-}
-
-add_test(function() {
-  open_manager("addons://list/extension", function(aManager) {
-    info("Testing plugin check information");
-    var button = aManager.document.querySelector("#list-view button.global-info-plugincheck");
-    is_element_hidden(button, "Plugin Check message button should be hidden");
-
-    info("Changing view to plugins")
-    EventUtils.synthesizeMouseAtCenter(aManager.document.getElementById("category-plugin"), { }, aManager);
-
-    wait_for_view_load(aManager, function(aManager) {
-      var button = aManager.document.querySelector("#list-view button.global-info-plugincheck");
-      is_element_visible(button, "Plugin Check message button should be visible");
-
-      info("Clicking 'Plugin Check' button");
-      EventUtils.synthesizeMouseAtCenter(button, { }, aManager);
-      function wantLoad(url) {
-        return url != "about:blank";
-      }
-      BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false, wantLoad).then(() => {
-        is(gBrowser.currentURI.spec, Services.urlFormatter.formatURLPref("plugins.update.url"), "Plugin Check URL should match");
-
-        gBrowser.removeCurrentTab();
-        close_manager(aManager, function() {
-          run_next_test();
-        });
-      });
-    });
-  });
-});
--- a/toolkit/mozapps/extensions/test/browser/browser_list.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_list.js
@@ -11,17 +11,16 @@ const { REQUIRE_SIGNING } = Components.u
 
 var gProvider;
 var gManagerWindow;
 var gCategoryUtilities;
 
 var gApp = document.getElementById("bundle_brand").getString("brandShortName");
 var gVersion = Services.appinfo.version;
 var gBlocklistURL = Services.urlFormatter.formatURLPref("extensions.blocklist.detailsURL");
-var gPluginURL = Services.urlFormatter.formatURLPref("plugins.update.url");
 var gDate = new Date(2010, 7, 16);
 var infoURL = Services.urlFormatter.formatURLPref("app.support.baseURL") + "unsigned-addons";
 
 const EXPECTED_ADDONS = 13;
 
 var gLWTheme = {
                 id: "4",
                 version: "1",
@@ -83,16 +82,17 @@ add_task(function*() {
     operationsRequiringRestart: AddonManager.OP_NEEDS_RESTART_NONE
   }, {
     id: "addon7@tests.mozilla.org",
     blocklistURL: "http://example.com/addon7@tests.mozilla.org",
     name: "Test add-on 7",
     blocklistState: Ci.nsIBlocklistService.STATE_OUTDATED,
   }, {
     id: "addon8@tests.mozilla.org",
+    blocklistURL: "http://example.com/addon8@tests.mozilla.org",
     name: "Test add-on 8",
     blocklistState: Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE,
   }, {
     id: "addon9@tests.mozilla.org",
     blocklistURL: "http://example.com/addon9@tests.mozilla.org",
     name: "Test add-on 9",
     blocklistState: Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE,
   }, {
@@ -352,17 +352,17 @@ add_task(function*() {
   is_element_hidden(get_node(addon, "enable-btn"), "Enable button should be hidden");
   is_element_visible(get_node(addon, "disable-btn"), "Disable button should be visible");
   is_element_visible(get_node(addon, "remove-btn"), "Remove button should be visible");
 
   is_element_visible(get_node(addon, "warning"), "Warning message should be hidden");
   is(get_node(addon, "warning").textContent, "An important update is available for Test add-on 7.", "Warning message should be correct");
   is_element_visible(get_node(addon, "warning-link"), "Warning link should be visible");
   is(get_node(addon, "warning-link").value, "Update Now", "Warning link text should be correct");
-  is(get_node(addon, "warning-link").href, gPluginURL, "Warning link should be correct");
+  is(get_node(addon, "warning-link").href, "http://example.com/addon7@tests.mozilla.org", "Warning link should be correct");
   is_element_hidden(get_node(addon, "error"), "Error message should be hidden");
   is_element_hidden(get_node(addon, "error-link"), "Error link should be hidden");
   is_element_hidden(get_node(addon, "pending"), "Pending message should be hidden");
 
   info("Disabling");
   EventUtils.synthesizeMouseAtCenter(get_node(addon, "disable-btn"), {}, gManagerWindow);
   is_element_hidden(get_node(addon, "preferences-btn"), "Preferences button should be hidden");
   is_element_visible(get_node(addon, "enable-btn"), "Enable button should be visible");
@@ -389,17 +389,17 @@ add_task(function*() {
   is_element_visible(get_node(addon, "remove-btn"), "Remove button should be visible");
 
   is_element_hidden(get_node(addon, "warning"), "Warning message should be hidden");
   is_element_hidden(get_node(addon, "warning-link"), "Warning link should be hidden");
   is_element_visible(get_node(addon, "error"), "Error message should be visible");
   is(get_node(addon, "error").textContent, "Test add-on 8 is known to be vulnerable and should be updated.", "Error message should be correct");
   is_element_visible(get_node(addon, "error-link"), "Error link should be visible");
   is(get_node(addon, "error-link").value, "Update Now", "Error link text should be correct");
-  is(get_node(addon, "error-link").href, gPluginURL, "Error link should be correct");
+  is(get_node(addon, "error-link").href, "http://example.com/addon8@tests.mozilla.org", "Error link should be correct");
   is_element_hidden(get_node(addon, "pending"), "Pending message should be hidden");
 
   info("Addon 9");
   addon = items["Test add-on 9"];
   addon.parentNode.ensureElementIsVisible(addon);
   ({ name, version } = yield get_tooltip_info(addon));
   is(get_node(addon, "name").value, "Test add-on 9", "Name should be correct");
   is(name, "Test add-on 9", "Tooltip name should be correct");
@@ -660,17 +660,17 @@ add_task(function*() {
   is_element_hidden(get_node(addon, "enable-btn"), "Enable button should be hidden");
   is_element_visible(get_node(addon, "disable-btn"), "Disable button should be visible");
   is_element_visible(get_node(addon, "remove-btn"), "Remove button should be visible");
 
   is_element_visible(get_node(addon, "warning"), "Warning message should be hidden");
   is(get_node(addon, "warning").textContent, "An important update is available for Test add-on 7.", "Warning message should be correct");
   is_element_visible(get_node(addon, "warning-link"), "Warning link should be visible");
   is(get_node(addon, "warning-link").value, "Update Now", "Warning link text should be correct");
-  is(get_node(addon, "warning-link").href, gPluginURL, "Warning link should be correct");
+  is(get_node(addon, "warning-link").href, "http://example.com/addon7@tests.mozilla.org", "Warning link should be correct");
   is_element_hidden(get_node(addon, "error"), "Error message should be hidden");
   is_element_hidden(get_node(addon, "error-link"), "Error link should be hidden");
   is_element_hidden(get_node(addon, "pending"), "Pending message should be hidden");
 });
 
 // Check the add-ons are now in the right state
 add_task(function*() {
   let [a1, a2, a4] = yield promiseAddonsByIDs(["addon1@tests.mozilla.org",
--- a/toolkit/mozapps/extensions/test/xpcshell/test_bug514327_2.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug514327_2.js
@@ -30,13 +30,10 @@ function run_test() {
   if (!plugin)
     do_throw("Plugin tag not found");
 
   //run the code after the blocklist is closed
   Services.obs.notifyObservers(null, "addon-blocklist-closed", null);
   do_execute_soon(function() {
     // should be marked as outdated by the blocklist
     do_check_true(blocklist.getPluginBlocklistState(plugin, "1", "1.9") == nsIBLS.STATE_OUTDATED);
-
-    // should indicate that a warning should be shown
-    do_check_true(prefs.getBoolPref("plugins.update.notifyUser"));
   });
 }
--- a/toolkit/mozapps/extensions/test/xpcshell/test_bug514327_3.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug514327_3.js
@@ -117,30 +117,23 @@ function run_test() {
 
   // update blocklist with data that marks the plugin as outdated
   do_update_blocklist("test_bug514327_3_outdated_1.xml", test_part_1);
 }
 
 function test_part_1() {
   // plugin should now be marked as outdated
   do_check_true(gBlocklist.getPluginBlocklistState(PLUGINS[0], "1", "1.9") == nsIBLS.STATE_OUTDATED);
-  // and the notifyUser pref should be set to true
-  do_check_true(gPrefs.getBoolPref("plugins.update.notifyUser"));
-
-  // preternd the user has been notified, reset the pref
-  gPrefs.setBoolPref("plugins.update.notifyUser", false);
 
   // update blocklist with data that marks the plugin as outdated
   do_update_blocklist("test_bug514327_3_outdated_2.xml", test_part_2);
 }
 
 function test_part_2() {
   // plugin should still be marked as outdated
   do_check_true(gBlocklist.getPluginBlocklistState(PLUGINS[0], "1", "1.9") == nsIBLS.STATE_OUTDATED);
-  // and the notifyUser pref should NOT be set to true, as the plugin was already outdated
-  do_check_false(gPrefs.getBoolPref("plugins.update.notifyUser"));
 
   finish();
 }
 
 function finish() {
   gTestserver.stop(do_test_finished);
 }
--- a/toolkit/mozapps/update/content/updates.js
+++ b/toolkit/mozapps/update/content/updates.js
@@ -22,18 +22,16 @@ const PREF_APP_UPDATE_ELEVATE_NEVER     
 const PREF_APP_UPDATE_ENABLED             = "app.update.enabled";
 const PREF_APP_UPDATE_LOG                 = "app.update.log";
 const PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED = "app.update.notifiedUnsupported";
 const PREF_APP_UPDATE_TEST_LOOP           = "app.update.test.loop";
 const PREF_APP_UPDATE_URL_MANUAL          = "app.update.url.manual";
 
 const PREFBRANCH_APP_UPDATE_NEVER         = "app.update.never.";
 
-const PREF_PLUGINS_UPDATE_URL             = "plugins.update.url";
-
 const UPDATE_TEST_LOOP_INTERVAL = 2000;
 
 const URI_UPDATES_PROPERTIES  = "chrome://mozapps/locale/update/updates.properties";
 
 const STATE_DOWNLOADING       = "downloading";
 const STATE_PENDING           = "pending";
 const STATE_PENDING_SERVICE   = "pending-service";
 const STATE_PENDING_ELEVATE   = "pending-elevate";
@@ -621,71 +619,16 @@ var gCheckingPage = {
           !aIID.equals(CoI.nsISupports))
         throw CoR.NS_ERROR_NO_INTERFACE;
       return this;
     }
   }
 };
 
 /**
- * The "You have outdated plugins" page
- */
-var gPluginsPage = {
-  /**
-   * URL of the plugin updates page
-   */
-  _url: null,
-
-  /**
-   * Initialize
-   */
-  onPageShow: function() {
-    var prefs = Services.prefs;
-    if (prefs.getPrefType(PREF_PLUGINS_UPDATE_URL) == prefs.PREF_INVALID) {
-      gUpdates.wiz.goTo("noupdatesfound");
-      return;
-    }
-
-    this._url = Services.urlFormatter.formatURLPref(PREF_PLUGINS_UPDATE_URL);
-    var link = document.getElementById("pluginupdateslink");
-    link.setAttribute("href", this._url);
-
-
-    var phs = CoC["@mozilla.org/plugin/host;1"].
-                 getService(CoI.nsIPluginHost);
-    var plugins = phs.getPluginTags();
-    var blocklist = CoC["@mozilla.org/extensions/blocklist;1"].
-                      getService(CoI.nsIBlocklistService);
-
-    var hasOutdated = false;
-    for (let i = 0; i < plugins.length; i++) {
-      let pluginState = blocklist.getPluginBlocklistState(plugins[i]);
-      if (pluginState == CoI.nsIBlocklistService.STATE_OUTDATED) {
-        hasOutdated = true;
-        break;
-      }
-    }
-    if (!hasOutdated) {
-      gUpdates.wiz.goTo("noupdatesfound");
-      return;
-    }
-
-    gUpdates.setButtons(null, null, "okButton", true);
-    gUpdates.wiz.getButton("finish").focus();
-  },
-
-  /**
-   * Finish button clicked.
-   */
-  onWizardFinish: function() {
-    openURL(this._url);
-  }
-};
-
-/**
  * The "No Updates Are Available" page
  */
 var gNoUpdatesPage = {
   /**
    * Initialize
    */
   onPageShow: function() {
     LOG("gNoUpdatesPage", "onPageShow - could not select an appropriate " +
--- a/toolkit/mozapps/update/content/updates.xul
+++ b/toolkit/mozapps/update/content/updates.xul
@@ -31,37 +31,26 @@
 
   <stringbundleset id="updateSet">
     <stringbundle id="brandStrings" src="chrome://branding/locale/brand.properties"/>
     <stringbundle id="updateStrings" src="chrome://mozapps/locale/update/updates.properties"/>
   </stringbundleset>
 
   <wizardpage id="dummy" pageid="dummy" firstpage="true"/>
 
-  <wizardpage id="checking" pageid="checking" next="pluginupdatesfound"
+  <wizardpage id="checking" pageid="checking" next="noupdatesfound"
               object="gCheckingPage" onpageshow="gCheckingPage.onPageShow();">
     <updateheader label="&checking.title;"/>
     <vbox class="update-content" flex="1">
       <label>&updateCheck.label;</label>
       <separator class="thin"/>
       <progressmeter id="checkingProgress" mode="undetermined"/>
     </vbox>
   </wizardpage>
 
-  <wizardpage id="pluginupdatesfound" pageid="pluginupdatesfound"
-              object="gPluginsPage" onpageshow="gPluginsPage.onPageShow();">
-    <updateheader label="&pluginupdatesfound.title;"/>
-    <vbox class="update-content" flex="1">
-      <label>&pluginupdatesfound.label;</label>
-      <separator class="thin"/>
-      <label id="pluginupdateslink" class="text-link"
-             onclick="openUpdateURL(event);">&pluginupdateslink.label;</label>
-    </vbox>
-  </wizardpage>
-
   <wizardpage id="noupdatesfound" pageid="noupdatesfound"
               object="gNoUpdatesPage" onpageshow="gNoUpdatesPage.onPageShow();">
     <updateheader label="&noupdatesfound.title;"/>
     <vbox class="update-content" flex="1">
       <label id="noUpdatesAutoEnabled" hidden="true">&noupdatesautoenabled.intro;</label>
       <label id="noUpdatesAutoDisabled" hidden="true">&noupdatesautodisabled.intro;</label>
     </vbox>
   </wizardpage>
--- a/toolkit/mozapps/update/tests/chrome/utils.js
+++ b/toolkit/mozapps/update/tests/chrome/utils.js
@@ -81,17 +81,16 @@ Cu.import("resource://gre/modules/Servic
 
 const IS_MACOSX = ("nsILocalFileMac" in Ci);
 const IS_WIN = ("@mozilla.org/windows-registry-key;1" in Cc);
 
 // The tests have to use the pageid instead of the pageIndex due to the
 // app update wizard's access method being random.
 const PAGEID_DUMMY            = "dummy";                 // Done
 const PAGEID_CHECKING         = "checking";              // Done
-const PAGEID_PLUGIN_UPDATES   = "pluginupdatesfound";
 const PAGEID_NO_UPDATES_FOUND = "noupdatesfound";        // Done
 const PAGEID_MANUAL_UPDATE    = "manualUpdate";          // Done
 const PAGEID_UNSUPPORTED      = "unsupported";           // Done
 const PAGEID_FOUND_BASIC      = "updatesfoundbasic";     // Done
 const PAGEID_FOUND_BILLBOARD  = "updatesfoundbillboard"; // Done
 const PAGEID_DOWNLOADING      = "downloading";           // Done
 const PAGEID_ERRORS           = "errors";                // Done
 const PAGEID_ERROR_EXTRA      = "errorextra";            // Done
--- a/widget/android/AndroidJavaWrappers.cpp
+++ b/widget/android/AndroidJavaWrappers.cpp
@@ -328,21 +328,16 @@ AndroidGeckoEvent::Init(JNIEnv *jenv, jo
 
         case NETWORK_CHANGED: {
             mConnectionType = jenv->GetIntField(jobj, jConnectionTypeField);
             mIsWifi = jenv->GetBooleanField(jobj, jIsWifiField);
             mDHCPGateway = jenv->GetIntField(jobj, jDHCPGatewayField);
             break;
         }
 
-        case VISITED: {
-            ReadCharactersField(jenv);
-            break;
-        }
-
         case THUMBNAIL: {
             mMetaState = jenv->GetIntField(jobj, jMetaStateField);
             ReadPointArray(mPoints, jenv, jPoints, 1);
             mByteBuffer = new RefCountedJavaObject(jenv, jenv->GetObjectField(jobj, jByteBufferField));
             break;
         }
 
         case ZOOMEDVIEW: {
--- a/widget/android/AndroidJavaWrappers.h
+++ b/widget/android/AndroidJavaWrappers.h
@@ -603,17 +603,16 @@ protected:
 public:
     enum {
         NATIVE_POKE = 0,
         MOTION_EVENT = 2,
         LOAD_URI = 12,
         NOOP = 15,
         APZ_INPUT_EVENT = 17, // used internally in AndroidJNI/nsAppShell/nsWindow
         VIEWPORT = 20,
-        VISITED = 21,
         NETWORK_CHANGED = 22,
         THUMBNAIL = 25,
         SCREENORIENTATION_CHANGED = 27,
         NATIVE_GESTURE_EVENT = 31,
         CALL_OBSERVER = 33,
         REMOVE_OBSERVER = 34,
         LOW_MEMORY = 35,
         NETWORK_LINK_CHANGE = 36,
--- a/widget/android/GeneratedJNINatives.h
+++ b/widget/android/GeneratedJNINatives.h
@@ -50,26 +50,30 @@ const JNINativeMethod AlarmReceiver::Nat
             mozilla::jni::NativeStub<AlarmReceiver::NotifyAlarmFired_t, Impl>
             ::template Wrap<&Impl::NotifyAlarmFired>)
 };
 
 template<class Impl>
 class GeckoAppShell::Natives : public mozilla::jni::NativeImpl<GeckoAppShell, Impl>
 {
 public:
-    static const JNINativeMethod methods[4];
+    static const JNINativeMethod methods[5];
 };
 
 template<class Impl>
 const JNINativeMethod GeckoAppShell::Natives<Impl>::methods[] = {
 
     mozilla::jni::MakeNativeMethod<GeckoAppShell::NotifyObservers_t>(
             mozilla::jni::NativeStub<GeckoAppShell::NotifyObservers_t, Impl>
             ::template Wrap<&Impl::NotifyObservers>),
 
+    mozilla::jni::MakeNativeMethod<GeckoAppShell::NotifyUriVisited_t>(
+            mozilla::jni::NativeStub<GeckoAppShell::NotifyUriVisited_t, Impl>
+            ::template Wrap<&Impl::NotifyUriVisited>),
+
     mozilla::jni::MakeNativeMethod<GeckoAppShell::OnLocationChanged_t>(
             mozilla::jni::NativeStub<GeckoAppShell::OnLocationChanged_t, Impl>
             ::template Wrap<&Impl::OnLocationChanged>),
 
     mozilla::jni::MakeNativeMethod<GeckoAppShell::OnSensorChanged_t>(
             mozilla::jni::NativeStub<GeckoAppShell::OnSensorChanged_t, Impl>
             ::template Wrap<&Impl::OnSensorChanged>),
 
--- a/widget/android/GeneratedJNIWrappers.cpp
+++ b/widget/android/GeneratedJNIWrappers.cpp
@@ -568,16 +568,19 @@ auto GeckoAppShell::NetworkLinkType() ->
 constexpr char GeckoAppShell::NotifyDefaultPrevented_t::name[];
 constexpr char GeckoAppShell::NotifyDefaultPrevented_t::signature[];
 
 auto GeckoAppShell::NotifyDefaultPrevented(bool a0) -> void
 {
     return mozilla::jni::Method<NotifyDefaultPrevented_t>::Call(GeckoAppShell::Context(), nullptr, a0);
 }
 
+constexpr char GeckoAppShell::NotifyUriVisited_t::name[];
+constexpr char GeckoAppShell::NotifyUriVisited_t::signature[];
+
 constexpr char GeckoAppShell::NotifyWakeLockChanged_t::name[];
 constexpr char GeckoAppShell::NotifyWakeLockChanged_t::signature[];
 
 auto GeckoAppShell::NotifyWakeLockChanged(mozilla::jni::String::Param a0, mozilla::jni::String::Param a1) -> void
 {
     return mozilla::jni::Method<NotifyWakeLockChanged_t>::Call(GeckoAppShell::Context(), nullptr, a0, a1);
 }
 
--- a/widget/android/GeneratedJNIWrappers.h
+++ b/widget/android/GeneratedJNIWrappers.h
@@ -1193,16 +1193,30 @@ public:
                 "(Z)V";
         static const bool isStatic = true;
         static const mozilla::jni::ExceptionMode exceptionMode =
                 mozilla::jni::ExceptionMode::ABORT;
     };
 
     static auto NotifyDefaultPrevented(bool) -> void;
 
+    struct NotifyUriVisited_t {
+        typedef GeckoAppShell Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<
+                mozilla::jni::String::Param> Args;
+        static constexpr char name[] = "notifyUriVisited";
+        static constexpr char signature[] =
+                "(Ljava/lang/String;)V";
+        static const bool isStatic = true;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+    };
+
     struct NotifyWakeLockChanged_t {
         typedef GeckoAppShell Owner;
         typedef void ReturnType;
         typedef void SetterType;
         typedef mozilla::jni::Args<
                 mozilla::jni::String::Param,
                 mozilla::jni::String::Param> Args;
         static constexpr char name[] = "notifyWakeLockChanged";
--- a/widget/android/nsAppShell.cpp
+++ b/widget/android/nsAppShell.cpp
@@ -368,16 +368,29 @@ public:
             return;
         }
 
         RefPtr<nsIDOMGeoPosition> geoPosition(
                 new nsGeoPosition(aLatitude, aLongitude, aAltitude, aAccuracy,
                                   aAccuracy, aBearing, aSpeed, aTime));
         gLocationCallback->Update(geoPosition);
     }
+
+    static void NotifyUriVisited(jni::String::Param aUri)
+    {
+#ifdef MOZ_ANDROID_HISTORY
+        nsCOMPtr<IHistory> history = services::GetHistoryService();
+        nsCOMPtr<nsIURI> visitedURI;
+        if (history &&
+            NS_SUCCEEDED(NS_NewURI(getter_AddRefs(visitedURI),
+                                   aUri->ToString()))) {
+            history->NotifyVisited(visitedURI);
+        }
+#endif
+    }
 };
 
 nsAppShell::nsAppShell()
     : mSyncRunFinished(*(sAppShellLock = new Mutex("nsAppShell")),
                        "nsAppShell.SyncRun")
     , mSyncRunQuit(false)
 {
     {
@@ -818,29 +831,16 @@ nsAppShell::LegacyGeckoEvent::Run()
         if (NS_SUCCEEDED(rv))
             cmdline->Run();
         free(uri);
         if (flag)
             free(flag);
         break;
     }
 
-    case AndroidGeckoEvent::VISITED: {
-#ifdef MOZ_ANDROID_HISTORY
-        nsCOMPtr<IHistory> history = services::GetHistoryService();
-        nsCOMPtr<nsIURI> visitedURI;
-        if (history &&
-            NS_SUCCEEDED(NS_NewURI(getter_AddRefs(visitedURI),
-                                   curEvent->Characters()))) {
-            history->NotifyVisited(visitedURI);
-        }
-#endif
-        break;
-    }
-
     case AndroidGeckoEvent::NETWORK_CHANGED: {
         hal::NotifyNetworkChange(hal::NetworkInformation(curEvent->ConnectionType(),
                                                          curEvent->IsWifi(),
                                                          curEvent->DHCPGateway()));
         nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
         if (os) {
             os->NotifyObservers(nullptr,
                                 NS_NETWORK_LINK_TYPE_TOPIC,