Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 10 Jun 2014 00:45:12 -0400
changeset 187822 9dc0ffca10f4
parent 187776 294f5dd90b12 (current diff)
parent 187821 4d0a7ec4a19b (diff)
child 187827 6d943e552660
child 187841 33ce82daa572
child 187860 eb8e390f34fc
push id26934
push userryanvm@gmail.com
push dateTue, 10 Jun 2014 04:45:09 +0000
treeherdermozilla-central@9dc0ffca10f4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone33.0a1
first release with
nightly linux32
9dc0ffca10f4 / 33.0a1 / 20140610030202 / files
nightly linux64
9dc0ffca10f4 / 33.0a1 / 20140610030202 / files
nightly mac
9dc0ffca10f4 / 33.0a1 / 20140610030202 / files
nightly win32
9dc0ffca10f4 / 33.0a1 / 20140610030202 / files
nightly win64
9dc0ffca10f4 / 33.0a1 / 20140610030202 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c. a=merge
gfx/ots/ots-woff2.patch
js/src/jit-test/tests/basic/number-tointeger.js
--- a/b2g/chrome/content/devtools.js
+++ b/b2g/chrome/content/devtools.js
@@ -267,17 +267,24 @@ Target.prototype = {
    * widgets.
    */
   destroy: function target_destroy() {
     delete this.metrics;
     this._send({});
   },
 
   _send: function target_send(data) {
-    shell.sendEvent(this.frame, 'developer-hud-update', Cu.cloneInto(data, this.frame));
+    let frame = this.frame;
+
+    let systemapp = document.querySelector('#systemapp');
+    if (this.frame === systemapp) {
+      frame = getContentWindow();
+    }
+
+    shell.sendEvent(frame, 'developer-hud-update', Cu.cloneInto(data, frame));
   }
 
 };
 
 
 /**
  * The Console Watcher tracks the following metrics in apps: reflows, warnings,
  * and errors, with security errors reported separately.
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -345,19 +345,17 @@ public:
   void ReallyCloseWindow();
 
   // nsISupports
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 
   // nsWrapperCache
   virtual JSObject *WrapObject(JSContext *cx) MOZ_OVERRIDE
   {
-    NS_ASSERTION(IsOuterWindow(),
-                 "Inner window supports nsWrapperCache, fix WrapObject!");
-    return EnsureInnerWindow() ? GetWrapper() : nullptr;
+    return IsInnerWindow() || EnsureInnerWindow() ? GetWrapper() : nullptr;
   }
 
   // nsIGlobalJSObjectHolder
   virtual JSObject *GetGlobalJSObject();
 
   // nsIScriptGlobalObject
   JSObject *FastGetGlobalJSObject() const
   {
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -40,24 +40,23 @@
 #include "nsJSUtils.h"
 #include "nsDOMFile.h"
 #include "nsGlobalWindow.h"
 
 /* Using WebRTC backend on Desktops (Mac, Windows, Linux), otherwise default */
 #include "MediaEngineDefault.h"
 #if defined(MOZ_WEBRTC)
 #include "MediaEngineWebRTC.h"
+#include "browser_logging/WebRtcLog.h"
 #endif
 
 #ifdef MOZ_B2G
 #include "MediaPermissionGonk.h"
 #endif
 
-#include "browser_logging/WebRtcLog.h"
-
 // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
 // GetTickCount() and conflicts with MediaStream::GetCurrentTime.
 #ifdef GetCurrentTime
 #undef GetCurrentTime
 #endif
 
 // XXX Workaround for bug 986974 to maintain the existing broken semantics
 template<>
@@ -1572,17 +1571,19 @@ MediaManager::GetUserMedia(bool aPrivile
       array->AppendElement(callID);
     }
     nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
     nsRefPtr<GetUserMediaRequest> req = new GetUserMediaRequest(aWindow,
                                                                 callID, c, isHTTPS);
     obs->NotifyObservers(req, "getUserMedia:request", nullptr);
   }
 
+#ifdef MOZ_WEBRTC
   EnableWebRtcLog();
+#endif
 
   return NS_OK;
 }
 
 nsresult
 MediaManager::GetUserMediaDevices(nsPIDOMWindow* aWindow,
   const MediaStreamConstraints& aConstraints,
   nsIGetUserMediaDevicesSuccessCallback* aOnSuccess,
--- a/dom/src/offline/crashtests/crashtests.list
+++ b/dom/src/offline/crashtests/crashtests.list
@@ -1,1 +1,1 @@
-skip load 408431-1.html # bug 1022509 - renable when the cause of that is backed out
+load 408431-1.html
--- a/dom/system/NetworkGeolocationProvider.js
+++ b/dom/system/NetworkGeolocationProvider.js
@@ -4,29 +4,37 @@
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 
 const POSITION_UNAVAILABLE = Ci.nsIDOMGeoPositionError.POSITION_UNAVAILABLE;
-const SETTING_DEBUG_ENABLED = "geolocation.debugging.enabled";
-const SETTING_CHANGED_TOPIC = "mozsettings-changed";
+const SETTINGS_DEBUG_ENABLED = "geolocation.debugging.enabled";
+const SETTINGS_CHANGED_TOPIC = "mozsettings-changed";
+const SETTINGS_WIFI_ENABLED = "wifi.enabled";
 
 let gLoggingEnabled = false;
 
-// if we don't see any wifi responses in 5 seconds, send the request.
-let gTimeToWaitBeforeSending = 5000; //ms
+/*
+   The gLocationRequestTimeout controls how long we wait on receiving an update
+   from the Wifi subsystem.  If this timer fires, we believe the Wifi scan has
+   had a problem and we no longer can use Wifi to position the user this time
+   around (we will continue to be hopeful that Wifi will recover).
+ 
+   This timeout value is also used when Wifi scanning is disabled (see
+   gWifiScanningEnabled).  In this case, we use this timer to collect cell/ip
+   data and xhr it to the location server.
+*/
+
+let gLocationRequestTimeout = 5000;
 
 let gWifiScanningEnabled = true;
-let gWifiResults;
-
 let gCellScanningEnabled = false;
-let gCellResults;
 
 function LOG(aMsg) {
   if (gLoggingEnabled) {
     aMsg = "*** WIFI GEO: " + aMsg + "\n";
     Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService).logStringMessage(aMsg);
     dump(aMsg);
   }
 }
@@ -54,121 +62,151 @@ WifiGeoPositionObject.prototype = {
 };
 
 function WifiGeoPositionProvider() {
   try {
     gLoggingEnabled = Services.prefs.getBoolPref("geo.wifi.logging.enabled");
   } catch (e) {}
 
   try {
-    gTimeToWaitBeforeSending = Services.prefs.getIntPref("geo.wifi.timeToWaitBeforeSending");
+    gLocationRequestTimeout = Services.prefs.getIntPref("geo.wifi.timeToWaitBeforeSending");
   } catch (e) {}
 
   try {
     gWifiScanningEnabled = Services.prefs.getBoolPref("geo.wifi.scan");
   } catch (e) {}
 
   try {
     gCellScanningEnabled = Services.prefs.getBoolPref("geo.cell.scan");
   } catch (e) {}
 
   this.wifiService = null;
-  this.timeoutTimer = null;
+  this.timer = null;
   this.started = false;
 }
 
 WifiGeoPositionProvider.prototype = {
   classID:          Components.ID("{77DA64D3-7458-4920-9491-86CC9914F904}"),
   QueryInterface:   XPCOMUtils.generateQI([Ci.nsIGeolocationProvider,
                                            Ci.nsIWifiListener,
                                            Ci.nsITimerCallback,
                                            Ci.nsIObserver]),
   listener: null,
 
   observe: function(aSubject, aTopic, aData) {
-    if (aTopic != SETTING_CHANGED_TOPIC) {
+    if (aTopic != SETTINGS_CHANGED_TOPIC) {
       return;
     }
 
     try {
       let setting = JSON.parse(aData);
-      if (setting.key != SETTING_DEBUG_ENABLED) {
-          return;
+      if (setting.key == SETTINGS_DEBUG_ENABLED) {
+        gLoggingEnabled = setting.value;
+      } else if (setting.key == SETTINGS_WIFI_ENABLED) {
+        gWifiScanningEnabled = setting.value;
       }
-      gLoggingEnabled = setting.value;
     } catch (e) {
     }
   },
 
+  resetTimer: function() {
+    if (this.timer) {
+      this.timer.cancel();
+      this.timer = null;
+    }
+    // wifi thread triggers WifiGeoPositionProvider to proceed, with no wifi, do manual timeout
+    this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+    this.timer.initWithCallback(this,
+                                gLocationRequestTimeout,
+                                this.timer.TYPE_REPEATING_SLACK);
+  },
+
   startup:  function() {
     if (this.started)
       return;
 
     this.started = true;
+    let self = this;
     let settingsCallback = {
       handle: function(name, result) {
-        gLoggingEnabled = result && result.value === true ? true : false;
+        if (name == SETTINGS_DEBUG_ENABLED) {
+          gLoggingEnabled = result;
+        } else if (name == SETTINGS_WIFI_ENABLED) {
+          gWifiScanningEnabled = result;
+          if (self.wifiService) {
+            self.wifiService.stopWatching(self);
+          }
+          if (gWifiScanningEnabled) {
+            self.wifiService = Cc["@mozilla.org/wifi/monitor;1"].getService(Ci.nsIWifiMonitor);
+            self.wifiService.startWatching(self);
+          }
+        }
       },
       
       handleError: function(message) {
         gLoggingEnabled = false;
         LOG("settings callback threw an exception, dropping");
       }
     };
 
     try {
-      Services.obs.addObserver(this, SETTING_CHANGED_TOPIC, false);
+      Services.obs.addObserver(this, SETTINGS_CHANGED_TOPIC, false);
       let settings = Cc["@mozilla.org/settingsService;1"].getService(Ci.nsISettingsService);
-      settings.createLock().get(SETTING_DEBUG_ENABLED, settingsCallback);
+      settings.createLock().get(SETTINGS_WIFI_ENABLED, settingsCallback);
+      settings.createLock().get(SETTINGS_DEBUG_ENABLED, settingsCallback);
     } catch(ex) {
       // This platform doesn't have the settings interface, and that is just peachy
     }
 
     if (gWifiScanningEnabled && Cc["@mozilla.org/wifi/monitor;1"]) {
-      this.wifiService = Cc["@mozilla.org/wifi/monitor;1"].getService(Components.interfaces.nsIWifiMonitor);
+      if (this.wifiService) {
+        this.wifiService.stopWatching(this);
+      }
+      this.wifiService = Cc["@mozilla.org/wifi/monitor;1"].getService(Ci.nsIWifiMonitor);
       this.wifiService.startWatching(this);
     }
-    this.timeoutTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-    this.timeoutTimer.initWithCallback(this,
-                                       gTimeToWaitBeforeSending,
-                                       this.timeoutTimer.TYPE_REPEATING_SLACK);
+
+    this.resetTimer();
     LOG("startup called.");
   },
 
   watch: function(c) {
     this.listener = c;
   },
 
   shutdown: function() {
     LOG("shutdown called");
     if (this.started == false) {
       return;
     }
 
-    if (this.timeoutTimer) {
-      this.timeoutTimer.cancel();
-      this.timeoutTimer = null;
+    if (this.timer) {
+      this.timer.cancel();
+      this.timer = null;
     }
 
     if(this.wifiService) {
       this.wifiService.stopWatching(this);
       this.wifiService = null;
     }
 
-    Services.obs.removeObserver(this, SETTING_CHANGED_TOPIC);
+    Services.obs.removeObserver(this, SETTINGS_CHANGED_TOPIC);
 
     this.listener = null;
     this.started = false;
   },
 
   setHighAccuracy: function(enable) {
   },
 
   onChange: function(accessPoints) {
 
+    // we got some wifi data, rearm the timer.
+    this.resetTimer();
+
     function isPublic(ap) {
       let mask = "_nomap"
       let result = ap.ssid.indexOf(mask, ap.ssid.length - mask.length);
       if (result != -1) {
         LOG("Filtering out " + ap.ssid + " " + result);
       }
       return result;
     };
@@ -176,28 +214,29 @@ WifiGeoPositionProvider.prototype = {
     function sort(a, b) {
       return b.signal - a.signal;
     };
 
     function encode(ap) {
       return { 'macAddress': ap.mac, 'signalStrength': ap.signal };
     };
 
+    let wifiData = null;
     if (accessPoints) {
-      gWifiResults = accessPoints.filter(isPublic).sort(sort).map(encode);
-    } else {
-      gWifiResults = null;
+      wifiData = accessPoints.filter(isPublic).sort(sort).map(encode);
     }
+    this.sendLocationRequest(wifiData);
   },
 
   onError: function (code) {
     LOG("wifi error: " + code);
+    this.sendLocationRequest(null);
   },
 
-  updateMobileInfo: function() {
+  getMobileInfo: function() {
     LOG("updateMobileInfo called");
     try {
       let radioService = Cc["@mozilla.org/ril;1"]
                     .getService(Ci.nsIRadioInterfaceLayer);
       let numInterfaces = radioService.numRadioInterfaces;
       let result = [];
       for (let i = 0; i < numInterfaces; i++) {
         LOG("Looking for SIM in slot:" + i + " of " + numInterfaces);
@@ -211,21 +250,25 @@ WifiGeoPositionProvider.prototype = {
                       mobileCountryCode: iccInfo.mcc,
                       mobileNetworkCode: iccInfo.mnc,
                       locationAreaCode: cell.gsmLocationAreaCode,
                       cellId: cell.gsmCellId });
         }
       }
       return result;
     } catch (e) {
-      gCellResults = null;
+      return null;
     }
   },
 
-  notify: function (timeoutTimer) {
+  notify: function (timer) {
+    this.sendLocationRequest(null);
+  },
+
+  sendLocationRequest: function (wifiData) {
     let url = Services.urlFormatter.formatURLPref("geo.wifi.uri");
     let listener = this.listener;
     LOG("Sending request: " + url + "\n");
 
     let xhr = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
                         .createInstance(Ci.nsIXMLHttpRequest);
 
     listener.locationUpdatePending();
@@ -253,27 +296,27 @@ WifiGeoPositionProvider.prototype = {
 
       let newLocation = new WifiGeoPositionObject(xhr.response.location.lat,
                                                   xhr.response.location.lng,
                                                   xhr.response.accuracy);
 
       listener.update(newLocation);
     };
 
-    if (gCellScanningEnabled) {
-      this.updateMobileInfo();
+    let data = {};
+    if (wifiData) {
+      data.wifiAccessPoints = wifiData;
     }
 
-    let data = {};
-    if (gWifiResults) {
-      data.wifiAccessPoints = gWifiResults;
+    if (gCellScanningEnabled) {
+      let cellData = this.getMobileInfo();
+      if (cellData) {
+        data.cellTowers = cellData;
+      }
     }
-    if (gCellResults) {
-      data.cellTowers = gCellResults;
-    }
+
     data = JSON.stringify(data);
-    gWifiResults = gCellResults = null;
     LOG("sending " + data);
     xhr.send(data);
   },
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([WifiGeoPositionProvider]);
--- a/dom/system/gonk/GonkGPSGeolocationProvider.cpp
+++ b/dom/system/gonk/GonkGPSGeolocationProvider.cpp
@@ -698,20 +698,21 @@ GonkGPSGeolocationProvider::NetworkLocat
 
   sLastMLSPosLat = lat;
   sLastMLSPosLon = lon;
 
   // if the MLS coord change is smaller than this arbitrarily small value
   // assume the MLS coord is unchanged, and stick with the GPS location
   const double kMinMLSCoordChangeInMeters = 10;
 
-  // if we haven't seen anything from the GPS device for 1s,
+  // if we haven't seen anything from the GPS device for 10s,
   // use this network derived location.
+  const int kMaxGPSDelayBeforeConsideringMLS = 10000;
   int64_t diff = PR_Now() - provider->mLastGPSDerivedLocationTime;
-  if (provider->mLocationCallback && diff > kDefaultPeriod
+  if (provider->mLocationCallback && diff > kMaxGPSDelayBeforeConsideringMLS
       && delta > kMinMLSCoordChangeInMeters)
   {
     provider->mLocationCallback->Update(position);
   }
 
   provider->InjectLocation(lat, lon, acc);
   return NS_OK;
 }
--- a/gfx/layers/LayersLogging.cpp
+++ b/gfx/layers/LayersLogging.cpp
@@ -113,16 +113,17 @@ AppendToString(nsACString& s, const nsIn
 }
 
 nsACString&
 AppendToString(nsACString& s, const FrameMetrics& m,
                const char* pfx, const char* sfx)
 {
   s += pfx;
   AppendToString(s, m.mViewport, "{ viewport=");
+  AppendToString(s, m.mCompositionBounds, " cb=");
   AppendToString(s, m.GetScrollOffset(), " viewportScroll=");
   AppendToString(s, m.mDisplayPort, " displayport=");
   AppendToString(s, m.mCriticalDisplayPort, " critdp=");
   AppendToString(s, m.mScrollableRect, " scrollableRect=");
   AppendToString(s, m.GetScrollId(), " scrollId=", " }");
   return s += sfx;
 }
 
--- a/gfx/layers/LayersLogging.h
+++ b/gfx/layers/LayersLogging.h
@@ -11,17 +11,16 @@
 #include "mozilla/gfx/Point.h"          // for IntSize, etc
 #include "mozilla/gfx/Types.h"          // for Filter, SurfaceFormat
 #include "mozilla/layers/CompositorTypes.h"  // for TextureFlags
 #include "nsAString.h"
 #include "nsPrintfCString.h"            // for nsPrintfCString
 #include "nsRegion.h"                   // for nsIntRegion
 #include "nscore.h"                     // for nsACString, etc
 
-class gfx3DMatrix;
 struct gfxRGBA;
 struct nsIntPoint;
 struct nsIntRect;
 struct nsIntSize;
 
 namespace mozilla {
 namespace gfx {
 class Matrix4x4;
@@ -42,20 +41,16 @@ nsACString&
 AppendToString(nsACString& s, FrameMetrics::ViewID n,
                const char* pfx="", const char* sfx="");
 
 nsACString&
 AppendToString(nsACString& s, const gfxRGBA& c,
                const char* pfx="", const char* sfx="");
 
 nsACString&
-AppendToString(nsACString& s, const gfx3DMatrix& m,
-               const char* pfx="", const char* sfx="");
-
-nsACString&
 AppendToString(nsACString& s, const nsIntPoint& p,
                const char* pfx="", const char* sfx="");
 
 template<class T>
 nsACString&
 AppendToString(nsACString& s, const mozilla::gfx::PointTyped<T>& p,
                const char* pfx="", const char* sfx="")
 {
@@ -75,16 +70,28 @@ AppendToString(nsACString& s, const mozi
 {
   s += pfx;
   s.AppendPrintf(
     "(x=%f, y=%f, w=%f, h=%f)",
     r.x, r.y, r.width, r.height);
   return s += sfx;
 }
 
+template<class T>
+nsACString&
+AppendToString(nsACString& s, const mozilla::gfx::IntRectTyped<T>& r,
+               const char* pfx="", const char* sfx="")
+{
+  s += pfx;
+  s.AppendPrintf(
+    "(x=%d, y=%d, w=%d, h=%d)",
+    r.x, r.y, r.width, r.height);
+  return s += sfx;
+}
+
 nsACString&
 AppendToString(nsACString& s, const nsIntRegion& r,
                const char* pfx="", const char* sfx="");
 
 nsACString&
 AppendToString(nsACString& s, const nsIntSize& sz,
                const char* pfx="", const char* sfx="");
 
--- a/gfx/layers/TiledLayerBuffer.h
+++ b/gfx/layers/TiledLayerBuffer.h
@@ -12,24 +12,43 @@
 #include <stdint.h>                     // for uint16_t, uint32_t
 #include <sys/types.h>                  // for int32_t
 #include "gfxPrefs.h"                   // for gfxPrefs::LayersTileWidth/Height
 #include "nsDebug.h"                    // for NS_ABORT_IF_FALSE
 #include "nsPoint.h"                    // for nsIntPoint
 #include "nsRect.h"                     // for nsIntRect
 #include "nsRegion.h"                   // for nsIntRegion
 #include "nsTArray.h"                   // for nsTArray
+#include "prlog.h"                      // for PR_LOG
 
 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
 #include <ui/Fence.h>
 #endif
 
 namespace mozilla {
 namespace layers {
 
+// To get this logging, you need PR logging enabled (either by
+// doing a debug build, or #define'ing FORCE_PR_LOG at the top
+// of a .cpp file), and then run with NSPR_LOG_MODULES=tiling:5
+// in your environment at runtime.
+#ifdef PR_LOGGING
+#  define TILING_PRLOG(_args) PR_LOG(gTilingLog, PR_LOG_DEBUG, _args)
+#  define TILING_PRLOG_OBJ(_args, obj) \
+    { \
+    nsAutoCString tmpstr; \
+    AppendToString(tmpstr, obj); \
+    PR_LOG(gTilingLog, PR_LOG_DEBUG, _args); \
+    }
+   extern PRLogModuleInfo* gTilingLog;
+#else
+#  define TILING_PRLOG(_args)
+#  define TILING_PRLOG_OBJ(_args, obj)
+#endif
+
 // An abstract implementation of a tile buffer. This code covers the logic of
 // moving and reusing tiles and leaves the validation up to the implementor. To
 // avoid the overhead of virtual dispatch, we employ the curiously recurring
 // template pattern.
 //
 // Tiles are aligned to a grid with one of the grid points at (0,0) and other
 // grid points spaced evenly in the x- and y-directions by GetTileSize()
 // multiplied by mResolution. GetScaledTileSize() provides convenience for
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -4,16 +4,17 @@
  * 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_layers_AsyncPanZoomController_h
 #define mozilla_layers_AsyncPanZoomController_h
 
 #include "CrossProcessMutex.h"
 #include "mozilla/layers/GeckoContentController.h"
+#include "mozilla/layers/APZCTreeManager.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/Atomics.h"
 #include "InputData.h"
 #include "Axis.h"
@@ -33,17 +34,16 @@ class SharedMemoryBasic;
 namespace layers {
 
 struct ScrollableLayerGuid;
 class CompositorParent;
 class GestureEventListener;
 class ContainerLayer;
 class PCompositorParent;
 class ViewTransform;
-class APZCTreeManager;
 class AsyncPanZoomAnimation;
 class FlingAnimation;
 
 /**
  * Controller for all panning and zooming logic. Any time a user input is
  * detected and it must be processed in some way to affect what the user sees,
  * it goes through here. Listens for any input event from InputData and can
  * optionally handle WidgetGUIEvent-derived touch events, but this must be done
--- a/gfx/layers/client/ClientThebesLayer.cpp
+++ b/gfx/layers/client/ClientThebesLayer.cpp
@@ -23,16 +23,18 @@
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsISupportsImpl.h"            // for Layer::AddRef, etc
 #include "nsRect.h"                     // for nsIntRect
 #include "gfx2DGlue.h"
 
 namespace mozilla {
 namespace layers {
 
+PRLogModuleInfo* gTilingLog;
+
 using namespace mozilla::gfx;
 
 void
 ClientThebesLayer::PaintThebes()
 {
   PROFILER_LABEL("ClientThebesLayer", "PaintThebes",
     js::ProfileEntry::Category::GRAPHICS);
 
@@ -138,16 +140,19 @@ ClientLayerManager::CreateThebesLayerWit
   if (
 #ifdef MOZ_B2G
       aHint == SCROLLABLE &&
 #endif
       gfxPrefs::LayersTilesEnabled() &&
       (AsShadowForwarder()->GetCompositorBackendType() == LayersBackend::LAYERS_OPENGL ||
        AsShadowForwarder()->GetCompositorBackendType() == LayersBackend::LAYERS_D3D9 ||
        AsShadowForwarder()->GetCompositorBackendType() == LayersBackend::LAYERS_D3D11)) {
+    if (!gTilingLog) {
+      gTilingLog = PR_NewLogModule("tiling");
+    }
     if (gfxPrefs::LayersUseSimpleTiles()) {
       nsRefPtr<SimpleClientTiledThebesLayer> layer =
         new SimpleClientTiledThebesLayer(this);
       CREATE_SHADOW(Thebes);
       return layer.forget();
     } else {
       nsRefPtr<ClientTiledThebesLayer> layer =
         new ClientTiledThebesLayer(this);
--- a/gfx/layers/client/ClientTiledThebesLayer.cpp
+++ b/gfx/layers/client/ClientTiledThebesLayer.cpp
@@ -1,28 +1,34 @@
 /* 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/. */
 
+// Uncomment this to enable the TILING_PRLOG stuff in this file
+// for release builds. To get the output you need to have
+// NSPR_LOG_MODULES=tiling:5 in your environment at runtime.
+// #define FORCE_PR_LOG
+
 #include "ClientTiledThebesLayer.h"
 #include "FrameMetrics.h"               // for FrameMetrics
 #include "Units.h"                      // for ScreenIntRect, CSSPoint, etc
 #include "UnitTransforms.h"             // for TransformTo
 #include "ClientLayerManager.h"         // for ClientLayerManager, etc
 #include "gfx3DMatrix.h"                // for gfx3DMatrix
 #include "gfxPlatform.h"                // for gfxPlatform
 #include "gfxPrefs.h"                   // for gfxPrefs
 #include "gfxRect.h"                    // for gfxRect
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/gfx/BaseSize.h"       // for BaseSize
 #include "mozilla/gfx/Rect.h"           // for Rect, RectTyped
 #include "mozilla/layers/LayersMessages.h"
 #include "mozilla/mozalloc.h"           // for operator delete, etc
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
 #include "nsRect.h"                     // for nsIntRect
+#include "LayersLogging.h"
 
 namespace mozilla {
 namespace layers {
 
 
 ClientTiledThebesLayer::ClientTiledThebesLayer(ClientLayerManager* const aManager)
   : ThebesLayer(aManager,
                 static_cast<ClientLayer*>(MOZ_THIS_IN_INITIALIZER_LIST()))
@@ -47,28 +53,34 @@ ClientTiledThebesLayer::ClearCachedResou
 }
 
 void
 ClientTiledThebesLayer::FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
 {
   aAttrs = ThebesLayerAttributes(GetValidRegion());
 }
 
-static LayoutDeviceRect
-ApplyParentLayerToLayoutTransform(const gfx3DMatrix& aTransform, const ParentLayerRect& aParentLayerRect)
+static LayerRect
+ApplyParentLayerToLayerTransform(const gfx3DMatrix& aTransform, const ParentLayerRect& aParentLayerRect)
 {
-  return TransformTo<LayoutDevicePixel>(aTransform, aParentLayerRect);
+  return TransformTo<LayerPixel>(aTransform, aParentLayerRect);
 }
 
 static gfx3DMatrix
 GetTransformToAncestorsParentLayer(Layer* aStart, Layer* aAncestor)
 {
   gfx::Matrix4x4 transform;
   Layer* ancestorParent = aAncestor->GetParent();
   for (Layer* iter = aStart; iter != ancestorParent; iter = iter->GetParent()) {
+    if (iter->AsContainerLayer()) {
+      // If the layer has a non-transient async transform then we need to apply it here
+      // because it will get applied by the APZ in the compositor as well
+      const FrameMetrics& metrics = iter->AsContainerLayer()->GetFrameMetrics();
+      transform = transform * gfx::Matrix4x4().Scale(metrics.mResolution.scale, metrics.mResolution.scale, 1.f);
+    }
     transform = transform * iter->GetTransform();
   }
   gfx3DMatrix ret;
   gfx::To3DMatrix(transform, ret);
   return ret;
 }
 
 void
@@ -112,71 +124,81 @@ ClientTiledThebesLayer::BeginPaint()
 #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_B2G)
     // Both Android and b2g are guaranteed to have a displayport set, so this
     // should never happen.
     NS_WARNING("Tiled Thebes layer with no scrollable container ancestor");
 #endif
     return;
   }
 
+  TILING_PRLOG(("TILING 0x%p: Found scrollAncestor 0x%p and displayPortAncestor 0x%p\n", this,
+    scrollAncestor, displayPortAncestor));
+
   const FrameMetrics& scrollMetrics = scrollAncestor->GetFrameMetrics();
   const FrameMetrics& displayportMetrics = displayPortAncestor->GetFrameMetrics();
 
   // Calculate the transform required to convert ParentLayer space of our
-  // display port ancestor to the LayoutDevice space of this layer.
-  gfx3DMatrix layoutDeviceToDisplayPort =
+  // display port ancestor to the Layer space of this layer.
+  gfx3DMatrix transformToDisplayPort =
     GetTransformToAncestorsParentLayer(this, displayPortAncestor);
-  layoutDeviceToDisplayPort.ScalePost(scrollMetrics.mCumulativeResolution.scale,
-                                      scrollMetrics.mCumulativeResolution.scale,
-                                      1.f);
+
+  mPaintData.mTransformDisplayPortToLayer = transformToDisplayPort.Inverse();
 
-  mPaintData.mTransformDisplayPortToLayoutDevice = layoutDeviceToDisplayPort.Inverse();
+  // Note that below we use GetZoomToParent() in a number of places. Because this
+  // code runs on the client side, the mTransformScale field of the FrameMetrics
+  // will not have been set. This can result in incorrect values being returned
+  // by GetZoomToParent() when we have CSS transforms set on some of these layers.
+  // This code should be audited and updated as part of fixing bug 993525.
 
   // Compute the critical display port that applies to this layer in the
   // LayoutDevice space of this layer.
   ParentLayerRect criticalDisplayPort =
     (displayportMetrics.mCriticalDisplayPort * displayportMetrics.GetZoomToParent())
     + displayportMetrics.mCompositionBounds.TopLeft();
-  mPaintData.mCriticalDisplayPort = LayoutDeviceIntRect::ToUntyped(RoundedOut(
-    ApplyParentLayerToLayoutTransform(mPaintData.mTransformDisplayPortToLayoutDevice,
-                                      criticalDisplayPort)));
+  mPaintData.mCriticalDisplayPort = RoundedOut(
+    ApplyParentLayerToLayerTransform(mPaintData.mTransformDisplayPortToLayer, criticalDisplayPort));
+  TILING_PRLOG_OBJ(("TILING 0x%p: Critical displayport %s\n", this, tmpstr.get()), mPaintData.mCriticalDisplayPort);
 
   // Compute the viewport that applies to this layer in the LayoutDevice
   // space of this layer.
   ParentLayerRect viewport =
     (displayportMetrics.mViewport * displayportMetrics.GetZoomToParent())
     + displayportMetrics.mCompositionBounds.TopLeft();
-  mPaintData.mViewport = ApplyParentLayerToLayoutTransform(
-    mPaintData.mTransformDisplayPortToLayoutDevice, viewport);
+  mPaintData.mViewport = ApplyParentLayerToLayerTransform(
+    mPaintData.mTransformDisplayPortToLayer, viewport);
+  TILING_PRLOG_OBJ(("TILING 0x%p: Viewport %s\n", this, tmpstr.get()), mPaintData.mViewport);
 
   // Store the resolution from the displayport ancestor layer. Because this is Gecko-side,
   // before any async transforms have occurred, we can use the zoom for this.
   mPaintData.mResolution = displayportMetrics.GetZoomToParent();
+  TILING_PRLOG(("TILING 0x%p: Resolution %f\n", this, mPaintData.mResolution.scale));
 
-  // Store the applicable composition bounds in this layer's LayoutDevice units.
-  gfx3DMatrix layoutDeviceToCompBounds =
+  // Store the applicable composition bounds in this layer's Layer units.
+  gfx3DMatrix transformToCompBounds =
     GetTransformToAncestorsParentLayer(this, scrollAncestor);
-  mPaintData.mCompositionBounds = TransformTo<LayoutDevicePixel>(
-    layoutDeviceToCompBounds.Inverse(),
-    scrollMetrics.mCompositionBounds / scrollMetrics.GetParentResolution());
+  mPaintData.mCompositionBounds = ApplyParentLayerToLayerTransform(
+    transformToCompBounds.Inverse(), ParentLayerRect(scrollMetrics.mCompositionBounds));
+  TILING_PRLOG_OBJ(("TILING 0x%p: Composition bounds %s\n", this, tmpstr.get()), mPaintData.mCompositionBounds);
 
   // Calculate the scroll offset since the last transaction
   mPaintData.mScrollOffset = displayportMetrics.GetScrollOffset() * displayportMetrics.GetZoomToParent();
+  TILING_PRLOG_OBJ(("TILING 0x%p: Scroll offset %s\n", this, tmpstr.get()), mPaintData.mScrollOffset);
 }
 
 void
 ClientTiledThebesLayer::EndPaint(bool aFinish)
 {
   if (!aFinish && !mPaintData.mPaintFinished) {
     return;
   }
 
   mPaintData.mLastScrollOffset = mPaintData.mScrollOffset;
   mPaintData.mPaintFinished = true;
   mPaintData.mFirstPaint = false;
+  TILING_PRLOG(("TILING 0x%p: Paint finished\n", this));
 }
 
 void
 ClientTiledThebesLayer::RenderLayer()
 {
   LayerManager::DrawThebesLayerCallback callback =
     ClientManager()->GetThebesLayerCallback();
   void *data = ClientManager()->GetThebesLayerCallbackData();
@@ -192,16 +214,19 @@ ClientTiledThebesLayer::RenderLayer()
     ClientManager()->AsShadowForwarder()->Attach(mContentClient, this);
     MOZ_ASSERT(mContentClient->GetForwarder());
   }
 
   if (mContentClient->mTiledBuffer.HasFormatChanged()) {
     mValidRegion = nsIntRegion();
   }
 
+  TILING_PRLOG_OBJ(("TILING 0x%p: Initial visible region %s\n", this, tmpstr.get()), mVisibleRegion);
+  TILING_PRLOG_OBJ(("TILING 0x%p: Initial valid region %s\n", this, tmpstr.get()), mValidRegion);
+
   nsIntRegion invalidRegion = mVisibleRegion;
   invalidRegion.Sub(invalidRegion, mValidRegion);
   if (invalidRegion.IsEmpty()) {
     EndPaint(true);
     return;
   }
 
   // Only paint the mask layer on the first transaction.
@@ -234,71 +259,82 @@ ClientTiledThebesLayer::RenderLayer()
   }
 
   // Calculate everything we need to perform the paint.
   BeginPaint();
   if (mPaintData.mPaintFinished) {
     return;
   }
 
+  TILING_PRLOG_OBJ(("TILING 0x%p: Valid region %s\n", this, tmpstr.get()), mValidRegion);
+  TILING_PRLOG_OBJ(("TILING 0x%p: Visible region %s\n", this, tmpstr.get()), mVisibleRegion);
+
   // Make sure that tiles that fall outside of the visible region are
   // discarded on the first update.
   if (!ClientManager()->IsRepeatTransaction()) {
     mValidRegion.And(mValidRegion, mVisibleRegion);
     if (!mPaintData.mCriticalDisplayPort.IsEmpty()) {
       // Make sure that tiles that fall outside of the critical displayport are
       // discarded on the first update.
-      mValidRegion.And(mValidRegion, mPaintData.mCriticalDisplayPort);
+      mValidRegion.And(mValidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort));
     }
   }
 
   nsIntRegion lowPrecisionInvalidRegion;
   if (!mPaintData.mCriticalDisplayPort.IsEmpty()) {
     if (gfxPrefs::UseLowPrecisionBuffer()) {
       // Calculate the invalid region for the low precision buffer
       lowPrecisionInvalidRegion.Sub(mVisibleRegion, mLowPrecisionValidRegion);
 
       // Remove the valid region from the low precision valid region (we don't
       // validate this part of the low precision buffer).
       lowPrecisionInvalidRegion.Sub(lowPrecisionInvalidRegion, mValidRegion);
     }
 
     // Clip the invalid region to the critical display-port
-    invalidRegion.And(invalidRegion, mPaintData.mCriticalDisplayPort);
+    invalidRegion.And(invalidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort));
     if (invalidRegion.IsEmpty() && lowPrecisionInvalidRegion.IsEmpty()) {
       EndPaint(true);
       return;
     }
   }
 
+  TILING_PRLOG_OBJ(("TILING 0x%p: Invalid region %s\n", this, tmpstr.get()), invalidRegion);
+
   if (!invalidRegion.IsEmpty() && mPaintData.mLowPrecisionPaintCount == 0) {
     bool updatedBuffer = false;
     // Only draw progressively when the resolution is unchanged.
     if (gfxPrefs::UseProgressiveTilePainting() &&
         !ClientManager()->HasShadowTarget() &&
         mContentClient->mTiledBuffer.GetFrameResolution() == mPaintData.mResolution) {
       // Store the old valid region, then clear it before painting.
       // We clip the old valid region to the visible region, as it only gets
       // used to decide stale content (currently valid and previously visible)
       nsIntRegion oldValidRegion = mContentClient->mTiledBuffer.GetValidRegion();
       oldValidRegion.And(oldValidRegion, mVisibleRegion);
       if (!mPaintData.mCriticalDisplayPort.IsEmpty()) {
-        oldValidRegion.And(oldValidRegion, mPaintData.mCriticalDisplayPort);
+        oldValidRegion.And(oldValidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort));
       }
 
+      TILING_PRLOG_OBJ(("TILING 0x%p: Progressive update with old valid region %s\n", this, tmpstr.get()), oldValidRegion);
+
       updatedBuffer =
         mContentClient->mTiledBuffer.ProgressiveUpdate(mValidRegion, invalidRegion,
                                                        oldValidRegion, &mPaintData,
                                                        callback, data);
     } else {
       updatedBuffer = true;
       mValidRegion = mVisibleRegion;
       if (!mPaintData.mCriticalDisplayPort.IsEmpty()) {
-        mValidRegion.And(mValidRegion, mPaintData.mCriticalDisplayPort);
+        mValidRegion.And(mValidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort));
       }
+
+      TILING_PRLOG_OBJ(("TILING 0x%p: Painting: valid region %s\n", this, tmpstr.get()), mValidRegion);
+      TILING_PRLOG_OBJ(("TILING 0x%p: and invalid region %s\n", this, tmpstr.get()), invalidRegion);
+
       mContentClient->mTiledBuffer.SetFrameResolution(mPaintData.mResolution);
       mContentClient->mTiledBuffer.PaintThebes(mValidRegion, invalidRegion,
                                                callback, data);
     }
 
     if (updatedBuffer) {
       ClientManager()->Hold(this);
       mContentClient->UseTiledLayerBuffer(TiledContentClient::TILED_BUFFER);
@@ -313,21 +349,24 @@ ClientTiledThebesLayer::RenderLayer()
 
       // Return so that low precision updates aren't performed in the same
       // transaction as high-precision updates.
       EndPaint(false);
       return;
     }
   }
 
+  TILING_PRLOG_OBJ(("TILING 0x%p: Low-precision valid region is %s\n", this, tmpstr.get()), mLowPrecisionValidRegion);
+  TILING_PRLOG_OBJ(("TILING 0x%p: Low-precision invalid region is %s\n", this, tmpstr.get()), lowPrecisionInvalidRegion);
+
   // Render the low precision buffer, if there's area to invalidate and the
   // visible region is larger than the critical display port.
   bool updatedLowPrecision = false;
   if (!lowPrecisionInvalidRegion.IsEmpty() &&
-      !nsIntRegion(mPaintData.mCriticalDisplayPort).Contains(mVisibleRegion)) {
+      !nsIntRegion(LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort)).Contains(mVisibleRegion)) {
     nsIntRegion oldValidRegion =
       mContentClient->mLowPrecisionTiledBuffer.GetValidRegion();
     oldValidRegion.And(oldValidRegion, mVisibleRegion);
 
     // If the frame resolution or format have changed, invalidate the buffer
     if (mContentClient->mLowPrecisionTiledBuffer.GetFrameResolution() != mPaintData.mResolution ||
         mContentClient->mLowPrecisionTiledBuffer.HasFormatChanged()) {
       if (!mLowPrecisionValidRegion.IsEmpty()) {
--- a/gfx/layers/client/ContentClient.cpp
+++ b/gfx/layers/client/ContentClient.cpp
@@ -924,16 +924,17 @@ ContentClientIncremental::GetUpdateSurfa
     NS_ERROR("update outside of image");
     return nullptr;
   }
   SurfaceDescriptor desc;
   if (!mForwarder->AllocSurfaceDescriptor(rgnSize.Size().ToIntSize(),
                                           mContentType,
                                           &desc)) {
     NS_WARNING("creating SurfaceDescriptor failed!");
+    Clear();
     return nullptr;
   }
 
   if (aType == BUFFER_BLACK) {
     MOZ_ASSERT(!IsSurfaceDescriptorValid(mUpdateDescriptor));
     mUpdateDescriptor = desc;
   } else {
     MOZ_ASSERT(!IsSurfaceDescriptorValid(mUpdateDescriptorOnWhite));
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -884,39 +884,39 @@ ClientTiledLayerBuffer::ValidateTile(Til
     // If our new buffer has an internal buffer, we don't want to keep another
     // TextureClient around unnecessarily, so discard the back-buffer.
     aTile.DiscardBackBuffer();
   }
 
   return aTile;
 }
 
-static LayoutDeviceRect
+static LayerRect
 TransformCompositionBounds(const ParentLayerRect& aCompositionBounds,
                            const CSSToParentLayerScale& aZoom,
                            const ParentLayerPoint& aScrollOffset,
                            const CSSToParentLayerScale& aResolution,
-                           const gfx3DMatrix& aTransformDisplayPortToLayoutDevice)
+                           const gfx3DMatrix& aTransformDisplayPortToLayer)
 {
   // Transform the composition bounds from the space of the displayport ancestor
-  // layer into the LayoutDevice space of this layer. Do this by
+  // layer into the Layer space of this layer. Do this by
   // compensating for the difference in resolution and subtracting the
   // old composition bounds origin.
   ParentLayerRect offsetViewportRect = (aCompositionBounds / aZoom) * aResolution;
   offsetViewportRect.MoveBy(-aScrollOffset);
 
   gfxRect transformedViewport =
-    aTransformDisplayPortToLayoutDevice.TransformBounds(
+    aTransformDisplayPortToLayer.TransformBounds(
       gfxRect(offsetViewportRect.x, offsetViewportRect.y,
               offsetViewportRect.width, offsetViewportRect.height));
 
-  return LayoutDeviceRect(transformedViewport.x,
-                          transformedViewport.y,
-                          transformedViewport.width,
-                          transformedViewport.height);
+  return LayerRect(transformedViewport.x,
+                   transformedViewport.y,
+                   transformedViewport.width,
+                   transformedViewport.height);
 }
 
 bool
 ClientTiledLayerBuffer::ComputeProgressiveUpdateRegion(const nsIntRegion& aInvalidRegion,
                                                        const nsIntRegion& aOldValidRegion,
                                                        nsIntRegion& aRegionToPaint,
                                                        BasicTiledLayerPaintData* aPaintData,
                                                        bool aIsRepeated)
@@ -971,46 +971,47 @@ ClientTiledLayerBuffer::ComputeProgressi
       PROFILER_LABEL("ClientTiledLayerBuffer", "ComputeProgressiveUpdateRegion",
         js::ProfileEntry::Category::GRAPHICS);
 
       aRegionToPaint.SetEmpty();
       return aIsRepeated;
     }
   }
 
-  LayoutDeviceRect transformedCompositionBounds =
+  LayerRect transformedCompositionBounds =
     TransformCompositionBounds(compositionBounds, zoom, aPaintData->mScrollOffset,
-                               aPaintData->mResolution, aPaintData->mTransformDisplayPortToLayoutDevice);
+                               aPaintData->mResolution, aPaintData->mTransformDisplayPortToLayer);
 
   // Paint tiles that have stale content or that intersected with the screen
   // at the time of issuing the draw command in a single transaction first.
   // This is to avoid rendering glitches on animated page content, and when
   // layers change size/shape.
-  LayoutDeviceRect typedCoherentUpdateRect =
+  LayerRect typedCoherentUpdateRect =
     transformedCompositionBounds.Intersect(aPaintData->mCompositionBounds);
 
   // Offset by the viewport origin, as the composition bounds are stored in
   // Layer space and not LayoutDevice space.
+  // TODO(kats): does this make sense?
   typedCoherentUpdateRect.MoveBy(aPaintData->mViewport.TopLeft());
 
   // Convert to untyped to intersect with the invalid region.
-  nsIntRect roundedCoherentUpdateRect =
-    LayoutDeviceIntRect::ToUntyped(RoundedOut(typedCoherentUpdateRect));
+  nsIntRect untypedCoherentUpdateRect(LayerIntRect::ToUntyped(
+    RoundedOut(typedCoherentUpdateRect)));
 
-  aRegionToPaint.And(aInvalidRegion, roundedCoherentUpdateRect);
+  aRegionToPaint.And(aInvalidRegion, untypedCoherentUpdateRect);
   aRegionToPaint.Or(aRegionToPaint, staleRegion);
   bool drawingStale = !aRegionToPaint.IsEmpty();
   if (!drawingStale) {
     aRegionToPaint = aInvalidRegion;
   }
 
   // Prioritise tiles that are currently visible on the screen.
   bool paintVisible = false;
-  if (aRegionToPaint.Intersects(roundedCoherentUpdateRect)) {
-    aRegionToPaint.And(aRegionToPaint, roundedCoherentUpdateRect);
+  if (aRegionToPaint.Intersects(untypedCoherentUpdateRect)) {
+    aRegionToPaint.And(aRegionToPaint, untypedCoherentUpdateRect);
     paintVisible = true;
   }
 
   // Paint area that's visible and overlaps previously valid content to avoid
   // visible glitches in animated elements, such as gifs.
   bool paintInSingleTransaction = paintVisible && (drawingStale || aPaintData->mFirstPaint);
 
   // The following code decides what order to draw tiles in, based on the
--- a/gfx/layers/client/TiledContentClient.h
+++ b/gfx/layers/client/TiledContentClient.h
@@ -250,49 +250,46 @@ struct BasicTiledLayerPaintData {
    * The scroll offset of the content from the nearest ancestor layer that
    * represents scrollable content with a display port set, for the last
    * layer update transaction.
    */
   ParentLayerPoint mLastScrollOffset;
 
   /*
    * The transform matrix to go from the display port layer's ParentLayer
-   * units to this layer's LayoutDevice units. The "display port layer" is
+   * units to this layer's Layer units. The "display port layer" is
    * the closest ancestor layer with a displayport.
    */
-  gfx3DMatrix mTransformDisplayPortToLayoutDevice;
+  gfx3DMatrix mTransformDisplayPortToLayer;
 
   /*
    * The critical displayport of the content from the nearest ancestor layer
    * that represents scrollable content with a display port set. Empty if a
    * critical displayport is not set.
-   *
-   * This is in LayoutDevice coordinates, but is stored as an nsIntRect for
-   * convenience when intersecting with the layer's mValidRegion.
    */
-  nsIntRect mCriticalDisplayPort;
+  LayerIntRect mCriticalDisplayPort;
 
   /*
    * The viewport of the content from the nearest ancestor layer that
    * represents scrollable content with a display port set.
    */
-  LayoutDeviceRect mViewport;
+  LayerRect mViewport;
 
   /*
    * The render resolution of the document that the content this layer
    * represents is in.
    */
   CSSToParentLayerScale mResolution;
 
   /*
-   * The composition bounds of the layer, in LayoutDevice coordinates. This is
+   * The composition bounds of the layer, in Layer coordinates. This is
    * used to make sure that tiled updates to regions that are visible to the
    * user are grouped coherently.
    */
-  LayoutDeviceRect mCompositionBounds;
+  LayerRect mCompositionBounds;
 
   /*
    * Low precision updates are always executed a tile at a time in repeated
    * transactions. This counter is set to 1 on the first transaction of a low
    * precision update, and incremented for each subsequent transaction.
    */
   uint16_t mLowPrecisionPaintCount;
 
--- a/gfx/layers/composite/ContentHost.cpp
+++ b/gfx/layers/composite/ContentHost.cpp
@@ -401,24 +401,16 @@ ContentHostIncremental::ContentHostIncre
   , mLocked(false)
 {
 }
 
 ContentHostIncremental::~ContentHostIncremental()
 {
 }
 
-void
-ContentHostIncremental::DestroyTextures()
-{
-  mSource = nullptr;
-  mSourceOnWhite = nullptr;
-  mUpdateList.Clear();
-}
-
 bool
 ContentHostIncremental::CreatedIncrementalTexture(ISurfaceAllocator* aAllocator,
                                                   const TextureInfo& aTextureInfo,
                                                   const nsIntRect& aBufferRect)
 {
   mUpdateList.AppendElement(new TextureCreationRequest(aTextureInfo,
                                                        aBufferRect));
   mDeAllocator = aAllocator;
@@ -496,16 +488,18 @@ ContentHostIncremental::TextureCreationR
     temp =
       compositor->CreateDataTextureSource(mTextureInfo.mTextureFlags);
     MOZ_ASSERT(temp->AsSourceOGL() &&
                temp->AsSourceOGL()->AsTextureImageTextureSource());
     newSourceOnWhite = temp->AsSourceOGL()->AsTextureImageTextureSource();
   }
 
   if (mTextureInfo.mDeprecatedTextureHostFlags & DeprecatedTextureHostFlags::COPY_PREVIOUS) {
+    MOZ_ASSERT(aHost->mSource);
+    MOZ_ASSERT(aHost->mSource->IsValid());
     nsIntRect bufferRect = aHost->mBufferRect;
     nsIntPoint bufferRotation = aHost->mBufferRotation;
     nsIntRect overlap;
 
     // The buffer looks like:
     //  ______
     // |1  |2 |  Where the center point is offset by mBufferRotation from the top-left corner.
     // |___|__|
--- a/gfx/layers/composite/ContentHost.h
+++ b/gfx/layers/composite/ContentHost.h
@@ -276,18 +276,16 @@ public:
                             nsIntRegion* aUpdatedRegionBack)
   {
     NS_ERROR("Shouldn't call this");
     return false;
   }
 
   virtual void PrintInfo(nsACString& aTo, const char* aPrefix) MOZ_OVERRIDE;
 
-  virtual void DestroyTextures();
-
   virtual bool Lock() {
     MOZ_ASSERT(!mLocked);
     ProcessTextureUpdates();
     mLocked = true;
     return true;
   }
 
   virtual void Unlock() {
--- a/gfx/layers/opengl/TextureHostOGL.cpp
+++ b/gfx/layers/opengl/TextureHostOGL.cpp
@@ -380,18 +380,21 @@ TextureImageTextureSourceOGL::GetSize() 
   }
   NS_WARNING("Trying to query the size of an empty TextureSource.");
   return gfx::IntSize(0, 0);
 }
 
 gfx::SurfaceFormat
 TextureImageTextureSourceOGL::GetFormat() const
 {
-  MOZ_ASSERT(mTexImage);
-  return mTexImage->GetTextureFormat();
+  if (mTexImage) {
+    return mTexImage->GetTextureFormat();
+  }
+  NS_WARNING("Trying to query the format of an empty TextureSource.");
+  return gfx::SurfaceFormat::UNKNOWN;
 }
 
 nsIntRect TextureImageTextureSourceOGL::GetTileRect()
 {
   return ThebesIntRect(mTexImage->GetTileRect());
 }
 
 void
--- a/gfx/ots/README.mozilla
+++ b/gfx/ots/README.mozilla
@@ -1,10 +1,11 @@
 This is the Sanitiser for OpenType project, from http://code.google.com/p/ots/.
 
 Our reference repository is https://github.com/khaledhosny/ots/.
 
-Current revision: d7d831edd171054c7974f5e0dec2fc19bf869574
+Current revision: d6018b62bf41f6b419aeae6d2795725a55715481
+
+Upstream files included: LICENSE, src/, include/
 
-Applied local patches:
-    ots-visibility.patch - make Process function externally visible for Windows DLL (bug 711079)
+Additional files: README.mozilla, src/moz.build
 
-    ots-woff2 - disable WOFF2 support (bug 941019)
+Additional patch: ots-visibility.patch (bug 711079).
--- a/gfx/ots/include/opentype-sanitiser.h
+++ b/gfx/ots/include/opentype-sanitiser.h
@@ -194,58 +194,81 @@ class OTSStream {
   }
 
  protected:
   uint32_t chksum_;
   uint8_t chksum_buffer_[4];
   unsigned chksum_buffer_offset_;
 };
 
-// -----------------------------------------------------------------------------
-// Process a given OpenType file and write out a sanitised version
-//   output: a pointer to an object implementing the OTSStream interface. The
-//     sanitisied output will be written to this. In the even of a failure,
-//     partial output may have been written.
-//   input: the OpenType file
-//   length: the size, in bytes, of |input|
-// -----------------------------------------------------------------------------
-bool OTS_API Process(OTSStream *output, const uint8_t *input, size_t length);
-
 // Signature of the function to be provided by the client in order to report errors.
 // The return type is a boolean so that it can be used within an expression,
 // but the actual value is ignored. (Suggested convention is to always return 'false'.)
 #ifdef __GCC__
 #define MSGFUNC_FMT_ATTR __attribute__((format(printf, 2, 3)))
 #else
 #define MSGFUNC_FMT_ATTR
 #endif
 typedef bool (*MessageFunc)(void *user_data, const char *format, ...)  MSGFUNC_FMT_ATTR;
 
-// Set a callback function that will be called when OTS is reporting an error.
-void OTS_API SetMessageCallback(MessageFunc func, void *user_data);
-
 enum TableAction {
   TABLE_ACTION_DEFAULT,  // Use OTS's default action for that table
   TABLE_ACTION_SANITIZE, // Sanitize the table, potentially droping it
   TABLE_ACTION_PASSTHRU, // Serialize the table unchanged
   TABLE_ACTION_DROP      // Drop the table
 };
 
 // Signature of the function to be provided by the client to decide what action
 // to do for a given table.
+//   tag: table tag as an integer in big-endian byte order, independent of platform endianness
+//   user_data: user defined data that are passed to SetTableActionCallback()
 typedef TableAction (*TableActionFunc)(uint32_t tag, void *user_data);
 
-// Set a callback function that will be called when OTS needs to decide what to
-// do for a font table.
-void OTS_API SetTableActionCallback(TableActionFunc func, void *user_data);
+class OTS_API OTSContext {
+  public:
+    OTSContext()
+        : message_func(0),
+          message_user_data(0),
+          table_action_func(0),
+          table_action_user_data(0)
+        {}
+
+    ~OTSContext() {}
+
+    // Process a given OpenType file and write out a sanitised version
+    //   output: a pointer to an object implementing the OTSStream interface. The
+    //     sanitisied output will be written to this. In the even of a failure,
+    //     partial output may have been written.
+    //   input: the OpenType file
+    //   length: the size, in bytes, of |input|
+    //   context: optional context that holds various OTS settings like user callbacks
+    bool Process(OTSStream *output, const uint8_t *input, size_t length);
+
+    // Set a callback function that will be called when OTS is reporting an error.
+    void SetMessageCallback(MessageFunc func, void *user_data) {
+      message_func = func;
+      message_user_data = user_data;
+    }
+
+    // Set a callback function that will be called when OTS needs to decide what to
+    // do for a font table.
+    void SetTableActionCallback(TableActionFunc func, void *user_data) {
+      table_action_func = func;
+      table_action_user_data = user_data;
+    }
+
+  private:
+    MessageFunc      message_func;
+    void            *message_user_data;
+    TableActionFunc  table_action_func;
+    void            *table_action_user_data;
+};
 
 // Force to disable debug output even when the library is compiled with
 // -DOTS_DEBUG.
 void DisableDebugOutput();
 
-#ifdef MOZ_OTS_WOFF2
 // Enable WOFF2 support(experimental).
 void EnableWOFF2();
-#endif
 
 }  // namespace ots
 
 #endif  // OPENTYPE_SANITISER_H_
--- a/gfx/ots/ots-visibility.patch
+++ b/gfx/ots/ots-visibility.patch
@@ -32,57 +32,27 @@ diff --git a/gfx/ots/include/opentype-sa
  #if defined(_WIN32)
  #include <stdlib.h>
  typedef signed char int8_t;
  typedef unsigned char uint8_t;
  typedef short int16_t;
  typedef unsigned short uint16_t;
  typedef int int32_t;
  typedef unsigned int uint32_t;
-@@ -182,45 +202,45 @@ class OTSStream {
- // -----------------------------------------------------------------------------
- // Process a given OpenType file and write out a sanitised version
- //   output: a pointer to an object implementing the OTSStream interface. The
- //     sanitisied output will be written to this. In the even of a failure,
- //     partial output may have been written.
- //   input: the OpenType file
- //   length: the size, in bytes, of |input|
- // -----------------------------------------------------------------------------
--bool Process(OTSStream *output, const uint8_t *input, size_t length);
-+bool OTS_API Process(OTSStream *output, const uint8_t *input, size_t length);
- 
- // Signature of the function to be provided by the client in order to report errors.
- // The return type is a boolean so that it can be used within an expression,
- // but the actual value is ignored. (Suggested convention is to always return 'false'.)
- #ifdef __GCC__
- #define MSGFUNC_FMT_ATTR __attribute__((format(printf, 2, 3)))
- #else
- #define MSGFUNC_FMT_ATTR
- #endif
- typedef bool (*MessageFunc)(void *user_data, const char *format, ...)  MSGFUNC_FMT_ATTR;
- 
- // Set a callback function that will be called when OTS is reporting an error.
--void SetMessageCallback(MessageFunc func, void *user_data);
-+void OTS_API SetMessageCallback(MessageFunc func, void *user_data);
- 
- enum TableAction {
-   TABLE_ACTION_DEFAULT,  // Use OTS's default action for that table
-   TABLE_ACTION_SANITIZE, // Sanitize the table, potentially droping it
-   TABLE_ACTION_PASSTHRU, // Serialize the table unchanged
-   TABLE_ACTION_DROP      // Drop the table
+@@ -197,17 +217,17 @@ enum TableAction {
  };
  
  // Signature of the function to be provided by the client to decide what action
  // to do for a given table.
+ //   tag: table tag as an integer in big-endian byte order, independent of platform endianness
+ //   user_data: user defined data that are passed to SetTableActionCallback()
  typedef TableAction (*TableActionFunc)(uint32_t tag, void *user_data);
  
- // Set a callback function that will be called when OTS needs to decide what to
- // do for a font table.
--void SetTableActionCallback(TableActionFunc func, void *user_data);
-+void OTS_API SetTableActionCallback(TableActionFunc func, void *user_data);
+-class OTSContext {
++class OTS_API OTSContext {
+   public:
+     OTSContext()
+         : message_func(0),
+           message_user_data(0),
+           table_action_func(0),
+           table_action_user_data(0)
+         {}
  
- // Force to disable debug output even when the library is compiled with
- // -DOTS_DEBUG.
- void DisableDebugOutput();
- 
- // Enable WOFF2 support(experimental).
- void EnableWOFF2();
- 
deleted file mode 100644
--- a/gfx/ots/ots-woff2.patch
+++ /dev/null
@@ -1,134 +0,0 @@
-diff --git a/gfx/ots/include/opentype-sanitiser.h b/gfx/ots/include/opentype-sanitiser.h
---- a/gfx/ots/include/opentype-sanitiser.h
-+++ b/gfx/ots/include/opentype-sanitiser.h
-@@ -236,14 +236,16 @@ typedef TableAction (*TableActionFunc)(u
- // Set a callback function that will be called when OTS needs to decide what to
- // do for a font table.
- void OTS_API SetTableActionCallback(TableActionFunc func, void *user_data);
- 
- // Force to disable debug output even when the library is compiled with
- // -DOTS_DEBUG.
- void DisableDebugOutput();
- 
-+#ifdef MOZ_OTS_WOFF2
- // Enable WOFF2 support(experimental).
- void EnableWOFF2();
-+#endif
- 
- }  // namespace ots
- 
- #endif  // OPENTYPE_SANITISER_H_
-diff --git a/gfx/ots/src/ots.cc b/gfx/ots/src/ots.cc
---- a/gfx/ots/src/ots.cc
-+++ b/gfx/ots/src/ots.cc
-@@ -9,25 +9,29 @@
- 
- #include <algorithm>
- #include <cstdlib>
- #include <cstring>
- #include <limits>
- #include <map>
- #include <vector>
- 
-+#ifdef MOZ_OTS_WOFF2
- #include "woff2.h"
-+#endif
- 
- // The OpenType Font File
- // http://www.microsoft.com/typography/otspec/cmap.htm
- 
- namespace {
- 
- bool g_debug_output = true;
-+#ifdef MOZ_OTS_WOFF2
- bool g_enable_woff2 = false;
-+#endif
- 
- ots::MessageFunc  g_message_func = NULL;
- void             *g_message_user_data = NULL;
- 
- ots::TableActionFunc  g_table_action_func = NULL;
- void                 *g_table_action_user_data = NULL;
- 
- // Generate a message with or without a table tag, when 'header' is the OpenTypeFile pointer
-@@ -395,16 +399,17 @@ bool ProcessWOFF(ots::OpenTypeFile *head
-   }
-   if (block_end != ots::Round4(length)) {
-     return OTS_FAILURE_MSG_HDR("file length mismatch (trailing junk?)");
-   }
- 
-   return ProcessGeneric(header, woff_tag, output, data, length, tables, file);
- }
- 
-+#ifdef MOZ_OTS_WOFF2
- bool ProcessWOFF2(ots::OpenTypeFile *header,
-                   ots::OTSStream *output, const uint8_t *data, size_t length) {
-   size_t decompressed_size = ots::ComputeWOFF2FinalSize(data, length);
-   if (decompressed_size == 0) {
-     return OTS_FAILURE();
-   }
-   // decompressed font must be <= 30MB
-   if (decompressed_size > 30 * 1024 * 1024) {
-@@ -413,16 +418,17 @@ bool ProcessWOFF2(ots::OpenTypeFile *hea
- 
-   std::vector<uint8_t> decompressed_buffer(decompressed_size);
-   if (!ots::ConvertWOFF2ToTTF(&decompressed_buffer[0], decompressed_size,
-                               data, length)) {
-     return OTS_FAILURE();
-   }
-   return ProcessTTF(header, output, &decompressed_buffer[0], decompressed_size);
- }
-+#endif
- 
- ots::TableAction GetTableAction(uint32_t tag) {
-   ots::TableAction action = ots::TABLE_ACTION_DEFAULT;
- 
-   if (g_table_action_func != NULL) {
-     action = g_table_action_func(htonl(tag), g_table_action_user_data);
-   }
- 
-@@ -795,19 +801,21 @@ bool IsValidVersionTag(uint32_t tag) {
-          tag == Tag("true") ||
-          tag == Tag("typ1");
- }
- 
- void DisableDebugOutput() {
-   g_debug_output = false;
- }
- 
-+#ifdef MOZ_OTS_WOFF2
- void EnableWOFF2() {
-   g_enable_woff2 = true;
- }
-+#endif
- 
- void SetMessageCallback(MessageFunc func, void *user_data) {
-   g_message_func = func;
-   g_message_user_data = user_data;
- }
- 
- void SetTableActionCallback(TableActionFunc func, void *user_data) {
-   g_table_action_func = func;
-@@ -822,20 +830,22 @@ bool Process(OTSStream *output, const ui
- 
-   if (length < 4) {
-     return OTS_FAILURE_MSG_(&header, "file less than 4 bytes");
-   }
- 
-   bool result;
-   if (data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && data[3] == 'F') {
-     result = ProcessWOFF(&header, output, data, length);
-+#ifdef MOZ_OTS_WOFF2
-   } else if (g_enable_woff2 &&
-              data[0] == 'w' && data[1] == 'O' && data[2] == 'F' &&
-              data[3] == '2') {
-     result = ProcessWOFF2(&header, output, data, length);
-+#endif
-   } else {
-     result = ProcessTTF(&header, output, data, length);
-   }
- 
-   for (unsigned i = 0; ; ++i) {
-     if (table_parsers[i].parse == NULL) break;
-     table_parsers[i].free(&header);
-   }
--- a/gfx/ots/src/cff.cc
+++ b/gfx/ots/src/cff.cc
@@ -1023,8 +1023,10 @@ void ots_cff_free(OpenTypeFile *file) {
       delete (file->cff->local_subrs_per_font)[i];
     }
     delete file->cff->local_subrs;
     delete file->cff;
   }
 }
 
 }  // namespace ots
+
+#undef TABLE_NAME
--- a/gfx/ots/src/cmap.cc
+++ b/gfx/ots/src/cmap.cc
@@ -355,18 +355,18 @@ bool Parse31013(ots::OpenTypeFile *file,
   ots::Buffer subtable(data, length);
 
   // Format 13 tables are simple. We parse these and fully serialise them
   // later.
 
   if (!subtable.Skip(8)) {
     return OTS_FAILURE_MSG("Bad cmap subtable length");
   }
-  uint16_t language = 0;
-  if (!subtable.ReadU16(&language)) {
+  uint32_t language = 0;
+  if (!subtable.ReadU32(&language)) {
     return OTS_FAILURE_MSG("Can't read cmap subtable language");
   }
   if (language) {
     return OTS_FAILURE_MSG("Cmap subtable language should be zero but is %d", language);
   }
 
   uint32_t num_groups = 0;
   if (!subtable.ReadU32(&num_groups)) {
@@ -871,17 +871,17 @@ bool ots_cmap_serialise(OTSStream *out, 
                                  static_cast<unsigned>(have_304) +
                                  static_cast<unsigned>(have_314) +
                                  static_cast<unsigned>(have_31012) +
                                  static_cast<unsigned>(have_31013);
   const off_t table_start = out->Tell();
 
   // Some fonts don't have 3-0-4 MS Symbol nor 3-1-4 Unicode BMP tables
   // (e.g., old fonts for Mac). We don't support them.
-  if (!have_304 && !have_314 && !have_034) {
+  if (!have_304 && !have_314 && !have_034 && !have_31012 && !have_31013) {
     return OTS_FAILURE();
   }
 
   if (!out->WriteU16(0) ||
       !out->WriteU16(num_subtables)) {
     return OTS_FAILURE();
   }
 
@@ -1000,17 +1000,17 @@ bool ots_cmap_serialise(OTSStream *out, 
   }
 
   const off_t offset_31013 = out->Tell();
   if (have_31013) {
     std::vector<OpenTypeCMAPSubtableRange> &groups
         = file->cmap->subtable_3_10_13;
     const unsigned num_groups = groups.size();
     if (!out->WriteU16(13) ||
-        !out->WriteU16(0) ||
+        !out->WriteU32(0) ||
         !out->WriteU32(num_groups * 12 + 14) ||
         !out->WriteU32(0) ||
         !out->WriteU32(num_groups)) {
       return OTS_FAILURE();
     }
 
     for (unsigned i = 0; i < num_groups; ++i) {
       if (!out->WriteU32(groups[i].start_range) ||
@@ -1096,8 +1096,10 @@ bool ots_cmap_serialise(OTSStream *out, 
   return true;
 }
 
 void ots_cmap_free(OpenTypeFile *file) {
   delete file->cmap;
 }
 
 }  // namespace ots
+
+#undef TABLE_NAME
--- a/gfx/ots/src/cvt.cc
+++ b/gfx/ots/src/cvt.cc
@@ -51,8 +51,10 @@ bool ots_cvt_serialise(OTSStream *out, O
   return true;
 }
 
 void ots_cvt_free(OpenTypeFile *file) {
   delete file->cvt;
 }
 
 }  // namespace ots
+
+#undef TABLE_NAME
--- a/gfx/ots/src/fpgm.cc
+++ b/gfx/ots/src/fpgm.cc
@@ -45,8 +45,10 @@ bool ots_fpgm_serialise(OTSStream *out, 
   return true;
 }
 
 void ots_fpgm_free(OpenTypeFile *file) {
   delete file->fpgm;
 }
 
 }  // namespace ots
+
+#undef TABLE_NAME
--- a/gfx/ots/src/gasp.cc
+++ b/gfx/ots/src/gasp.cc
@@ -105,8 +105,11 @@ bool ots_gasp_serialise(OTSStream *out, 
   return true;
 }
 
 void ots_gasp_free(OpenTypeFile *file) {
   delete file->gasp;
 }
 
 }  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
--- a/gfx/ots/src/gdef.cc
+++ b/gfx/ots/src/gdef.cc
@@ -379,8 +379,10 @@ bool ots_gdef_serialise(OTSStream *out, 
 }
 
 void ots_gdef_free(OpenTypeFile *file) {
   delete file->gdef;
 }
 
 }  // namespace ots
 
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
--- a/gfx/ots/src/glyf.cc
+++ b/gfx/ots/src/glyf.cc
@@ -302,8 +302,10 @@ bool ots_glyf_serialise(OTSStream *out, 
   return true;
 }
 
 void ots_glyf_free(OpenTypeFile *file) {
   delete file->glyf;
 }
 
 }  // namespace ots
+
+#undef TABLE_NAME
--- a/gfx/ots/src/gpos.cc
+++ b/gfx/ots/src/gpos.cc
@@ -752,46 +752,58 @@ bool ots_gpos_parse(OpenTypeFile *file, 
     DROP_THIS_TABLE("Incomplete table");
     return true;
   }
 
   if (version != 0x00010000) {
     DROP_THIS_TABLE("Bad version");
     return true;
   }
-  if ((offset_script_list < kGposHeaderSize ||
-       offset_script_list >= length) ||
-      (offset_feature_list < kGposHeaderSize ||
-       offset_feature_list >= length) ||
-      (offset_lookup_list < kGposHeaderSize ||
-       offset_lookup_list >= length)) {
-    DROP_THIS_TABLE("Bad offset in table header");
-    return true;
-  }
+
+  if (offset_lookup_list) {
+    if (offset_lookup_list < kGposHeaderSize || offset_lookup_list >= length) {
+      DROP_THIS_TABLE("Bad lookup list offset in table header");
+      return true;
+    }
 
-  if (!ParseLookupListTable(file, data + offset_lookup_list,
-                            length - offset_lookup_list,
-                            &kGposLookupSubtableParser,
-                            &gpos->num_lookups)) {
-    DROP_THIS_TABLE("Failed to parse lookup list table");
-    return true;
+    if (!ParseLookupListTable(file, data + offset_lookup_list,
+                              length - offset_lookup_list,
+                              &kGposLookupSubtableParser,
+                              &gpos->num_lookups)) {
+      DROP_THIS_TABLE("Failed to parse lookup list table");
+      return true;
+    }
   }
 
   uint16_t num_features = 0;
-  if (!ParseFeatureListTable(file, data + offset_feature_list,
-                             length - offset_feature_list, gpos->num_lookups,
-                             &num_features)) {
-    DROP_THIS_TABLE("Failed to parse feature list table");
-    return true;
+  if (offset_feature_list) {
+    if (offset_feature_list < kGposHeaderSize || offset_feature_list >= length) {
+      DROP_THIS_TABLE("Bad feature list offset in table header");
+      return true;
+    }
+
+    if (!ParseFeatureListTable(file, data + offset_feature_list,
+                               length - offset_feature_list, gpos->num_lookups,
+                               &num_features)) {
+      DROP_THIS_TABLE("Failed to parse feature list table");
+      return true;
+    }
   }
 
-  if (!ParseScriptListTable(file, data + offset_script_list,
-                            length - offset_script_list, num_features)) {
-    DROP_THIS_TABLE("Failed to parse script list table");
-    return true;
+  if (offset_script_list) {
+    if (offset_script_list < kGposHeaderSize || offset_script_list >= length) {
+      DROP_THIS_TABLE("Bad script list offset in table header");
+      return true;
+    }
+
+    if (!ParseScriptListTable(file, data + offset_script_list,
+                              length - offset_script_list, num_features)) {
+      DROP_THIS_TABLE("Failed to parse script list table");
+      return true;
+    }
   }
 
   gpos->data = data;
   gpos->length = length;
   return true;
 }
 
 bool ots_gpos_should_serialise(OpenTypeFile *file) {
@@ -807,8 +819,10 @@ bool ots_gpos_serialise(OTSStream *out, 
 }
 
 void ots_gpos_free(OpenTypeFile *file) {
   delete file->gpos;
 }
 
 }  // namespace ots
 
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
--- a/gfx/ots/src/gsub.cc
+++ b/gfx/ots/src/gsub.cc
@@ -609,46 +609,58 @@ bool ots_gsub_parse(OpenTypeFile *file, 
     DROP_THIS_TABLE("Incomplete table");
     return true;
   }
 
   if (version != 0x00010000) {
     DROP_THIS_TABLE("Bad version");
     return true;
   }
-  if ((offset_script_list < kGsubHeaderSize ||
-       offset_script_list >= length) ||
-      (offset_feature_list < kGsubHeaderSize ||
-       offset_feature_list >= length) ||
-      (offset_lookup_list < kGsubHeaderSize ||
-       offset_lookup_list >= length)) {
-    DROP_THIS_TABLE("Bad offset in table header");
-    return true;
-  }
+
+  if (offset_lookup_list) {
+    if (offset_lookup_list < kGsubHeaderSize || offset_lookup_list >= length) {
+      DROP_THIS_TABLE("Bad lookup list offset in table header");
+      return true;
+    }
 
-  if (!ParseLookupListTable(file, data + offset_lookup_list,
-                            length - offset_lookup_list,
-                            &kGsubLookupSubtableParser,
-                            &gsub->num_lookups)) {
-    DROP_THIS_TABLE("Failed to parse lookup list table");
-    return true;
+    if (!ParseLookupListTable(file, data + offset_lookup_list,
+                              length - offset_lookup_list,
+                              &kGsubLookupSubtableParser,
+                              &gsub->num_lookups)) {
+      DROP_THIS_TABLE("Failed to parse lookup list table");
+      return true;
+    }
   }
 
   uint16_t num_features = 0;
-  if (!ParseFeatureListTable(file, data + offset_feature_list,
-                             length - offset_feature_list, gsub->num_lookups,
-                             &num_features)) {
-    DROP_THIS_TABLE("Failed to parse feature list table");
-    return true;
+  if (offset_feature_list) {
+    if (offset_feature_list < kGsubHeaderSize || offset_feature_list >= length) {
+      DROP_THIS_TABLE("Bad feature list offset in table header");
+      return true;
+    }
+
+    if (!ParseFeatureListTable(file, data + offset_feature_list,
+                               length - offset_feature_list, gsub->num_lookups,
+                               &num_features)) {
+      DROP_THIS_TABLE("Failed to parse feature list table");
+      return true;
+    }
   }
 
-  if (!ParseScriptListTable(file, data + offset_script_list,
-                            length - offset_script_list, num_features)) {
-    DROP_THIS_TABLE("Failed to parse script list table");
-    return true;
+  if (offset_script_list) {
+    if (offset_script_list < kGsubHeaderSize || offset_script_list >= length) {
+      DROP_THIS_TABLE("Bad script list offset in table header");
+      return true;
+    }
+
+    if (!ParseScriptListTable(file, data + offset_script_list,
+                              length - offset_script_list, num_features)) {
+      DROP_THIS_TABLE("Failed to parse script list table");
+      return true;
+    }
   }
 
   gsub->data = data;
   gsub->length = length;
   return true;
 }
 
 bool ots_gsub_should_serialise(OpenTypeFile *file) {
@@ -664,8 +676,10 @@ bool ots_gsub_serialise(OTSStream *out, 
 }
 
 void ots_gsub_free(OpenTypeFile *file) {
   delete file->gsub;
 }
 
 }  // namespace ots
 
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
--- a/gfx/ots/src/hdmx.cc
+++ b/gfx/ots/src/hdmx.cc
@@ -133,8 +133,11 @@ bool ots_hdmx_serialise(OTSStream *out, 
   return true;
 }
 
 void ots_hdmx_free(OpenTypeFile *file) {
   delete file->hdmx;
 }
 
 }  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
--- a/gfx/ots/src/head.cc
+++ b/gfx/ots/src/head.cc
@@ -144,8 +144,10 @@ bool ots_head_serialise(OTSStream *out, 
   return true;
 }
 
 void ots_head_free(OpenTypeFile *file) {
   delete file->head;
 }
 
 }  // namespace
+
+#undef TABLE_NAME
--- a/gfx/ots/src/hhea.cc
+++ b/gfx/ots/src/hhea.cc
@@ -44,8 +44,10 @@ bool ots_hhea_serialise(OTSStream *out, 
   return true;
 }
 
 void ots_hhea_free(OpenTypeFile *file) {
   delete file->hhea;
 }
 
 }  // namespace ots
+
+#undef TABLE_NAME
--- a/gfx/ots/src/hmtx.cc
+++ b/gfx/ots/src/hmtx.cc
@@ -42,8 +42,10 @@ bool ots_hmtx_serialise(OTSStream *out, 
   return true;
 }
 
 void ots_hmtx_free(OpenTypeFile *file) {
   delete file->hmtx;
 }
 
 }  // namespace ots
+
+#undef TABLE_NAME
--- a/gfx/ots/src/kern.cc
+++ b/gfx/ots/src/kern.cc
@@ -195,8 +195,11 @@ bool ots_kern_serialise(OTSStream *out, 
   return true;
 }
 
 void ots_kern_free(OpenTypeFile *file) {
   delete file->kern;
 }
 
 }  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
--- a/gfx/ots/src/layout.cc
+++ b/gfx/ots/src/layout.cc
@@ -1505,8 +1505,9 @@ bool ParseExtensionSubtable(const OpenTy
     return OTS_FAILURE_MSG("Failed to parse lookup from extension lookup");
   }
 
   return true;
 }
 
 }  // namespace ots
 
+#undef TABLE_NAME
--- a/gfx/ots/src/loca.cc
+++ b/gfx/ots/src/loca.cc
@@ -93,8 +93,10 @@ bool ots_loca_serialise(OTSStream *out, 
   return true;
 }
 
 void ots_loca_free(OpenTypeFile *file) {
   delete file->loca;
 }
 
 }  // namespace ots
+
+#undef TABLE_NAME
--- a/gfx/ots/src/ltsh.cc
+++ b/gfx/ots/src/ltsh.cc
@@ -81,8 +81,11 @@ bool ots_ltsh_serialise(OTSStream *out, 
   return true;
 }
 
 void ots_ltsh_free(OpenTypeFile *file) {
   delete file->ltsh;
 }
 
 }  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
--- a/gfx/ots/src/math.cc
+++ b/gfx/ots/src/math.cc
@@ -601,8 +601,10 @@ bool ots_math_serialise(OTSStream *out, 
 }
 
 void ots_math_free(OpenTypeFile *file) {
   delete file->math;
 }
 
 }  // namespace ots
 
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
--- a/gfx/ots/src/maxp.cc
+++ b/gfx/ots/src/maxp.cc
@@ -123,8 +123,10 @@ bool ots_maxp_serialise(OTSStream *out, 
   return true;
 }
 
 void ots_maxp_free(OpenTypeFile *file) {
   delete file->maxp;
 }
 
 }  // namespace ots
+
+#undef TABLE_NAME
--- a/gfx/ots/src/metrics.cc
+++ b/gfx/ots/src/metrics.cc
@@ -179,8 +179,9 @@ bool SerialiseMetricsTable(const ots::Op
     }
   }
 
   return true;
 }
 
 }  // namespace ots
 
+#undef TABLE_NAME
--- a/gfx/ots/src/moz.build
+++ b/gfx/ots/src/moz.build
@@ -55,8 +55,11 @@ FINAL_LIBRARY = 'gkmedias'
 
 DEFINES['PACKAGE_VERSION'] = '"moz"'
 DEFINES['PACKAGE_BUGREPORT'] = '"http://bugzilla.mozilla.org/"'
 DEFINES['NOMINMAX'] = True
 
 if CONFIG['OS_TARGET'] == 'WINNT':
     DEFINES['OTS_DLL'] = True
     DEFINES['OTS_DLL_EXPORTS'] = True
+
+# Disable WOFF2 support.
+DEFINES['OTS_DISABLE_WOFF2'] = True;
--- a/gfx/ots/src/name.cc
+++ b/gfx/ots/src/name.cc
@@ -327,8 +327,10 @@ bool ots_name_serialise(OTSStream* out, 
   return true;
 }
 
 void ots_name_free(OpenTypeFile* file) {
   delete file->name;
 }
 
 }  // namespace
+
+#undef TABLE_NAME
--- a/gfx/ots/src/os2.cc
+++ b/gfx/ots/src/os2.cc
@@ -285,8 +285,10 @@ bool ots_os2_serialise(OTSStream *out, O
   return true;
 }
 
 void ots_os2_free(OpenTypeFile *file) {
   delete file->os2;
 }
 
 }  // namespace ots
+
+#undef TABLE_NAME
--- a/gfx/ots/src/ots.cc
+++ b/gfx/ots/src/ots.cc
@@ -9,35 +9,27 @@
 
 #include <algorithm>
 #include <cstdlib>
 #include <cstring>
 #include <limits>
 #include <map>
 #include <vector>
 
-#ifdef MOZ_OTS_WOFF2
+#ifndef OTS_DISABLE_WOFF2
 #include "woff2.h"
 #endif
 
 // The OpenType Font File
 // http://www.microsoft.com/typography/otspec/cmap.htm
 
 namespace {
 
 bool g_debug_output = true;
-#ifdef MOZ_OTS_WOFF2
 bool g_enable_woff2 = false;
-#endif
-
-ots::MessageFunc  g_message_func = NULL;
-void             *g_message_user_data = NULL;
-
-ots::TableActionFunc  g_table_action_func = NULL;
-void                 *g_table_action_user_data = NULL;
 
 // Generate a message with or without a table tag, when 'header' is the OpenTypeFile pointer
 #define OTS_FAILURE_MSG_TAG(msg_,tag_) OTS_FAILURE_MSG_TAG_(header, msg_, tag_)
 #define OTS_FAILURE_MSG_HDR(msg_)      OTS_FAILURE_MSG_(header, msg_)
 
 
 struct OpenTypeTable {
   uint32_t tag;
@@ -399,17 +391,17 @@ bool ProcessWOFF(ots::OpenTypeFile *head
   }
   if (block_end != ots::Round4(length)) {
     return OTS_FAILURE_MSG_HDR("file length mismatch (trailing junk?)");
   }
 
   return ProcessGeneric(header, woff_tag, output, data, length, tables, file);
 }
 
-#ifdef MOZ_OTS_WOFF2
+#ifndef OTS_DISABLE_WOFF2
 bool ProcessWOFF2(ots::OpenTypeFile *header,
                   ots::OTSStream *output, const uint8_t *data, size_t length) {
   size_t decompressed_size = ots::ComputeWOFF2FinalSize(data, length);
   if (decompressed_size == 0) {
     return OTS_FAILURE();
   }
   // decompressed font must be <= 30MB
   if (decompressed_size > 30 * 1024 * 1024) {
@@ -420,21 +412,21 @@ bool ProcessWOFF2(ots::OpenTypeFile *hea
   if (!ots::ConvertWOFF2ToTTF(&decompressed_buffer[0], decompressed_size,
                               data, length)) {
     return OTS_FAILURE();
   }
   return ProcessTTF(header, output, &decompressed_buffer[0], decompressed_size);
 }
 #endif
 
-ots::TableAction GetTableAction(uint32_t tag) {
+ots::TableAction GetTableAction(ots::OpenTypeFile *header, uint32_t tag) {
   ots::TableAction action = ots::TABLE_ACTION_DEFAULT;
 
-  if (g_table_action_func != NULL) {
-    action = g_table_action_func(htonl(tag), g_table_action_user_data);
+  if (header->table_action_func != NULL) {
+    action = header->table_action_func(htonl(tag), header->table_action_user_data);
   }
 
   if (action == ots::TABLE_ACTION_DEFAULT) {
     action = ots::TABLE_ACTION_DROP;
 
     for (unsigned i = 0; ; ++i) {
       if (table_parsers[i].parse == NULL) break;
 
@@ -573,20 +565,20 @@ bool ProcessGeneric(ots::OpenTypeFile *h
     }
   }
 
   Arena arena;
 
   for (unsigned i = 0; ; ++i) {
     if (table_parsers[i].parse == NULL) break;
 
-    const std::map<uint32_t, OpenTypeTable>::const_iterator it
-        = table_map.find(Tag(table_parsers[i].tag));
+    uint32_t tag = Tag(table_parsers[i].tag);
+    const std::map<uint32_t, OpenTypeTable>::const_iterator it = table_map.find(tag);
 
-    ots::TableAction action = GetTableAction(Tag(table_parsers[i].tag));
+    ots::TableAction action = GetTableAction(header, tag);
     if (it == table_map.end()) {
       if (table_parsers[i].required && action == ots::TABLE_ACTION_SANITIZE) {
         return OTS_FAILURE_MSG_TAG("missing required table", table_parsers[i].tag);
       }
       continue;
     }
 
     const uint8_t* table_data;
@@ -629,17 +621,17 @@ bool ProcessGeneric(ots::OpenTypeFile *h
 
     if (table_parsers[i].should_serialise(header)) {
       num_output_tables++;
     }
   }
 
   for (std::map<uint32_t, OpenTypeTable>::const_iterator it = table_map.begin();
        it != table_map.end(); ++it) {
-    ots::TableAction action = GetTableAction(it->first);
+    ots::TableAction action = GetTableAction(header, it->first);
     if (action == ots::TABLE_ACTION_PASSTHRU) {
       num_output_tables++;
     }
   }
 
   unsigned max_pow2 = 0;
   while (1u << (max_pow2 + 1) <= num_output_tables) {
     max_pow2++;
@@ -701,17 +693,17 @@ bool ProcessGeneric(ots::OpenTypeFile *h
       return OTS_FAILURE_MSG_HDR("error writing output");
     }
     out.chksum = output->chksum();
     out_tables.push_back(out);
   }
 
   for (std::map<uint32_t, OpenTypeTable>::const_iterator it = table_map.begin();
        it != table_map.end(); ++it) {
-    ots::TableAction action = GetTableAction(it->first);
+    ots::TableAction action = GetTableAction(header, it->first);
     if (action == ots::TABLE_ACTION_PASSTHRU) {
       OutputTable out;
       out.tag = it->second.tag;
       out.offset = output->Tell();
 
       output->ResetChecksum();
       if (it->second.tag == Tag("head")) {
         head_table_offset = out.offset;
@@ -801,46 +793,38 @@ bool IsValidVersionTag(uint32_t tag) {
          tag == Tag("true") ||
          tag == Tag("typ1");
 }
 
 void DisableDebugOutput() {
   g_debug_output = false;
 }
 
-#ifdef MOZ_OTS_WOFF2
 void EnableWOFF2() {
   g_enable_woff2 = true;
 }
-#endif
 
-void SetMessageCallback(MessageFunc func, void *user_data) {
-  g_message_func = func;
-  g_message_user_data = user_data;
-}
-
-void SetTableActionCallback(TableActionFunc func, void *user_data) {
-  g_table_action_func = func;
-  g_table_action_user_data = user_data;
-}
-
-bool Process(OTSStream *output, const uint8_t *data, size_t length) {
+bool OTSContext::Process(OTSStream *output,
+                         const uint8_t *data,
+                         size_t length) {
   OpenTypeFile header;
 
-  header.message_func = g_message_func;
-  header.user_data = g_message_user_data;
+  header.message_func = message_func;
+  header.message_user_data = message_user_data;
+  header.table_action_func = table_action_func;
+  header.table_action_user_data = table_action_user_data;
 
   if (length < 4) {
     return OTS_FAILURE_MSG_(&header, "file less than 4 bytes");
   }
 
   bool result;
   if (data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && data[3] == 'F') {
     result = ProcessWOFF(&header, output, data, length);
-#ifdef MOZ_OTS_WOFF2
+#ifndef OTS_DISABLE_WOFF2
   } else if (g_enable_woff2 &&
              data[0] == 'w' && data[1] == 'O' && data[2] == 'F' &&
              data[3] == '2') {
     result = ProcessWOFF2(&header, output, data, length);
 #endif
   } else {
     result = ProcessTTF(&header, output, data, length);
   }
--- a/gfx/ots/src/ots.h
+++ b/gfx/ots/src/ots.h
@@ -48,23 +48,23 @@ void Warning(const char *f, int l, const
 // message-less OTS_FAILURE(), so that the current parser will return 'false' as
 // its result (indicating a failure).
 // If a message_func pointer has been provided, this will be called before returning
 // the 'false' status.
 
 // Generate a simple message
 #define OTS_FAILURE_MSG_(otf_,...) \
   ((otf_)->message_func && \
-    (*(otf_)->message_func)((otf_)->user_data, __VA_ARGS__) && \
+    (*(otf_)->message_func)((otf_)->message_user_data, __VA_ARGS__) && \
     false)
 
 // Generate a message with an associated table tag
 #define OTS_FAILURE_MSG_TAG_(otf_,msg_,tag_) \
   ((otf_)->message_func && \
-    (*(otf_)->message_func)((otf_)->user_data, "%4.4s: %s", tag_, msg_) && \
+    (*(otf_)->message_func)((otf_)->message_user_data, "%4.4s: %s", tag_, msg_) && \
     false)
 
 // Convenience macro for use in files that only handle a single table tag,
 // defined as TABLE_NAME at the top of the file; the 'file' variable is
 // expected to be the current OpenTypeFile pointer.
 #define OTS_FAILURE_MSG(...) OTS_FAILURE_MSG_(file, TABLE_NAME ": " __VA_ARGS__)
 
 // Define OTS_NO_TRANSCODE_HINTS (i.e., g++ -DOTS_NO_TRANSCODE_HINTS) if you
@@ -245,18 +245,21 @@ struct OpenTypeFile {
   }
 
   uint32_t version;
   uint16_t num_tables;
   uint16_t search_range;
   uint16_t entry_selector;
   uint16_t range_shift;
 
-  MessageFunc message_func;
-  void        *user_data;
+  MessageFunc  message_func;
+  void        *message_user_data;
+
+  TableActionFunc  table_action_func;
+  void            *table_action_user_data;
 
 #define F(name, capname) OpenType##capname *name;
 FOR_EACH_TABLE_TYPE
 #undef F
 };
 
 #define F(name, capname) \
 bool ots_##name##_parse(OpenTypeFile *f, const uint8_t *d, size_t l); \
--- a/gfx/ots/src/post.cc
+++ b/gfx/ots/src/post.cc
@@ -175,8 +175,10 @@ bool ots_post_serialise(OTSStream *out, 
   return true;
 }
 
 void ots_post_free(OpenTypeFile *file) {
   delete file->post;
 }
 
 }  // namespace ots
+
+#undef TABLE_NAME
--- a/gfx/ots/src/prep.cc
+++ b/gfx/ots/src/prep.cc
@@ -45,8 +45,10 @@ bool ots_prep_serialise(OTSStream *out, 
   return true;
 }
 
 void ots_prep_free(OpenTypeFile *file) {
   delete file->prep;
 }
 
 }  // namespace ots
+
+#undef TABLE_NAME
--- a/gfx/ots/src/vdmx.cc
+++ b/gfx/ots/src/vdmx.cc
@@ -176,8 +176,11 @@ bool ots_vdmx_serialise(OTSStream *out, 
   return true;
 }
 
 void ots_vdmx_free(OpenTypeFile *file) {
   delete file->vdmx;
 }
 
 }  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
--- a/gfx/ots/src/vhea.cc
+++ b/gfx/ots/src/vhea.cc
@@ -51,8 +51,9 @@ bool ots_vhea_serialise(OTSStream *out, 
 }
 
 void ots_vhea_free(OpenTypeFile *file) {
   delete file->vhea;
 }
 
 }  // namespace ots
 
+#undef TABLE_NAME
--- a/gfx/ots/src/vmtx.cc
+++ b/gfx/ots/src/vmtx.cc
@@ -47,8 +47,9 @@ bool ots_vmtx_serialise(OTSStream *out, 
 }
 
 void ots_vmtx_free(OpenTypeFile *file) {
   delete file->vmtx;
 }
 
 }  // namespace ots
 
+#undef TABLE_NAME
--- a/gfx/ots/src/vorg.cc
+++ b/gfx/ots/src/vorg.cc
@@ -96,8 +96,11 @@ bool ots_vorg_serialise(OTSStream *out, 
   return true;
 }
 
 void ots_vorg_free(OpenTypeFile *file) {
   delete file->vorg;
 }
 
 }  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
new file mode 100644
--- /dev/null
+++ b/gfx/ots/src/woff2.cc
@@ -0,0 +1,1022 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This is the implementation of decompression of the proposed WOFF Ultra
+// Condensed file format.
+
+#include <cassert>
+#include <cstdlib>
+#include <vector>
+
+#include <zlib.h>
+
+#include "third_party/brotli/src/brotli/dec/decode.h"
+
+#include "opentype-sanitiser.h"
+#include "ots-memory-stream.h"
+#include "ots.h"
+#include "woff2.h"
+
+namespace {
+
+// simple glyph flags
+const int kGlyfOnCurve = 1 << 0;
+const int kGlyfXShort = 1 << 1;
+const int kGlyfYShort = 1 << 2;
+const int kGlyfRepeat = 1 << 3;
+const int kGlyfThisXIsSame = 1 << 4;
+const int kGlyfThisYIsSame = 1 << 5;
+
+// composite glyph flags
+const int FLAG_ARG_1_AND_2_ARE_WORDS = 1 << 0;
+const int FLAG_WE_HAVE_A_SCALE = 1 << 3;
+const int FLAG_MORE_COMPONENTS = 1 << 5;
+const int FLAG_WE_HAVE_AN_X_AND_Y_SCALE = 1 << 6;
+const int FLAG_WE_HAVE_A_TWO_BY_TWO = 1 << 7;
+const int FLAG_WE_HAVE_INSTRUCTIONS = 1 << 8;
+
+const size_t kSfntHeaderSize = 12;
+const size_t kSfntEntrySize = 16;
+const size_t kCheckSumAdjustmentOffset = 8;
+
+const size_t kEndPtsOfContoursOffset = 10;
+const size_t kCompositeGlyphBegin = 10;
+
+// Note that the byte order is big-endian, not the same as ots.cc
+#define TAG(a, b, c, d) ((a << 24) | (b << 16) | (c << 8) | d)
+
+const unsigned int kWoff2FlagsContinueStream = 1 << 4;
+const unsigned int kWoff2FlagsTransform = 1 << 5;
+
+// Compression type values common to both short and long formats
+const uint32_t kCompressionTypeMask = 0xf;
+const uint32_t kCompressionTypeNone = 0;
+const uint32_t kCompressionTypeGzip = 1;
+const uint32_t kCompressionTypeBrotli = 2;
+
+// This is a special value for the short format only, as described in
+// "Design for compressed header format" in draft doc.
+const uint32_t kShortFlagsContinue = 3;
+
+const uint32_t kKnownTags[] = {
+  TAG('c', 'm', 'a', 'p'),  // 0
+  TAG('h', 'e', 'a', 'd'),  // 1
+  TAG('h', 'h', 'e', 'a'),  // 2
+  TAG('h', 'm', 't', 'x'),  // 3
+  TAG('m', 'a', 'x', 'p'),  // 4
+  TAG('n', 'a', 'm', 'e'),  // 5
+  TAG('O', 'S', '/', '2'),  // 6
+  TAG('p', 'o', 's', 't'),  // 7
+  TAG('c', 'v', 't', ' '),  // 8
+  TAG('f', 'p', 'g', 'm'),  // 9
+  TAG('g', 'l', 'y', 'f'),  // 10
+  TAG('l', 'o', 'c', 'a'),  // 11
+  TAG('p', 'r', 'e', 'p'),  // 12
+  TAG('C', 'F', 'F', ' '),  // 13
+  TAG('V', 'O', 'R', 'G'),  // 14
+  TAG('E', 'B', 'D', 'T'),  // 15
+  TAG('E', 'B', 'L', 'C'),  // 16
+  TAG('g', 'a', 's', 'p'),  // 17
+  TAG('h', 'd', 'm', 'x'),  // 18
+  TAG('k', 'e', 'r', 'n'),  // 19
+  TAG('L', 'T', 'S', 'H'),  // 20
+  TAG('P', 'C', 'L', 'T'),  // 21
+  TAG('V', 'D', 'M', 'X'),  // 22
+  TAG('v', 'h', 'e', 'a'),  // 23
+  TAG('v', 'm', 't', 'x'),  // 24
+  TAG('B', 'A', 'S', 'E'),  // 25
+  TAG('G', 'D', 'E', 'F'),  // 26
+  TAG('G', 'P', 'O', 'S'),  // 27
+  TAG('G', 'S', 'U', 'B'),  // 28
+};
+
+struct Point {
+  int x;
+  int y;
+  bool on_curve;
+};
+
+struct Table {
+  uint32_t tag;
+  uint32_t flags;
+  uint32_t src_offset;
+  uint32_t src_length;
+
+  uint32_t transform_length;
+
+  uint32_t dst_offset;
+  uint32_t dst_length;
+
+  Table()
+      : tag(0),
+        flags(0),
+        src_offset(0),
+        src_length(0),
+        transform_length(0),
+        dst_offset(0),
+        dst_length(0) {}
+};
+
+// Based on section 6.1.1 of MicroType Express draft spec
+bool Read255UShort(ots::Buffer* buf, unsigned int* value) {
+  static const int kWordCode = 253;
+  static const int kOneMoreByteCode2 = 254;
+  static const int kOneMoreByteCode1 = 255;
+  static const int kLowestUCode = 253;
+  uint8_t code = 0;
+  if (!buf->ReadU8(&code)) {
+    return OTS_FAILURE();
+  }
+  if (code == kWordCode) {
+    uint16_t result = 0;
+    if (!buf->ReadU16(&result)) {
+      return OTS_FAILURE();
+    }
+    *value = result;
+    return true;
+  } else if (code == kOneMoreByteCode1) {
+    uint8_t result = 0;
+    if (!buf->ReadU8(&result)) {
+      return OTS_FAILURE();
+    }
+    *value = result + kLowestUCode;
+    return true;
+  } else if (code == kOneMoreByteCode2) {
+    uint8_t result = 0;
+    if (!buf->ReadU8(&result)) {
+      return OTS_FAILURE();
+    }
+    *value = result + kLowestUCode * 2;
+    return true;
+  } else {
+    *value = code;
+    return true;
+  }
+}
+
+bool ReadBase128(ots::Buffer* buf, uint32_t* value) {
+  uint32_t result = 0;
+  for (size_t i = 0; i < 5; ++i) {
+    uint8_t code = 0;
+    if (!buf->ReadU8(&code)) {
+      return OTS_FAILURE();
+    }
+    // If any of the top seven bits are set then we're about to overflow.
+    if (result & 0xe0000000U) {
+      return OTS_FAILURE();
+    }
+    result = (result << 7) | (code & 0x7f);
+    if ((code & 0x80) == 0) {
+      *value = result;
+      return true;
+    }
+  }
+  // Make sure not to exceed the size bound
+  return OTS_FAILURE();
+}
+
+// Caller must ensure that buffer overrun won't happen.
+// TODO(ksakamaoto): Consider creating 'writer' version of the Buffer class
+// and use it across the code.
+size_t StoreU32(uint8_t* dst, size_t offset, uint32_t x) {
+  dst[offset] = x >> 24;
+  dst[offset + 1] = x >> 16;
+  dst[offset + 2] = x >> 8;
+  dst[offset + 3] = x;
+  return offset + 4;
+}
+
+size_t Store16(uint8_t* dst, size_t offset, int x) {
+  dst[offset] = x >> 8;
+  dst[offset + 1] = x;
+  return offset + 2;
+}
+
+int WithSign(int flag, int baseval) {
+  assert(0 <= baseval && baseval < 65536);
+  return (flag & 1) ? baseval : -baseval;
+}
+
+bool TripletDecode(const uint8_t* flags_in, const uint8_t* in, size_t in_size,
+    unsigned int n_points, std::vector<Point>* result,
+    size_t* in_bytes_consumed) {
+  int x = 0;
+  int y = 0;
+
+  // Early return if |in| buffer is too small. Each point consumes 1-4 bytes.
+  if (n_points > in_size) {
+    return OTS_FAILURE();
+  }
+  unsigned int triplet_index = 0;
+
+  for (unsigned int i = 0; i < n_points; ++i) {
+    uint8_t flag = flags_in[i];
+    bool on_curve = !(flag >> 7);
+    flag &= 0x7f;
+    unsigned int n_data_bytes;
+    if (flag < 84) {
+      n_data_bytes = 1;
+    } else if (flag < 120) {
+      n_data_bytes = 2;
+    } else if (flag < 124) {
+      n_data_bytes = 3;
+    } else {
+      n_data_bytes = 4;
+    }
+    if (triplet_index + n_data_bytes > in_size ||
+        triplet_index + n_data_bytes < triplet_index) {
+      return OTS_FAILURE();
+    }
+    int dx, dy;
+    if (flag < 10) {
+      dx = 0;
+      dy = WithSign(flag, ((flag & 14) << 7) + in[triplet_index]);
+    } else if (flag < 20) {
+      dx = WithSign(flag, (((flag - 10) & 14) << 7) + in[triplet_index]);
+      dy = 0;
+    } else if (flag < 84) {
+      int b0 = flag - 20;
+      int b1 = in[triplet_index];
+      dx = WithSign(flag, 1 + (b0 & 0x30) + (b1 >> 4));
+      dy = WithSign(flag >> 1, 1 + ((b0 & 0x0c) << 2) + (b1 & 0x0f));
+    } else if (flag < 120) {
+      int b0 = flag - 84;
+      dx = WithSign(flag, 1 + ((b0 / 12) << 8) + in[triplet_index]);
+      dy = WithSign(flag >> 1,
+                    1 + (((b0 % 12) >> 2) << 8) + in[triplet_index + 1]);
+    } else if (flag < 124) {
+      int b2 = in[triplet_index + 1];
+      dx = WithSign(flag, (in[triplet_index] << 4) + (b2 >> 4));
+      dy = WithSign(flag >> 1, ((b2 & 0x0f) << 8) + in[triplet_index + 2]);
+    } else {
+      dx = WithSign(flag, (in[triplet_index] << 8) + in[triplet_index + 1]);
+      dy = WithSign(flag >> 1,
+          (in[triplet_index + 2] << 8) + in[triplet_index + 3]);
+    }
+    triplet_index += n_data_bytes;
+    // Possible overflow but coordinate values are not security sensitive
+    x += dx;
+    y += dy;
+    result->push_back(Point());
+    Point& back = result->back();
+    back.x = x;
+    back.y = y;
+    back.on_curve = on_curve;
+  }
+  *in_bytes_consumed = triplet_index;
+  return true;
+}
+
+// This function stores just the point data. On entry, dst points to the
+// beginning of a simple glyph. Returns true on success.
+bool StorePoints(const std::vector<Point>& points,
+    unsigned int n_contours, unsigned int instruction_length,
+    uint8_t* dst, size_t dst_size, size_t* glyph_size) {
+  // I believe that n_contours < 65536, in which case this is safe. However, a
+  // comment and/or an assert would be good.
+  unsigned int flag_offset = kEndPtsOfContoursOffset + 2 * n_contours + 2 +
+    instruction_length;
+  int last_flag = -1;
+  int repeat_count = 0;
+  int last_x = 0;
+  int last_y = 0;
+  unsigned int x_bytes = 0;
+  unsigned int y_bytes = 0;
+
+  for (size_t i = 0; i < points.size(); ++i) {
+    const Point& point = points.at(i);
+    int flag = point.on_curve ? kGlyfOnCurve : 0;
+    int dx = point.x - last_x;
+    int dy = point.y - last_y;
+    if (dx == 0) {
+      flag |= kGlyfThisXIsSame;
+    } else if (dx > -256 && dx < 256) {
+      flag |= kGlyfXShort | (dx > 0 ? kGlyfThisXIsSame : 0);
+      x_bytes += 1;
+    } else {
+      x_bytes += 2;
+    }
+    if (dy == 0) {
+      flag |= kGlyfThisYIsSame;
+    } else if (dy > -256 && dy < 256) {
+      flag |= kGlyfYShort | (dy > 0 ? kGlyfThisYIsSame : 0);
+      y_bytes += 1;
+    } else {
+      y_bytes += 2;
+    }
+
+    if (flag == last_flag && repeat_count != 255) {
+      dst[flag_offset - 1] |= kGlyfRepeat;
+      repeat_count++;
+    } else {
+      if (repeat_count != 0) {
+        if (flag_offset >= dst_size) {
+          return OTS_FAILURE();
+        }
+        dst[flag_offset++] = repeat_count;
+      }
+      if (flag_offset >= dst_size) {
+        return OTS_FAILURE();
+      }
+      dst[flag_offset++] = flag;
+      repeat_count = 0;
+    }
+    last_x = point.x;
+    last_y = point.y;
+    last_flag = flag;
+  }
+
+  if (repeat_count != 0) {
+    if (flag_offset >= dst_size) {
+      return OTS_FAILURE();
+    }
+    dst[flag_offset++] = repeat_count;
+  }
+  unsigned int xy_bytes = x_bytes + y_bytes;
+  if (xy_bytes < x_bytes ||
+      flag_offset + xy_bytes < flag_offset ||
+      flag_offset + xy_bytes > dst_size) {
+    return OTS_FAILURE();
+  }
+
+  int x_offset = flag_offset;
+  int y_offset = flag_offset + x_bytes;
+  last_x = 0;
+  last_y = 0;
+  for (size_t i = 0; i < points.size(); ++i) {
+    int dx = points.at(i).x - last_x;
+    if (dx == 0) {
+      // pass
+    } else if (dx > -256 && dx < 256) {
+      dst[x_offset++] = std::abs(dx);
+    } else {
+      // will always fit for valid input, but overflow is harmless
+      x_offset = Store16(dst, x_offset, dx);
+    }
+    last_x += dx;
+    int dy = points.at(i).y - last_y;
+    if (dy == 0) {
+      // pass
+    } else if (dy > -256 && dy < 256) {
+      dst[y_offset++] = std::abs(dy);
+    } else {
+      y_offset = Store16(dst, y_offset, dy);
+    }
+    last_y += dy;
+  }
+  *glyph_size = y_offset;
+  return true;
+}
+
+// Compute the bounding box of the coordinates, and store into a glyf buffer.
+// A precondition is that there are at least 10 bytes available.
+void ComputeBbox(const std::vector<Point>& points, uint8_t* dst) {
+  int x_min = 0;
+  int y_min = 0;
+  int x_max = 0;
+  int y_max = 0;
+
+  for (size_t i = 0; i < points.size(); ++i) {
+    int x = points.at(i).x;
+    int y = points.at(i).y;
+    if (i == 0 || x < x_min) x_min = x;
+    if (i == 0 || x > x_max) x_max = x;
+    if (i == 0 || y < y_min) y_min = y;
+    if (i == 0 || y > y_max) y_max = y;
+  }
+  size_t offset = 2;
+  offset = Store16(dst, offset, x_min);
+  offset = Store16(dst, offset, y_min);
+  offset = Store16(dst, offset, x_max);
+  offset = Store16(dst, offset, y_max);
+}
+
+// Process entire bbox stream. This is done as a separate pass to allow for
+// composite bbox computations (an optional more aggressive transform).
+bool ProcessBboxStream(ots::Buffer* bbox_stream, unsigned int n_glyphs,
+    const std::vector<uint32_t>& loca_values, uint8_t* glyf_buf,
+    size_t glyf_buf_length) {
+  const uint8_t* buf = bbox_stream->buffer();
+  if (n_glyphs >= 65536 || loca_values.size() != n_glyphs + 1) {
+    return OTS_FAILURE();
+  }
+  // Safe because n_glyphs is bounded
+  unsigned int bitmap_length = ((n_glyphs + 31) >> 5) << 2;
+  if (!bbox_stream->Skip(bitmap_length)) {
+    return OTS_FAILURE();
+  }
+  for (unsigned int i = 0; i < n_glyphs; ++i) {
+    if (buf[i >> 3] & (0x80 >> (i & 7))) {
+      uint32_t loca_offset = loca_values.at(i);
+      if (loca_values.at(i + 1) - loca_offset < kEndPtsOfContoursOffset) {
+        return OTS_FAILURE();
+      }
+      if (glyf_buf_length < 2 + 10 ||
+          loca_offset > glyf_buf_length - 2 - 10) {
+        return OTS_FAILURE();
+      }
+      if (!bbox_stream->Read(glyf_buf + loca_offset + 2, 8)) {
+        return OTS_FAILURE();
+      }
+    }
+  }
+  return true;
+}
+
+bool ProcessComposite(ots::Buffer* composite_stream, uint8_t* dst,
+    size_t dst_size, size_t* glyph_size, bool* have_instructions) {
+  size_t start_offset = composite_stream->offset();
+  bool we_have_instructions = false;
+
+  uint16_t flags = FLAG_MORE_COMPONENTS;
+  while (flags & FLAG_MORE_COMPONENTS) {
+    if (!composite_stream->ReadU16(&flags)) {
+      return OTS_FAILURE();
+    }
+    we_have_instructions |= (flags & FLAG_WE_HAVE_INSTRUCTIONS) != 0;
+    size_t arg_size = 2;  // glyph index
+    if (flags & FLAG_ARG_1_AND_2_ARE_WORDS) {
+      arg_size += 4;
+    } else {
+      arg_size += 2;
+    }
+    if (flags & FLAG_WE_HAVE_A_SCALE) {
+      arg_size += 2;
+    } else if (flags & FLAG_WE_HAVE_AN_X_AND_Y_SCALE) {
+      arg_size += 4;
+    } else if (flags & FLAG_WE_HAVE_A_TWO_BY_TWO) {
+      arg_size += 8;
+    }
+    if (!composite_stream->Skip(arg_size)) {
+      return OTS_FAILURE();
+    }
+  }
+  size_t composite_glyph_size = composite_stream->offset() - start_offset;
+  if (composite_glyph_size + kCompositeGlyphBegin > dst_size) {
+    return OTS_FAILURE();
+  }
+  Store16(dst, 0, 0xffff);  // nContours = -1 for composite glyph
+  std::memcpy(dst + kCompositeGlyphBegin,
+      composite_stream->buffer() + start_offset,
+      composite_glyph_size);
+  *glyph_size = kCompositeGlyphBegin + composite_glyph_size;
+  *have_instructions = we_have_instructions;
+  return true;
+}
+
+// Build TrueType loca table
+bool StoreLoca(const std::vector<uint32_t>& loca_values, int index_format,
+    uint8_t* dst, size_t dst_size) {
+  const uint64_t loca_size = loca_values.size();
+  const uint64_t offset_size = index_format ? 4 : 2;
+  if ((loca_size << 2) >> 2 != loca_size) {
+    return OTS_FAILURE();
+  }
+  // No integer overflow here (loca_size <= 2^16).
+  if (offset_size * loca_size > dst_size) {
+    return OTS_FAILURE();
+  }
+  size_t offset = 0;
+  for (size_t i = 0; i < loca_values.size(); ++i) {
+    uint32_t value = loca_values.at(i);
+    if (index_format) {
+      offset = StoreU32(dst, offset, value);
+    } else {
+      offset = Store16(dst, offset, value >> 1);
+    }
+  }
+  return true;
+}
+
+// Reconstruct entire glyf table based on transformed original
+bool ReconstructGlyf(const uint8_t* data, size_t data_size,
+    uint8_t* dst, size_t dst_size,
+    uint8_t* loca_buf, size_t loca_size) {
+  static const int kNumSubStreams = 7;
+  ots::Buffer file(data, data_size);
+  uint32_t version;
+  std::vector<std::pair<const uint8_t*, size_t> > substreams(kNumSubStreams);
+
+  if (!file.ReadU32(&version)) {
+    return OTS_FAILURE();
+  }
+  uint16_t num_glyphs;
+  uint16_t index_format;
+  if (!file.ReadU16(&num_glyphs) ||
+      !file.ReadU16(&index_format)) {
+    return OTS_FAILURE();
+  }
+  unsigned int offset = (2 + kNumSubStreams) * 4;
+  if (offset > data_size) {
+    return OTS_FAILURE();
+  }
+  // Invariant from here on: data_size >= offset
+  for (int i = 0; i < kNumSubStreams; ++i) {
+    uint32_t substream_size;
+    if (!file.ReadU32(&substream_size)) {
+      return OTS_FAILURE();
+    }
+    if (substream_size > data_size - offset) {
+      return OTS_FAILURE();
+    }
+    substreams.at(i) = std::make_pair(data + offset, substream_size);
+    offset += substream_size;
+  }
+  ots::Buffer n_contour_stream(substreams.at(0).first, substreams.at(0).second);
+  ots::Buffer n_points_stream(substreams.at(1).first, substreams.at(1).second);
+  ots::Buffer flag_stream(substreams.at(2).first, substreams.at(2).second);
+  ots::Buffer glyph_stream(substreams.at(3).first, substreams.at(3).second);
+  ots::Buffer composite_stream(substreams.at(4).first, substreams.at(4).second);
+  ots::Buffer bbox_stream(substreams.at(5).first, substreams.at(5).second);
+  ots::Buffer instruction_stream(substreams.at(6).first,
+                                 substreams.at(6).second);
+
+  std::vector<uint32_t> loca_values;
+  loca_values.reserve(num_glyphs + 1);
+  std::vector<unsigned int> n_points_vec;
+  std::vector<Point> points;
+  uint32_t loca_offset = 0;
+  for (unsigned int i = 0; i < num_glyphs; ++i) {
+    size_t glyph_size = 0;
+    uint16_t n_contours = 0;
+    if (!n_contour_stream.ReadU16(&n_contours)) {
+      return OTS_FAILURE();
+    }
+    uint8_t* glyf_dst = dst + loca_offset;
+    size_t glyf_dst_size = dst_size - loca_offset;
+    if (n_contours == 0xffff) {
+      // composite glyph
+      bool have_instructions = false;
+      unsigned int instruction_size = 0;
+      if (!ProcessComposite(&composite_stream, glyf_dst, glyf_dst_size,
+            &glyph_size, &have_instructions)) {
+        return OTS_FAILURE();
+      }
+      if (have_instructions) {
+        if (!Read255UShort(&glyph_stream, &instruction_size)) {
+          return OTS_FAILURE();
+        }
+        // No integer overflow here (instruction_size < 2^16).
+        if (instruction_size + 2 > glyf_dst_size - glyph_size) {
+          return OTS_FAILURE();
+        }
+        Store16(glyf_dst, glyph_size, instruction_size);
+        if (!instruction_stream.Read(glyf_dst + glyph_size + 2,
+              instruction_size)) {
+          return OTS_FAILURE();
+        }
+        glyph_size += instruction_size + 2;
+      }
+    } else if (n_contours > 0) {
+      // simple glyph
+      n_points_vec.clear();
+      points.clear();
+      unsigned int total_n_points = 0;
+      unsigned int n_points_contour;
+      for (unsigned int j = 0; j < n_contours; ++j) {
+        if (!Read255UShort(&n_points_stream, &n_points_contour)) {
+          return OTS_FAILURE();
+        }
+        n_points_vec.push_back(n_points_contour);
+        if (total_n_points + n_points_contour < total_n_points) {
+          return OTS_FAILURE();
+        }
+        total_n_points += n_points_contour;
+      }
+      unsigned int flag_size = total_n_points;
+      if (flag_size > flag_stream.length() - flag_stream.offset()) {
+        return OTS_FAILURE();
+      }
+      const uint8_t* flags_buf = flag_stream.buffer() + flag_stream.offset();
+      const uint8_t* triplet_buf = glyph_stream.buffer() +
+        glyph_stream.offset();
+      size_t triplet_size = glyph_stream.length() - glyph_stream.offset();
+      size_t triplet_bytes_consumed = 0;
+      if (!TripletDecode(flags_buf, triplet_buf, triplet_size, total_n_points,
+            &points, &triplet_bytes_consumed)) {
+        return OTS_FAILURE();
+      }
+      const uint32_t header_and_endpts_contours_size =
+          kEndPtsOfContoursOffset + 2 * n_contours;
+      if (glyf_dst_size < header_and_endpts_contours_size) {
+        return OTS_FAILURE();
+      }
+      Store16(glyf_dst, 0, n_contours);
+      ComputeBbox(points, glyf_dst);
+      size_t endpts_offset = kEndPtsOfContoursOffset;
+      int end_point = -1;
+      for (unsigned int contour_ix = 0; contour_ix < n_contours; ++contour_ix) {
+        end_point += n_points_vec.at(contour_ix);
+        if (end_point >= 65536) {
+          return OTS_FAILURE();
+        }
+        endpts_offset = Store16(glyf_dst, endpts_offset, end_point);
+      }
+      if (!flag_stream.Skip(flag_size)) {
+        return OTS_FAILURE();
+      }
+      if (!glyph_stream.Skip(triplet_bytes_consumed)) {
+        return OTS_FAILURE();
+      }
+      unsigned int instruction_size;
+      if (!Read255UShort(&glyph_stream, &instruction_size)) {
+        return OTS_FAILURE();
+      }
+      // No integer overflow here (instruction_size < 2^16).
+      if (glyf_dst_size - header_and_endpts_contours_size <
+          instruction_size + 2) {
+        return OTS_FAILURE();
+      }
+      uint8_t* instruction_dst = glyf_dst + header_and_endpts_contours_size;
+      Store16(instruction_dst, 0, instruction_size);
+      if (!instruction_stream.Read(instruction_dst + 2, instruction_size)) {
+        return OTS_FAILURE();
+      }
+      if (!StorePoints(points, n_contours, instruction_size,
+            glyf_dst, glyf_dst_size, &glyph_size)) {
+        return OTS_FAILURE();
+      }
+    } else {
+      glyph_size = 0;
+    }
+    loca_values.push_back(loca_offset);
+    if (glyph_size + 3 < glyph_size) {
+      return OTS_FAILURE();
+    }
+    glyph_size = ots::Round2(glyph_size);
+    if (glyph_size > dst_size - loca_offset) {
+      // This shouldn't happen, but this test defensively maintains the
+      // invariant that loca_offset <= dst_size.
+      return OTS_FAILURE();
+    }
+    loca_offset += glyph_size;
+  }
+  loca_values.push_back(loca_offset);
+  assert(loca_values.size() == static_cast<size_t>(num_glyphs + 1));
+  if (!ProcessBboxStream(&bbox_stream, num_glyphs, loca_values,
+          dst, dst_size)) {
+    return OTS_FAILURE();
+  }
+  return StoreLoca(loca_values, index_format, loca_buf, loca_size);
+}
+
+// This is linear search, but could be changed to binary because we
+// do have a guarantee that the tables are sorted by tag. But the total
+// cpu time is expected to be very small in any case.
+const Table* FindTable(const std::vector<Table>& tables, uint32_t tag) {
+  size_t n_tables = tables.size();
+  for (size_t i = 0; i < n_tables; ++i) {
+    if (tables.at(i).tag == tag) {
+      return &tables.at(i);
+    }
+  }
+  return NULL;
+}
+
+bool ReconstructTransformed(const std::vector<Table>& tables, uint32_t tag,
+    const uint8_t* transformed_buf, size_t transformed_size,
+    uint8_t* dst, size_t dst_length) {
+  if (tag == TAG('g', 'l', 'y', 'f')) {
+    const Table* glyf_table = FindTable(tables, tag);
+    const Table* loca_table = FindTable(tables, TAG('l', 'o', 'c', 'a'));
+    if (glyf_table == NULL || loca_table == NULL) {
+      return OTS_FAILURE();
+    }
+    if (static_cast<uint64_t>(glyf_table->dst_offset) + glyf_table->dst_length >
+        dst_length) {
+      return OTS_FAILURE();
+    }
+    if (static_cast<uint64_t>(loca_table->dst_offset) + loca_table->dst_length >
+        dst_length) {
+      return OTS_FAILURE();
+    }
+    return ReconstructGlyf(transformed_buf, transformed_size,
+        dst + glyf_table->dst_offset, glyf_table->dst_length,
+        dst + loca_table->dst_offset, loca_table->dst_length);
+  } else if (tag == TAG('l', 'o', 'c', 'a')) {
+    // processing was already done by glyf table, but validate
+    if (!FindTable(tables, TAG('g', 'l', 'y', 'f'))) {
+      return OTS_FAILURE();
+    }
+  } else {
+    // transform for the tag is not known
+    return OTS_FAILURE();
+  }
+  return true;
+}
+
+uint32_t ComputeChecksum(const uint8_t* buf, size_t size) {
+  uint32_t checksum = 0;
+  for (size_t i = 0; i < size; i += 4) {
+    // We assume the addition is mod 2^32, which is valid because unsigned
+    checksum += (buf[i] << 24) | (buf[i + 1] << 16) |
+      (buf[i + 2] << 8) | buf[i + 3];
+  }
+  return checksum;
+}
+
+bool FixChecksums(const std::vector<Table>& tables, uint8_t* dst) {
+  const Table* head_table = FindTable(tables, TAG('h', 'e', 'a', 'd'));
+  if (head_table == NULL ||
+      head_table->dst_length < kCheckSumAdjustmentOffset + 4) {
+    return OTS_FAILURE();
+  }
+  size_t adjustment_offset = head_table->dst_offset + kCheckSumAdjustmentOffset;
+  if (adjustment_offset < head_table->dst_offset) {
+    return OTS_FAILURE();
+  }
+  StoreU32(dst, adjustment_offset, 0);
+  size_t n_tables = tables.size();
+  uint32_t file_checksum = 0;
+  for (size_t i = 0; i < n_tables; ++i) {
+    const Table* table = &tables.at(i);
+    size_t table_length = table->dst_length;
+    uint8_t* table_data = dst + table->dst_offset;
+    uint32_t checksum = ComputeChecksum(table_data, table_length);
+    StoreU32(dst, kSfntHeaderSize + i * kSfntEntrySize + 4, checksum);
+    file_checksum += checksum;  // The addition is mod 2^32
+  }
+  file_checksum += ComputeChecksum(dst,
+      kSfntHeaderSize + kSfntEntrySize * n_tables);
+  uint32_t checksum_adjustment = 0xb1b0afba - file_checksum;
+  StoreU32(dst, adjustment_offset, checksum_adjustment);
+  return true;
+}
+
+bool Woff2Uncompress(uint8_t* dst_buf, size_t dst_size,
+    const uint8_t* src_buf, size_t src_size, uint32_t compression_type) {
+  if (compression_type == kCompressionTypeGzip) {
+    uLongf uncompressed_length = dst_size;
+    int r = uncompress(reinterpret_cast<Bytef *>(dst_buf), &uncompressed_length,
+        src_buf, src_size);
+    if (r != Z_OK || uncompressed_length != dst_size) {
+      return OTS_FAILURE();
+    }
+    return true;
+  } else if (compression_type == kCompressionTypeBrotli) {
+    size_t uncompressed_size = dst_size;
+    int ok = BrotliDecompressBuffer(src_size, src_buf,
+                                    &uncompressed_size, dst_buf);
+    if (!ok || uncompressed_size != dst_size) {
+      return OTS_FAILURE();
+    }
+    return true;
+  }
+  // Unknown compression type
+  return OTS_FAILURE();
+}
+
+bool ReadShortDirectory(ots::Buffer* file, std::vector<Table>* tables,
+    size_t num_tables) {
+  uint32_t last_compression_type = 0;
+  for (size_t i = 0; i < num_tables; ++i) {
+    Table* table = &tables->at(i);
+    uint8_t flag_byte;
+    if (!file->ReadU8(&flag_byte)) {
+      return OTS_FAILURE();
+    }
+    uint32_t tag;
+    if ((flag_byte & 0x1f) == 0x1f) {
+      if (!file->ReadU32(&tag)) {
+        return OTS_FAILURE();
+      }
+    } else {
+      if ((flag_byte & 0x1f) >= arraysize(kKnownTags)) {
+        return OTS_FAILURE();
+      }
+      tag = kKnownTags[flag_byte & 0x1f];
+    }
+    uint32_t flags = flag_byte >> 6;
+    if (flags == kShortFlagsContinue) {
+      flags = last_compression_type | kWoff2FlagsContinueStream;
+    } else {
+      if (flags == kCompressionTypeNone ||
+          flags == kCompressionTypeGzip ||
+          flags == kCompressionTypeBrotli) {
+        last_compression_type = flags;
+      } else {
+        return OTS_FAILURE();
+      }
+    }
+    if ((flag_byte & 0x20) != 0) {
+      flags |= kWoff2FlagsTransform;
+    }
+    uint32_t dst_length;
+    if (!ReadBase128(file, &dst_length)) {
+      return OTS_FAILURE();
+    }
+    uint32_t transform_length = dst_length;
+    if ((flags & kWoff2FlagsTransform) != 0) {
+      if (!ReadBase128(file, &transform_length)) {
+        return OTS_FAILURE();
+      }
+    }
+    uint32_t src_length = transform_length;
+    if ((flag_byte >> 6) == 1 || (flag_byte >> 6) == 2) {
+      if (!ReadBase128(file, &src_length)) {
+        return OTS_FAILURE();
+      }
+    } else if (static_cast<uint32_t>(flag_byte >> 6) == kShortFlagsContinue) {
+      // The compressed data for this table is in a previuos table, so we set
+      // the src_length to zero.
+      src_length = 0;
+    }
+    // Disallow huge numbers (> 1GB) for sanity.
+    if (src_length > 1024 * 1024 * 1024 ||
+        transform_length > 1024 * 1024 * 1024 ||
+        dst_length > 1024 * 1024 * 1024) {
+      return OTS_FAILURE();
+    }
+
+    table->tag = tag;
+    table->flags = flags;
+    table->src_length = src_length;
+    table->transform_length = transform_length;
+    table->dst_length = dst_length;
+  }
+  return true;
+}
+
+}  // namespace
+
+namespace ots {
+
+size_t ComputeWOFF2FinalSize(const uint8_t* data, size_t length) {
+  ots::Buffer file(data, length);
+  uint32_t total_length;
+
+  if (!file.Skip(16) ||
+      !file.ReadU32(&total_length)) {
+    return 0;
+  }
+  return total_length;
+}
+
+bool ConvertWOFF2ToTTF(uint8_t* result, size_t result_length,
+                       const uint8_t* data, size_t length) {
+  static const uint32_t kWoff2Signature = 0x774f4632;  // "wOF2"
+  ots::Buffer file(data, length);
+
+  uint32_t signature;
+  uint32_t flavor;
+  if (!file.ReadU32(&signature) || signature != kWoff2Signature ||
+      !file.ReadU32(&flavor)) {
+    return OTS_FAILURE();
+  }
+
+  if (!IsValidVersionTag(ntohl(flavor))) {
+    return OTS_FAILURE();
+  }
+
+  uint32_t reported_length;
+  if (!file.ReadU32(&reported_length) || length != reported_length) {
+    return OTS_FAILURE();
+  }
+  uint16_t num_tables;
+  if (!file.ReadU16(&num_tables) || !num_tables) {
+    return OTS_FAILURE();
+  }
+  // We don't care about these fields of the header:
+  //   uint16_t reserved
+  //   uint32_t total_sfnt_size
+  //   uint16_t major_version, minor_version
+  //   uint32_t meta_offset, meta_length, meta_orig_length
+  //   uint32_t priv_offset, priv_length
+  if (!file.Skip(30)) {
+    return OTS_FAILURE();
+  }
+  std::vector<Table> tables(num_tables);
+  if (!ReadShortDirectory(&file, &tables, num_tables)) {
+    return OTS_FAILURE();
+  }
+  uint64_t src_offset = file.offset();
+  uint64_t dst_offset = kSfntHeaderSize +
+      kSfntEntrySize * static_cast<uint64_t>(num_tables);
+  uint64_t uncompressed_sum = 0;
+  for (uint16_t i = 0; i < num_tables; ++i) {
+    Table* table = &tables.at(i);
+    table->src_offset = src_offset;
+    src_offset += table->src_length;
+    if (src_offset > std::numeric_limits<uint32_t>::max()) {
+      return OTS_FAILURE();
+    }
+    src_offset = ots::Round4(src_offset);
+    table->dst_offset = dst_offset;
+    dst_offset += table->dst_length;
+    if (dst_offset > std::numeric_limits<uint32_t>::max()) {
+      return OTS_FAILURE();
+    }
+    dst_offset = ots::Round4(dst_offset);
+    if ((table->flags & kCompressionTypeMask) != kCompressionTypeNone) {
+      uncompressed_sum += table->src_length;
+      if (uncompressed_sum > std::numeric_limits<uint32_t>::max()) {
+        return OTS_FAILURE();
+      }
+    }
+  }
+  // Enforce same 30M limit on uncompressed tables as OTS
+  if (uncompressed_sum > 30 * 1024 * 1024) {
+    return OTS_FAILURE();
+  }
+  if (src_offset > length || dst_offset > result_length) {
+    return OTS_FAILURE();
+  }
+
+  const uint32_t sfnt_header_and_table_directory_size = 12 + 16 * num_tables;
+  if (sfnt_header_and_table_directory_size > result_length) {
+    return OTS_FAILURE();
+  }
+
+  // Start building the font
+  size_t offset = 0;
+  offset = StoreU32(result, offset, flavor);
+  offset = Store16(result, offset, num_tables);
+  unsigned max_pow2 = 0;
+  while (1u << (max_pow2 + 1) <= num_tables) {
+    max_pow2++;
+  }
+  const uint16_t output_search_range = (1u << max_pow2) << 4;
+  offset = Store16(result, offset, output_search_range);
+  offset = Store16(result, offset, max_pow2);
+  offset = Store16(result, offset, (num_tables << 4) - output_search_range);
+  for (uint16_t i = 0; i < num_tables; ++i) {
+    const Table* table = &tables.at(i);
+    offset = StoreU32(result, offset, table->tag);
+    offset = StoreU32(result, offset, 0);  // checksum, to fill in later
+    offset = StoreU32(result, offset, table->dst_offset);
+    offset = StoreU32(result, offset, table->dst_length);
+  }
+  std::vector<uint8_t> uncompressed_buf;
+  bool continue_valid = false;
+  const uint8_t* transform_buf = NULL;
+  for (uint16_t i = 0; i < num_tables; ++i) {
+    const Table* table = &tables.at(i);
+    uint32_t flags = table->flags;
+    const uint8_t* src_buf = data + table->src_offset;
+    uint32_t compression_type = flags & kCompressionTypeMask;
+    size_t transform_length = table->transform_length;
+    if ((flags & kWoff2FlagsContinueStream) != 0) {
+      if (!continue_valid) {
+        return OTS_FAILURE();
+      }
+    } else if (compression_type == kCompressionTypeNone) {
+      if (transform_length != table->src_length) {
+        return OTS_FAILURE();
+      }
+      transform_buf = src_buf;
+      continue_valid = false;
+    } else if ((flags & kWoff2FlagsContinueStream) == 0) {
+      uint64_t total_size = transform_length;
+      for (uint16_t j = i + 1; j < num_tables; ++j) {
+        if ((tables.at(j).flags & kWoff2FlagsContinueStream) == 0) {
+          break;
+        }
+        total_size += tables.at(j).transform_length;
+        if (total_size > std::numeric_limits<uint32_t>::max()) {
+          return OTS_FAILURE();
+        }
+      }
+      // Enforce same 30M limit on uncompressed tables as OTS
+      if (total_size > 30 * 1024 * 1024) {
+        return OTS_FAILURE();
+      }
+      uncompressed_buf.resize(total_size);
+      if (!Woff2Uncompress(&uncompressed_buf[0], total_size,
+          src_buf, table->src_length, compression_type)) {
+        return OTS_FAILURE();
+      }
+      transform_buf = &uncompressed_buf[0];
+      continue_valid = true;
+    } else {
+      return OTS_FAILURE();
+    }
+
+    if ((flags & kWoff2FlagsTransform) == 0) {
+      if (transform_length != table->dst_length) {
+        return OTS_FAILURE();
+      }
+      if (static_cast<uint64_t>(table->dst_offset) + transform_length >
+          result_length) {
+        return OTS_FAILURE();
+      }
+      std::memcpy(result + table->dst_offset, transform_buf,
+          transform_length);
+    } else {
+      if (!ReconstructTransformed(tables, table->tag,
+            transform_buf, transform_length, result, result_length)) {
+        return OTS_FAILURE();
+      }
+    }
+    if (continue_valid) {
+      transform_buf += transform_length;
+      if (transform_buf > &uncompressed_buf[0] + uncompressed_buf.size()) {
+        return OTS_FAILURE();
+      }
+    }
+  }
+
+  return FixChecksums(tables, result);
+}
+
+}  // namespace ots
new file mode 100644
--- /dev/null
+++ b/gfx/ots/src/woff2.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_WOFF2_H_
+#define OTS_WOFF2_H_
+
+namespace ots {
+
+// Compute the size of the final uncompressed font, or 0 on error.
+size_t ComputeWOFF2FinalSize(const uint8_t *data, size_t length);
+
+// Decompresses the font into the target buffer. The result_length should
+// be the same as determined by ComputeFinalSize(). Returns true on successful
+// decompression.
+bool ConvertWOFF2ToTTF(uint8_t *result, size_t result_length,
+                       const uint8_t *data, size_t length);
+}
+
+#endif  // OTS_WOFF2_H_
--- a/gfx/thebes/gfxUserFontSet.cpp
+++ b/gfx/thebes/gfxUserFontSet.cpp
@@ -383,20 +383,21 @@ gfxUserFontSet::SanitizeOpenTypeData(gfx
     ExpandingMemoryStream output(aIsCompressed ? aLength * 2 : aLength,
                                  1024 * 1024 * 256);
 
     OTSCallbackUserData userData;
     userData.mFontSet = this;
     userData.mFamily = aFamily;
     userData.mProxy = aProxy;
 
-    ots::SetTableActionCallback(&OTSTableAction, nullptr);
-    ots::SetMessageCallback(&gfxUserFontSet::OTSMessage, &userData);
+    ots::OTSContext otsContext;
+    otsContext.SetTableActionCallback(&OTSTableAction, nullptr);
+    otsContext.SetMessageCallback(&gfxUserFontSet::OTSMessage, &userData);
 
-    if (ots::Process(&output, aData, aLength)) {
+    if (otsContext.Process(&output, aData, aLength)) {
         aSaneLength = output.Tell();
         return static_cast<uint8_t*>(output.forget());
     } else {
         aSaneLength = 0;
         return nullptr;
     }
 }
 
--- a/js/public/GCAPI.h
+++ b/js/public/GCAPI.h
@@ -40,17 +40,17 @@ namespace JS {
     D(API)                                      \
     D(MAYBEGC)                                  \
     D(DESTROY_RUNTIME)                          \
     D(DESTROY_CONTEXT)                          \
     D(LAST_DITCH)                               \
     D(TOO_MUCH_MALLOC)                          \
     D(ALLOC_TRIGGER)                            \
     D(DEBUG_GC)                                 \
-    D(COMPARTMENT_REVIVED)                      \
+    D(TRANSPLANT)                               \
     D(RESET)                                    \
     D(OUT_OF_NURSERY)                           \
     D(EVICT_NURSERY)                            \
     D(FULL_STORE_BUFFER)                        \
                                                 \
     /* These are reserved for future use. */    \
     D(RESERVED0)                                \
     D(RESERVED1)                                \
--- a/js/src/NamespaceImports.h
+++ b/js/src/NamespaceImports.h
@@ -27,28 +27,25 @@ class TwoByteChars;
 class AutoFunctionVector;
 class AutoIdVector;
 class AutoObjectVector;
 class AutoScriptVector;
 class AutoValueVector;
 
 class AutoIdArray;
 
-class JS_PUBLIC_API(AutoGCRooter);
 template <typename T> class AutoVectorRooter;
 template<typename K, typename V> class AutoHashMapRooter;
 template<typename T> class AutoHashSetRooter;
 template<typename T> class RootedGeneric;
 
 class MOZ_STACK_CLASS SourceBufferHolder;
 
 class HandleValueArray;
 
-class AutoCheckCannotGC;
-
 }
 
 // Do the importing.
 namespace js {
 
 using JS::Value;
 using JS::BooleanValue;
 using JS::DoubleValue;
@@ -75,17 +72,16 @@ using JS::TwoByteChars;
 using JS::AutoFunctionVector;
 using JS::AutoIdVector;
 using JS::AutoObjectVector;
 using JS::AutoScriptVector;
 using JS::AutoValueVector;
 
 using JS::AutoIdArray;
 
-using JS::AutoGCRooter;
 using JS::AutoHashMapRooter;
 using JS::AutoHashSetRooter;
 using JS::AutoVectorRooter;
 using JS::RootedGeneric;
 
 using JS::CallArgs;
 using JS::CallNonGenericMethod;
 using JS::CallReceiver;
@@ -130,13 +126,11 @@ using JS::MutableHandleValue;
 
 using JS::NullHandleValue;
 using JS::UndefinedHandleValue;
 
 using JS::HandleValueArray;
 
 using JS::Zone;
 
-using JS::AutoCheckCannotGC;
-
 } /* namespace js */
 
 #endif /* NamespaceImports_h */
--- a/js/src/builtin/MapObject.h
+++ b/js/src/builtin/MapObject.h
@@ -37,37 +37,37 @@ class HashableValue {
 
     bool setValue(JSContext *cx, HandleValue v);
     HashNumber hash() const;
     bool operator==(const HashableValue &other) const;
     HashableValue mark(JSTracer *trc) const;
     Value get() const { return value.get(); }
 };
 
-class AutoHashableValueRooter : private AutoGCRooter
+class AutoHashableValueRooter : private JS::AutoGCRooter
 {
   public:
     explicit AutoHashableValueRooter(JSContext *cx
                                      MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-        : AutoGCRooter(cx, HASHABLEVALUE)
+        : JS::AutoGCRooter(cx, HASHABLEVALUE)
         {
             MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         }
 
     bool setValue(JSContext *cx, HandleValue v) {
         return value.setValue(cx, v);
     }
 
     operator const HashableValue & () {
         return value;
     }
 
     Value get() const { return value.get(); }
 
-    friend void AutoGCRooter::trace(JSTracer *trc);
+    friend void JS::AutoGCRooter::trace(JSTracer *trc);
     void trace(JSTracer *trc);
 
   private:
     HashableValue value;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 template <class Key, class Value, class OrderedHashPolicy, class AllocPolicy>
--- a/js/src/frontend/BytecodeCompiler.h
+++ b/js/src/frontend/BytecodeCompiler.h
@@ -63,14 +63,14 @@ bool
 IsIdentifier(JSLinearString *str);
 
 /* True if str is a keyword. Defined in TokenStream.cpp. */
 bool
 IsKeyword(JSLinearString *str);
 
 /* GC marking. Defined in Parser.cpp. */
 void
-MarkParser(JSTracer *trc, AutoGCRooter *parser);
+MarkParser(JSTracer *trc, JS::AutoGCRooter *parser);
 
 } /* namespace frontend */
 } /* namespace js */
 
 #endif /* frontend_BytecodeCompiler_h */
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -37,18 +37,21 @@
 
 #include "jsatominlines.h"
 #include "jsscriptinlines.h"
 
 #include "frontend/ParseNode-inl.h"
 
 using namespace js;
 using namespace js::gc;
+
 using mozilla::Maybe;
 
+using JS::AutoGCRooter;
+
 namespace js {
 namespace frontend {
 
 typedef Rooted<StaticBlockObject*> RootedStaticBlockObject;
 typedef Handle<StaticBlockObject*> HandleStaticBlockObject;
 typedef Rooted<NestedScopeObject*> RootedNestedScopeObject;
 typedef Handle<NestedScopeObject*> HandleNestedScopeObject;
 
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -308,17 +308,17 @@ struct BindData;
 
 class CompExprTransplanter;
 
 enum LetContext { LetExpresion, LetStatement };
 enum VarContext { HoistVars, DontHoistVars };
 enum FunctionType { Getter, Setter, Normal };
 
 template <typename ParseHandler>
-class Parser : private AutoGCRooter, public StrictModeGetter
+class Parser : private JS::AutoGCRooter, public StrictModeGetter
 {
   public:
     ExclusiveContext *const context;
     LifoAlloc &alloc;
 
     TokenStream         tokenStream;
     LifoAlloc::Mark     tempPoolMark;
 
@@ -387,17 +387,17 @@ class Parser : private AutoGCRooter, pub
         m.traceListHead = traceListHead;
         return m;
     }
     void release(Mark m) {
         alloc.release(m.mark);
         traceListHead = m.traceListHead;
     }
 
-    friend void js::frontend::MarkParser(JSTracer *trc, AutoGCRooter *parser);
+    friend void js::frontend::MarkParser(JSTracer *trc, JS::AutoGCRooter *parser);
 
     const char *getFilename() const { return tokenStream.getFilename(); }
     JSVersion versionNumber() const { return tokenStream.versionNumber(); }
 
     /*
      * Parse a top-level JS script.
      */
     Node parse(JSObject *chain);
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -183,16 +183,19 @@ CheckMarkedThing(JSTracer *trc, T **thin
         return;
 
     JS_ASSERT(thing->zone());
     JS_ASSERT(thing->zone()->runtimeFromMainThread() == trc->runtime());
     JS_ASSERT(trc->hasTracingDetails());
 
     DebugOnly<JSRuntime *> rt = trc->runtime();
 
+    JS_ASSERT_IF(IS_GC_MARKING_TRACER(trc) && rt->gc.manipulatingDeadZones,
+                 !thing->zone()->scheduledForDestruction);
+
     JS_ASSERT(CurrentThreadCanAccessRuntime(rt));
 
     JS_ASSERT_IF(thing->zone()->requireGCTracer(),
                  IS_GC_MARKING_TRACER(trc));
 
     JS_ASSERT(thing->isAligned());
 
     JS_ASSERT(MapTypeToTraceKind<T>::kind == GetGCThingTraceKind(thing));
@@ -213,43 +216,16 @@ CheckMarkedThing(JSTracer *trc, T **thin
      * ArenaHeader may not be synced with the real one in ArenaLists.
      */
     JS_ASSERT_IF(IsThingPoisoned(thing) && rt->isHeapBusy(),
                  !InFreeList(thing->arenaHeader(), thing));
 #endif
 
 }
 
-/*
- * We only set the maybeAlive flag for objects and scripts. It's assumed that,
- * if a compartment is alive, then it will have at least some live object or
- * script it in. Even if we get this wrong, the worst that will happen is that
- * scheduledForDestruction will be set on the compartment, which will cause some
- * extra GC activity to try to free the compartment.
- */
-template<typename T>
-static inline void
-SetMaybeAliveFlag(T *thing)
-{
-}
-
-template<>
-void
-SetMaybeAliveFlag(JSObject *thing)
-{
-    thing->compartment()->maybeAlive = true;
-}
-
-template<>
-void
-SetMaybeAliveFlag(JSScript *thing)
-{
-    thing->compartment()->maybeAlive = true;
-}
-
 template<typename T>
 static void
 MarkInternal(JSTracer *trc, T **thingp)
 {
     CheckMarkedThing(trc, thingp);
     T *thing = *thingp;
 
     if (!trc->callback) {
@@ -273,17 +249,17 @@ MarkInternal(JSTracer *trc, T **thingp)
         /*
          * Don't mark things outside a compartment if we are in a
          * per-compartment GC.
          */
         if (!thing->zone()->isGCMarking())
             return;
 
         PushMarkStack(AsGCMarker(trc), thing);
-        SetMaybeAliveFlag(thing);
+        thing->zone()->maybeAlive = true;
     } else {
         trc->callback(trc, (void **)thingp, MapTypeToTraceKind<T>::kind);
         trc->unsetTracingLocation();
     }
 
     trc->clearTracingDetails();
 }
 
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -239,30 +239,31 @@ js::Nursery::reallocateSlots(JSContext *
     size_t oldSize = oldCount * sizeof(HeapSlot);
     size_t newSize = newCount * sizeof(HeapSlot);
 
     if (!IsInsideNursery(obj))
         return static_cast<HeapSlot *>(cx->realloc_(oldSlots, oldSize, newSize));
 
     if (!isInside(oldSlots)) {
         HeapSlot *newSlots = static_cast<HeapSlot *>(cx->realloc_(oldSlots, oldSize, newSize));
-        if (oldSlots != newSlots) {
+        if (newSlots && oldSlots != newSlots) {
             hugeSlots.remove(oldSlots);
             /* If this put fails, we will only leak the slots. */
             (void)hugeSlots.put(newSlots);
         }
         return newSlots;
     }
 
     /* The nursery cannot make use of the returned slots data. */
     if (newCount < oldCount)
         return oldSlots;
 
     HeapSlot *newSlots = allocateSlots(cx, obj, newCount);
-    PodCopy(newSlots, oldSlots, oldCount);
+    if (newSlots)
+        PodCopy(newSlots, oldSlots, oldCount);
     return newSlots;
 }
 
 ObjectElements *
 js::Nursery::reallocateElements(JSContext *cx, JSObject *obj, ObjectElements *oldHeader,
                                 uint32_t oldCount, uint32_t newCount)
 {
     HeapSlot *slots = reallocateSlots(cx, obj, reinterpret_cast<HeapSlot *>(oldHeader),
@@ -279,17 +280,18 @@ js::Nursery::freeSlots(JSContext *cx, He
     }
 }
 
 HeapSlot *
 js::Nursery::allocateHugeSlots(JSContext *cx, size_t nslots)
 {
     HeapSlot *slots = cx->pod_malloc<HeapSlot>(nslots);
     /* If this put fails, we will only leak the slots. */
-    (void)hugeSlots.put(slots);
+    if (slots)
+        (void)hugeSlots.put(slots);
     return slots;
 }
 
 namespace js {
 namespace gc {
 
 class MinorCollectionTracer : public JSTracer
 {
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -31,16 +31,18 @@
 #include "jsgcinlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace js::gc;
 
 using mozilla::ArrayEnd;
 
+using JS::AutoGCRooter;
+
 typedef RootedValueMap::Range RootRange;
 typedef RootedValueMap::Entry RootEntry;
 typedef RootedValueMap::Enum RootEnum;
 
 #ifdef JSGC_USE_EXACT_ROOTING
 
 // Note: the following two functions cannot be static as long as we are using
 // GCC 4.4, since it requires template function parameters to have external
@@ -577,26 +579,26 @@ AutoGCRooter::trace(JSTracer *trc)
     if (Value *vp = static_cast<AutoArrayRooter *>(this)->array)
         MarkValueRootRange(trc, tag_, vp, "JS::AutoArrayRooter.array");
 }
 
 /* static */ void
 AutoGCRooter::traceAll(JSTracer *trc)
 {
     for (ContextIter cx(trc->runtime()); !cx.done(); cx.next()) {
-        for (js::AutoGCRooter *gcr = cx->autoGCRooters; gcr; gcr = gcr->down)
+        for (AutoGCRooter *gcr = cx->autoGCRooters; gcr; gcr = gcr->down)
             gcr->trace(trc);
     }
 }
 
 /* static */ void
 AutoGCRooter::traceAllWrappers(JSTracer *trc)
 {
     for (ContextIter cx(trc->runtime()); !cx.done(); cx.next()) {
-        for (js::AutoGCRooter *gcr = cx->autoGCRooters; gcr; gcr = gcr->down) {
+        for (AutoGCRooter *gcr = cx->autoGCRooters; gcr; gcr = gcr->down) {
             if (gcr->tag_ == WRAPVECTOR || gcr->tag_ == WRAPPER)
                 gcr->trace(trc);
         }
     }
 }
 
 void
 AutoHashableValueRooter::trace(JSTracer *trc)
--- a/js/src/gc/Tracer.cpp
+++ b/js/src/gc/Tracer.cpp
@@ -633,30 +633,17 @@ GCMarker::appendGrayRoot(void *thing, JS
 #ifdef DEBUG
     root.debugPrinter = debugPrinter();
     root.debugPrintArg = debugPrintArg();
     root.debugPrintIndex = debugPrintIndex();
 #endif
 
     Zone *zone = static_cast<Cell *>(thing)->tenuredZone();
     if (zone->isCollecting()) {
-        // See the comment on SetMaybeAliveFlag to see why we only do this for
-        // objects and scripts. We rely on gray root buffering for this to work,
-        // but we only need to worry about uncollected dead compartments during
-        // incremental GCs (when we do gray root buffering).
-        switch (kind) {
-          case JSTRACE_OBJECT:
-            static_cast<JSObject *>(thing)->compartment()->maybeAlive = true;
-            break;
-          case JSTRACE_SCRIPT:
-            static_cast<JSScript *>(thing)->compartment()->maybeAlive = true;
-            break;
-          default:
-            break;
-        }
+        zone->maybeAlive = true;
         if (!zone->gcGrayRoots.append(root)) {
             resetBufferedGrayRoots();
             grayBufferState = GRAY_BUFFER_FAILED;
         }
     }
 }
 
 void
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -30,16 +30,18 @@ JS::Zone::Zone(JSRuntime *rt)
     gcHeapGrowthFactor(3.0),
     gcMallocBytes(0),
     gcMallocGCTriggered(false),
     gcBytes(0),
     gcTriggerBytes(0),
     data(nullptr),
     isSystem(false),
     usedByExclusiveThread(false),
+    scheduledForDestruction(false),
+    maybeAlive(true),
     active(false),
     jitZone_(nullptr),
     gcState_(NoGC),
     gcScheduled_(false),
     gcPreserveCode_(false),
     ionUsingBarriers_(false)
 {
     /* Ensure that there are no vtables to mess us up here. */
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -253,16 +253,21 @@ struct Zone : public JS::shadow::Zone,
 
     // Per-zone data for use by an embedder.
     void *data;
 
     bool isSystem;
 
     bool usedByExclusiveThread;
 
+    // These flags help us to discover if a compartment that shouldn't be alive
+    // manages to outlive a GC.
+    bool scheduledForDestruction;
+    bool maybeAlive;
+
     // True when there are active frames.
     bool active;
 
     mozilla::DebugOnly<unsigned> gcLastZoneGroupIndex;
 
   private:
     js::jit::JitZone *jitZone_;
 
deleted file mode 100644
--- a/js/src/jit-test/tests/basic/number-tointeger.js
+++ /dev/null
@@ -1,20 +0,0 @@
-assertEq(Number.toInteger(4), 4);
-assertEq(Number.toInteger(4.), 4);
-assertEq(Number.toInteger(4.3), 4);
-assertEq(Number.toInteger(-4), -4);
-assertEq(Number.toInteger(-4.), -4);
-assertEq(Number.toInteger(-4.3), -4);
-assertEq(Number.toInteger(0.), 0.);
-assertEq(Number.toInteger(-0.), -0.);
-assertEq(Number.toInteger(Infinity), Infinity);
-assertEq(Number.toInteger(-Infinity), -Infinity);
-assertEq(Number.toInteger(NaN), 0);
-assertEq(Number.toInteger(null), 0);
-assertEq(Number.toInteger(undefined), 0);
-assertEq(Number.toInteger(true), 1);
-assertEq(Number.toInteger(false), 0);
-assertEq(Number.toInteger({valueOf : function () { return 4; }}), 4);
-assertEq(Number.toInteger({valueOf : function () { return 4.3; }}), 4);
-assertEq(Number.toInteger({valueOf : function () { return "4"; }}), 4);
-assertEq(Number.toInteger({valueOf : function () { return {};}}), 0);
-assertEq(Number.toInteger(), 0);
--- a/js/src/jit/IonAllocPolicy.h
+++ b/js/src/jit/IonAllocPolicy.h
@@ -69,27 +69,27 @@ class TempAllocator
     bool ensureBallast() {
         // Most infallible Ion allocations are small, so we use a ballast of
         // ~16K for now.
         return lifoScope_.alloc().ensureUnusedApproximate(16 * 1024);
     }
 };
 
 // Stack allocated rooter for all roots associated with a TempAllocator
-class AutoTempAllocatorRooter : private AutoGCRooter
+class AutoTempAllocatorRooter : private JS::AutoGCRooter
 {
   public:
     explicit AutoTempAllocatorRooter(JSContext *cx, TempAllocator *temp
                                      MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-      : AutoGCRooter(cx, IONALLOC), temp(temp)
+      : JS::AutoGCRooter(cx, IONALLOC), temp(temp)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
-    friend void AutoGCRooter::trace(JSTracer *trc);
+    friend void JS::AutoGCRooter::trace(JSTracer *trc);
     void trace(JSTracer *trc);
 
   private:
     TempAllocator *temp;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 class IonAllocPolicy
--- a/js/src/jit/IonMacroAssembler.cpp
+++ b/js/src/jit/IonMacroAssembler.cpp
@@ -543,16 +543,23 @@ MacroAssembler::callFreeStub(Register sl
 
 // Inlined equivalent of gc::AllocateObject, without failure case handling.
 void
 MacroAssembler::allocateObject(Register result, Register slots, gc::AllocKind allocKind,
                                uint32_t nDynamicSlots, gc::InitialHeap initialHeap, Label *fail)
 {
     JS_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST);
 
+#ifdef JS_CODEGEN_ARM
+    // Bug 1011474: Always take the ool path when allocating malloc slots on
+    //              ARM to work around a top-crasher while we investigate.
+    if (nDynamicSlots)
+        return jump(fail);
+#endif
+
     checkAllocatorState(fail);
 
     if (shouldNurseryAllocate(allocKind, initialHeap))
         return nurseryAllocate(result, slots, allocKind, nDynamicSlots, initialHeap, fail);
 
     if (!nDynamicSlots)
         return freeSpanAllocate(result, slots, allocKind, fail);
 
--- a/js/src/jit/IonMacroAssembler.h
+++ b/js/src/jit/IonMacroAssembler.h
@@ -36,23 +36,23 @@ namespace jit {
 // lifoAlloc use if one will be destroyed before the other.
 class MacroAssembler : public MacroAssemblerSpecific
 {
     MacroAssembler *thisFromCtor() {
         return this;
     }
 
   public:
-    class AutoRooter : public AutoGCRooter
+    class AutoRooter : public JS::AutoGCRooter
     {
         MacroAssembler *masm_;
 
       public:
         AutoRooter(JSContext *cx, MacroAssembler *masm)
-          : AutoGCRooter(cx, IONMASM),
+          : JS::AutoGCRooter(cx, IONMASM),
             masm_(masm)
         { }
 
         MacroAssembler *masm() const {
             return masm_;
         }
     };
 
--- a/js/src/jit/ParallelFunctions.cpp
+++ b/js/src/jit/ParallelFunctions.cpp
@@ -12,16 +12,18 @@
 #include "vm/ArrayObject.h"
 
 #include "jsgcinlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace jit;
 
+using JS::AutoCheckCannotGC;
+
 using parallel::Spew;
 using parallel::SpewOps;
 using parallel::SpewBailouts;
 using parallel::SpewBailoutIR;
 
 // Load the current thread context.
 ForkJoinContext *
 jit::ForkJoinContextPar()
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -93,16 +93,18 @@
 using namespace js;
 using namespace js::gc;
 using namespace js::types;
 
 using mozilla::Maybe;
 using mozilla::PodCopy;
 using mozilla::PodZero;
 
+using JS::AutoGCRooter;
+
 using js::frontend::Parser;
 
 #ifdef HAVE_VA_LIST_AS_ARRAY
 #define JS_ADDRESSOF_VA_LIST(ap) ((va_list *)(ap))
 #else
 #define JS_ADDRESSOF_VA_LIST(ap) (&(ap))
 #endif
 
@@ -1083,16 +1085,17 @@ JS_WrapId(JSContext *cx, JS::MutableHand
 JS_PUBLIC_API(JSObject *)
 JS_TransplantObject(JSContext *cx, HandleObject origobj, HandleObject target)
 {
     AssertHeapIsIdle(cx);
     JS_ASSERT(origobj != target);
     JS_ASSERT(!origobj->is<CrossCompartmentWrapperObject>());
     JS_ASSERT(!target->is<CrossCompartmentWrapperObject>());
 
+    AutoMaybeTouchDeadZones agc(cx);
     AutoDisableProxyCheck adpc(cx->runtime());
 
     JSCompartment *destination = target->compartment();
     RootedValue origv(cx, ObjectValue(*origobj));
     RootedObject newIdentity(cx);
 
     if (origobj->compartment() == destination) {
         // If the original object is in the same compartment as the
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -69,76 +69,16 @@ class JS_PUBLIC_API(AutoCheckRequestDept
 JS_PUBLIC_API(void)
 AssertArgumentsAreSane(JSContext *cx, JS::HandleValue v);
 #else
 inline void AssertArgumentsAreSane(JSContext *cx, JS::HandleValue v) {
     /* Do nothing */
 }
 #endif /* JS_DEBUG */
 
-class JS_PUBLIC_API(AutoGCRooter) {
-  public:
-    AutoGCRooter(JSContext *cx, ptrdiff_t tag);
-    AutoGCRooter(js::ContextFriendFields *cx, ptrdiff_t tag);
-
-    ~AutoGCRooter() {
-        JS_ASSERT(this == *stackTop);
-        *stackTop = down;
-    }
-
-    /* Implemented in gc/RootMarking.cpp. */
-    inline void trace(JSTracer *trc);
-    static void traceAll(JSTracer *trc);
-    static void traceAllWrappers(JSTracer *trc);
-
-  protected:
-    AutoGCRooter * const down;
-
-    /*
-     * Discriminates actual subclass of this being used.  If non-negative, the
-     * subclass roots an array of values of the length stored in this field.
-     * If negative, meaning is indicated by the corresponding value in the enum
-     * below.  Any other negative value indicates some deeper problem such as
-     * memory corruption.
-     */
-    ptrdiff_t tag_;
-
-    enum {
-        VALARRAY =     -2, /* js::AutoValueArray */
-        PARSER =       -3, /* js::frontend::Parser */
-        SHAPEVECTOR =  -4, /* js::AutoShapeVector */
-        IDARRAY =      -6, /* js::AutoIdArray */
-        DESCVECTOR =   -7, /* js::AutoPropDescVector */
-        VALVECTOR =   -10, /* js::AutoValueVector */
-        IDVECTOR =    -13, /* js::AutoIdVector */
-        OBJVECTOR =   -14, /* js::AutoObjectVector */
-        STRINGVECTOR =-15, /* js::AutoStringVector */
-        SCRIPTVECTOR =-16, /* js::AutoScriptVector */
-        NAMEVECTOR =  -17, /* js::AutoNameVector */
-        HASHABLEVALUE=-18, /* js::HashableValue */
-        IONMASM =     -19, /* js::jit::MacroAssembler */
-        IONALLOC =    -20, /* js::jit::AutoTempAllocatorRooter */
-        WRAPVECTOR =  -21, /* js::AutoWrapperVector */
-        WRAPPER =     -22, /* js::AutoWrapperRooter */
-        OBJOBJHASHMAP=-23, /* js::AutoObjectObjectHashMap */
-        OBJU32HASHMAP=-24, /* js::AutoObjectUnsigned32HashMap */
-        OBJHASHSET =  -25, /* js::AutoObjectHashSet */
-        JSONPARSER =  -26, /* js::JSONParser */
-        CUSTOM =      -27, /* js::CustomAutoRooter */
-        FUNVECTOR =   -28  /* js::AutoFunctionVector */
-    };
-
-  private:
-    AutoGCRooter ** const stackTop;
-
-    /* No copy or assignment semantics. */
-    AutoGCRooter(AutoGCRooter &ida) MOZ_DELETE;
-    void operator=(AutoGCRooter &ida) MOZ_DELETE;
-};
-
 /* AutoValueArray roots an internal fixed-size array of Values. */
 template <size_t N>
 class AutoValueArray : public AutoGCRooter
 {
     const size_t length_;
     Value elements_[N];
 
   public:
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -44,16 +44,18 @@ using namespace js::types;
 
 using mozilla::Abs;
 using mozilla::ArrayLength;
 using mozilla::CeilingLog2;
 using mozilla::DebugOnly;
 using mozilla::IsNaN;
 using mozilla::PointerRangeSize;
 
+using JS::AutoCheckCannotGC;
+
 bool
 js::GetLengthProperty(JSContext *cx, HandleObject obj, uint32_t *lengthp)
 {
     if (obj->is<ArrayObject>()) {
         *lengthp = obj->as<ArrayObject>().length();
         return true;
     }
 
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -909,22 +909,22 @@ class AutoObjectHashSet : public AutoHas
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 /* AutoArrayRooter roots an external array of Values. */
-class AutoArrayRooter : private AutoGCRooter
+class AutoArrayRooter : private JS::AutoGCRooter
 {
   public:
     AutoArrayRooter(JSContext *cx, size_t len, Value *vec
                     MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-      : AutoGCRooter(cx, len), array(vec)
+      : JS::AutoGCRooter(cx, len), array(vec)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         JS_ASSERT(tag_ >= 0);
     }
 
     void changeLength(size_t newLength) {
         tag_ = ptrdiff_t(newLength);
         JS_ASSERT(tag_ >= 0);
@@ -956,17 +956,17 @@ class AutoArrayRooter : private AutoGCRo
         JS_ASSERT(i < size_t(tag_));
         return MutableHandleValue::fromMarkedLocation(&array[i]);
     }
     HandleValue operator[](size_t i) const {
         JS_ASSERT(i < size_t(tag_));
         return HandleValue::fromMarkedLocation(&array[i]);
     }
 
-    friend void AutoGCRooter::trace(JSTracer *trc);
+    friend void JS::AutoGCRooter::trace(JSTracer *trc);
 
   private:
     Value *array;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 class AutoAssertNoException
 {
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -60,19 +60,17 @@ JSCompartment::JSCompartment(Zone *zone,
     gcWeakMapList(nullptr),
     debugModeBits(runtime_->debugMode ? DebugFromC : 0),
     rngState(0),
     watchpointMap(nullptr),
     scriptCountsMap(nullptr),
     debugScriptMap(nullptr),
     debugScopes(nullptr),
     enumerators(nullptr),
-    compartmentStats(nullptr),
-    scheduledForDestruction(false),
-    maybeAlive(true)
+    compartmentStats(nullptr)
 #ifdef JS_ION
     , jitCompartment_(nullptr)
 #endif
 {
     runtime_->numCompartments++;
     JS_ASSERT_IF(options.mergeable(), options.invisibleToDebugger());
 }
 
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -443,21 +443,16 @@ struct JSCompartment
      * List of potentially active iterators that may need deleted property
      * suppression.
      */
     js::NativeIterator *enumerators;
 
     /* Used by memory reporters and invalid otherwise. */
     void               *compartmentStats;
 
-    // These flags help us to discover if a compartment that shouldn't be alive
-    // manages to outlive a GC.
-    bool scheduledForDestruction;
-    bool maybeAlive;
-
 #ifdef JS_ION
   private:
     js::jit::JitCompartment *jitCompartment_;
 
   public:
     bool ensureJitCompartmentExists(JSContext *cx);
     js::jit::JitCompartment *jitCompartment() {
         return jitCompartment_;
@@ -651,30 +646,30 @@ class AutoWrapperVector : public AutoVec
         : AutoVectorRooter<WrapperValue>(cx, WRAPVECTOR)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
-class AutoWrapperRooter : private AutoGCRooter {
+class AutoWrapperRooter : private JS::AutoGCRooter {
   public:
     AutoWrapperRooter(JSContext *cx, WrapperValue v
                       MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-      : AutoGCRooter(cx, WRAPPER), value(v)
+      : JS::AutoGCRooter(cx, WRAPPER), value(v)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
     operator JSObject *() const {
         return value.get().toObjectOrNull();
     }
 
-    friend void AutoGCRooter::trace(JSTracer *trc);
+    friend void JS::AutoGCRooter::trace(JSTracer *trc);
 
   private:
     WrapperValue value;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 } /* namespace js */
 
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -973,33 +973,35 @@ JS::IsIncrementalBarrierNeeded(JSContext
 JS_FRIEND_API(void)
 JS::IncrementalObjectBarrier(JSObject *obj)
 {
     if (!obj)
         return;
 
     JS_ASSERT(!obj->zone()->runtimeFromMainThread()->isHeapMajorCollecting());
 
+    AutoMarkInDeadZone amn(obj->zone());
+
     JSObject::writeBarrierPre(obj);
 }
 
 JS_FRIEND_API(void)
 JS::IncrementalReferenceBarrier(void *ptr, JSGCTraceKind kind)
 {
     if (!ptr)
         return;
 
     gc::Cell *cell = static_cast<gc::Cell *>(ptr);
-
-#ifdef DEBUG
     Zone *zone = kind == JSTRACE_OBJECT
                  ? static_cast<JSObject *>(cell)->zone()
                  : cell->tenuredZone();
+
     JS_ASSERT(!zone->runtimeFromMainThread()->isHeapMajorCollecting());
-#endif
+
+    AutoMarkInDeadZone amn(zone);
 
     if (kind == JSTRACE_OBJECT)
         JSObject::writeBarrierPre(static_cast<JSObject*>(cell));
     else if (kind == JSTRACE_STRING)
         JSString::writeBarrierPre(static_cast<JSString*>(cell));
     else if (kind == JSTRACE_SCRIPT)
         JSScript::writeBarrierPre(static_cast<JSScript*>(cell));
     else if (kind == JSTRACE_LAZY_SCRIPT)
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -225,16 +225,18 @@
 using namespace js;
 using namespace js::gc;
 
 using mozilla::ArrayEnd;
 using mozilla::DebugOnly;
 using mozilla::Maybe;
 using mozilla::Swap;
 
+using JS::AutoGCRooter;
+
 /* Perform a Full GC every 20 seconds if MaybeGC is called */
 static const uint64_t GC_IDLE_FULL_SPAN = 20 * 1000 * 1000;
 
 /* Increase the IGC marking slice time if we are in highFrequencyGC mode. */
 static const int IGC_MARK_SLICE_MULTIPLIER = 2;
 
 #if defined(ANDROID) || defined(MOZ_B2G)
 static const int MAX_EMPTY_CHUNK_COUNT = 2;
@@ -2977,24 +2979,24 @@ GCRuntime::beginMarkPhase()
             if (!rt->isAtomsZone(zone)) {
                 any = true;
                 zone->setGCState(Zone::Mark);
             }
         } else {
             isFull = false;
         }
 
+        zone->scheduledForDestruction = false;
+        zone->maybeAlive = false;
         zone->setPreservingCode(false);
     }
 
     for (CompartmentsIter c(rt, WithAtoms); !c.done(); c.next()) {
         JS_ASSERT(c->gcLiveArrayBuffers.empty());
         c->marked = false;
-        c->scheduledForDestruction = false;
-        c->maybeAlive = false;
         if (shouldPreserveJITCode(c, currentTime))
             c->zone()->setPreservingCode(true);
     }
 
     if (!rt->gc.shouldCleanUpEverything) {
 #ifdef JS_ION
         if (JSCompartment *comp = jit::TopmostIonActivationCompartment(rt))
             comp->zone()->setPreservingCode(true);
@@ -3085,71 +3087,61 @@ GCRuntime::beginMarkPhase()
     if (isFull)
         UnmarkScriptData(rt);
 
     markRuntime(gcmarker);
     if (isIncremental)
         bufferGrayRoots();
 
     /*
-     * This code ensures that if a compartment is "dead", then it will be
-     * collected in this GC. A compartment is considered dead if its maybeAlive
+     * This code ensures that if a zone is "dead", then it will be
+     * collected in this GC. A zone is considered dead if its maybeAlive
      * flag is false. The maybeAlive flag is set if:
-     *   (1) the compartment has incoming cross-compartment edges, or
-     *   (2) an object in the compartment was marked during root marking, either
+     *   (1) the zone has incoming cross-compartment edges, or
+     *   (2) an object in the zone was marked during root marking, either
      *       as a black root or a gray root.
      * If the maybeAlive is false, then we set the scheduledForDestruction flag.
-     * At the end of the GC, we look for compartments where
-     * scheduledForDestruction is true. These are compartments that were somehow
-     * "revived" during the incremental GC. If any are found, we do a special,
-     * non-incremental GC of those compartments to try to collect them.
+     * At any time later in the GC, if we try to mark an object whose
+     * zone is scheduled for destruction, we will assert.
+     * NOTE: Due to bug 811587, we only assert if gcManipulatingDeadCompartments
+     * is true (e.g., if we're doing a brain transplant).
      *
-     * Compartments can be revived for a variety of reasons. On reason is bug
-     * 811587, where a reflector that was dead can be revived by DOM code that
-     * still refers to the underlying DOM node.
+     * The purpose of this check is to ensure that a zone that we would
+     * normally destroy is not resurrected by a read barrier or an
+     * allocation. This might happen during a function like JS_TransplantObject,
+     * which iterates over all compartments, live or dead, and operates on their
+     * objects. See bug 803376 for details on this problem. To avoid the
+     * problem, we are very careful to avoid allocation and read barriers during
+     * JS_TransplantObject and the like. The code here ensures that we don't
+     * regress.
      *
-     * Read barriers and allocations can also cause revival. This might happen
-     * during a function like JS_TransplantObject, which iterates over all
-     * compartments, live or dead, and operates on their objects. See bug 803376
-     * for details on this problem. To avoid the problem, we try to avoid
-     * allocation and read barriers during JS_TransplantObject and the like.
+     * Note that there are certain cases where allocations or read barriers in
+     * dead zone are difficult to avoid. We detect such cases (via the
+     * gcObjectsMarkedInDeadCompartment counter) and redo any ongoing GCs after
+     * the JS_TransplantObject function has finished. This ensures that the dead
+     * zones will be cleaned up. See AutoMarkInDeadZone and
+     * AutoMaybeTouchDeadZones for details.
      */
 
     /* Set the maybeAlive flag based on cross-compartment edges. */
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
         for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
-            const CrossCompartmentKey &key = e.front().key();
-            JSCompartment *dest;
-            switch (key.kind) {
-              case CrossCompartmentKey::ObjectWrapper:
-              case CrossCompartmentKey::DebuggerObject:
-              case CrossCompartmentKey::DebuggerSource:
-              case CrossCompartmentKey::DebuggerEnvironment:
-                dest = static_cast<JSObject *>(key.wrapped)->compartment();
-                break;
-              case CrossCompartmentKey::DebuggerScript:
-                dest = static_cast<JSScript *>(key.wrapped)->compartment();
-                break;
-              default:
-                dest = nullptr;
-                break;
-            }
-            if (dest)
-                dest->maybeAlive = true;
+            Cell *dst = e.front().key().wrapped;
+            dst->tenuredZone()->maybeAlive = true;
         }
     }
 
     /*
      * For black roots, code in gc/Marking.cpp will already have set maybeAlive
      * during MarkRuntime.
      */
 
-    for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
-        if (!c->maybeAlive && !rt->isAtomsCompartment(c))
-            c->scheduledForDestruction = true;
+    for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
+        if (!zone->maybeAlive && !rt->isAtomsZone(zone))
+            zone->scheduledForDestruction = true;
     }
     foundBlackGrayEdges = false;
 
     return true;
 }
 
 template <class CompartmentIterT>
 void
@@ -4515,18 +4507,18 @@ GCRuntime::resetIncrementalGC(const char
         JS_ASSERT(!strictCompartmentChecking);
 
         break;
       }
 
       case SWEEP:
         marker.reset();
 
-        for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
-            c->scheduledForDestruction = false;
+        for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next())
+            zone->scheduledForDestruction = false;
 
         /* Finish sweeping the current zone group, then abort. */
         abortSweepAfterCurrentGroup = true;
         incrementalCollectSlice(SliceBudget::Unlimited, JS::gcreason::RESET, GC_NORMAL);
 
         {
             gcstats::AutoPhase ap(stats, gcstats::PHASE_WAIT_BACKGROUND_THREAD);
             rt->gc.waitBackgroundSweepOrAllocEnd();
@@ -4995,39 +4987,22 @@ GCRuntime::collect(bool incremental, int
                 gcCallback.op(rt, JSGC_END, gcCallback.data);
         }
 
         /* Need to re-schedule all zones for GC. */
         if (poked && shouldCleanUpEverything)
             JS::PrepareForFullGC(rt);
 
         /*
-         * This code makes an extra effort to collect compartments that we
-         * thought were dead at the start of the GC. See the large comment in
-         * beginMarkPhase.
-         */
-        bool repeatForDeadZone = false;
-        if (incremental && incrementalState == NO_INCREMENTAL) {
-            for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
-                if (c->scheduledForDestruction) {
-                    incremental = false;
-                    repeatForDeadZone = true;
-                    reason = JS::gcreason::COMPARTMENT_REVIVED;
-                    c->zone()->scheduleGC();
-                }
-            }
-        }
-
-        /*
          * If we reset an existing GC, we need to start a new one. Also, we
          * repeat GCs that happen during shutdown (the gcShouldCleanUpEverything
          * case) until we can be sure that no additional garbage is created
          * (which typically happens if roots are dropped during finalizers).
          */
-        repeat = (poked && shouldCleanUpEverything) || wasReset || repeatForDeadZone;
+        repeat = (poked && shouldCleanUpEverything) || wasReset;
     } while (repeat);
 
     if (incrementalState == NO_INCREMENTAL) {
 #ifdef JS_THREADSAFE
         EnqueuePendingParseTasksAfterGC(rt);
 #endif
     }
 }
@@ -5554,16 +5529,44 @@ ArenaLists::containsArena(JSRuntime *rt,
     for (ArenaHeader *aheader = arenaLists[allocKind].head(); aheader; aheader = aheader->next) {
         if (aheader == needle)
             return true;
     }
     return false;
 }
 
 
+AutoMaybeTouchDeadZones::AutoMaybeTouchDeadZones(JSContext *cx)
+  : runtime(cx->runtime()),
+    markCount(runtime->gc.objectsMarkedInDeadZones),
+    inIncremental(JS::IsIncrementalGCInProgress(runtime)),
+    manipulatingDeadZones(runtime->gc.manipulatingDeadZones)
+{
+    runtime->gc.manipulatingDeadZones = true;
+}
+
+AutoMaybeTouchDeadZones::AutoMaybeTouchDeadZones(JSObject *obj)
+  : runtime(obj->compartment()->runtimeFromMainThread()),
+    markCount(runtime->gc.objectsMarkedInDeadZones),
+    inIncremental(JS::IsIncrementalGCInProgress(runtime)),
+    manipulatingDeadZones(runtime->gc.manipulatingDeadZones)
+{
+    runtime->gc.manipulatingDeadZones = true;
+}
+
+AutoMaybeTouchDeadZones::~AutoMaybeTouchDeadZones()
+{
+    runtime->gc.manipulatingDeadZones = manipulatingDeadZones;
+
+    if (inIncremental && runtime->gc.objectsMarkedInDeadZones != markCount) {
+        JS::PrepareForFullGC(runtime);
+        js::GC(runtime, GC_NORMAL, JS::gcreason::TRANSPLANT);
+    }
+}
+
 AutoSuppressGC::AutoSuppressGC(ExclusiveContext *cx)
   : suppressGC_(cx->perThreadData->suppressGC)
 {
     suppressGC_++;
 }
 
 AutoSuppressGC::AutoSuppressGC(JSCompartment *comp)
   : suppressGC_(comp->runtimeFromMainThread()->mainThread.suppressGC)
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -10,16 +10,43 @@
 #include "jsgc.h"
 
 #include "gc/Zone.h"
 
 namespace js {
 
 class Shape;
 
+/*
+ * This auto class should be used around any code that might cause a mark bit to
+ * be set on an object in a dead zone. See AutoMaybeTouchDeadZones
+ * for more details.
+ */
+struct AutoMarkInDeadZone
+{
+    explicit AutoMarkInDeadZone(JS::Zone *zone)
+      : zone(zone),
+        scheduled(zone->scheduledForDestruction)
+    {
+        JSRuntime *rt = zone->runtimeFromMainThread();
+        if (rt->gc.manipulatingDeadZones && zone->scheduledForDestruction) {
+            rt->gc.objectsMarkedInDeadZones++;
+            zone->scheduledForDestruction = false;
+        }
+    }
+
+    ~AutoMarkInDeadZone() {
+        zone->scheduledForDestruction = scheduled;
+    }
+
+  private:
+    JS::Zone *zone;
+    bool scheduled;
+};
+
 inline Allocator *
 ThreadSafeContext::allocator() const
 {
     JS_ASSERT_IF(isJSContext(), &asJSContext()->zone()->allocator == allocator_);
     return allocator_;
 }
 
 template <typename T>
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -198,17 +198,17 @@ IdToTypeId(jsid id)
      * All integers must map to the aggregate property for index types, including
      * negative integers.
      */
     if (JSID_IS_INT(id))
         return JSID_VOID;
 
     if (JSID_IS_STRING(id)) {
         JSAtom *atom = JSID_TO_ATOM(id);
-        AutoCheckCannotGC nogc;
+        JS::AutoCheckCannotGC nogc;
         bool isNumeric = atom->hasLatin1Chars()
                          ? IdIsNumericTypeId(atom->latin1Range(nogc))
                          : IdIsNumericTypeId(atom->twoByteRange(nogc));
         return isNumeric ? JSID_VOID : id;
     }
 
     return JSID_VOID;
 }
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -41,16 +41,18 @@ using namespace js;
 using namespace js::types;
 
 using mozilla::Abs;
 using mozilla::MinNumberValue;
 using mozilla::NegativeInfinity;
 using mozilla::PodCopy;
 using mozilla::PositiveInfinity;
 using mozilla::RangedPtr;
+
+using JS::AutoCheckCannotGC;
 using JS::GenericNaN;
 
 /*
  * If we're accumulating a decimal number and the number is >= 2^53, then the
  * fast result from the loop in Get{Prefix,Decimal}Integer may be inaccurate.
  * Call js_strtod_harder to get the correct answer.
  */
 template <typename CharT>
@@ -1053,41 +1055,24 @@ Number_isInteger(JSContext *cx, unsigned
     }
     Value val = args[0];
     args.rval().setBoolean(val.isInt32() ||
                            (mozilla::IsFinite(val.toDouble()) &&
                             ToInteger(val.toDouble()) == val.toDouble()));
     return true;
 }
 
-// ES6 drafult ES6 15.7.3.13
-static bool
-Number_toInteger(JSContext *cx, unsigned argc, Value *vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    if (args.length() < 1) {
-        args.rval().setInt32(0);
-        return true;
-    }
-    double asint;
-    if (!ToInteger(cx, args[0], &asint))
-        return false;
-    args.rval().setNumber(asint);
-    return true;
-}
-
 
 static const JSFunctionSpec number_static_methods[] = {
     JS_SELF_HOSTED_FN("isFinite", "Number_isFinite", 1,0),
     JS_FN("isInteger", Number_isInteger, 1, 0),
     JS_SELF_HOSTED_FN("isNaN", "Number_isNaN", 1,0),
     JS_SELF_HOSTED_FN("isSafeInteger", "Number_isSafeInteger", 1,0),
     JS_FN("parseFloat", num_parseFloat, 1, 0),
     JS_FN("parseInt", num_parseInt, 2, 0),
-    JS_FN("toInteger", Number_toInteger, 1, 0),
     JS_FS_END
 };
 
 
 /* NB: Keep this in synch with number_constants[]. */
 enum nc_slot {
     NC_NaN,
     NC_POSITIVE_INFINITY,
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -2500,16 +2500,19 @@ JSObject::TradeGuts(JSContext *cx, JSObj
     }
 #endif
 }
 
 /* Use this method with extreme caution. It trades the guts of two objects. */
 bool
 JSObject::swap(JSContext *cx, HandleObject a, HandleObject b)
 {
+    AutoMarkInDeadZone adc1(a->zone());
+    AutoMarkInDeadZone adc2(b->zone());
+
     // Ensure swap doesn't cause a finalizer to not be run.
     JS_ASSERT(IsBackgroundFinalized(a->tenuredGetAllocKind()) ==
               IsBackgroundFinalized(b->tenuredGetAllocKind()));
     JS_ASSERT(a->compartment() == b->compartment());
 
     unsigned r = NotifyGCPreSwap(a, b);
 
     TradeGutsReserved reserved(cx);
--- a/js/src/jsonparser.h
+++ b/js/src/jsonparser.h
@@ -4,22 +4,24 @@
  * 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 jsonparser_h
 #define jsonparser_h
 
 #include "mozilla/Attributes.h"
 
+#include "jspubtd.h"
+
 #include "ds/IdValuePair.h"
 #include "vm/String.h"
 
 namespace js {
 
-class MOZ_STACK_CLASS JSONParser : private AutoGCRooter
+class MOZ_STACK_CLASS JSONParser : private JS::AutoGCRooter
 {
   public:
     enum ErrorHandling { RaiseError, NoError };
 
   private:
     /* Data members */
 
     JSContext * const cx;
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -40,16 +40,18 @@
 #include "jscompartmentinlines.h"
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 using namespace js;
 using namespace js::gc;
 
+using JS::AutoCheckCannotGC;
+
 using js::frontend::IsIdentifier;
 
 /*
  * Index limit must stay within 32 bits.
  */
 JS_STATIC_ASSERT(sizeof(uint32_t) * JS_BITS_PER_BYTE >= INDEX_LIMIT_LOG2 + 1);
 
 const JSCodeSpec js_CodeSpec[] = {
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -1089,105 +1089,16 @@ class ScriptedDirectProxyHandler : publi
     virtual bool isScripted() MOZ_OVERRIDE { return true; }
 
     static ScriptedDirectProxyHandler singleton;
 };
 
 // This variable exists solely to provide a unique address for use as an identifier.
 static const char sScriptedDirectProxyHandlerFamily = 0;
 
-// Aux.2 FromGenericPropertyDescriptor(Desc)
-static bool
-FromGenericPropertyDescriptor(JSContext *cx, MutableHandle<PropDesc> desc, MutableHandleValue rval)
-{
-    // Aux.2 step 1
-    if (desc.isUndefined()) {
-        rval.setUndefined();
-        return true;
-    }
-
-    // steps 3-9
-    if (!desc.makeObject(cx))
-        return false;
-    rval.set(desc.descriptorValue());
-    return true;
-}
-
-/*
- * Aux.3 NormalizePropertyDescriptor(Attributes)
- *
- * NOTE: to minimize code duplication, the code for this function is shared with
- * that for Aux.4 NormalizeAndCompletePropertyDescriptor (see below). The
- * argument complete is used to distinguish between the two.
- */
-static bool
-NormalizePropertyDescriptor(JSContext *cx, MutableHandleValue vp, bool complete = false)
-{
-    // Aux.4 step 1
-    if (complete && vp.isUndefined())
-        return true;
-
-    // Aux.3 steps 1-2 / Aux.4 steps 2-3
-    Rooted<PropDesc> desc(cx);
-    if (!desc.initialize(cx, vp.get()))
-        return false;
-    if (complete)
-        desc.complete();
-    JS_ASSERT(vp.isObject()); // due to desc->initialize
-    RootedObject attributes(cx, &vp.toObject());
-
-    /*
-     * Aux.3 step 3 / Aux.4 step 4
-     *
-     * NOTE: Aux.4 step 4 actually specifies FromPropertyDescriptor here.
-     * However, the way FromPropertyDescriptor is implemented (PropDesc::
-     * makeObject) is actually closer to FromGenericPropertyDescriptor,
-     * and is in fact used to implement the latter, so we might as well call it
-     * directly.
-     */
-    if (!FromGenericPropertyDescriptor(cx, &desc, vp))
-        return false;
-    if (vp.isUndefined())
-        return true;
-    RootedObject descObj(cx, &vp.toObject());
-
-    // Aux.3 steps 4-5 / Aux.4 steps 5-6
-    AutoIdVector props(cx);
-    if (!GetPropertyNames(cx, attributes, 0, &props))
-        return false;
-    size_t n = props.length();
-    for (size_t i = 0; i < n; ++i) {
-        RootedId id(cx, props[i]);
-        if (JSID_IS_ATOM(id)) {
-            JSAtom *atom = JSID_TO_ATOM(id);
-            const JSAtomState &atomState = cx->names();
-            if (atom == atomState.value || atom == atomState.writable ||
-                atom == atomState.get || atom == atomState.set ||
-                atom == atomState.enumerable || atom == atomState.configurable)
-            {
-                continue;
-            }
-        }
-
-        RootedValue v(cx);
-        if (!JSObject::getGeneric(cx, descObj, attributes, id, &v))
-            return false;
-        if (!JSObject::defineGeneric(cx, descObj, id, v, nullptr, nullptr, JSPROP_ENUMERATE))
-            return false;
-    }
-    return true;
-}
-
-// Aux.4 NormalizeAndCompletePropertyDescriptor(Attributes)
-static inline bool
-NormalizeAndCompletePropertyDescriptor(JSContext *cx, MutableHandleValue vp)
-{
-    return NormalizePropertyDescriptor(cx, vp, true);
-}
-
 static inline bool
 IsDataDescriptor(const PropertyDescriptor &desc)
 {
     return desc.obj && !(desc.attrs & (JSPROP_GETTER | JSPROP_SETTER));
 }
 
 static inline bool
 IsAccessorDescriptor(const PropertyDescriptor &desc)
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -6,16 +6,17 @@
 
 #ifndef jspubtd_h
 #define jspubtd_h
 
 /*
  * JS public API typedefs.
  */
 
+#include "mozilla/Assertions.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/NullPtr.h"
 #include "mozilla/PodOperations.h"
 
 #include "jsprototypes.h"
 #include "jstypes.h"
 
 #include "js/TypeDecls.h"
@@ -27,27 +28,29 @@
 namespace JS {
 
 class AutoIdVector;
 class CallArgs;
 
 template <typename T>
 class Rooted;
 
-class JS_PUBLIC_API(AutoGCRooter);
-
 class JS_FRIEND_API(CompileOptions);
 class JS_FRIEND_API(ReadOnlyCompileOptions);
 class JS_FRIEND_API(OwningCompileOptions);
 class JS_PUBLIC_API(CompartmentOptions);
 
 struct Zone;
 
 } /* namespace JS */
 
+namespace js {
+struct ContextFriendFields;
+} // namespace js
+
 /*
  * Run-time version enumeration.  For compile-time version checking, please use
  * the JS_HAS_* macros in jsversion.h, or use MOZJS_MAJOR_VERSION,
  * MOZJS_MINOR_VERSION, MOZJS_PATCH_VERSION, and MOZJS_ALPHA definitions.
  */
 enum JSVersion {
     JSVERSION_ECMA_3  = 148,
     JSVERSION_1_6     = 160,
@@ -241,16 +244,78 @@ template<>
 inline mozilla::LinkedList<PersistentRootedString>
 &Runtime::getPersistentRootedList<JSString *>() { return stringPersistentRooteds; }
 
 template<>
 inline mozilla::LinkedList<PersistentRootedValue>
 &Runtime::getPersistentRootedList<Value>() { return valuePersistentRooteds; }
 
 } /* namespace shadow */
+
+class JS_PUBLIC_API(AutoGCRooter)
+{
+  public:
+    AutoGCRooter(JSContext *cx, ptrdiff_t tag);
+    AutoGCRooter(js::ContextFriendFields *cx, ptrdiff_t tag);
+
+    ~AutoGCRooter() {
+        MOZ_ASSERT(this == *stackTop);
+        *stackTop = down;
+    }
+
+    /* Implemented in gc/RootMarking.cpp. */
+    inline void trace(JSTracer *trc);
+    static void traceAll(JSTracer *trc);
+    static void traceAllWrappers(JSTracer *trc);
+
+  protected:
+    AutoGCRooter * const down;
+
+    /*
+     * Discriminates actual subclass of this being used.  If non-negative, the
+     * subclass roots an array of values of the length stored in this field.
+     * If negative, meaning is indicated by the corresponding value in the enum
+     * below.  Any other negative value indicates some deeper problem such as
+     * memory corruption.
+     */
+    ptrdiff_t tag_;
+
+    enum {
+        VALARRAY =     -2, /* js::AutoValueArray */
+        PARSER =       -3, /* js::frontend::Parser */
+        SHAPEVECTOR =  -4, /* js::AutoShapeVector */
+        IDARRAY =      -6, /* js::AutoIdArray */
+        DESCVECTOR =   -7, /* js::AutoPropDescVector */
+        VALVECTOR =   -10, /* js::AutoValueVector */
+        IDVECTOR =    -13, /* js::AutoIdVector */
+        OBJVECTOR =   -14, /* js::AutoObjectVector */
+        STRINGVECTOR =-15, /* js::AutoStringVector */
+        SCRIPTVECTOR =-16, /* js::AutoScriptVector */
+        NAMEVECTOR =  -17, /* js::AutoNameVector */
+        HASHABLEVALUE=-18, /* js::HashableValue */
+        IONMASM =     -19, /* js::jit::MacroAssembler */
+        IONALLOC =    -20, /* js::jit::AutoTempAllocatorRooter */
+        WRAPVECTOR =  -21, /* js::AutoWrapperVector */
+        WRAPPER =     -22, /* js::AutoWrapperRooter */
+        OBJOBJHASHMAP=-23, /* js::AutoObjectObjectHashMap */
+        OBJU32HASHMAP=-24, /* js::AutoObjectUnsigned32HashMap */
+        OBJHASHSET =  -25, /* js::AutoObjectHashSet */
+        JSONPARSER =  -26, /* js::JSONParser */
+        CUSTOM =      -27, /* js::CustomAutoRooter */
+        FUNVECTOR =   -28  /* js::AutoFunctionVector */
+    };
+
+  private:
+    AutoGCRooter ** const stackTop;
+
+    /* No copy or assignment semantics. */
+    AutoGCRooter(AutoGCRooter &ida) MOZ_DELETE;
+    void operator=(AutoGCRooter &ida) MOZ_DELETE;
+};
+
 } /* namespace JS */
 
 namespace js {
 
 /*
  * Parallel operations in general can have one of three states. They may
  * succeed, fail, or "bail", where bail indicates that the code encountered an
  * unexpected condition and should be re-run sequentially. Different
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -68,16 +68,18 @@ using namespace js::unicode;
 using mozilla::CheckedInt;
 using mozilla::IsNaN;
 using mozilla::IsNegativeZero;
 using mozilla::IsSame;
 using mozilla::PodCopy;
 using mozilla::PodEqual;
 using mozilla::SafeCast;
 
+using JS::AutoCheckCannotGC;
+
 typedef Handle<JSLinearString*> HandleLinearString;
 
 static JSLinearString *
 ArgToRootedString(JSContext *cx, CallArgs &args, unsigned argno)
 {
     if (argno >= args.length())
         return cx->names().undefined;
 
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -39,16 +39,18 @@ Wrapper::defaultValue(JSContext *cx, Han
 }
 
 JSObject *
 Wrapper::New(JSContext *cx, JSObject *obj, JSObject *parent, Wrapper *handler,
              const WrapperOptions *options)
 {
     JS_ASSERT(parent);
 
+    AutoMarkInDeadZone amd(cx->zone());
+
     RootedValue priv(cx, ObjectValue(*obj));
     mozilla::Maybe<WrapperOptions> opts;
     if (!options) {
         opts.construct();
         opts.ref().selectDefaultClass(obj->isCallable());
         options = opts.addr();
     }
     return NewProxyObject(cx, handler, priv, options->proto(), parent, *options);
@@ -1038,16 +1040,18 @@ js::RemapAllWrappersForObject(JSContext 
 
     return true;
 }
 
 JS_FRIEND_API(bool)
 js::RecomputeWrappers(JSContext *cx, const CompartmentFilter &sourceFilter,
                       const CompartmentFilter &targetFilter)
 {
+    AutoMaybeTouchDeadZones agc(cx);
+
     AutoWrapperVector toRecompute(cx);
 
     for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {
         // Filter by source compartment.
         if (!sourceFilter.match(c))
             continue;
 
         // Iterate over the wrappers, filtering appropriately.
--- a/js/src/jswrapper.h
+++ b/js/src/jswrapper.h
@@ -299,11 +299,39 @@ RemapAllWrappersForObject(JSContext *cx,
                           JSObject *newTarget);
 
 // API to recompute all cross-compartment wrappers whose source and target
 // match the given filters.
 JS_FRIEND_API(bool)
 RecomputeWrappers(JSContext *cx, const CompartmentFilter &sourceFilter,
                   const CompartmentFilter &targetFilter);
 
+/*
+ * This auto class should be used around any code, such as brain transplants,
+ * that may touch dead zones. Brain transplants can cause problems
+ * because they operate on all compartments, whether live or dead. A brain
+ * transplant can cause a formerly dead object to be "reanimated" by causing a
+ * read or write barrier to be invoked on it during the transplant. In this way,
+ * a zone becomes a zombie, kept alive by repeatedly consuming
+ * (transplanted) brains.
+ *
+ * To work around this issue, we observe when mark bits are set on objects in
+ * dead zones. If this happens during a brain transplant, we do a full,
+ * non-incremental GC at the end of the brain transplant. This will clean up any
+ * objects that were improperly marked.
+ */
+struct JS_FRIEND_API(AutoMaybeTouchDeadZones)
+{
+    // The version that takes an object just uses it for its runtime.
+    explicit AutoMaybeTouchDeadZones(JSContext *cx);
+    explicit AutoMaybeTouchDeadZones(JSObject *obj);
+    ~AutoMaybeTouchDeadZones();
+
+  private:
+    JSRuntime *runtime;
+    unsigned markCount;
+    bool inIncremental;
+    bool manipulatingDeadZones;
+};
+
 } /* namespace js */
 
 #endif /* jswrapper_h */
--- a/js/src/tests/user.js
+++ b/js/src/tests/user.js
@@ -20,8 +20,10 @@ user_pref("extensions.checkUpdateSecurit
 user_pref("browser.EULA.override", true);
 user_pref("javascript.options.strict", false);
 user_pref("javascript.options.werror", false);
 user_pref("toolkit.startup.max_resumed_crashes", -1);
 user_pref("security.turn_off_all_security_so_that_viruses_can_take_over_this_computer", true);
 user_pref("toolkit.telemetry.enabled", false);
 user_pref("browser.safebrowsing.enabled", false);
 user_pref("browser.safebrowsing.malware.enabled", false);
+user_pref("browser.snippets.enabled", false);
+user_pref("browser.snippets.syncPromo.enabled", false);
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -2088,17 +2088,17 @@ Debugger::addAllGlobalsAsDebuggees(JSCon
     for (ZonesIter zone(cx->runtime(), SkipAtoms); !zone.done(); zone.next()) {
         // Invalidate a zone at a time to avoid doing a ZoneCellIter
         // per compartment.
         AutoDebugModeInvalidation invalidate(zone);
 
         for (CompartmentsInZoneIter c(zone); !c.done(); c.next()) {
             if (c == dbg->object->compartment() || c->options().invisibleToDebugger())
                 continue;
-            c->scheduledForDestruction = false;
+            c->zone()->scheduledForDestruction = false;
             GlobalObject *global = c->maybeGlobal();
             if (global) {
                 Rooted<GlobalObject*> rg(cx, global);
                 if (!dbg->addDebuggeeGlobal(cx, rg, invalidate))
                     return false;
             }
         }
     }
@@ -2885,17 +2885,17 @@ Debugger::findAllGlobals(JSContext *cx, 
     RootedObject result(cx, NewDenseEmptyArray(cx));
     if (!result)
         return false;
 
     for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {
         if (c->options().invisibleToDebugger())
             continue;
 
-        c->scheduledForDestruction = false;
+        c->zone()->scheduledForDestruction = false;
 
         GlobalObject *global = c->maybeGlobal();
 
         if (cx->runtime()->isSelfHostingGlobal(global))
             continue;
 
         if (global) {
             /*
--- a/js/src/vm/ProxyObject.cpp
+++ b/js/src/vm/ProxyObject.cpp
@@ -72,17 +72,24 @@ void
 ProxyObject::initHandler(BaseProxyHandler *handler)
 {
     initSlot(HANDLER_SLOT, PrivateValue(handler));
 }
 
 static void
 NukeSlot(ProxyObject *proxy, uint32_t slot)
 {
-    proxy->setReservedSlot(slot, NullValue());
+    Value old = proxy->getSlot(slot);
+    if (old.isMarkable()) {
+        Zone *zone = ZoneOfValue(old);
+        AutoMarkInDeadZone amd(zone);
+        proxy->setReservedSlot(slot, NullValue());
+    } else {
+        proxy->setReservedSlot(slot, NullValue());
+    }
 }
 
 void
 ProxyObject::nuke(BaseProxyHandler *handler)
 {
     /* Allow people to add their own number of reserved slots beyond the expected 4 */
     unsigned numSlots = JSCLASS_RESERVED_SLOTS(getClass());
     for (unsigned i = 0; i < numSlots; i++)
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -28,16 +28,18 @@
 
 #include "vm/BooleanObject-inl.h"
 #include "vm/NumberObject-inl.h"
 #include "vm/StringObject-inl.h"
 
 using namespace js;
 using namespace js::selfhosted;
 
+using JS::AutoCheckCannotGC;
+
 static void
 selfHosting_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
 {
     PrintError(cx, stderr, message, report, true);
 }
 
 static const JSClass self_hosting_global_class = {
     "self-hosting-global", JSCLASS_GLOBAL_FLAGS,
--- a/js/src/vm/String-inl.h
+++ b/js/src/vm/String-inl.h
@@ -83,17 +83,17 @@ NewFatInlineString(ExclusiveContext *cx,
 {
     MOZ_ASSERT(JSFatInlineString::lengthFits<CharT>(length));
 
     CharT *chars;
     JSInlineString *s = AllocateFatInlineString<CanGC>(cx, length, &chars);
     if (!s)
         return nullptr;
 
-    AutoCheckCannotGC nogc;
+    JS::AutoCheckCannotGC nogc;
     mozilla::PodCopy(chars, base->chars<CharT>(nogc) + start, length);
     chars[length] = 0;
     return s;
 }
 
 static inline void
 StringWriteBarrierPost(js::ThreadSafeContext *maybecx, JSString **strp)
 {
--- a/js/src/vm/String.cpp
+++ b/js/src/vm/String.cpp
@@ -19,16 +19,18 @@
 
 using namespace js;
 
 using mozilla::IsSame;
 using mozilla::PodCopy;
 using mozilla::RangedPtr;
 using mozilla::RoundUpPow2;
 
+using JS::AutoCheckCannotGC;
+
 size_t
 JSString::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
 {
     // JSRope: do nothing, we'll count all children chars when we hit the leaf strings.
     if (isRope())
         return 0;
 
     JS_ASSERT(isLinear());
--- a/js/src/vm/TypedArrayObject.h
+++ b/js/src/vm/TypedArrayObject.h
@@ -183,17 +183,17 @@ IsTypedArrayIndex(jsid id, uint64_t *ind
         JS_ASSERT(i >= 0);
         *indexp = (double)i;
         return true;
     }
 
     if (MOZ_UNLIKELY(!JSID_IS_STRING(id)))
         return false;
 
-    AutoCheckCannotGC nogc;
+    JS::AutoCheckCannotGC nogc;
     JSAtom *atom = JSID_TO_ATOM(id);
     size_t length = atom->length();
 
     if (atom->hasLatin1Chars()) {
         const Latin1Char *s = atom->latin1Chars(nogc);
         if (!JS7_ISDEC(*s) && *s != '-')
             return false;
         return StringIsTypedArrayIndex(s, length, indexp);
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -351,16 +351,19 @@ XPCWrappedNative::GetNewOrUsed(xpcObject
 
     RootedObject parent(cx, Scope->GetGlobalJSObject());
 
     RootedValue newParentVal(cx, NullValue());
 
     mozilla::Maybe<JSAutoCompartment> ac;
 
     if (sciWrapper.GetFlags().WantPreCreate()) {
+        // PreCreate may touch dead compartments.
+        js::AutoMaybeTouchDeadZones agc(parent);
+
         RootedObject plannedParent(cx, parent);
         nsresult rv = sciWrapper.GetCallback()->PreCreate(identity, cx,
                                                           parent, parent.address());
         if (NS_FAILED(rv))
             return rv;
         rv = NS_OK;
 
         MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(parent),
@@ -1277,16 +1280,19 @@ RescueOrphans(HandleObject obj)
     // NB: We pass stopAtOuter=false during the unwrap because Location objects
     // are parented to outer window proxies.
     nsresult rv;
     RootedObject parentObj(cx, js::GetObjectParent(obj));
     if (!parentObj)
         return NS_OK; // Global object. We're done.
     parentObj = js::UncheckedUnwrap(parentObj, /* stopAtOuter = */ false);
 
+    // PreCreate may touch dead compartments.
+    js::AutoMaybeTouchDeadZones agc(parentObj);
+
     // Recursively fix up orphans on the parent chain.
     rv = RescueOrphans(parentObj);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Now that we know our parent is in the right place, determine if we've
     // been orphaned. If not, we have nothing to do.
     if (!js::IsCrossCompartmentWrapper(parentObj))
         return NS_OK;
--- a/layout/base/Units.h
+++ b/layout/base/Units.h
@@ -184,16 +184,19 @@ struct LayoutDevicePixel {
  * The pixels that layout rasterizes and delivers to the graphics code.
  * These are generally referred to as "device pixels" in layout code. Layer
  * pixels are affected by:
  * 1) the "display resolution" (see nsIPresShell::SetResolution)
  * 2) the "full zoom" (see nsPresContext::SetFullZoom)
  * 3) the "widget scale" (see nsIWidget::GetDefaultScale)
  */
 struct LayerPixel {
+  static nsIntRect ToUntyped(const LayerIntRect& aRect) {
+    return nsIntRect(aRect.x, aRect.y, aRect.width, aRect.height);
+  }
 };
 
 /*
  * The pixels that are displayed on the screen.
  * On non-OMTC platforms this should be equivalent to LayerPixel units.
  * On OMTC platforms these may diverge from LayerPixel units temporarily,
  * while an asynchronous zoom is happening, but should eventually converge
  * back to LayerPixel units. Some variables (such as those representing
--- a/layout/tools/reftest/runreftest.py
+++ b/layout/tools/reftest/runreftest.py
@@ -166,16 +166,19 @@ class RefTest(object):
     prefs['reftest.focusFilterMode'] = options.focusFilterMode
 
     # Ensure that telemetry is disabled, so we don't connect to the telemetry
     # server in the middle of the tests.
     prefs['toolkit.telemetry.enabled'] = False
     # Likewise for safebrowsing.
     prefs['browser.safebrowsing.enabled'] = False
     prefs['browser.safebrowsing.malware.enabled'] = False
+    # And for snippets.
+    prefs['browser.snippets.enabled'] = False
+    prefs['browser.snippets.syncPromo.enabled'] = False
 
     if options.e10s:
       prefs['browser.tabs.remote.autostart'] = True
 
     for v in options.extraPrefs:
       thispref = v.split('=')
       if len(thispref) < 2:
         print "Error: syntax error in --setpref=" + v
--- a/mfbt/Atomics.h
+++ b/mfbt/Atomics.h
@@ -861,16 +861,17 @@ struct IntrinsicAddSub : public Intrinsi
     return applyBinaryFunction(&Primitives::sub, aPtr, aVal);
   }
 };
 
 template<typename T>
 struct IntrinsicAddSub<T*> : public IntrinsicApplyHelper<T*>
 {
   typedef typename IntrinsicApplyHelper<T*>::ValueType ValueType;
+  typedef typename IntrinsicBase<T*>::Primitives Primitives;
 
   static ValueType add(ValueType& aPtr, ptrdiff_t aAmount)
   {
     return applyBinaryFunction(&Primitives::add, aPtr,
                                (ValueType)(aAmount * sizeof(T)));
   }
 
   static ValueType sub(ValueType& aPtr, ptrdiff_t aAmount)
@@ -888,16 +889,17 @@ struct IntrinsicIncDec : public Intrinsi
   static ValueType dec(ValueType& aPtr) { return sub(aPtr, 1); }
 };
 
 template<typename T, MemoryOrdering Order>
 struct AtomicIntrinsics : public IntrinsicMemoryOps<T, Order>,
                           public IntrinsicIncDec<T>
 {
   typedef typename IntrinsicIncDec<T>::ValueType ValueType;
+  typedef typename IntrinsicBase<T>::Primitives Primitives;
 
   static ValueType or_(ValueType& aPtr, T aVal)
   {
     return applyBinaryFunction(&Primitives::or_, aPtr, aVal);
   }
 
   static ValueType xor_(ValueType& aPtr, T aVal)
   {
@@ -910,16 +912,19 @@ struct AtomicIntrinsics : public Intrins
   }
 };
 
 template<typename T, MemoryOrdering Order>
 struct AtomicIntrinsics<T*, Order> : public IntrinsicMemoryOps<T*, Order>,
                                      public IntrinsicIncDec<T*>
 {
   typedef typename IntrinsicMemoryOps<T*, Order>::ValueType ValueType;
+  // This is required to make us be able to build with MSVC10, for unknown
+  // reasons.
+  typedef typename IntrinsicBase<T*>::Primitives Primitives;
 };
 
 } // namespace detail
 } // namespace mozilla
 
 #else
 # error "Atomic compiler intrinsics are not supported on your platform"
 #endif
--- a/netwerk/test/unit/test_speculative_connect.js
+++ b/netwerk/test/unit/test_speculative_connect.js
@@ -32,53 +32,28 @@ var localIPv6Literals =
       "fc00::1", "fdfe:dcba:9876:abcd:ef01:2345:6789:abcd",
       "fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
       // IPv6 Link Local fe80::/10
       "fe80::1", "fe80::abcd:ef01:2345:6789",
       "febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
     ];
 var localIPLiterals = localIPv4Literals.concat(localIPv6Literals);
 
-/** Example remote IP addresses
- *
- * Note: The test environment may not have external network access, so
- * resolving hostnames may not be possible. Thus, literals are used here, and
- * should be directly converted by the stub resolver to IP addresses.
- */
-var remoteIPv4Literals =
-    [ "93.184.216.119", // example.com
-      "74.125.239.130", // google.com
-      "63.245.217.105", // mozilla.org
-      "173.252.110.27"  // facebook.com
-    ];
-
-var remoteIPv6Literals =
-    [ "2607:f8b0:4005:802::1009",        // google.com
-      "2620:101:8008:5::2:1",            // mozilla.org
-      "2a03:2880:2110:df07:face:b00c::1" // facebook.com
-    ];
-
-var remoteIPLiterals = remoteIPv4Literals.concat(remoteIPv6Literals);
-
 /** Test function list and descriptions.
  */
 var testList =
     [ test_speculative_connect,
       test_hostnames_resolving_to_local_addresses,
-      test_hostnames_resolving_to_remote_addresses,
-      test_proxies_with_local_addresses,
-      test_proxies_with_remote_addresses
+      test_proxies_with_local_addresses
     ];
 
 var testDescription =
     [ "Expect pass with localhost",
       "Expect failure with resolved local IPs",
-      "Expect success with resolved remote IPs",
-      "Expect failure for proxies with local IPs",
-      "Expect success for proxies with remote IPs"
+      "Expect failure for proxies with local IPs"
     ];
 
 var testIdx = 0;
 var hostIdx = 0;
 
 
 /** TestServer
  *
@@ -245,40 +220,16 @@ function test_hostnames_resolving_to_loc
         return;
     }
     var host = localIPLiterals[hostIdx++];
     // Test another local IP address when the current one is done.
     var next = test_hostnames_resolving_to_local_addresses;
     test_hostnames_resolving_to_addresses(host, false, next);
 }
 
-/**
- * test_hostnames_resolving_to_remote_addresses
- *
- * Creates an nsISocketTransport and simulates a speculative connect request
- * for a hostname that resolves to a local IP address.
- * Runs asynchronously; on test success (i.e. failure to connect), the callback
- * will call this function again until all hostnames in the test list are done.
- *
- * Note: This test also uses an IP literal for the hostname. This should be ok,
- * as the socket layer will ask for the hostname to be resolved anyway, and DNS
- * code should return a numerical version of the address internally.
- */
-function test_hostnames_resolving_to_remote_addresses() {
-    if (hostIdx >= remoteIPLiterals.length) {
-        // No more remote IP addresses; move on.
-        next_test();
-        return;
-    }
-    var host = remoteIPLiterals[hostIdx++];
-    // Test another remote IP address when the current one is done.
-    var next = test_hostnames_resolving_to_remote_addresses;
-    test_hostnames_resolving_to_addresses(host, true, next);
-}
-
 /** test_speculative_connect_with_host_list
  *
  * Common test function for resolved proxy hosts. Takes a list of hosts, a
  * boolean to determine if the test is expected to succeed or fail, and a
  * function to call the next test case.
  */
 function test_proxies(proxyHost, expectSuccess, next) {
     do_print("Proxy: " + proxyHost);
@@ -343,40 +294,16 @@ function test_proxies_with_local_address
         return;
     }
     var host = localIPLiterals[hostIdx++];
     // Test another local IP address when the current one is done.
     var next = test_proxies_with_local_addresses;
     test_proxies(host, false, next);
 }
 
-/**
- * test_proxies_with_remote_addresses
- *
- * Creates an nsISocketTransport and simulates a speculative connect request
- * for a proxy that resolves to a local IP address.
- * Runs asynchronously; on test success (i.e. failure to connect), the callback
- * will call this function again until all proxies in the test list are done.
- *
- * Note: This test also uses an IP literal for the proxy. This should be ok,
- * as the socket layer will ask for the proxy to be resolved anyway, and DNS
- * code should return a numerical version of the address internally.
- */
-function test_proxies_with_remote_addresses() {
-    if (hostIdx >= remoteIPLiterals.length) {
-        // No more local IP addresses; move on.
-        next_test();
-        return;
-    }
-    var host = remoteIPLiterals[hostIdx++];
-    // Test another local IP address when the current one is done.
-    var next = test_proxies_with_remote_addresses;
-    test_proxies(host, true, next);
-}
-
 /** next_test
  *
  * Calls the next test in testList. Each test is responsible for calling this
  * function when its test cases are complete.
  */
 function next_test() {
     if (testIdx >= testList.length) {
         // No more tests; we're done.
--- a/netwerk/wifi/nsWifiAccessPoint.cpp
+++ b/netwerk/wifi/nsWifiAccessPoint.cpp
@@ -16,16 +16,17 @@ extern PRLogModuleInfo *gWifiMonitorLog;
 NS_IMPL_ISUPPORTS(nsWifiAccessPoint, nsIWifiAccessPoint)
 
 nsWifiAccessPoint::nsWifiAccessPoint()
 {
   // make sure these are null terminated (because we are paranoid)
   mMac[0] = '\0';
   mSsid[0] = '\0';
   mSsidLen = 0;
+  mSignal = -1000;
 }
 
 nsWifiAccessPoint::~nsWifiAccessPoint()
 {
 }
 
 NS_IMETHODIMP nsWifiAccessPoint::GetMac(nsACString& aMac)
 {
@@ -65,17 +66,18 @@ bool AccessPointsEqual(nsCOMArray<nsWifi
   }
 
   for (int32_t i = 0; i < a.Count(); i++) {
     LOG(("++ Looking for %s\n", a[i]->mSsid));
     bool found = false;
     for (int32_t j = 0; j < b.Count(); j++) {
       LOG(("   %s->%s | %s->%s\n", a[i]->mSsid, b[j]->mSsid, a[i]->mMac, b[j]->mMac));
       if (!strcmp(a[i]->mSsid, b[j]->mSsid) &&
-          !strcmp(a[i]->mMac, b[j]->mMac)) {
+          !strcmp(a[i]->mMac, b[j]->mMac) &&
+          a[i]->mSignal == b[j]->mSignal) {
         found = true;
       }
     }
     if (!found)
       return false;
   }
   LOG(("   match!\n"));
   return true;
--- a/netwerk/wifi/nsWifiMonitor.h
+++ b/netwerk/wifi/nsWifiMonitor.h
@@ -23,16 +23,18 @@
 
 #if defined(PR_LOGGING)
 extern PRLogModuleInfo *gWifiMonitorLog;
 #endif
 #define LOG(args)     PR_LOG(gWifiMonitorLog, PR_LOG_DEBUG, args)
 
 class nsWifiAccessPoint;
 
+#define kDefaultWifiScanInterval 5 /* seconds */
+
 class nsWifiListener
 {
  public:
 
   nsWifiListener(nsMainThreadPtrHolder<nsIWifiListener>* aListener)
   {
     mListener = aListener;
     mHasSentData = false;
--- a/netwerk/wifi/nsWifiScannerDBus.cpp
+++ b/netwerk/wifi/nsWifiScannerDBus.cpp
@@ -327,13 +327,13 @@ nsWifiMonitor::DoScan()
                                                   lastAccessPoints);
     ReplaceArray(lastAccessPoints, accessPoints);
 
     rv = CallWifiListeners(lastAccessPoints, accessPointsChanged);
     NS_ENSURE_SUCCESS(rv, rv);
 
     LOG(("waiting on monitor\n"));
     mozilla::ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-    mon.Wait(PR_SecondsToInterval(60));
+    mon.Wait(PR_SecondsToInterval(kDefaultWifiScanInterval));
   }
 
   return NS_OK;
 }
--- a/netwerk/wifi/nsWifiScannerFreeBSD.cpp
+++ b/netwerk/wifi/nsWifiScannerFreeBSD.cpp
@@ -154,14 +154,14 @@ nsWifiMonitor::DoScan()
 
     rv = CallWifiListeners(lastAccessPoints, accessPointsChanged);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // wait for some reasonable amount of time. pref?
     LOG(("waiting on monitor\n"));
 
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-    mon.Wait(PR_SecondsToInterval(60));
+    mon.Wait(PR_SecondsToInterval(kDefaultWifiScanInterval));
   }
   while (mKeepGoing);
 
   return NS_OK;
 }
--- a/netwerk/wifi/nsWifiScannerMac.cpp
+++ b/netwerk/wifi/nsWifiScannerMac.cpp
@@ -42,14 +42,14 @@ nsWifiMonitor::DoScan()
 
     rv = CallWifiListeners(lastAccessPoints, accessPointsChanged);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // wait for some reasonable amount of time.  pref?
     LOG(("waiting on monitor\n"));
 
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-    mon.Wait(PR_SecondsToInterval(60));
+    mon.Wait(PR_SecondsToInterval(kDefaultWifiScanInterval));
   }
   while (mKeepGoing);
 
   return NS_OK;
 }
--- a/netwerk/wifi/nsWifiScannerSolaris.cpp
+++ b/netwerk/wifi/nsWifiScannerSolaris.cpp
@@ -137,13 +137,13 @@ nsWifiMonitor::DoScan()
     ReplaceArray(lastAccessPoints, accessPoints);
 
     nsresult rv = CallWifiListeners(lastAccessPoints, accessPointsChanged);
     NS_ENSURE_SUCCESS(rv, rv);
 
     LOG(("waiting on monitor\n"));
 
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-    mon.Wait(PR_SecondsToInterval(60));
+    mon.Wait(PR_SecondsToInterval(kDefaultWifiScanInterval));
   }
 
   return NS_OK;
 }
--- a/netwerk/wifi/nsWifiScannerWin.cpp
+++ b/netwerk/wifi/nsWifiScannerWin.cpp
@@ -118,14 +118,14 @@ nsWifiMonitor::DoScan()
 
       nsresult rv = CallWifiListeners(lastAccessPoints, accessPointsChanged);
       NS_ENSURE_SUCCESS(rv, rv);
 
       // wait for some reasonable amount of time.  pref?
       LOG(("waiting on monitor\n"));
 
       ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-      mon.Wait(PR_SecondsToInterval(60));
+      mon.Wait(PR_SecondsToInterval(kDefaultWifiScanInterval));
     }
     while (mKeepGoing);
 
     return NS_OK;
 }
--- a/python/mozboot/mozboot/bootstrap.py
+++ b/python/mozboot/mozboot/bootstrap.py
@@ -74,19 +74,21 @@ class Bootstrapper(object):
 
             cls = OSXBootstrapper
             args['version'] = osx_version
 
         elif sys.platform.startswith('openbsd'):
             cls = OpenBSDBootstrapper
             args['version'] = platform.uname()[2]
 
-        elif sys.platform.startswith('freebsd'):
+        elif sys.platform.startswith('dragonfly') or \
+             sys.platform.startswith('freebsd'):
             cls = FreeBSDBootstrapper
             args['version'] = platform.release()
+            args['flavor']  = platform.system()
 
         if cls is None:
             raise NotImplementedError('Bootstrap support is not yet available '
                                       'for your OS.')
 
         self.instance = cls(**args)
 
 
--- a/python/mozboot/mozboot/freebsd.py
+++ b/python/mozboot/mozboot/freebsd.py
@@ -1,52 +1,48 @@
 # 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/.
 
-from __future__ import print_function
-
-import os
-import subprocess
-import sys
-
 from mozboot.base import BaseBootstrapper
 
 class FreeBSDBootstrapper(BaseBootstrapper):
-    def __init__(self, version):
+    def __init__(self, version, flavor):
         BaseBootstrapper.__init__(self)
         self.version = int(version.split('.')[0])
+        self.flavor  = flavor.lower()
 
         self.packages = [
-            ('autoconf-2.13', 'autoconf213'),
-            ('dbus-glib',),
-            ('gmake',),
-            ('gstreamer-plugins',),
-            ('gtk-2', 'gtk20'),
-            ('libGL',),
-            ('libIDL',),
-            ('libv4l',),
-            ('mercurial',),
-            ('pulseaudio',),
-            ('yasm',),
-            ('zip',),
+            'autoconf213',
+            'dbus-glib',
+            'gmake',
+            'gstreamer-plugins',
+            'gtk2',
+            'libGL',
+            'mercurial',
+            'pkgconf',
+            'pulseaudio',
+            'v4l_compat',
+            'yasm',
+            'zip',
         ]
 
-        # using clang since 9.0
-        if self.version < 9:
-            self.packages.append(('gcc',))
+        if self.flavor == 'dragonfly':
+            self.packages.append('unzip')
 
+        # gcc in base is too old
+        if self.flavor == 'freebsd' and self.version < 9:
+            self.packages.append('gcc')
 
     def pkg_install(self, *packages):
         if self.which('pkg'):
-            command = ['pkg', 'install', '-x']
-            command.extend([i[0] for i in packages])
+            command = ['pkg', 'install']
         else:
             command = ['pkg_add', '-Fr']
-            command.extend([i[-1] for i in packages])
 
+        command.extend(packages)
         self.run_as_root(command)
 
     def install_system_packages(self):
         self.pkg_install(*self.packages)
 
     def upgrade_mercurial(self, current):
         self.pkg_install('mercurial')
--- a/security/manager/ssl/public/nsIX509Cert.idl
+++ b/security/manager/ssl/public/nsIX509Cert.idl
@@ -69,17 +69,17 @@ interface nsIX509Cert : nsISupports {
 
   /**
    *  The fingerprint of the certificate's DER encoding,
    *  calculated using the SHA-256 algorithm.
    */
   readonly attribute AString sha256Fingerprint;
 
   /**
-   *  The fingerprint of the certificate's public key,
+   *  The fingerprint of the certificate's DER encoding,
    *  calculated using the SHA1 algorithm.
    */
   readonly attribute AString sha1Fingerprint;
 
   /**
    *  A human readable name identifying the hardware or
    *  software token the certificate is stored on.
    */
--- a/security/pkix/lib/pkixbuild.cpp
+++ b/security/pkix/lib/pkixbuild.cpp
@@ -70,45 +70,53 @@ BackCert::Init(const SECItem& certDER)
   const SECItem* dummyEncodedSubjectKeyIdentifier = nullptr;
   const SECItem* dummyEncodedAuthorityKeyIdentifier = nullptr;
   const SECItem* dummyEncodedAuthorityInfoAccess = nullptr;
   const SECItem* dummyEncodedSubjectAltName = nullptr;
 
   for (const CERTCertExtension* ext = *exts; ext; ext = *++exts) {
     const SECItem** out = nullptr;
 
-    if (ext->id.len == 3 &&
-        ext->id.data[0] == 0x55 && ext->id.data[1] == 0x1d) {
-      // { id-ce x }
-      switch (ext->id.data[2]) {
+    // python DottedOIDToCode.py id-ce 2.5.29
+    static const uint8_t id_ce[] = {
+      0x55, 0x1d
+    };
+
+    // python DottedOIDToCode.py id-pe-authorityInfoAccess 1.3.6.1.5.5.7.1.1
+    static const uint8_t id_pe_authorityInfoAccess[] = {
+      0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01
+    };
+
+    if (ext->id.len == PR_ARRAY_SIZE(id_ce) + 1 &&
+        !memcmp(ext->id.data, id_ce, PR_ARRAY_SIZE(id_ce))) {
+      switch (ext->id.data[ext->id.len - 1]) {
         case 14: out = &dummyEncodedSubjectKeyIdentifier; break; // bug 965136
         case 15: out = &encodedKeyUsage; break;
         case 17: out = &dummyEncodedSubjectAltName; break; // bug 970542
         case 19: out = &encodedBasicConstraints; break;
         case 30: out = &encodedNameConstraints; break;
         case 32: out = &encodedCertificatePolicies; break;
         case 35: out = &dummyEncodedAuthorityKeyIdentifier; break; // bug 965136
         case 37: out = &encodedExtendedKeyUsage; break;
         case 54: out = &encodedInhibitAnyPolicy; break; // Bug 989051
       }
-    } else if (ext->id.len == 9 &&
-               ext->id.data[0] == 0x2b && ext->id.data[1] == 0x06 &&
-               ext->id.data[2] == 0x06 && ext->id.data[3] == 0x01 &&
-               ext->id.data[4] == 0x05 && ext->id.data[5] == 0x05 &&
-               ext->id.data[6] == 0x07 && ext->id.data[7] == 0x01) {
-      // { id-pe x }
-      switch (ext->id.data[8]) {
-        // We should remember the value of the encoded AIA extension here, but
-        // since our TrustDomain implementations get the OCSP URI using
-        // CERT_GetOCSPAuthorityInfoAccessLocation, we currently don't need to.
-        case 1: out = &dummyEncodedAuthorityInfoAccess; break;
-      }
-    } else if (ext->critical.data && ext->critical.len > 0) {
-      // The only valid explicit value of the critical flag is TRUE because
-      // it is defined as BOOLEAN DEFAULT FALSE, so we just assume it is true.
+    } else if (ext->id.len == PR_ARRAY_SIZE(id_pe_authorityInfoAccess) &&
+               !memcmp(ext->id.data, id_pe_authorityInfoAccess,
+                       PR_ARRAY_SIZE(id_pe_authorityInfoAccess))) {
+      // We should remember the value of the encoded AIA extension here, but
+      // since our TrustDomain implementations get the OCSP URI using
+      // CERT_GetOCSPAuthorityInfoAccessLocation, we currently don't need to.
+      out = &dummyEncodedAuthorityInfoAccess;
+    }
+
+    // If this is an extension we don't understand and it's marked critical,
+    // we must reject this certificate.
+    // (The only valid explicit value of the critical flag is TRUE because
+    // it is defined as BOOLEAN DEFAULT FALSE, so we just assume it is true.)
+    if (!out && ext->critical.data && ext->critical.len > 0) {
       return Fail(RecoverableError, SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION);
     }
 
     if (out) {
       // This is an extension we understand. Save it in results unless we've
       // already found the extension previously.
       if (*out) {
         // Duplicate extension
--- a/security/pkix/test/gtest/moz.build
+++ b/security/pkix/test/gtest/moz.build
@@ -4,16 +4,17 @@
 # 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/.
 
 LIBRARY_NAME = 'mozillapkix_gtest'
 
 SOURCES += [
     'nssgtest.cpp',
     'pkix_cert_chain_length_tests.cpp',
+    'pkix_cert_extension_tests.cpp',
     'pkix_ocsp_request_tests.cpp',
     'pkixder_input_tests.cpp',
     'pkixder_pki_types_tests.cpp',
     'pkixder_universal_types_tests.cpp',
 ]
 
 LOCAL_INCLUDES += [
     '../../include',
new file mode 100644
--- /dev/null
+++ b/security/pkix/test/gtest/pkix_cert_extension_tests.cpp
@@ -0,0 +1,319 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This code is made available to you under your choice of the following sets
+ * of licensing terms:
+ */
+/* 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/.
+ */
+/* Copyright 2013 Mozilla Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "nssgtest.h"
+#include "pkix/pkix.h"
+#include "secerr.h"
+
+using namespace mozilla::pkix;
+using namespace mozilla::pkix::test;
+
+// Creates a self-signed certificate with the given extension.
+static bool
+CreateCert(PLArenaPool* arena, const char* subjectStr,
+           const SECItem* extension,
+           /*out*/ ScopedSECKEYPrivateKey& subjectKey,
+           /*out*/ ScopedCERTCertificate& subjectCert)
+{
+  static long serialNumberValue = 0;
+  ++serialNumberValue;
+  const SECItem* serialNumber(CreateEncodedSerialNumber(arena,
+                                                        serialNumberValue));
+  if (!serialNumber) {
+    return false;
+  }
+  const SECItem* issuerDER(ASCIIToDERName(arena, subjectStr));
+  if (!issuerDER) {
+    return false;
+  }
+  const SECItem* subjectDER(ASCIIToDERName(arena, subjectStr));
+  if (!subjectDER) {
+    return false;
+  }
+
+  const SECItem* extensions[2] = { extension, nullptr };
+
+  SECItem* certDER(CreateEncodedCertificate(arena, v3, SEC_OID_SHA256,
+                                            serialNumber, issuerDER,
+                                            PR_Now() - ONE_DAY,
+                                            PR_Now() + ONE_DAY,
+                                            subjectDER, extensions,
+                                            nullptr, SEC_OID_SHA256,
+                                            subjectKey));
+  if (!certDER) {
+    return false;
+  }
+  subjectCert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), certDER,
+                                        nullptr, false, true);
+  return subjectCert.get() != nullptr;
+}
+
+class TrustEverythingTrustDomain : public TrustDomain
+{
+private:
+  SECStatus GetCertTrust(EndEntityOrCA,
+                         const CertPolicyId&,
+                         const SECItem& candidateCert,
+                         /*out*/ TrustLevel* trustLevel)
+  {
+    *trustLevel = TrustLevel::TrustAnchor;
+    return SECSuccess;
+  }
+
+  SECStatus FindPotentialIssuers(const SECItem* encodedIssuerName,
+                                 PRTime time,
+                                 /*out*/ ScopedCERTCertList& results)
+  {
+    return SECSuccess;
+  }
+
+  SECStatus VerifySignedData(const CERTSignedData* signedData,
+                             const SECItem& subjectPublicKeyInfo)
+  {
+    return ::mozilla::pkix::VerifySignedData(signedData, subjectPublicKeyInfo,
+                                             nullptr);
+  }
+
+  SECStatus CheckRevocation(EndEntityOrCA, const CERTCertificate*,
+                            /*const*/ CERTCertificate*, PRTime,
+                            /*optional*/ const SECItem*)
+  {
+    return SECSuccess;
+  }
+
+  virtual SECStatus IsChainValid(const CERTCertList*)
+  {
+    return SECSuccess;
+  }
+};
+
+class pkix_cert_extensions: public NSSTest
+{
+public:
+  static void SetUpTestCase()
+  {
+    NSSTest::SetUpTestCase();
+  }
+
+protected:
+  static TrustEverythingTrustDomain trustDomain;
+};
+
+/*static*/ TrustEverythingTrustDomain pkix_cert_extensions::trustDomain;
+
+// Tests that a critical extension not in the id-ce or id-pe arcs (which is
+// thus unknown to us) is detected and that verification fails with the
+// appropriate error.
+TEST_F(pkix_cert_extensions, UnknownCriticalExtension)
+{
+  static const uint8_t unknownCriticalExtensionBytes[] = {
+    0x30, 0x19, // SEQUENCE (length = 25)
+      0x06, 0x12, // OID (length = 18)
+        // 1.3.6.1.4.1.13769.666.666.666.1.500.9.3
+        0x2b, 0x06, 0x01, 0x04, 0x01, 0xeb, 0x49, 0x85, 0x1a,
+        0x85, 0x1a, 0x85, 0x1a, 0x01, 0x83, 0x74, 0x09, 0x03,
+      0x01, 0x01, 0xff, // BOOLEAN (length = 1) TRUE
+      0x04, 0x00 // OCTET STRING (length = 0)
+  };
+  static const SECItem unknownCriticalExtension = {
+    siBuffer,
+    const_cast<unsigned char*>(unknownCriticalExtensionBytes),
+    sizeof(unknownCriticalExtensionBytes)
+  };
+  const char* certCN = "CN=Cert With Unknown Critical Extension";
+  ScopedSECKEYPrivateKey key;
+  ScopedCERTCertificate cert;
+  ASSERT_TRUE(CreateCert(arena.get(), certCN, &unknownCriticalExtension, key,
+                         cert));
+  ScopedCERTCertList results;
+  ASSERT_SECFailure(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION,
+                    BuildCertChain(trustDomain, cert.get(),
+                                   now, EndEntityOrCA::MustBeEndEntity,
+                                   0, // key usage
+                                   KeyPurposeId::anyExtendedKeyUsage,
+                                   CertPolicyId::anyPolicy,
+                                   nullptr, // stapled OCSP response
+                                   results));
+}
+
+// Tests that a non-critical extension not in the id-ce or id-pe arcs (which is
+// thus unknown to us) verifies successfully.
+TEST_F(pkix_cert_extensions, UnknownNonCriticalExtension)
+{
+  static const uint8_t unknownNonCriticalExtensionBytes[] = {
+    0x30, 0x16, // SEQUENCE (length = 22)
+      0x06, 0x12, // OID (length = 18)
+        // 1.3.6.1.4.1.13769.666.666.666.1.500.9.3
+        0x2b, 0x06, 0x01, 0x04, 0x01, 0xeb, 0x49, 0x85, 0x1a,
+        0x85, 0x1a, 0x85, 0x1a, 0x01, 0x83, 0x74, 0x09, 0x03,
+      0x04, 0x00 // OCTET STRING (length = 0)
+  };
+  static const SECItem unknownNonCriticalExtension = {
+    siBuffer,
+    const_cast<unsigned char*>(unknownNonCriticalExtensionBytes),
+    sizeof(unknownNonCriticalExtensionBytes)
+  };
+  const char* certCN = "CN=Cert With Unknown NonCritical Extension";
+  ScopedSECKEYPrivateKey key;
+  ScopedCERTCertificate cert;
+  ASSERT_TRUE(CreateCert(arena.get(), certCN, &unknownNonCriticalExtension, key,
+                         cert));
+  ScopedCERTCertList results;
+  ASSERT_SECSuccess(BuildCertChain(trustDomain, cert.get(),
+                                   now, EndEntityOrCA::MustBeEndEntity,
+                                   0, // key usage
+                                   KeyPurposeId::anyExtendedKeyUsage,
+                                   CertPolicyId::anyPolicy,
+                                   nullptr, // stapled OCSP response
+                                   results));
+}
+
+// Tests that an incorrect OID for id-pe-authorityInformationAccess
+// (when marked critical) is detected and that verification fails.
+// (Until bug 1020993 was fixed, the code checked for this OID.)
+TEST_F(pkix_cert_extensions, WrongOIDCriticalExtension)
+{
+  static const uint8_t wrongOIDCriticalExtensionBytes[] = {
+    0x30, 0x10, // SEQUENCE (length = 16)
+      0x06, 0x09, // OID (length = 9)
+        // 1.3.6.6.1.5.5.7.1.1 (there is an extra "6" that shouldn't be there)
+        0x2b, 0x06, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01,
+      0x01, 0x01, 0xff, // BOOLEAN (length = 1) TRUE
+      0x04, 0x00 // OCTET STRING (length = 0)
+  };
+  static const SECItem wrongOIDCriticalExtension = {
+    siBuffer,
+    const_cast<unsigned char*>(wrongOIDCriticalExtensionBytes),
+    sizeof(wrongOIDCriticalExtensionBytes)
+  };
+  const char* certCN = "CN=Cert With Critical Wrong OID Extension";
+  ScopedSECKEYPrivateKey key;
+  ScopedCERTCertificate cert;
+  ASSERT_TRUE(CreateCert(arena.get(), certCN, &wrongOIDCriticalExtension, key,
+                         cert));
+  ScopedCERTCertList results;
+  ASSERT_SECFailure(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION,
+                    BuildCertChain(trustDomain, cert.get(),
+                                   now, EndEntityOrCA::MustBeEndEntity,
+                                   0, // key usage
+                                   KeyPurposeId::anyExtendedKeyUsage,
+                                   CertPolicyId::anyPolicy,
+                                   nullptr, // stapled OCSP response
+                                   results));
+}
+
+// Tests that a id-pe-authorityInformationAccess critical extension
+// is detected and that verification succeeds.
+TEST_F(pkix_cert_extensions, CriticalAIAExtension)
+{
+  static const uint8_t criticalAIAExtensionBytes[] = {
+    0x30, 0x0f, // SEQUENCE (length = 15)
+      0x06, 0x08, // OID (length = 8)
+        // 1.3.6.1.5.5.7.1.1
+        0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01,
+      0x01, 0x01, 0xff, // BOOLEAN (length = 1) TRUE
+      0x04, 0x00 // OCTET STRING (length = 0)
+  };
+  static const SECItem criticalAIAExtension = {
+    siBuffer,
+    const_cast<unsigned char*>(criticalAIAExtensionBytes),
+    sizeof(criticalAIAExtensionBytes)
+  };
+  const char* certCN = "CN=Cert With Critical AIA Extension";
+  ScopedSECKEYPrivateKey key;
+  ScopedCERTCertificate cert;
+  ASSERT_TRUE(CreateCert(arena.get(), certCN, &criticalAIAExtension, key,
+                         cert));
+  ScopedCERTCertList results;
+  ASSERT_SECSuccess(BuildCertChain(trustDomain, cert.get(),
+                                   now, EndEntityOrCA::MustBeEndEntity,
+                                   0, // key usage
+                                   KeyPurposeId::anyExtendedKeyUsage,
+                                   CertPolicyId::anyPolicy,
+                                   nullptr, // stapled OCSP response
+                                   results));
+}
+
+// We know about some id-ce extensions (OID arc 2.5.29), but not all of them.
+// Tests that an unknown id-ce extension is detected and that verification
+// fails.
+TEST_F(pkix_cert_extensions, UnknownCriticalCEExtension)
+{
+  static const uint8_t unknownCriticalCEExtensionBytes[] = {
+    0x30, 0x0a, // SEQUENCE (length = 10)
+      0x06, 0x03, // OID (length = 3)
+        0x55, 0x1d, 0x37, // 2.5.29.55
+      0x01, 0x01, 0xff, // BOOLEAN (length = 1) TRUE
+      0x04, 0x00 // OCTET STRING (length = 0)
+  };
+  static const SECItem unknownCriticalCEExtension = {
+    siBuffer,
+    const_cast<unsigned char*>(unknownCriticalCEExtensionBytes),
+    sizeof(unknownCriticalCEExtensionBytes)
+  };
+  const char* certCN = "CN=Cert With Unknown Critical id-ce Extension";
+  ScopedSECKEYPrivateKey key;
+  ScopedCERTCertificate cert;
+  ASSERT_TRUE(CreateCert(arena.get(), certCN, &unknownCriticalCEExtension, key,
+                         cert));
+  ScopedCERTCertList results;
+  ASSERT_SECFailure(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION,
+                    BuildCertChain(trustDomain, cert.get(),
+                                   now, EndEntityOrCA::MustBeEndEntity,
+                                   0, // key usage
+                                   KeyPurposeId::anyExtendedKeyUsage,
+                                   CertPolicyId::anyPolicy,
+                                   nullptr, // stapled OCSP response
+                                   results));
+}
+
+// Tests that a certificate with a known critical id-ce extension (in this case,
+// OID 2.5.29.54, which is id-ce-inhibitAnyPolicy), verifies successfully.
+TEST_F(pkix_cert_extensions, KnownCriticalCEExtension)
+{
+  static const uint8_t criticalCEExtensionBytes[] = {
+    0x30, 0x0a, // SEQUENCE (length = 10)
+      0x06, 0x03, // OID (length = 3)
+        0x55, 0x1d, 0x36, // 2.5.29.54 (id-ce-inhibitAnyPolicy)
+      0x01, 0x01, 0xff, // BOOLEAN (length = 1) TRUE
+      0x04, 0x00 // OCTET STRING (length = 0)
+  };
+  static const SECItem criticalCEExtension = {
+    siBuffer,
+    const_cast<unsigned char*>(criticalCEExtensionBytes),
+    sizeof(criticalCEExtensionBytes)
+  };
+  const char* certCN = "CN=Cert With Known Critical id-ce Extension";
+  ScopedSECKEYPrivateKey key;
+  ScopedCERTCertificate cert;
+  ASSERT_TRUE(CreateCert(arena.get(), certCN, &criticalCEExtension, key, cert));
+  ScopedCERTCertList results;
+  ASSERT_SECSuccess(BuildCertChain(trustDomain, cert.get(),
+                                   now, EndEntityOrCA::MustBeEndEntity,
+                                   0, // key usage
+                                   KeyPurposeId::anyExtendedKeyUsage,
+                                   CertPolicyId::anyPolicy,
+                                   nullptr, // stapled OCSP response
+                                   results));
+}
--- a/services/healthreport/healthreporter.jsm
+++ b/services/healthreport/healthreporter.jsm
@@ -397,27 +397,35 @@ AbstractHealthReporter.prototype = Objec
 
       // As soon as we have could have storage, we need to register cleanup or
       // else bad things happen on shutdown.
       Services.obs.addObserver(this, "quit-application", false);
 
       // The database needs to be shut down by the end of shutdown
       // phase profileBeforeChange.
       Metrics.Storage.shutdown.addBlocker("FHR: Flushing storage shutdown",
-        this._promiseShutdown,
+        () => {
+          // Workaround bug 1017706
+          // Apparently, in some cases, quit-application is not triggered
+          // (or is triggered after profile-before-change), so we need to
+          // make sure that `_initiateShutdown()` is triggered at least
+          // once.
+          this._initiateShutdown();
+          return this._promiseShutdown;
+        },
         () => ({
             shutdownInitiated: this._shutdownInitiated,
             initialized: this._initialized,
             shutdownRequested: this._shutdownRequested,
             initializeHadError: this._initializeHadError,
             providerManagerInProgress: this._providerManagerInProgress,
             storageInProgress: this._storageInProgress,
             hasProviderManager: !!this._providerManager,
             hasStorage: !!this._storage,
-            shutdownComplete: this.shutdownComplete
+            shutdownComplete: this._shutdownComplete
           }));
 
       try {
         this._storageInProgress = true;
         TelemetryStopwatch.start(this._dbOpenHistogram, this);
         let storage = yield Metrics.Storage(this._dbName);
         TelemetryStopwatch.finish(this._dbOpenHistogram, this);
         yield this._onStorageCreated();
--- a/toolkit/components/osfile/modules/osfile_async_front.jsm
+++ b/toolkit/components/osfile/modules/osfile_async_front.jsm
@@ -187,23 +187,31 @@ let Scheduler = {
 
   /**
    * |true| once shutdown has begun i.e. we should reject any
    * message, including resets.
    */
   shutdown: false,
 
   /**
-   * A promise resolved once all operations are complete.
+   * A promise resolved once all currently pending operations are complete.
    *
    * This promise is never rejected and the result is always undefined.
    */
   queue: Promise.resolve(),
 
   /**
+   * A promise resolved once all currently pending `kill` operations
+   * are complete.
+   *
+   * This promise is never rejected and the result is always undefined.
+   */
+  _killQueue: Promise.resolve(),
+
+  /**
    * Miscellaneous debugging information
    */
   Debugging: {
     /**
      * The latest message sent and still waiting for a reply.
      */
     latestSent: undefined,
 
@@ -281,17 +289,24 @@ let Scheduler = {
    * @param {*} options
    * - {boolean} shutdown If |true|, reject any further request. Otherwise,
    *   further requests will resurrect the worker.
    * - {boolean} reset If |true|, instruct the worker to shutdown if this
    *   would not cause leaks. Otherwise, assume that the worker will be shutdown
    *   through some other mean.
    */
   kill: function({shutdown, reset}) {
-    return Task.spawn(function*() {
+    // Grab the kill queue to make sure that we
+    // cannot be interrupted by another call to `kill`.
+    let killQueue = this._killQueue;
+    return this._killQueue = Task.spawn(function*() {
+
+      yield killQueue;
+      // From this point, and until the end of the Task, we are the
+      // only call to `kill`, regardless of any `yield`.
 
       yield this.queue;
 
       // Enter critical section: no yield in this block
       // (we want to make sure that we remain the only
       // request in the queue).
 
       if (!this.launched || this.shutdown || !this._worker) {
--- a/toolkit/components/osfile/tests/xpcshell/test_reset.js
+++ b/toolkit/components/osfile/tests/xpcshell/test_reset.js
@@ -1,75 +1,90 @@
-Components.utils.import("resource://gre/modules/Services.jsm", this);
-Components.utils.import("resource://gre/modules/Promise.jsm", this);
-Components.utils.import("resource://gre/modules/Task.jsm", this);
-Components.utils.import("resource://gre/modules/osfile.jsm", this);
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 let Path = OS.Constants.Path;
 
-add_task(function init() {
+add_task(function* init() {
   do_get_profile();
 });
 
-add_task(function reset_before_launching() {
+add_task(function* reset_before_launching() {
   do_print("Reset without launching OS.File, it shouldn't break");
   yield OS.File.resetWorker();
 });
 
-add_task(function transparent_reset() {
+add_task(function* transparent_reset() {
   for (let i = 1; i < 3; ++i) {
     do_print("Do stome stuff before and after " + i + " reset(s), " +
              "it shouldn't break");
     let CONTENT = "some content " + i;
     let path = OS.Path.join(Path.profileDir, "tmp");
     yield OS.File.writeAtomic(path, CONTENT);
     for (let j = 0; j < i; ++j) {
       yield OS.File.resetWorker();
     }
     let data = yield OS.File.read(path);
     let string = (new TextDecoder()).decode(data);
     do_check_eq(string, CONTENT);
   }
 });
 
-add_task(function file_open_cannot_reset() {
+add_task(function* file_open_cannot_reset() {
   let TEST_FILE = OS.Path.join(Path.profileDir, "tmp-" + Math.random());
   do_print("Leaking file descriptor " + TEST_FILE + ", we shouldn't be able to reset");
   let openedFile = yield OS.File.open(TEST_FILE, { create: true} );
   let thrown = false;
   try {
     yield OS.File.resetWorker();
   } catch (ex if ex.message.indexOf(OS.Path.basename(TEST_FILE)) != -1 ) {
     thrown = true;
   }
   do_check_true(thrown);
 
   do_print("Closing the file, we should now be able to reset");
   yield openedFile.close();
   yield OS.File.resetWorker();
 });
 
-add_task(function dir_open_cannot_reset() {
+add_task(function* dir_open_cannot_reset() {
   let TEST_DIR = yield OS.File.getCurrentDirectory();
   do_print("Leaking directory " + TEST_DIR + ", we shouldn't be able to reset");
   let iterator = new OS.File.DirectoryIterator(TEST_DIR);
   let thrown = false;
   try {
     yield OS.File.resetWorker();
   } catch (ex if ex.message.indexOf(OS.Path.basename(TEST_DIR)) != -1 ) {
     thrown = true;
   }
   do_check_true(thrown);
 
   do_print("Closing the directory, we should now be able to reset");
   yield iterator.close();
   yield OS.File.resetWorker();
 });
 
-add_task(function finish_with_a_reset() {
+add_task(function* race_against_itself() {
+  do_print("Attempt to get resetWorker() to race against itself");
+  // Arbitrary operation, just to wake up the worker
+  try {
+    yield OS.File.read("/foo");
+  } catch (ex) {
+  }
+
+  let all = [];
+  for (let i = 0; i < 100; ++i) {
+    all.push(OS.File.resetWorker());
+  }
+
+  yield Promise.all(all);
+});
+
+
+add_task(function* finish_with_a_reset() {
   do_print("Reset without waiting for the result");
   // Arbitrary operation, just to wake up the worker
   try {
     yield OS.File.read("/foo");
   } catch (ex) {
   }
   // Now reset
   /*don't yield*/ OS.File.resetWorker();
--- a/toolkit/content/aboutWebrtc.xhtml
+++ b/toolkit/content/aboutWebrtc.xhtml
@@ -363,30 +363,16 @@ function displayStats(globalReport) {
     newPcDiv.id = pcDivHeading;
 
     if (!pcDiv) {
       document.getElementById('stats').appendChild(newPcDiv);
     } else {
       document.getElementById('stats').replaceChild(newPcDiv, pcDiv);
     }
   });
-
-  globalReport.errors.forEach(function (error) {
-    var pcDivHeading = 'PeerConnection:' + error.pcid;
-
-    var pcDiv = document.getElementById(pcDivHeading);
-    var newPcDiv = buildPcDiv(error, pcDivHeading);
-    newPcDiv.id = pcDivHeading;
-
-    if (pcDiv) {
-      document.getElementById('stats').replaceChild(newPcDiv, pcDiv);
-    } else {
-      document.getElementById('stats').appendChild(newPcDiv);
-    }
-  });
 }
 
 function onLoad() {
   WebrtcGlobalInformation.getAllStats(displayStats);
   if (WebrtcGlobalInformation.debugLevel) {
     setDebugButton(true);
   } else {
     setDebugButton(false);