Merge inbound to m-c a=merge
authorWes Kocher <wkocher@mozilla.com>
Wed, 11 Jun 2014 18:46:34 -0700
changeset 188334 9e8e3e903484f1986d18f07aba2a60d226241d70
parent 188333 6c44c84703fa693bd8d91b066900af8d7b6c4acd (current diff)
parent 188239 9ccdc477061e8ce33f789694b63acd19770146d0 (diff)
child 188335 1d9513f22934f62fc9228b7e027feaa59695a0f6
child 188355 354a7e95abb84cdb6b25e85a8b70da8b610016c7
child 188370 c264299e4823798dcfc01f26fd2caaea00e69723
push id44805
push useremorley@mozilla.com
push dateThu, 12 Jun 2014 15:02:56 +0000
treeherdermozilla-inbound@b53b467cc4ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone33.0a1
first release with
nightly linux32
9e8e3e903484 / 33.0a1 / 20140612030349 / files
nightly linux64
9e8e3e903484 / 33.0a1 / 20140612030349 / files
nightly mac
9e8e3e903484 / 33.0a1 / 20140612030349 / files
nightly win32
9e8e3e903484 / 33.0a1 / 20140612030349 / files
nightly win64
9e8e3e903484 / 33.0a1 / 20140612030349 / 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
--- a/browser/devtools/webconsole/test/browser_webconsole_output_04.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_output_04.js
@@ -45,17 +45,17 @@ let inputTests = [
     inspectable: true,
     variablesViewLabel: "TypeError",
   },
 
   // 4
   {
     input: "testDOMException()",
     output: 'DOMException [SyntaxError: "An invalid or illegal string was specified"',
-    printOutput: '[object XrayWrapper [object DOMException]]"',
+    printOutput: '"SyntaxError: An invalid or illegal string was specified"',
     inspectable: true,
     variablesViewLabel: "SyntaxError",
   },
 
   // 5
   {
     input: "testCSSStyleDeclaration()",
     output: 'CSS2Properties { color: "green", font-size: "2em" }',
--- a/dom/apps/src/Webapps.js
+++ b/dom/apps/src/Webapps.js
@@ -16,17 +16,24 @@ Cu.import("resource://gre/modules/Browse
 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
                                    "@mozilla.org/childprocessmessagemanager;1",
                                    "nsIMessageSender");
 
 function convertAppsArray(aApps, aWindow) {
   let apps = new aWindow.Array();
   for (let i = 0; i < aApps.length; i++) {
     let app = aApps[i];
-    apps.push(createApplicationObject(aWindow, app));
+    // Our application objects are JS-implemented XPCOM objects with DOM_OBJECT
+    // set in classinfo. These objects are reflector-per-scope, so as soon as we
+    // pass them to content, we'll end up with a new object in content. But from
+    // this code, they _appear_ to be chrome objects, and so the Array Xray code
+    // vetos the attempt to define a chrome-privileged object on a content Array.
+    // Very carefully waive Xrays so that this can keep working until we convert
+    // mozApps to WebIDL in bug 899322.
+    Cu.waiveXrays(apps)[i] = createApplicationObject(aWindow, app);
   }
 
   return apps;
 }
 
 function WebappsRegistry() {
 }
 
--- a/dom/tests/mochitest/geolocation/mochitest.ini
+++ b/dom/tests/mochitest/geolocation/mochitest.ini
@@ -1,9 +1,10 @@
 [DEFAULT]
+skip-if = os == 'mac'
 support-files =
   geolocation.html
   geolocation_common.js
   network_geolocation.sjs
   windowTest.html
 
 [test_allowCurrent.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT # b2g(Bug 910235 - Error: no message manager set when calling method: [nsIObserver::observe]) b2g-debug(Bug 910235 - Error: no message manager set when calling method: [nsIObserver::observe]) b2g-desktop(Bug 910235 - Error: no message manager set when calling method: [nsIObserver::observe])
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -993,16 +993,28 @@ protected:
 
 class DrawEventRecorder : public RefCounted<DrawEventRecorder>
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorder)
   virtual ~DrawEventRecorder() { }
 };
 
+struct Tile
+{
+  RefPtr<DrawTarget> mDrawTarget;
+  IntPoint mTileOrigin;
+};
+
+struct TileSet
+{
+  Tile* mTiles;
+  size_t mTileCount;
+};
+
 class GFX2D_API Factory
 {
 public:
   static bool HasSSE2();
 
   /** Make sure that the given dimensions don't overflow a 32-bit signed int
    * using 4 bytes per pixel; optionally, make sure that either dimension
    * doesn't exceed the given limit.
@@ -1086,16 +1098,24 @@ public:
 
 #if defined(USE_SKIA) && defined(MOZ_ENABLE_FREETYPE)
   static TemporaryRef<GlyphRenderingOptions>
     CreateCairoGlyphRenderingOptions(FontHinting aHinting, bool aAutoHinting);
 #endif
   static TemporaryRef<DrawTarget>
     CreateDualDrawTarget(DrawTarget *targetA, DrawTarget *targetB);
 
+  /*
+   * This creates a new tiled DrawTarget. When a tiled drawtarget is used the
+   * drawing is distributed over number of tiles which may each hold an
+   * individual offset. The tiles in the set must each have the same backend
+   * and format.
+   */
+  static TemporaryRef<DrawTarget> CreateTiledDrawTarget(const TileSet& aTileSet);
+
 #ifdef XP_MACOSX
   static TemporaryRef<DrawTarget> CreateDrawTargetForCairoCGContext(CGContextRef cg, const IntSize& aSize);
 #endif
 
 #ifdef WIN32
   static TemporaryRef<DrawTarget> CreateDrawTargetForD3D10Texture(ID3D10Texture2D *aTexture, SurfaceFormat aFormat);
   static TemporaryRef<DrawTarget>
     CreateDualDrawTargetForD3D10Textures(ID3D10Texture2D *aTextureA,
new file mode 100644
--- /dev/null
+++ b/gfx/2d/DrawTargetTiled.cpp
@@ -0,0 +1,195 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * 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/. */
+
+#include "DrawTargetTiled.h"
+
+using namespace std;
+
+namespace mozilla {
+namespace gfx {
+
+DrawTargetTiled::DrawTargetTiled()
+{
+}
+
+bool
+DrawTargetTiled::Init(const TileSet& aTiles)
+{
+  if (!aTiles.mTileCount) {
+    return false;
+  }
+
+  mTiles.resize(aTiles.mTileCount);
+  memcpy(&mTiles.front(), aTiles.mTiles, aTiles.mTileCount * sizeof(Tile));
+
+  for (size_t i = 0; i < mTiles.size(); i++) {
+    if (mTiles[0].mDrawTarget->GetFormat() != mTiles[i].mDrawTarget->GetFormat() ||
+        mTiles[0].mDrawTarget->GetType() != mTiles[i].mDrawTarget->GetType()) {
+      return false;
+    }
+    uint32_t newXMost = max(mRect.XMost(),
+                            mTiles[i].mTileOrigin.x + mTiles[i].mDrawTarget->GetSize().width);
+    uint32_t newYMost = max(mRect.YMost(),
+                            mTiles[i].mTileOrigin.y + mTiles[i].mDrawTarget->GetSize().height);
+    mRect.x = min(mRect.x, mTiles[i].mTileOrigin.x);
+    mRect.y = min(mRect.y, mTiles[i].mTileOrigin.y);
+    mRect.width = newXMost - mRect.x;
+    mRect.height = newYMost - mRect.y;
+  }
+  return true;
+}
+
+class SnapshotTiled : public SourceSurface
+{
+public:
+  SnapshotTiled(const vector<Tile>& aTiles, const IntRect& aRect)
+    : mRect(aRect)
+  {
+    for (size_t i = 0; i < aTiles.size(); i++) {
+      mSnapshots.push_back(aTiles[i].mDrawTarget->Snapshot());
+      mOrigins.push_back(aTiles[i].mTileOrigin);
+    }
+  }
+
+  virtual SurfaceType GetType() const { return SurfaceType::TILED; }
+  virtual IntSize GetSize() const { return IntSize(mRect.XMost(), mRect.YMost()); }
+  virtual SurfaceFormat GetFormat() const { return mSnapshots[0]->GetFormat(); }
+
+  virtual TemporaryRef<DataSourceSurface> GetDataSurface()
+  {
+    RefPtr<DataSourceSurface> surf = Factory::CreateDataSourceSurface(GetSize(), GetFormat());
+
+    DataSourceSurface::MappedSurface mappedSurf;
+    surf->Map(DataSourceSurface::MapType::WRITE, &mappedSurf);
+
+    {
+      RefPtr<DrawTarget> dt =
+        Factory::CreateDrawTargetForData(BackendType::CAIRO, mappedSurf.mData,
+        GetSize(), mappedSurf.mStride, GetFormat());
+
+      for (size_t i = 0; i < mSnapshots.size(); i++) {
+        RefPtr<DataSourceSurface> dataSurf = mSnapshots[i]->GetDataSurface();
+        dt->CopySurface(dataSurf, IntRect(IntPoint(0, 0), mSnapshots[i]->GetSize()), mOrigins[i]);
+      }
+    }
+    surf->Unmap();
+
+    return surf;
+  }
+private:
+  vector<RefPtr<SourceSurface>> mSnapshots;
+  vector<IntPoint> mOrigins;
+  IntRect mRect;
+};
+
+TemporaryRef<SourceSurface>
+DrawTargetTiled::Snapshot()
+{
+  return new SnapshotTiled(mTiles, mRect);
+}
+
+#define TILED_COMMAND(command) \
+  void \
+  DrawTargetTiled::command() \
+  { \
+    for (size_t i = 0; i < mTiles.size(); i++) { \
+    \
+    \
+    mTiles[i].mDrawTarget->command(); \
+    } \
+  }
+#define TILED_COMMAND1(command, type1) \
+  void \
+  DrawTargetTiled::command(type1 arg1) \
+  { \
+    for (size_t i = 0; i < mTiles.size(); i++) { \
+    \
+    \
+    mTiles[i].mDrawTarget->command(arg1); \
+    } \
+  }
+#define TILED_COMMAND3(command, type1, type2, type3) \
+  void \
+  DrawTargetTiled::command(type1 arg1, type2 arg2, type3 arg3) \
+  { \
+    for (size_t i = 0; i < mTiles.size(); i++) { \
+    \
+    \
+    mTiles[i].mDrawTarget->command(arg1, arg2, arg3); \
+    } \
+  }
+#define TILED_COMMAND4(command, type1, type2, type3, type4) \
+  void \
+  DrawTargetTiled::command(type1 arg1, type2 arg2, type3 arg3, type4 arg4) \
+  { \
+    for (size_t i = 0; i < mTiles.size(); i++) { \
+    \
+    \
+    mTiles[i].mDrawTarget->command(arg1, arg2, arg3, arg4); \
+    } \
+  }
+#define TILED_COMMAND5(command, type1, type2, type3, type4, type5) \
+  void \
+  DrawTargetTiled::command(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) \
+  { \
+    for (size_t i = 0; i < mTiles.size(); i++) { \
+    \
+    \
+    mTiles[i].mDrawTarget->command(arg1, arg2, arg3, arg4, arg5); \
+    } \
+  }
+
+TILED_COMMAND(Flush)
+TILED_COMMAND5(DrawSurface, SourceSurface*, const Rect&,
+                            const Rect&, const DrawSurfaceOptions&,
+                            const DrawOptions&)
+TILED_COMMAND4(DrawFilter, FilterNode*, const Rect&, const Point&, const DrawOptions&)
+TILED_COMMAND1(ClearRect, const Rect&)
+TILED_COMMAND4(MaskSurface, const Pattern&, SourceSurface*, Point, const DrawOptions&)
+TILED_COMMAND3(FillRect, const Rect&, const Pattern&, const DrawOptions&)
+TILED_COMMAND4(StrokeRect, const Rect&, const Pattern&, const StrokeOptions&, const DrawOptions&)
+TILED_COMMAND5(StrokeLine, const Point&, const Point&, const Pattern&, const StrokeOptions&, const DrawOptions&)
+TILED_COMMAND4(Stroke, const Path*, const Pattern&, const StrokeOptions&, const DrawOptions&)
+TILED_COMMAND3(Fill, const Path*, const Pattern&, const DrawOptions&)
+TILED_COMMAND5(FillGlyphs, ScaledFont*, const GlyphBuffer&, const Pattern&, const DrawOptions&, const GlyphRenderingOptions*)
+TILED_COMMAND3(Mask, const Pattern&, const Pattern&, const DrawOptions&)
+TILED_COMMAND1(PushClip, const Path*)
+TILED_COMMAND1(PushClipRect, const Rect&)
+TILED_COMMAND(PopClip)
+
+void
+DrawTargetTiled::CopySurface(SourceSurface *aSurface,
+                             const IntRect &aSourceRect,
+                             const IntPoint &aDestination)
+{
+  // CopySurface ignores the transform, account for that here.
+  for (size_t i = 0; i < mTiles.size(); i++) {
+    IntRect src = aSourceRect;
+    src.x += mTiles[i].mTileOrigin.x;
+    src.width -= mTiles[i].mTileOrigin.x;
+    src.y = mTiles[i].mTileOrigin.y;
+    src.height -= mTiles[i].mTileOrigin.y;
+
+    if (src.width <= 0 || src.height <= 0) {
+      continue;
+    }
+
+    mTiles[i].mDrawTarget->CopySurface(aSurface, src, aDestination);
+  }
+}
+
+void
+DrawTargetTiled::SetTransform(const Matrix& aTransform)
+{
+  for (size_t i = 0; i < mTiles.size(); i++) {
+    Matrix mat = aTransform;
+    mat.PostTranslate(Float(-mTiles[i].mTileOrigin.x), Float(-mTiles[i].mTileOrigin.y));
+    mTiles[i].mDrawTarget->SetTransform(mat);
+  }
+  DrawTarget::SetTransform(aTransform);
+}
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/gfx/2d/DrawTargetTiled.h
@@ -0,0 +1,137 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * 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/. */
+
+#ifndef MOZILLA_GFX_DRAWTARGETTILED_H_
+#define MOZILLA_GFX_DRAWTARGETTILED_H_
+
+#include "2D.h"
+#include "Filters.h"
+
+#include <vector>
+
+namespace mozilla {
+namespace gfx {
+
+class DrawTargetTiled : public DrawTarget
+{
+public:
+  DrawTargetTiled();
+
+  bool Init(const TileSet& mTiles);
+
+  virtual BackendType GetType() const { return mTiles[0].mDrawTarget->GetType(); }
+  virtual TemporaryRef<SourceSurface> Snapshot();
+  virtual IntSize GetSize() { return IntSize(mRect.XMost(), mRect.YMost()); }
+
+  virtual void Flush();
+  virtual void DrawSurface(SourceSurface *aSurface,
+                           const Rect &aDest,
+                           const Rect &aSource,
+                           const DrawSurfaceOptions &aSurfOptions,
+                           const DrawOptions &aOptions);
+  virtual void DrawFilter(FilterNode *aNode,
+                          const Rect &aSourceRect,
+                          const Point &aDestPoint,
+                          const DrawOptions &aOptions = DrawOptions());
+  virtual void DrawSurfaceWithShadow(SourceSurface *aSurface,
+                                     const Point &aDest,
+                                     const Color &aColor,
+                                     const Point &aOffset,
+                                     Float aSigma,
+                                     CompositionOp aOperator) { /* Not implemented */ MOZ_CRASH(); }
+
+  virtual void ClearRect(const Rect &aRect);
+  virtual void MaskSurface(const Pattern &aSource,
+                           SourceSurface *aMask,
+                           Point aOffset,
+                           const DrawOptions &aOptions = DrawOptions());
+
+  virtual void CopySurface(SourceSurface *aSurface,
+                           const IntRect &aSourceRect,
+                           const IntPoint &aDestination);
+
+  virtual void FillRect(const Rect &aRect,
+                        const Pattern &aPattern,
+                        const DrawOptions &aOptions = DrawOptions());
+  virtual void StrokeRect(const Rect &aRect,
+                          const Pattern &aPattern,
+                          const StrokeOptions &aStrokeOptions = StrokeOptions(),
+                          const DrawOptions &aOptions = DrawOptions());
+  virtual void StrokeLine(const Point &aStart,
+                          const Point &aEnd,
+                          const Pattern &aPattern,
+                          const StrokeOptions &aStrokeOptions = StrokeOptions(),
+                          const DrawOptions &aOptions = DrawOptions());
+  virtual void Stroke(const Path *aPath,
+                      const Pattern &aPattern,
+                      const StrokeOptions &aStrokeOptions = StrokeOptions(),
+                      const DrawOptions &aOptions = DrawOptions());
+  virtual void Fill(const Path *aPath,
+                    const Pattern &aPattern,
+                    const DrawOptions &aOptions = DrawOptions());
+  virtual void FillGlyphs(ScaledFont *aFont,
+                          const GlyphBuffer &aBuffer,
+                          const Pattern &aPattern,
+                          const DrawOptions &aOptions = DrawOptions(),
+                          const GlyphRenderingOptions *aRenderingOptions = nullptr);
+  virtual void Mask(const Pattern &aSource,
+                    const Pattern &aMask,
+                    const DrawOptions &aOptions = DrawOptions());
+  virtual void PushClip(const Path *aPath);
+  virtual void PushClipRect(const Rect &aRect);
+  virtual void PopClip();
+
+  virtual void SetTransform(const Matrix &aTransform);
+
+  virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
+                                                                  const IntSize &aSize,
+                                                                  int32_t aStride,
+                                                                  SurfaceFormat aFormat) const
+  {
+    return mTiles[0].mDrawTarget->CreateSourceSurfaceFromData(aData, aSize, aStride, aFormat);
+  }
+  virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const
+  {
+    return mTiles[0].mDrawTarget->OptimizeSourceSurface(aSurface);
+  }
+
+  virtual TemporaryRef<SourceSurface>
+    CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const
+  {
+    return mTiles[0].mDrawTarget->CreateSourceSurfaceFromNativeSurface(aSurface);
+  }
+
+  virtual TemporaryRef<DrawTarget>
+    CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
+  {
+    return mTiles[0].mDrawTarget->CreateSimilarDrawTarget(aSize, aFormat);
+  }
+
+  virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const
+  {
+    return mTiles[0].mDrawTarget->CreatePathBuilder(aFillRule);
+  }
+
+  virtual TemporaryRef<GradientStops>
+    CreateGradientStops(GradientStop *aStops,
+                        uint32_t aNumStops,
+                        ExtendMode aExtendMode = ExtendMode::CLAMP) const
+  {
+    return mTiles[0].mDrawTarget->CreateGradientStops(aStops, aNumStops, aExtendMode);
+  }
+  virtual TemporaryRef<FilterNode> CreateFilter(FilterType aType)
+  {
+    return mTiles[0].mDrawTarget->CreateFilter(aType);
+  }
+
+private:
+  std::vector<Tile> mTiles;
+  IntRect mRect;
+};
+
+}
+}
+
+#endif
--- a/gfx/2d/Factory.cpp
+++ b/gfx/2d/Factory.cpp
@@ -38,16 +38,17 @@
 #include "DrawTargetD2D1.h"
 #endif
 #include "ScaledFontDWrite.h"
 #include <d3d10_1.h>
 #include "HelpersD2D.h"
 #endif
 
 #include "DrawTargetDual.h"
+#include "DrawTargetTiled.h"
 #include "DrawTargetRecording.h"
 
 #include "SourceSurfaceRawData.h"
 
 #include "DrawEventRecorder.h"
 
 #include "Logging.h"
 
@@ -373,16 +374,28 @@ Factory::CreateDrawTargetForData(Backend
 
   if (!retVal) {
     gfxDebug() << "Failed to create DrawTarget, Type: " << int(aBackend) << " Size: " << aSize;
   }
 
   return retVal;
 }
 
+TemporaryRef<DrawTarget>
+Factory::CreateTiledDrawTarget(const TileSet& aTileSet)
+{
+  RefPtr<DrawTargetTiled> dt = new DrawTargetTiled();
+
+  if (!dt->Init(aTileSet)) {
+    return nullptr;
+  }
+
+  return dt;
+}
+
 TemporaryRef<ScaledFont>
 Factory::CreateScaledFontForNativeFont(const NativeFont &aNativeFont, Float aSize)
 {
   switch (aNativeFont.mType) {
 #ifdef WIN32
   case NativeFontType::DWRITE_FONT_FACE:
     {
       return new ScaledFontDWrite(static_cast<IDWriteFontFace*>(aNativeFont.mFont), aSize);
--- a/gfx/2d/QuartzSupport.h
+++ b/gfx/2d/QuartzSupport.h
@@ -48,17 +48,17 @@ public:
                   double aContentsScaleFactor,
                   CGImageRef *aOutCAImage);
   bool isInit() { return mCARenderer != nullptr; }
   /*
    * Render the CALayer to an IOSurface. If no IOSurface
    * is attached then an internal pixel buffer will be
    * used.
    */
-  void AttachIOSurface(mozilla::RefPtr<MacIOSurface> aSurface);
+  void AttachIOSurface(MacIOSurface *aSurface);
   IOSurfaceID GetIOSurfaceID();
   // aX, aY, aWidth and aHeight are in "display pixels".  Multiply by
   // surf->GetContentsScaleFactor() to get device pixels.
   static nsresult DrawSurfaceToCGContext(CGContextRef aContext,
                                          MacIOSurface *surf,
                                          CGColorSpaceRef aColorSpace,
                                          int aX, int aY,
                                          size_t aWidth, size_t aHeight);
--- a/gfx/2d/QuartzSupport.mm
+++ b/gfx/2d/QuartzSupport.mm
@@ -332,22 +332,19 @@ void nsCARenderer::SetViewport(int aWidt
   ::glLoadIdentity();
   ::glOrtho (0.0, aWidth, 0.0, aHeight, -1, 1);
 
   // Render upside down to speed up CGContextDrawImage
   ::glTranslatef(0.0f, aHeight, 0.0);
   ::glScalef(1.0, -1.0, 1.0);
 }
 
-void nsCARenderer::AttachIOSurface(RefPtr<MacIOSurface> aSurface) {
+void nsCARenderer::AttachIOSurface(MacIOSurface *aSurface) {
   if (mIOSurface &&
       aSurface->GetIOSurfaceID() == mIOSurface->GetIOSurfaceID()) {
-    // This object isn't needed since we already have a
-    // handle to the same io surface.
-    aSurface = nullptr;
     return;
   }
 
   mIOSurface = aSurface;
 
   // Update the framebuffer and viewport
   if (mCARenderer) {
     CARenderer* caRenderer = (CARenderer*)mCARenderer;
--- a/gfx/2d/Types.h
+++ b/gfx/2d/Types.h
@@ -23,17 +23,18 @@ MOZ_BEGIN_ENUM_CLASS(SurfaceType, int8_t
   D2D1_DRAWTARGET, /* Surface made from a D2D draw target */
   CAIRO, /* Surface wrapping a cairo surface */
   CAIRO_IMAGE, /* Data surface wrapping a cairo image surface */
   COREGRAPHICS_IMAGE, /* Surface wrapping a CoreGraphics Image */
   COREGRAPHICS_CGCONTEXT, /* Surface wrapping a CG context */
   SKIA, /* Surface wrapping a Skia bitmap */
   DUAL_DT, /* Snapshot of a dual drawtarget */
   D2D1_1_IMAGE, /* A D2D 1.1 ID2D1Image SourceSurface */
-  RECORDING /* Surface used for recording */
+  RECORDING, /* Surface used for recording */
+  TILED /* Surface from a tiled DrawTarget */
 MOZ_END_ENUM_CLASS(SurfaceType)
 
 MOZ_BEGIN_ENUM_CLASS(SurfaceFormat, int8_t)
   B8G8R8A8,
   B8G8R8X8,
   R8G8B8A8,
   R8G8B8X8,
   R5G6B5,
--- a/gfx/2d/moz.build
+++ b/gfx/2d/moz.build
@@ -97,16 +97,17 @@ if CONFIG['INTEL_ARCHITECTURE']:
 UNIFIED_SOURCES += [
     'Blur.cpp',
     'DataSourceSurface.cpp',
     'DataSurfaceHelpers.cpp',
     'DrawEventRecorder.cpp',
     'DrawTargetCairo.cpp',
     'DrawTargetDual.cpp',
     'DrawTargetRecording.cpp',
+    'DrawTargetTiled.cpp',
     'Factory.cpp',
     'FilterNodeSoftware.cpp',
     'FilterProcessing.cpp',
     'FilterProcessingScalar.cpp',
     'ImageScaling.cpp',
     'Matrix.cpp',
     'Path.cpp',
     'PathCairo.cpp',
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -2256,24 +2256,27 @@ void AsyncPanZoomController::NotifyLayer
     mFrameMetrics.mScrollableRect = aLayerMetrics.mScrollableRect;
     mFrameMetrics.mCompositionBounds = aLayerMetrics.mCompositionBounds;
     mFrameMetrics.SetRootCompositionSize(aLayerMetrics.GetRootCompositionSize());
     mFrameMetrics.mResolution = aLayerMetrics.mResolution;
     mFrameMetrics.mCumulativeResolution = aLayerMetrics.mCumulativeResolution;
     mFrameMetrics.mHasScrollgrab = aLayerMetrics.mHasScrollgrab;
 
     if (scrollOffsetUpdated) {
-      CancelAnimation();
-
       APZC_LOG("%p updating scroll offset from (%f, %f) to (%f, %f)\n", this,
         mFrameMetrics.GetScrollOffset().x, mFrameMetrics.GetScrollOffset().y,
         aLayerMetrics.GetScrollOffset().x, aLayerMetrics.GetScrollOffset().y);
 
       mFrameMetrics.CopyScrollInfoFrom(aLayerMetrics);
 
+      // Cancel the animation (which will also trigger a repaint request)
+      // after we update the scroll offset above. Otherwise we can be left
+      // in a state where things are out of sync.
+      CancelAnimation();
+
       // Because of the scroll offset update, any inflight paint requests are
       // going to be ignored by layout, and so mLastDispatchedPaintMetrics
       // becomes incorrect for the purposes of calculating the LD transform. To
       // correct this we need to update mLastDispatchedPaintMetrics to be the
       // last thing we know was painted by Gecko.
       mLastDispatchedPaintMetrics = aLayerMetrics;
     }
   }
--- a/gfx/layers/client/ClientTiledThebesLayer.cpp
+++ b/gfx/layers/client/ClientTiledThebesLayer.cpp
@@ -81,16 +81,20 @@ GetTransformToAncestorsParentLayer(Layer
   gfx3DMatrix ret;
   gfx::To3DMatrix(transform, ret);
   return ret;
 }
 
 void
 ClientTiledThebesLayer::BeginPaint()
 {
+  if (ClientManager()->IsRepeatTransaction()) {
+    return;
+  }
+
   mPaintData.mLowPrecisionPaintCount = 0;
   mPaintData.mPaintFinished = false;
   mPaintData.mCompositionBounds.SetEmpty();
   mPaintData.mCriticalDisplayPort.SetEmpty();
 
   if (!GetBaseTransform().Is2D()) {
     // Give up if there is a complex CSS transform on the layer. We might
     // eventually support these but for now it's too complicated to handle
@@ -174,133 +178,23 @@ ClientTiledThebesLayer::BeginPaint()
     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);
 }
 
-bool
-ClientTiledThebesLayer::UseFastPath()
-{
-  const FrameMetrics& parentMetrics = GetParent()->GetFrameMetrics();
-  bool multipleTransactionsNeeded = gfxPrefs::UseProgressiveTilePainting()
-                                 || gfxPrefs::UseLowPrecisionBuffer()
-                                 || !parentMetrics.mCriticalDisplayPort.IsEmpty();
-  bool isFixed = GetIsFixedPosition() || GetParent()->GetIsFixedPosition();
-  return !multipleTransactionsNeeded || isFixed || parentMetrics.mDisplayPort.IsEmpty();
-}
-
-bool
-ClientTiledThebesLayer::RenderHighPrecision(nsIntRegion& aInvalidRegion,
-                                            LayerManager::DrawThebesLayerCallback aCallback,
-                                            void* aCallbackData)
+void
+ClientTiledThebesLayer::EndPaint(bool aFinish)
 {
-  // If we have no high-precision stuff to draw, or we have started drawing low-precision
-  // already, then we shouldn't do anything there.
-  if (aInvalidRegion.IsEmpty() || mPaintData.mLowPrecisionPaintCount != 0) {
-    return 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, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort));
-    }
-
-    TILING_PRLOG_OBJ(("TILING 0x%p: Progressive update with old valid region %s\n", this, tmpstr.get()), oldValidRegion);
-
-    return mContentClient->mTiledBuffer.ProgressiveUpdate(mValidRegion, aInvalidRegion,
-                      oldValidRegion, &mPaintData, aCallback, aCallbackData);
-  }
-
-  // Otherwise do a non-progressive paint
-
-  mValidRegion = mVisibleRegion;
-  if (!mPaintData.mCriticalDisplayPort.IsEmpty()) {
-    mValidRegion.And(mValidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort));
+  if (!aFinish && !mPaintData.mPaintFinished) {
+    return;
   }
 
-  TILING_PRLOG_OBJ(("TILING 0x%p: Non-progressive paint invalid region %s\n", this, tmpstr.get()), aInvalidRegion);
-  TILING_PRLOG_OBJ(("TILING 0x%p: Non-progressive paint new valid region %s\n", this, tmpstr.get()), mValidRegion);
-
-  mContentClient->mTiledBuffer.SetFrameResolution(mPaintData.mResolution);
-  mContentClient->mTiledBuffer.PaintThebes(mValidRegion, aInvalidRegion, aCallback, aCallbackData);
-  return true;
-}
-
-bool
-ClientTiledThebesLayer::RenderLowPrecision(nsIntRegion& aInvalidRegion,
-                                           LayerManager::DrawThebesLayerCallback aCallback,
-                                           void* aCallbackData)
-{
-  // Render the low precision buffer, if the visible region is larger than the
-  // critical display port.
-  if (!nsIntRegion(LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort)).Contains(mVisibleRegion)) {
-    nsIntRegion oldValidRegion = mContentClient->mLowPrecisionTiledBuffer.GetValidRegion();
-    oldValidRegion.And(oldValidRegion, mVisibleRegion);
-
-    bool updatedBuffer = false;
-
-    // If the frame resolution or format have changed, invalidate the buffer
-    if (mContentClient->mLowPrecisionTiledBuffer.GetFrameResolution() != mPaintData.mResolution ||
-        mContentClient->mLowPrecisionTiledBuffer.HasFormatChanged()) {
-      if (!mLowPrecisionValidRegion.IsEmpty()) {
-        updatedBuffer = true;
-      }
-      oldValidRegion.SetEmpty();
-      mLowPrecisionValidRegion.SetEmpty();
-      mContentClient->mLowPrecisionTiledBuffer.SetFrameResolution(mPaintData.mResolution);
-      aInvalidRegion = mVisibleRegion;
-    }
-
-    // Invalidate previously valid content that is no longer visible
-    if (mPaintData.mLowPrecisionPaintCount == 1) {
-      mLowPrecisionValidRegion.And(mLowPrecisionValidRegion, mVisibleRegion);
-    }
-    mPaintData.mLowPrecisionPaintCount++;
-
-    // Remove the valid high-precision region from the invalid low-precision
-    // region. We don't want to spend time drawing things twice.
-    aInvalidRegion.Sub(aInvalidRegion, mValidRegion);
-
-    TILING_PRLOG_OBJ(("TILING 0x%p: Progressive paint: low-precision invalid region is %s\n", this, tmpstr.get()), aInvalidRegion);
-    TILING_PRLOG_OBJ(("TILING 0x%p: Progressive paint: low-precision new valid region is %s\n", this, tmpstr.get()), mLowPrecisionValidRegion);
-
-    if (!aInvalidRegion.IsEmpty()) {
-      updatedBuffer = mContentClient->mLowPrecisionTiledBuffer.ProgressiveUpdate(
-                            mLowPrecisionValidRegion, aInvalidRegion, oldValidRegion,
-                            &mPaintData, aCallback, aCallbackData);
-    }
-    return updatedBuffer;
-  }
-  if (!mLowPrecisionValidRegion.IsEmpty()) {
-    TILING_PRLOG(("TILING 0x%p: Clearing low-precision buffer\n", this));
-    // Clear the low precision tiled buffer.
-    mLowPrecisionValidRegion.SetEmpty();
-    mContentClient->mLowPrecisionTiledBuffer.ResetPaintedAndValidState();
-    // Return true here so we send a Painted callback after clearing the valid
-    // region of the low precision buffer. This allows the shadow buffer's valid
-    // region to be updated and the associated resources to be freed.
-    return true;
-  }
-  return false;
-}
-
-void
-ClientTiledThebesLayer::EndPaint()
-{
   mPaintData.mLastScrollOffset = mPaintData.mScrollOffset;
   mPaintData.mPaintFinished = true;
   mPaintData.mFirstPaint = false;
   TILING_PRLOG(("TILING 0x%p: Paint finished\n", this));
 }
 
 void
 ClientTiledThebesLayer::RenderLayer()
@@ -322,118 +216,202 @@ ClientTiledThebesLayer::RenderLayer()
   }
 
   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);
-  TILING_PRLOG_OBJ(("TILING 0x%p: Initial low-precision valid region %s\n", this, tmpstr.get()), mLowPrecisionValidRegion);
 
-  nsIntRegion invalidRegion;
-  invalidRegion.Sub(mVisibleRegion, mValidRegion);
+  nsIntRegion invalidRegion = mVisibleRegion;
+  invalidRegion.Sub(invalidRegion, mValidRegion);
   if (invalidRegion.IsEmpty()) {
-    EndPaint();
+    EndPaint(true);
     return;
   }
 
-  if (!ClientManager()->IsRepeatTransaction()) {
-    // Only paint the mask layer on the first transaction.
-    if (GetMaskLayer()) {
-      ToClientLayer(GetMaskLayer())->RenderLayer();
-    }
-
-    // In some cases we can take a fast path and just be done with it.
-    if (UseFastPath()) {
-      TILING_PRLOG(("TILING 0x%p: Taking fast-path\n"));
-      mValidRegion = mVisibleRegion;
-      mContentClient->mTiledBuffer.PaintThebes(mValidRegion, invalidRegion, callback, data);
-      ClientManager()->Hold(this);
-      mContentClient->UseTiledLayerBuffer(TiledContentClient::TILED_BUFFER);
-      return;
-    }
-
-    // For more complex cases we need to calculate a bunch of metrics before we
-    // can do the paint.
-    BeginPaint();
-    if (mPaintData.mPaintFinished) {
-      return;
-    }
-
-    // Make sure that tiles that fall outside of the visible region or outside of the
-    // critical displayport are discarded on the first update. Also make sure that we
-    // only draw stuff inside the critical displayport on the first update.
-    mValidRegion.And(mValidRegion, mVisibleRegion);
-    if (!mPaintData.mCriticalDisplayPort.IsEmpty()) {
-      mValidRegion.And(mValidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort));
-      invalidRegion.And(invalidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort));
-    }
-
-    TILING_PRLOG_OBJ(("TILING 0x%p: First-transaction valid region %s\n", this, tmpstr.get()), mValidRegion);
-    TILING_PRLOG_OBJ(("TILING 0x%p: First-transaction invalid region %s\n", this, tmpstr.get()), invalidRegion);
-  } else {
-    if (!mPaintData.mCriticalDisplayPort.IsEmpty()) {
-      invalidRegion.And(invalidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort));
-    }
-    TILING_PRLOG_OBJ(("TILING 0x%p: Repeat-transaction invalid region %s\n", this, tmpstr.get()), invalidRegion);
+  // Only paint the mask layer on the first transaction.
+  if (GetMaskLayer() && !ClientManager()->IsRepeatTransaction()) {
+    ToClientLayer(GetMaskLayer())->RenderLayer();
   }
 
-  bool updatedHighPrecision = RenderHighPrecision(invalidRegion, callback, data);
-  if (updatedHighPrecision) {
+  bool isFixed = GetIsFixedPosition() || GetParent()->GetIsFixedPosition();
+
+  // Fast path for no progressive updates, no low-precision updates and no
+  // critical display-port set, or no display-port set, or this is a fixed
+  // position layer/contained in a fixed position layer
+  const FrameMetrics& parentMetrics = GetParent()->GetFrameMetrics();
+  if ((!gfxPrefs::UseProgressiveTilePainting() &&
+       !gfxPrefs::UseLowPrecisionBuffer() &&
+       parentMetrics.mCriticalDisplayPort.IsEmpty()) ||
+       parentMetrics.mDisplayPort.IsEmpty() ||
+       isFixed) {
+    mValidRegion = mVisibleRegion;
+
+    NS_ASSERTION(!ClientManager()->IsRepeatTransaction(), "Didn't paint our mask layer");
+
+    mContentClient->mTiledBuffer.PaintThebes(mValidRegion, invalidRegion,
+                                             callback, data);
+
     ClientManager()->Hold(this);
     mContentClient->UseTiledLayerBuffer(TiledContentClient::TILED_BUFFER);
 
-    if (!mPaintData.mPaintFinished) {
-      // There is still more high-res stuff to paint, so we're not
-      // done yet. A subsequent transaction will take care of this.
-      ClientManager()->SetRepeatTransaction();
+    return;
+  }
+
+  // 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, 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, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort));
+    if (invalidRegion.IsEmpty() && lowPrecisionInvalidRegion.IsEmpty()) {
+      EndPaint(true);
       return;
     }
   }
 
-  nsIntRegion lowPrecisionInvalidRegion;
-  if (gfxPrefs::UseLowPrecisionBuffer()) {
-    // Calculate the invalid region for the low precision buffer. Make sure
-    // to remove the valid high-precision area so we don't double-paint it.
-    lowPrecisionInvalidRegion.Sub(mVisibleRegion, mLowPrecisionValidRegion);
-    lowPrecisionInvalidRegion.Sub(lowPrecisionInvalidRegion, mValidRegion);
-  }
-  TILING_PRLOG_OBJ(("TILING 0x%p: Low-precision invalid region %s\n", this, tmpstr.get()), lowPrecisionInvalidRegion);
+  TILING_PRLOG_OBJ(("TILING 0x%p: Invalid region %s\n", this, tmpstr.get()), invalidRegion);
 
-  // If there is nothing to draw in low-precision, then we're done.
-  if (lowPrecisionInvalidRegion.IsEmpty()) {
-    EndPaint();
-    return;
-  }
+  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, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort));
+      }
+
+      TILING_PRLOG_OBJ(("TILING 0x%p: Progressive update with old valid region %s\n", this, tmpstr.get()), oldValidRegion);
 
-  if (updatedHighPrecision) {
-    // If there are low precision updates, but we just did some high-precision
-    // updates, then mark the paint as unfinished and request a repeat transaction.
-    // This is so that we don't perform low-precision updates in the same transaction
-    // as high-precision updates.
-    TILING_PRLOG(("TILING 0x%p: Scheduling repeat transaction for low-precision painting\n", this));
-    ClientManager()->SetRepeatTransaction();
-    mPaintData.mLowPrecisionPaintCount = 1;
-    mPaintData.mPaintFinished = false;
-    return;
-  }
+      updatedBuffer =
+        mContentClient->mTiledBuffer.ProgressiveUpdate(mValidRegion, invalidRegion,
+                                                       oldValidRegion, &mPaintData,
+                                                       callback, data);
+    } else {
+      updatedBuffer = true;
+      mValidRegion = mVisibleRegion;
+      if (!mPaintData.mCriticalDisplayPort.IsEmpty()) {
+        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);
 
-  bool updatedLowPrecision = RenderLowPrecision(lowPrecisionInvalidRegion, callback, data);
-  if (updatedLowPrecision) {
-    ClientManager()->Hold(this);
-    mContentClient->UseTiledLayerBuffer(TiledContentClient::LOW_PRECISION_TILED_BUFFER);
+      mContentClient->mTiledBuffer.SetFrameResolution(mPaintData.mResolution);
+      mContentClient->mTiledBuffer.PaintThebes(mValidRegion, invalidRegion,
+                                               callback, data);
+    }
+
+    if (updatedBuffer) {
+      ClientManager()->Hold(this);
+      mContentClient->UseTiledLayerBuffer(TiledContentClient::TILED_BUFFER);
 
-    if (!mPaintData.mPaintFinished) {
-      // There is still more low-res stuff to paint, so we're not
-      // done yet. A subsequent transaction will take care of this.
-      ClientManager()->SetRepeatTransaction();
+      // If there are low precision updates, mark the paint as unfinished and
+      // request a repeat transaction.
+      if (!lowPrecisionInvalidRegion.IsEmpty() && mPaintData.mPaintFinished) {
+        ClientManager()->SetRepeatTransaction();
+        mPaintData.mLowPrecisionPaintCount = 1;
+        mPaintData.mPaintFinished = false;
+      }
+
+      // Return so that low precision updates aren't performed in the same
+      // transaction as high-precision updates.
+      EndPaint(false);
       return;
     }
   }
 
-  // If we get here, we've done all the high- and low-precision
-  // paints we wanted to do, so we can finish the paint and chill.
-  EndPaint();
+  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(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()) {
+        updatedLowPrecision = true;
+      }
+      oldValidRegion.SetEmpty();
+      mLowPrecisionValidRegion.SetEmpty();
+      mContentClient->mLowPrecisionTiledBuffer.SetFrameResolution(mPaintData.mResolution);
+      lowPrecisionInvalidRegion = mVisibleRegion;
+    }
+
+    // Invalidate previously valid content that is no longer visible
+    if (mPaintData.mLowPrecisionPaintCount == 1) {
+      mLowPrecisionValidRegion.And(mLowPrecisionValidRegion, mVisibleRegion);
+    }
+    mPaintData.mLowPrecisionPaintCount++;
+
+    // Remove the valid high-precision region from the invalid low-precision
+    // region. We don't want to spend time drawing things twice.
+    lowPrecisionInvalidRegion.Sub(lowPrecisionInvalidRegion, mValidRegion);
+
+    if (!lowPrecisionInvalidRegion.IsEmpty()) {
+      updatedLowPrecision = mContentClient->mLowPrecisionTiledBuffer
+                              .ProgressiveUpdate(mLowPrecisionValidRegion,
+                                                 lowPrecisionInvalidRegion,
+                                                 oldValidRegion, &mPaintData,
+                                                 callback, data);
+    }
+  } else if (!mLowPrecisionValidRegion.IsEmpty()) {
+    // Clear the low precision tiled buffer
+    updatedLowPrecision = true;
+    mLowPrecisionValidRegion.SetEmpty();
+    mContentClient->mLowPrecisionTiledBuffer.ResetPaintedAndValidState();
+  }
+
+  // We send a Painted callback if we clear the valid region of the low
+  // precision buffer, so that the shadow buffer's valid region can be updated
+  // and the associated resources can be freed.
+  if (updatedLowPrecision) {
+    ClientManager()->Hold(this);
+    mContentClient->UseTiledLayerBuffer(TiledContentClient::LOW_PRECISION_TILED_BUFFER);
+  }
+
+  EndPaint(false);
 }
 
 } // mozilla
 } // layers
--- a/gfx/layers/client/ClientTiledThebesLayer.h
+++ b/gfx/layers/client/ClientTiledThebesLayer.h
@@ -76,42 +76,21 @@ private:
 
   /**
    * For the initial PaintThebes of a transaction, calculates all the data
    * needed for that paint and any repeated transactions.
    */
   void BeginPaint();
 
   /**
-   * Determine if we can use a fast path to just do a single high-precision,
-   * non-progressive paint.
-   */
-  bool UseFastPath();
-
-  /**
-   * Helper function to do the high-precision paint.
-   * This function returns true if it updated the paint buffer.
+   * When a paint ends, updates any data necessary to persist until the next
+   * paint. If aFinish is true, this will cause the paint to be marked as
+   * finished.
    */
-  bool RenderHighPrecision(nsIntRegion& aInvalidRegion,
-                           LayerManager::DrawThebesLayerCallback aCallback,
-                           void* aCallbackData);
-
-  /**
-   * Helper function to do the low-precision paint.
-   * This function returns true if it updated the paint buffer.
-   */
-  bool RenderLowPrecision(nsIntRegion& aInvalidRegion,
-                          LayerManager::DrawThebesLayerCallback aCallback,
-                          void* aCallbackData);
-
-  /**
-   * This causes the paint to be marked as finished, and updates any data
-   * necessary to persist until the next paint.
-   */
-  void EndPaint();
+  void EndPaint(bool aFinish);
 
   RefPtr<TiledContentClient> mContentClient;
   nsIntRegion mLowPrecisionValidRegion;
   BasicTiledLayerPaintData mPaintData;
 };
 
 } // layers
 } // mozilla
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -1059,20 +1059,23 @@ ClientTiledLayerBuffer::ComputeProgressi
   if (!aRegionToPaint.Contains(aInvalidRegion)) {
     // The region needed to paint is larger then our progressive chunk size
     // therefore update what we want to paint and ask for a new paint transaction.
 
     // If we need to draw more than one tile to maintain coherency, make
     // sure it happens in the same transaction by requesting this work be
     // repeated immediately.
     // If this is unnecessary, the remaining work will be done tile-by-tile in
-    // subsequent transactions. The caller code is responsible for scheduling
-    // the subsequent transactions as long as we don't set the mPaintFinished
-    // flag to true.
-    return (!drawingLowPrecision && paintInSingleTransaction);
+    // subsequent transactions.
+    if (!drawingLowPrecision && paintInSingleTransaction) {
+      return true;
+    }
+
+    mManager->SetRepeatTransaction();
+    return false;
   }
 
   // We're not repeating painting and we've not requested a repeat transaction,
   // so the paint is finished. If there's still a separate low precision
   // paint to do, it will get marked as unfinished later.
   aPaintData->mPaintFinished = true;
   return false;
 }
--- a/js/src/gc/Barrier.cpp
+++ b/js/src/gc/Barrier.cpp
@@ -11,16 +11,17 @@
 
 #include "gc/Zone.h"
 
 namespace js {
 
 void
 ValueReadBarrier(const Value &value)
 {
+    JS_ASSERT(!CurrentThreadIsIonCompiling());
     if (value.isObject())
         JSObject::readBarrier(&value.toObject());
     else if (value.isString())
         JSString::readBarrier(value.toString());
     else
         JS_ASSERT(!value.isMarkable());
 }
 
@@ -50,16 +51,22 @@ HeapSlot::preconditionForWriteBarrierPos
          : static_cast<HeapSlot *>(obj->getDenseElements() + slot)->get() == target;
 }
 
 bool
 RuntimeFromMainThreadIsHeapMajorCollecting(JS::shadow::Zone *shadowZone)
 {
     return shadowZone->runtimeFromMainThread()->isHeapMajorCollecting();
 }
+
+bool
+CurrentThreadIsIonCompiling()
+{
+    return TlsPerThreadData.get()->ionCompiling;
+}
 #endif // DEBUG
 
 bool
 StringIsPermanentAtom(JSString *str)
 {
     return str->isPermanentAtom();
 }
 
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -156,16 +156,21 @@ class JSLinearString;
 
 namespace js {
 
 class PropertyName;
 
 #ifdef DEBUG
 bool
 RuntimeFromMainThreadIsHeapMajorCollecting(JS::shadow::Zone *shadowZone);
+
+// Barriers can't be triggered during backend Ion compilation, which may run on
+// a helper thread.
+bool
+CurrentThreadIsIonCompiling();
 #endif
 
 bool
 StringIsPermanentAtom(JSString *str);
 
 namespace gc {
 
 template <typename T>
@@ -193,16 +198,17 @@ class BarrieredCell : public gc::Cell
     MOZ_ALWAYS_INLINE JS::shadow::Zone *shadowZone() const { return JS::shadow::Zone::asShadowZone(zone()); }
     MOZ_ALWAYS_INLINE JS::Zone *zoneFromAnyThread() const { return tenuredZoneFromAnyThread(); }
     MOZ_ALWAYS_INLINE JS::shadow::Zone *shadowZoneFromAnyThread() const {
         return JS::shadow::Zone::asShadowZone(zoneFromAnyThread());
     }
 
     static MOZ_ALWAYS_INLINE void readBarrier(T *thing) {
 #ifdef JSGC_INCREMENTAL
+        JS_ASSERT(!CurrentThreadIsIonCompiling());
         JS::shadow::Zone *shadowZone = thing->shadowZoneFromAnyThread();
         if (shadowZone->needsBarrier()) {
             MOZ_ASSERT(!RuntimeFromMainThreadIsHeapMajorCollecting(shadowZone));
             T *tmp = thing;
             js::gc::MarkUnbarriered<T>(shadowZone->barrierTracer(), &tmp, "read barrier");
             JS_ASSERT(tmp == thing);
         }
 #endif
@@ -215,16 +221,17 @@ class BarrieredCell : public gc::Cell
         return false;
 #endif
     }
 
     static MOZ_ALWAYS_INLINE bool isNullLike(T *thing) { return !thing; }
 
     static MOZ_ALWAYS_INLINE void writeBarrierPre(T *thing) {
 #ifdef JSGC_INCREMENTAL
+        JS_ASSERT(!CurrentThreadIsIonCompiling());
         if (isNullLike(thing) || !thing->shadowRuntimeFromAnyThread()->needsBarrier())
             return;
 
         JS::shadow::Zone *shadowZone = thing->shadowZoneFromAnyThread();
         if (shadowZone->needsBarrier()) {
             MOZ_ASSERT(!RuntimeFromMainThreadIsHeapMajorCollecting(shadowZone));
             T *tmp = thing;
             js::gc::MarkUnbarriered<T>(shadowZone->barrierTracer(), &tmp, "write barrier");
@@ -312,59 +319,64 @@ struct InternalGCMethods<Value>
     static JS::shadow::Runtime *shadowRuntimeFromMainThread(const Value &v) {
         return reinterpret_cast<JS::shadow::Runtime*>(runtimeFromMainThread(v));
     }
 
     static bool isMarkable(Value v) { return v.isMarkable(); }
 
     static void preBarrier(Value v) {
 #ifdef JSGC_INCREMENTAL
+        JS_ASSERT(!CurrentThreadIsIonCompiling());
         if (v.isMarkable() && shadowRuntimeFromAnyThread(v)->needsBarrier())
             preBarrier(ZoneOfValueFromAnyThread(v), v);
 #endif
     }
 
     static void preBarrier(Zone *zone, Value v) {
 #ifdef JSGC_INCREMENTAL
+        JS_ASSERT(!CurrentThreadIsIonCompiling());
         if (v.isString() && StringIsPermanentAtom(v.toString()))
             return;
         JS::shadow::Zone *shadowZone = JS::shadow::Zone::asShadowZone(zone);
         if (shadowZone->needsBarrier()) {
             JS_ASSERT_IF(v.isMarkable(), shadowRuntimeFromMainThread(v)->needsBarrier());
             Value tmp(v);
             js::gc::MarkValueUnbarriered(shadowZone->barrierTracer(), &tmp, "write barrier");
             JS_ASSERT(tmp == v);
         }
 #endif
     }
 
     static void postBarrier(Value *vp) {
 #ifdef JSGC_GENERATIONAL
+        JS_ASSERT(!CurrentThreadIsIonCompiling());
         if (vp->isObject()) {
             gc::StoreBuffer *sb = reinterpret_cast<gc::Cell *>(&vp->toObject())->storeBuffer();
             if (sb)
                 sb->putValueFromAnyThread(vp);
         }
 #endif
     }
 
     static void postBarrierRelocate(Value *vp) {
 #ifdef JSGC_GENERATIONAL
+        JS_ASSERT(!CurrentThreadIsIonCompiling());
         if (vp->isObject()) {
             gc::StoreBuffer *sb = reinterpret_cast<gc::Cell *>(&vp->toObject())->storeBuffer();
             if (sb)
                 sb->putRelocatableValueFromAnyThread(vp);
         }
 #endif
     }
 
     static void postBarrierRemove(Value *vp) {
 #ifdef JSGC_GENERATIONAL
         JS_ASSERT(vp);
         JS_ASSERT(vp->isMarkable());
+        JS_ASSERT(!CurrentThreadIsIonCompiling());
         JSRuntime *rt = static_cast<js::gc::Cell *>(vp->toGCThing())->runtimeFromAnyThread();
         JS::shadow::Runtime *shadowRuntime = JS::shadow::Runtime::asShadowRuntime(rt);
         shadowRuntime->gcStoreBufferPtr()->removeRelocatableValueFromAnyThread(vp);
 #endif
     }
 
     static void readBarrier(const Value &v) { ValueReadBarrier(v); }
 };
--- a/js/src/jit-test/tests/asm.js/testFFI.js
+++ b/js/src/jit-test/tests/asm.js/testFFI.js
@@ -91,8 +91,18 @@ assertEq(f(4,5), 10);
 
 var recurse = function(i,j) { if (i == 0) throw j; f(i-1,j) }
 var f = asmLink(asmCompile('glob', 'imp', USE_ASM + 'var ffi=imp.ffi; function g(i,j,k) { i=i|0;j=+j;k=k|0; if (!(k|0)) ffi(i|0,j)|0; else g(i, j+1.0, (k-1)|0) } function f(i,j) { i=i|0;j=+j; g(i,j,4) } return f'), null, {ffi:recurse});
 assertThrowsValue(function() { f(0,2.4) }, 2.4+4);
 assertThrowsValue(function() { f(1,2.4) }, 2.4+8);
 assertThrowsValue(function() { f(8,2.4) }, 2.4+36);
 
 assertEq(asmLink(asmCompile('glob', 'imp', USE_ASM + 'var identity=imp.identity; function g(x) { x=+x; return +identity(x) } return g'), null, imp)(13.37), 13.37);
+
+setJitCompilerOption("ion.usecount.trigger", 20);
+function ffiInt(a,b,c,d,e,f,g,h,i,j) { return j+1 }
+var f = asmLink(asmCompile('glob', 'imp', USE_ASM + 'var ffi=imp.ffi; function f(i) { i=i|0; return ffi(i|0,(i+1)|0,(i+2)|0,(i+3)|0,(i+4)|0,(i+5)|0,(i+6)|0,(i+7)|0,(i+8)|0,(i+9)|0)|0 } return f'), null, {ffi:ffiInt});
+for (var i = 0; i < 40; i++)
+    assertEq(f(i), i+10);
+function ffiDouble(a,b,c,d,e,f,g,h,i,j) { return j+1 }
+var f = asmLink(asmCompile('glob', 'imp', USE_ASM + 'var ffi=imp.ffi; function f(i) { i=+i; return +ffi(i,i+1.0,i+2.0,i+3.0,i+4.0,i+5.0,i+6.0,i+7.0,i+8.0,i+9.0) } return f'), null, {ffi:ffiDouble});
+for (var i = 0; i < 40; i++)
+    assertEq(f(i), i+10);
--- a/js/src/jit/AsmJS.cpp
+++ b/js/src/jit/AsmJS.cpp
@@ -6622,20 +6622,17 @@ GenerateFFIIonExit(ModuleCompiler &m, co
     masm.storePtr(ImmWord(uintptr_t(argc)), Address(StackPointer, argOffset));
     argOffset += sizeof(size_t);
 
     // 4. |this| value
     masm.storeValue(UndefinedValue(), Address(StackPointer, argOffset));
     argOffset += sizeof(Value);
 
     // 5. Fill the arguments
-    unsigned offsetToCallerStackArgs = masm.framePushed();
-#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
-    offsetToCallerStackArgs += NativeFrameSize;
-#endif
+    unsigned offsetToCallerStackArgs = masm.framePushed() + NativeFrameSize;
     FillArgumentArray(m, exit.sig().args(), argOffset, offsetToCallerStackArgs, scratch);
     argOffset += exit.sig().args().length() * sizeof(Value);
     JS_ASSERT(argOffset == offsetToArgs + argBytes);
 
     // Get the pointer to the ion code
     Label done, oolConvert;
     Label *maybeDebugBreakpoint = nullptr;
 
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -5133,17 +5133,17 @@ bool
 CodeGenerator::emitConcat(LInstruction *lir, Register lhs, Register rhs, Register output)
 {
     OutOfLineCode *ool = oolCallVM(ConcatStringsInfo, lir, (ArgList(), lhs, rhs),
                                    StoreRegisterTo(output));
     if (!ool)
         return false;
 
     ExecutionMode mode = gen->info().executionMode();
-    JitCode *stringConcatStub = gen->compartment->jitCompartment()->stringConcatStub(mode);
+    JitCode *stringConcatStub = gen->compartment->jitCompartment()->stringConcatStubNoBarrier(mode);
     masm.call(stringConcatStub);
     masm.branchTestPtr(Assembler::Zero, output, output, ool->entry());
 
     masm.bind(ool->rejoin());
     return true;
 }
 
 bool
@@ -6800,16 +6800,20 @@ CodeGenerator::link(JSContext *cx, types
 
     // Make sure we don't segv while filling in the code, to avoid deadlocking
     // inside the signal handler.
     cx->runtime()->jitRuntime()->ensureIonCodeAccessible(cx->runtime());
 
     // Implicit interrupts are used only for sequential code. In parallel mode
     // use the normal executable allocator so that we cannot segv during
     // execution off the main thread.
+    //
+    // Also, note that creating the code here during an incremental GC will
+    // trace the code and mark all GC things it refers to. This captures any
+    // read barriers which were skipped while compiling the script off thread.
     Linker linker(masm);
     AutoFlushICache afc("IonLink");
     JitCode *code = (executionMode == SequentialExecution)
                     ? linker.newCodeForIonScript(cx)
                     : linker.newCode<CanGC>(cx, JSC::ION_CODE);
     if (!code) {
         // Use js_free instead of IonScript::Destroy: the cache list and
         // backedge list are still uninitialized.
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -491,24 +491,24 @@ JitCompartment::initialize(JSContext *cx
 
     return true;
 }
 
 bool
 JitCompartment::ensureIonStubsExist(JSContext *cx)
 {
     if (!stringConcatStub_) {
-        stringConcatStub_.set(generateStringConcatStub(cx, SequentialExecution));
+        stringConcatStub_ = generateStringConcatStub(cx, SequentialExecution);
         if (!stringConcatStub_)
             return false;
     }
 
 #ifdef JS_THREADSAFE
     if (!parallelStringConcatStub_) {
-        parallelStringConcatStub_.set(generateStringConcatStub(cx, ParallelExecution));
+        parallelStringConcatStub_ = generateStringConcatStub(cx, ParallelExecution);
         if (!parallelStringConcatStub_)
             return false;
     }
 #endif
 
     return true;
 }
 
@@ -586,23 +586,16 @@ JitRuntime::Mark(JSTracer *trc)
         JitCode *code = i.get<JitCode>();
         MarkJitCodeRoot(trc, &code, "wrapper");
     }
 }
 
 void
 JitCompartment::mark(JSTracer *trc, JSCompartment *compartment)
 {
-    // Cancel any active or pending off thread compilations. Note that the
-    // MIR graph does not hold any nursery pointers, so there's no need to
-    // do this for minor GCs.
-    JS_ASSERT(!trc->runtime()->isHeapMinorCollecting());
-    CancelOffThreadIonCompile(compartment, nullptr);
-    FinishAllOffThreadCompilations(compartment);
-
     // Free temporary OSR buffer.
     trc->runtime()->jitRuntime()->freeOsrTempData();
 
     // Mark scripts with parallel IonScripts if we should preserve them.
     if (activeParallelEntryScripts_) {
         for (ScriptSet::Enum e(*activeParallelEntryScripts_); !e.empty(); e.popFront()) {
             JSScript *script = e.front();
 
@@ -626,34 +619,41 @@ JitCompartment::mark(JSTracer *trc, JSCo
                 MarkScript(trc, const_cast<PreBarrieredScript *>(&e.front()), "par-script");
                 MOZ_ASSERT(script == e.front());
             }
         }
     }
 }
 
 void
-JitCompartment::sweep(FreeOp *fop)
+JitCompartment::sweep(FreeOp *fop, JSCompartment *compartment)
 {
+    // Cancel any active or pending off thread compilations. Note that the
+    // MIR graph does not hold any nursery pointers, so there's no need to
+    // do this for minor GCs.
+    JS_ASSERT(!fop->runtime()->isHeapMinorCollecting());
+    CancelOffThreadIonCompile(compartment, nullptr);
+    FinishAllOffThreadCompilations(compartment);
+
     stubCodes_->sweep(fop);
 
     // If the sweep removed the ICCall_Fallback stub, nullptr the baselineCallReturnAddr_ field.
     if (!stubCodes_->lookup(static_cast<uint32_t>(ICStub::Call_Fallback)))
         baselineCallReturnAddr_ = nullptr;
     // Similarly for the ICGetProp_Fallback stub.
     if (!stubCodes_->lookup(static_cast<uint32_t>(ICStub::GetProp_Fallback)))
         baselineGetPropReturnAddr_ = nullptr;
     if (!stubCodes_->lookup(static_cast<uint32_t>(ICStub::SetProp_Fallback)))
         baselineSetPropReturnAddr_ = nullptr;
 
-    if (stringConcatStub_ && !IsJitCodeMarked(stringConcatStub_.unsafeGet()))
-        stringConcatStub_.set(nullptr);
-
-    if (parallelStringConcatStub_ && !IsJitCodeMarked(parallelStringConcatStub_.unsafeGet()))
-        parallelStringConcatStub_.set(nullptr);
+    if (stringConcatStub_ && !IsJitCodeMarked(&stringConcatStub_))
+        stringConcatStub_ = nullptr;
+
+    if (parallelStringConcatStub_ && !IsJitCodeMarked(&parallelStringConcatStub_))
+        parallelStringConcatStub_ = nullptr;
 
     if (activeParallelEntryScripts_) {
         for (ScriptSet::Enum e(*activeParallelEntryScripts_); !e.empty(); e.popFront()) {
             JSScript *script = e.front();
             if (!IsScriptMarked(&script))
                 e.removeFront();
             else
                 MOZ_ASSERT(script == e.front());
@@ -1668,16 +1668,19 @@ GenerateCode(MIRGenerator *mir, LIRGraph
     }
 
     return codegen;
 }
 
 CodeGenerator *
 CompileBackEnd(MIRGenerator *mir)
 {
+    // Everything in CompileBackEnd can potentially run on a helper thread.
+    AutoEnterIonCompilation enter;
+
     if (!OptimizeMIR(mir))
         return nullptr;
 
     LIRGraph *lir = GenerateLIR(mir);
     if (!lir)
         return nullptr;
 
     return GenerateCode(mir, lir);
@@ -1750,24 +1753,22 @@ AttachFinishedCompilations(JSContext *cx
 
 static const size_t BUILDER_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12;
 
 static inline bool
 OffThreadCompilationAvailable(JSContext *cx)
 {
 #ifdef JS_THREADSAFE
     // Even if off thread compilation is enabled, compilation must still occur
-    // on the main thread in some cases. Do not compile off thread during an
-    // incremental GC, as this may trip incremental read barriers.
+    // on the main thread in some cases.
     //
     // Require cpuCount > 1 so that Ion compilation jobs and main-thread
     // execution are not competing for the same resources.
     return cx->runtime()->canUseParallelIonCompilation()
-        && HelperThreadState().cpuCount > 1
-        && cx->runtime()->gc.incrementalState == gc::NO_INCREMENTAL;
+        && HelperThreadState().cpuCount > 1;
 #else
     return false;
 #endif
 }
 
 static void
 TrackAllProperties(JSContext *cx, JSObject *obj)
 {
@@ -1984,33 +1985,17 @@ CheckScriptSize(JSContext *cx, JSScript*
         return Method_CantCompile;
     }
 
     uint32_t numLocalsAndArgs = NumLocalsAndArgs(script);
 
     if (script->length() > MAX_MAIN_THREAD_SCRIPT_SIZE ||
         numLocalsAndArgs > MAX_MAIN_THREAD_LOCALS_AND_ARGS)
     {
-#ifdef JS_THREADSAFE
-        size_t cpuCount = HelperThreadState().cpuCount;
-#else
-        size_t cpuCount = 1;
-#endif
-        if (cx->runtime()->canUseParallelIonCompilation() && cpuCount > 1) {
-            // Even if off thread compilation is enabled, there are cases where
-            // compilation must still occur on the main thread. Don't compile
-            // in these cases, but do not forbid compilation so that the script
-            // may be compiled later.
-            if (!OffThreadCompilationAvailable(cx)) {
-                IonSpew(IonSpew_Abort,
-                        "Script too large for main thread, skipping (%u bytes) (%u locals/args)",
-                        script->length(), numLocalsAndArgs);
-                return Method_Skipped;
-            }
-        } else {
+        if (!OffThreadCompilationAvailable(cx)) {
             IonSpew(IonSpew_Abort, "Script too large (%u bytes) (%u locals/args)",
                     script->length(), numLocalsAndArgs);
             return Method_CantCompile;
         }
     }
 
     return Method_Compiled;
 }
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -1955,16 +1955,19 @@ GenerateSetSlot(JSContext *cx, MacroAsse
                        ImmGCPtr(type), &failures);
 
         if (checkTypeset) {
             TypedOrValueRegister valReg = value.reg();
             types::HeapTypeSet *propTypes = type->maybeGetProperty(shape->propid());
             JS_ASSERT(propTypes);
             JS_ASSERT(!propTypes->unknown());
 
+            // guardTypeSet can read from type sets without triggering read barriers.
+            types::TypeSet::readBarrier(propTypes);
+
             Register scratchReg = object;
             masm.push(scratchReg);
 
             masm.guardTypeSet(valReg, propTypes, BarrierKind::TypeSet, scratchReg, &barrierFailure);
             masm.pop(object);
         }
     }
 
@@ -2510,16 +2513,19 @@ GenerateAddSlot(JSContext *cx, MacroAsse
     // if a type barrier is required.
     if (checkTypeset) {
         TypedOrValueRegister valReg = value.reg();
         types::TypeObject *type = obj->type();
         types::HeapTypeSet *propTypes = type->maybeGetProperty(obj->lastProperty()->propid());
         JS_ASSERT(propTypes);
         JS_ASSERT(!propTypes->unknown());
 
+        // guardTypeSet can read from type sets without triggering read barriers.
+        types::TypeSet::readBarrier(propTypes);
+
         Register scratchReg = object;
         masm.guardTypeSet(valReg, propTypes, BarrierKind::TypeSet, scratchReg, &failuresPopObject);
         masm.loadPtr(Address(StackPointer, 0), object);
     }
 
     JSObject *proto = obj->getProto();
     Register protoReg = object;
     while (proto) {
--- a/js/src/jit/IonMacroAssembler.cpp
+++ b/js/src/jit/IonMacroAssembler.cpp
@@ -49,24 +49,24 @@ class TypeWrapper {
             return t == t_ || t_ == types::Type::DoubleType();
         return t == t_;
     }
     inline unsigned getObjectCount() const {
         if (t_.isAnyObject() || t_.isUnknown() || !t_.isObject())
             return 0;
         return 1;
     }
-    inline JSObject *getSingleObject(unsigned) const {
+    inline JSObject *getSingleObjectNoBarrier(unsigned) const {
         if (t_.isSingleObject())
-            return t_.singleObject();
+            return t_.singleObjectNoBarrier();
         return nullptr;
     }
-    inline types::TypeObject *getTypeObject(unsigned) const {
+    inline types::TypeObject *getTypeObjectNoBarrier(unsigned) const {
         if (t_.isTypeObject())
-            return t_.typeObject();
+            return t_.typeObjectNoBarrier();
         return nullptr;
     }
 };
 
 } /* anonymous namespace */
 
 template <typename Source, typename TypeSet> void
 MacroAssembler::guardTypeSet(const Source &address, const TypeSet *types, BarrierKind kind,
@@ -139,32 +139,38 @@ template <typename TypeSet> void
 MacroAssembler::guardObjectType(Register obj, const TypeSet *types,
                                 Register scratch, Label *miss)
 {
     JS_ASSERT(!types->unknown());
     JS_ASSERT(!types->hasType(types::Type::AnyObjectType()));
     JS_ASSERT(types->getObjectCount());
     JS_ASSERT(scratch != InvalidReg);
 
+    // Note: this method elides read barriers on values read from type sets, as
+    // this may be called off the main thread during Ion compilation. This is
+    // safe to do as the final JitCode object will be allocated during the
+    // incremental GC (or the compilation canceled before we start sweeping),
+    // see CodeGenerator::link. Other callers should use TypeSet::readBarrier
+    // to trigger the barrier on the contents of type sets passed in here.
     Label matched;
 
     BranchGCPtr lastBranch;
     JS_ASSERT(!lastBranch.isInitialized());
     bool hasTypeObjects = false;
     unsigned count = types->getObjectCount();
     for (unsigned i = 0; i < count; i++) {
-        if (!types->getSingleObject(i)) {
-            hasTypeObjects = hasTypeObjects || types->getTypeObject(i);
+        if (!types->getSingleObjectNoBarrier(i)) {
+            hasTypeObjects = hasTypeObjects || types->getTypeObjectNoBarrier(i);
             continue;
         }
 
         if (lastBranch.isInitialized())
             lastBranch.emit(*this);
 
-        JSObject *object = types->getSingleObject(i);
+        JSObject *object = types->getSingleObjectNoBarrier(i);
         lastBranch = BranchGCPtr(Equal, obj, ImmGCPtr(object), &matched);
     }
 
     if (hasTypeObjects) {
         // We are possibly going to overwrite the obj register. So already
         // emit the branch, since branch depends on previous value of obj
         // register and there is definitely a branch following. So no need
         // to invert the condition.
@@ -172,23 +178,23 @@ MacroAssembler::guardObjectType(Register
             lastBranch.emit(*this);
         lastBranch = BranchGCPtr();
 
         // Note: Some platforms give the same register for obj and scratch.
         // Make sure when writing to scratch, the obj register isn't used anymore!
         loadPtr(Address(obj, JSObject::offsetOfType()), scratch);
 
         for (unsigned i = 0; i < count; i++) {
-            if (!types->getTypeObject(i))
+            if (!types->getTypeObjectNoBarrier(i))
                 continue;
 
             if (lastBranch.isInitialized())
                 lastBranch.emit(*this);
 
-            types::TypeObject *object = types->getTypeObject(i);
+            types::TypeObject *object = types->getTypeObjectNoBarrier(i);
             lastBranch = BranchGCPtr(Equal, scratch, ImmGCPtr(object), &matched);
         }
     }
 
     if (!lastBranch.isInitialized()) {
         jump(miss);
         return;
     }
--- a/js/src/jit/JitCompartment.h
+++ b/js/src/jit/JitCompartment.h
@@ -400,20 +400,21 @@ class JitCompartment
     // Keep track of offset into various baseline stubs' code at return
     // point from called script.
     void *baselineCallReturnAddr_;
     void *baselineGetPropReturnAddr_;
     void *baselineSetPropReturnAddr_;
 
     // Stub to concatenate two strings inline. Note that it can't be
     // stored in JitRuntime because masm.newGCString bakes in zone-specific
-    // pointers. This has to be a weak pointer to avoid keeping the whole
-    // compartment alive.
-    ReadBarrieredJitCode stringConcatStub_;
-    ReadBarrieredJitCode parallelStringConcatStub_;
+    // pointers. These are weak pointers, but are not declared as ReadBarriered
+    // since they are only read from during Ion compilation, which may occur
+    // off thread and whose barriers are captured during CodeGenerator::link.
+    JitCode *stringConcatStub_;
+    JitCode *parallelStringConcatStub_;
 
     // Set of JSScripts invoked by ForkJoin (i.e. the entry script). These
     // scripts are marked if their respective parallel IonScripts' age is less
     // than a certain amount. See IonScript::parallelAge_.
     typedef HashSet<PreBarrieredScript> ScriptSet;
     ScriptSet *activeParallelEntryScripts_;
 
     JitCode *generateStringConcatStub(JSContext *cx, ExecutionMode mode);
@@ -469,19 +470,19 @@ class JitCompartment
     ~JitCompartment();
 
     bool initialize(JSContext *cx);
 
     // Initialize code stubs only used by Ion, not Baseline.
     bool ensureIonStubsExist(JSContext *cx);
 
     void mark(JSTracer *trc, JSCompartment *compartment);
-    void sweep(FreeOp *fop);
+    void sweep(FreeOp *fop, JSCompartment *compartment);
 
-    JitCode *stringConcatStub(ExecutionMode mode) const {
+    JitCode *stringConcatStubNoBarrier(ExecutionMode mode) const {
         switch (mode) {
           case SequentialExecution: return stringConcatStub_;
           case ParallelExecution:   return parallelStringConcatStub_;
           default:                  MOZ_ASSUME_UNREACHABLE("No such execution mode");
         }
     }
 };
 
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -607,17 +607,17 @@ JSCompartment::sweep(FreeOp *fop, bool r
         if (selfHostingScriptSource &&
             IsObjectAboutToBeFinalized((JSObject **) selfHostingScriptSource.unsafeGet()))
         {
             selfHostingScriptSource.set(nullptr);
         }
 
 #ifdef JS_ION
         if (jitCompartment_)
-            jitCompartment_->sweep(fop);
+            jitCompartment_->sweep(fop, this);
 #endif
 
         /*
          * JIT code increments activeUseCount for any RegExpShared used by jit
          * code for the lifetime of the JIT script. Thus, we must perform
          * sweeping after clearing jit code.
          */
         regExps.sweep(rt);
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -609,16 +609,32 @@ TypeSet::print()
         for (unsigned i = 0; i < count; i++) {
             TypeObjectKey *object = getObject(i);
             if (object)
                 fprintf(stderr, " %s", TypeString(Type::ObjectType(object)));
         }
     }
 }
 
+/* static */ void
+TypeSet::readBarrier(const TypeSet *types)
+{
+    if (types->unknownObject())
+        return;
+
+    for (unsigned i = 0; i < types->getObjectCount(); i++) {
+        if (TypeObjectKey *object = types->getObject(i)) {
+            if (object->isSingleObject())
+                (void) object->asSingleObject();
+            else
+                (void) object->asTypeObject();
+        }
+    }
+}
+
 bool
 TypeSet::clone(LifoAlloc *alloc, TemporaryTypeSet *result) const
 {
     JS_ASSERT(result->empty());
 
     unsigned objectCount = baseObjectCount();
     unsigned capacity = (objectCount >= 2) ? HashSetCapacity(objectCount) : 0;
 
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -272,24 +272,26 @@ class Type
 
     /* Accessors for JSObject types */
 
     bool isSingleObject() const {
         return isObject() && !!(data & 1);
     }
 
     inline JSObject *singleObject() const;
+    inline JSObject *singleObjectNoBarrier() const;
 
     /* Accessors for TypeObject types */
 
     bool isTypeObject() const {
         return isObject() && !(data & 1);
     }
 
     inline TypeObject *typeObject() const;
+    inline TypeObject *typeObjectNoBarrier() const;
 
     bool operator == (Type o) const { return data == o.data; }
     bool operator != (Type o) const { return data != o.data; }
 
     static inline Type UndefinedType() { return Type(JSVAL_TYPE_UNDEFINED); }
     static inline Type NullType()      { return Type(JSVAL_TYPE_NULL); }
     static inline Type BooleanType()   { return Type(JSVAL_TYPE_BOOLEAN); }
     static inline Type Int32Type()     { return Type(JSVAL_TYPE_INT32); }
@@ -573,16 +575,18 @@ class TypeSet
      * Iterate through the objects in this set. getObjectCount overapproximates
      * in the hash case (see SET_ARRAY_SIZE in jsinferinlines.h), and getObject
      * may return nullptr.
      */
     inline unsigned getObjectCount() const;
     inline TypeObjectKey *getObject(unsigned i) const;
     inline JSObject *getSingleObject(unsigned i) const;
     inline TypeObject *getTypeObject(unsigned i) const;
+    inline JSObject *getSingleObjectNoBarrier(unsigned i) const;
+    inline TypeObject *getTypeObjectNoBarrier(unsigned i) const;
 
     /* The Class of an object in this set. */
     inline const Class *getObjectClass(unsigned i) const;
 
     bool canSetDefinite(unsigned slot) {
         // Note: the cast is required to work around an MSVC issue.
         return (slot + 1) <= (unsigned(TYPE_FLAG_DEFINITE_MASK) >> TYPE_FLAG_DEFINITE_SHIFT);
     }
@@ -612,16 +616,19 @@ class TypeSet
 
     // Clone a type set into an arbitrary allocator.
     TemporaryTypeSet *clone(LifoAlloc *alloc) const;
     bool clone(LifoAlloc *alloc, TemporaryTypeSet *result) const;
 
     // Create a new TemporaryTypeSet where undefined and/or null has been filtered out.
     TemporaryTypeSet *filter(LifoAlloc *alloc, bool filterUndefined, bool filterNull) const;
 
+    // Trigger a read barrier on all the contents of a type set.
+    static void readBarrier(const TypeSet *types);
+
   protected:
     uint32_t baseObjectCount() const {
         return (flags & TYPE_FLAG_OBJECT_COUNT_MASK) >> TYPE_FLAG_OBJECT_COUNT_SHIFT;
     }
     inline void setBaseObjectCount(uint32_t count);
 
     void clearObjects();
 };
@@ -1355,24 +1362,21 @@ struct TypeObjectKey
 
     bool isTypeObject() {
         return (uintptr_t(this) & 1) == 0;
     }
     bool isSingleObject() {
         return (uintptr_t(this) & 1) != 0;
     }
 
-    TypeObject *asTypeObject() {
-        JS_ASSERT(isTypeObject());
-        return (TypeObject *) this;
-    }
-    JSObject *asSingleObject() {
-        JS_ASSERT(isSingleObject());
-        return (JSObject *) (uintptr_t(this) & ~1);
-    }
+    inline TypeObject *asTypeObject();
+    inline JSObject *asSingleObject();
+
+    inline TypeObject *asTypeObjectNoBarrier();
+    inline JSObject *asSingleObjectNoBarrier();
 
     const Class *clasp();
     TaggedProto proto();
     bool hasTenuredProto();
     JSObject *singleton();
     TypeNewScript *newScript();
 
     bool unknownProperties();
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -71,16 +71,46 @@ RecompileInfo::shouldSweep(TypeZone &typ
     outputIndex = output->sweepIndex();
     return false;
 }
 
 /////////////////////////////////////////////////////////////////////
 // Types
 /////////////////////////////////////////////////////////////////////
 
+inline TypeObject *
+TypeObjectKey::asTypeObjectNoBarrier()
+{
+    JS_ASSERT(isTypeObject());
+    return (TypeObject *) this;
+}
+
+inline JSObject *
+TypeObjectKey::asSingleObjectNoBarrier()
+{
+    JS_ASSERT(isSingleObject());
+    return (JSObject *) (uintptr_t(this) & ~1);
+}
+
+inline TypeObject *
+TypeObjectKey::asTypeObject()
+{
+    TypeObject *res = asTypeObjectNoBarrier();
+    TypeObject::readBarrier(res);
+    return res;
+}
+
+inline JSObject *
+TypeObjectKey::asSingleObject()
+{
+    JSObject *res = asSingleObjectNoBarrier();
+    JSObject::readBarrier(res);
+    return res;
+}
+
 /* static */ inline Type
 Type::ObjectType(JSObject *obj)
 {
     if (obj->hasSingletonType())
         return Type(uintptr_t(obj) | 1);
     return Type(uintptr_t(obj->type()));
 }
 
@@ -978,37 +1008,41 @@ HashSetLookup(U **values, unsigned count
 
     return nullptr;
 }
 
 inline TypeObjectKey *
 Type::objectKey() const
 {
     JS_ASSERT(isObject());
-    if (isTypeObject())
-        TypeObject::readBarrier((TypeObject *) data);
-    else
-        JSObject::readBarrier((JSObject *) (data ^ 1));
     return (TypeObjectKey *) data;
 }
 
 inline JSObject *
 Type::singleObject() const
 {
-    JS_ASSERT(isSingleObject());
-    JSObject::readBarrier((JSObject *) (data ^ 1));
-    return (JSObject *) (data ^ 1);
+    return objectKey()->asSingleObject();
 }
 
 inline TypeObject *
 Type::typeObject() const
 {
-    JS_ASSERT(isTypeObject());
-    TypeObject::readBarrier((TypeObject *) data);
-    return (TypeObject *) data;
+    return objectKey()->asTypeObject();
+}
+
+inline JSObject *
+Type::singleObjectNoBarrier() const
+{
+    return objectKey()->asSingleObjectNoBarrier();
+}
+
+inline TypeObject *
+Type::typeObjectNoBarrier() const
+{
+    return objectKey()->asTypeObjectNoBarrier();
 }
 
 inline bool
 TypeSet::hasType(Type type) const
 {
     if (unknown())
         return true;
 
@@ -1104,16 +1138,30 @@ TypeSet::getSingleObject(unsigned i) con
 
 inline TypeObject *
 TypeSet::getTypeObject(unsigned i) const
 {
     TypeObjectKey *key = getObject(i);
     return (key && key->isTypeObject()) ? key->asTypeObject() : nullptr;
 }
 
+inline JSObject *
+TypeSet::getSingleObjectNoBarrier(unsigned i) const
+{
+    TypeObjectKey *key = getObject(i);
+    return (key && key->isSingleObject()) ? key->asSingleObjectNoBarrier() : nullptr;
+}
+
+inline TypeObject *
+TypeSet::getTypeObjectNoBarrier(unsigned i) const
+{
+    TypeObjectKey *key = getObject(i);
+    return (key && key->isTypeObject()) ? key->asTypeObjectNoBarrier() : nullptr;
+}
+
 inline const Class *
 TypeSet::getObjectClass(unsigned i) const
 {
     if (JSObject *object = getSingleObject(i))
         return object->getClass();
     if (TypeObject *object = getTypeObject(i))
         return object->clasp();
     return nullptr;
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -2449,19 +2449,26 @@ Proxy::set(JSContext *cx, HandleObject p
     Rooted<PropertyDescriptor> desc(cx);
     if (!Proxy::getPropertyDescriptor(cx, proxy, id, &desc))
         return false;
     if (desc.object() && desc.setter() && desc.setter() != JS_StrictPropertyStub)
         return CallSetter(cx, receiver, id, desc.setter(), desc.attributes(), strict, vp);
 
     // Ok. Either there was no pre-existing property, or it was a value prop
     // that we're going to shadow. Make a property descriptor and define it.
+    //
+    // Note that for pre-existing own value properties, we inherit the existing
+    // attributes, since we're really just changing the value and not trying to
+    // reconfigure the property.
     Rooted<PropertyDescriptor> newDesc(cx);
+    if (desc.object() == proxy)
+        newDesc.setAttributes(desc.attributes());
+    else
+        newDesc.setAttributes(JSPROP_ENUMERATE);
     newDesc.value().set(vp);
-    newDesc.setAttributes(JSPROP_ENUMERATE);
     return handler->defineProperty(cx, receiver, id, &newDesc);
 }
 
 bool
 Proxy::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props)
 {
     JS_CHECK_RECURSION(cx, return false);
     BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -82,16 +82,19 @@ PerThreadData::PerThreadData(JSRuntime *
     asmJSActivationStack_(nullptr),
     autoFlushICache_(nullptr),
 #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
     simulator_(nullptr),
     simulatorStackLimit_(0),
 #endif
     dtoaState(nullptr),
     suppressGC(0),
+#ifdef DEBUG
+    ionCompiling(false),
+#endif
     activeCompilations(0)
 {}
 
 PerThreadData::~PerThreadData()
 {
     if (dtoaState)
         js_DestroyDtoaState(dtoaState);
 
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -574,16 +574,21 @@ class PerThreadData : public PerThreadDa
      * to suppress GC when reporting an OOM (see js_ReportOutOfMemory) and in
      * debugging facilities that cannot tolerate a GC and would rather OOM
      * immediately, such as utilities exposed to GDB. Setting this flag is
      * extremely dangerous and should only be used when in an OOM situation or
      * in non-exposed debugging facilities.
      */
     int32_t suppressGC;
 
+#ifdef DEBUG
+    // Whether this thread is actively Ion compiling.
+    bool ionCompiling;
+#endif
+
     // Number of active bytecode compilation on this thread.
     unsigned activeCompilations;
 
     explicit PerThreadData(JSRuntime *runtime);
     ~PerThreadData();
 
     bool init();
 
@@ -1658,15 +1663,41 @@ class RuntimeAllocPolicy
     void *calloc_(size_t bytes) { return runtime->calloc_(bytes); }
     void *realloc_(void *p, size_t bytes) { return runtime->realloc_(p, bytes); }
     void free_(void *p) { js_free(p); }
     void reportAllocOverflow() const {}
 };
 
 extern const JSSecurityCallbacks NullSecurityCallbacks;
 
+// Debugging RAII class which marks the current thread as performing an Ion
+// compilation, for use by CurrentThreadCan{Read,Write}CompilationData
+class AutoEnterIonCompilation
+{
+  public:
+    AutoEnterIonCompilation(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) {
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+
+#if defined(DEBUG) && defined(JS_THREADSAFE)
+        PerThreadData *pt = js::TlsPerThreadData.get();
+        JS_ASSERT(!pt->ionCompiling);
+        pt->ionCompiling = true;
+#endif
+    }
+
+    ~AutoEnterIonCompilation() {
+#if defined(DEBUG) && defined(JS_THREADSAFE)
+        PerThreadData *pt = js::TlsPerThreadData.get();
+        JS_ASSERT(pt->ionCompiling);
+        pt->ionCompiling = false;
+#endif
+    }
+
+    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+};
+
 } /* namespace js */
 
 #ifdef _MSC_VER
 #pragma warning(pop)
 #endif
 
 #endif /* vm_Runtime_h */
--- a/js/xpconnect/tests/chrome/test_xrayToJS.xul
+++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul
@@ -110,16 +110,18 @@ https://bugzilla.mozilla.org/show_bug.cg
     is(global(iwin.eval(toEval)), iwin, "eval creates objects in the correct global");
     is(iwin.eval(toEval).b.foo, 'bar', "eval-ed object looks right");
     is(Cu.waiveXrays(iwin.eval(toEval)).f(), Cu.waiveXrays(iwin), "evaled function works right");
 
     testDate();
 
     testObject();
 
+    testArray();
+
     // We could also test DataView and Iterator here for completeness, but it's
     // more trouble than it's worth.
 
 
     SimpleTest.finish();
   }
 
   // Maintain a static list of the properties that are available on each standard
@@ -140,16 +142,22 @@ https://bugzilla.mozilla.org/show_bug.cg
     "toLocaleDateString", "toLocaleTimeString", "toDateString", "toTimeString",
     "toISOString", "toJSON", "toSource", "toString", "valueOf", "constructor",
     "toGMTString"];
   gPrototypeProperties['Object'] = /* __proto__ is intentionally excluded here, because
                                       the JS engine filters it out of getOwnPropertyNames */
     ["constructor", "toSource", "toString", "toLocaleString", "valueOf", "watch",
      "unwatch", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable",
      "__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__"];
+  gPrototypeProperties['Array'] =
+    ["length", "toSource", "toString", "toLocaleString", "join", "reverse", "sort", "push",
+      "pop", "shift", "unshift", "splice", "concat", "slice", "lastIndexOf", "indexOf",
+      "forEach", "map", "reduce", "reduceRight", "filter", "some", "every", "mapPar",
+      "reducePar", "scanPar", "scatterPar", "filterPar", "find", "findIndex", "copyWithin",
+      "fill", "@@iterator", "entries", "keys", "constructor"];
 
   function filterOut(array, props) {
     return array.filter(p => props.indexOf(p) == -1);
   }
 
   function testXray(classname, xray, xray2, propsToSkip) {
     propsToSkip = propsToSkip || [];
     let xrayProto = Object.getPrototypeOf(xray);
@@ -220,24 +228,80 @@ https://bugzilla.mozilla.org/show_bug.cg
                    primitiveProp: 42, objectProp: { foo: 2 }, \
                    xoProp: top.location, hasOwnProperty: 10, \
                    get getterProp() { return 2; }, \
                    set setterProp(x) { }, \
                    get getterSetterProp() { return 3; }, \
                    set getterSetterProp(x) { }, \
                    callableProp: function() { }, \
                    nonXrayableProp: new WeakMap() })');
+    testTrickyObject(trickyObject);
+
+  }
+
+function testArray() {
+    // The |length| property is generally very weird, especially with respect
+    // to its behavior on the prototype. Array.prototype is actually an Array
+    // instance, and therefore has a vestigial .length. But we don't want to
+    // show that over Xrays, and generally want .length to just appear as an
+    // |own| data property. So we add it to the ignore list here, and check it
+    // separately.
+    let propsToSkip = ['length'];
+    testXray('Array', new iwin.Array(20), new iwin.Array(), propsToSkip);
+
+    var trickyArray =
+      iwin.eval("var trickyArray = []; \
+                 trickyArray.primitiveProp = 42; \
+                 trickyArray.objectProp = { foo: 2 }; \
+                 trickyArray.xoProp = top.location; \
+                 trickyArray.hasOwnProperty = 10; \
+                 Object.defineProperty(trickyArray, 'getterProp', { get: function() { return 2; }}); \
+                 Object.defineProperty(trickyArray, 'setterProp', { set: function(x) {}}); \
+                 Object.defineProperty(trickyArray, 'getterSetterProp', { get: function() { return 3; }, set: function(x) {}}); \
+                 trickyArray.callableProp = function() {}; \
+                 trickyArray.nonXrayableProp = new WeakMap(); \
+                 trickyArray;");
+
+    // Test indexed access.
+    trickyArray.wrappedJSObject[9] = "some indexed property";
+    is(trickyArray[9], "some indexed property", "indexed properties work correctly over Xrays");
+    is(trickyArray.length, 10, "Length works correctly over Xrays");
+    checkThrows(function() { "use strict"; delete trickyArray.length; }, /config/, "Can't delete non-configurable 'length' property");
+    delete trickyArray[9];
+    is(trickyArray[9], undefined, "Delete works correctly over Xrays");
+    is(trickyArray.wrappedJSObject[9], undefined, "Delete works correctly over Xrays (viewed via waiver)");
+    is(trickyArray.length, 10, "length doesn't change");
+    trickyArray[11] = "some other indexed property";
+    is(trickyArray.length, 12, "length now changes");
+    is(trickyArray.wrappedJSObject[11], "some other indexed property");
+    trickyArray.length = 0;
+    is(trickyArray.length, 0, "Setting length works over Xray");
+    is(trickyArray[11], undefined, "Setting length truncates over Xray");
+    Object.defineProperty(trickyArray, 'length', { configurable: false, enumerable: false, writable: false, value: 0 });
+    trickyArray[1] = "hi";
+    is(trickyArray.length, 0, "Length remains non-writable");
+    is(trickyArray[1], undefined, "Frozen length forbids new properties");
+
+    testTrickyObject(trickyArray);
+}
+
+// Parts of this function are kind of specific to testing Object, but we factor
+// it out so that we can re-use the trickyObject stuff on Arrays.
+function testTrickyObject(trickyObject) {
 
     // Make sure it looks right under the hood.
     is(trickyObject.wrappedJSObject.getterProp, 2, "Underlying object has getter");
     is(Cu.unwaiveXrays(trickyObject.wrappedJSObject.xoProp), top.location, "Underlying object has xo property");
 
     // Test getOwnPropertyNames.
+    var expectedNames = ['objectProp', 'primitiveProp'];
+    if (trickyObject instanceof iwin.Array)
+      expectedNames.push('length');
     is(Object.getOwnPropertyNames(trickyObject).sort().toSource(),
-       ['objectProp', 'primitiveProp'].toSource(), "getOwnPropertyNames should be filtered correctly");
+       expectedNames.sort().toSource(), "getOwnPropertyNames should be filtered correctly");
 
     // Test iteration and in-place modification. Beware of 'expando', which is the property
     // we placed on the xray proto.
     var propCount = 0;
     for (let prop in trickyObject) {
       if (prop == 'primitiveProp')
         trickyObject[prop] = trickyObject[prop] - 10;
       if (prop != 'expando')
--- a/js/xpconnect/wrappers/WrapperFactory.cpp
+++ b/js/xpconnect/wrappers/WrapperFactory.cpp
@@ -117,24 +117,26 @@ WrapperFactory::DoubleWrap(JSContext *cx
         JSAutoCompartment ac(cx, obj);
         return WaiveXray(cx, obj);
     }
     return obj;
 }
 
 // In general, we're trying to deprecate COWs incrementally as we introduce
 // Xrays to the corresponding object types. But switching off COWs for Object
-// instances would be too tumultuous at present, so we punt on that for later.
+// and Array instances would be too tumultuous at present, so we punt on that
+// for later.
 static bool
 ForceCOWBehavior(JSObject *obj)
 {
-    if (IdentifyStandardInstanceOrPrototype(obj) == JSProto_Object) {
+    JSProtoKey key = IdentifyStandardInstanceOrPrototype(obj);
+    if (key == JSProto_Object || key == JSProto_Array) {
         MOZ_ASSERT(GetXrayType(obj) == XrayForJSObject,
-                   "We should use XrayWrappers for standard ES Object instances "
-                   "modulo this hack");
+                   "We should use XrayWrappers for standard ES Object and Array "
+                   "instances modulo this hack");
         return true;
     }
     return false;
 }
 
 JSObject *
 WrapperFactory::PrepareForWrapping(JSContext *cx, HandleObject scope,
                                    HandleObject objArg, unsigned flags)
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -41,16 +41,17 @@ using namespace XrayUtils;
 
 // Whitelist for the standard ES classes we can Xray to.
 static bool
 IsJSXraySupported(JSProtoKey key)
 {
     switch (key) {
       case JSProto_Date:
       case JSProto_Object:
+      case JSProto_Array:
         return true;
       default:
         return false;
     }
 }
 
 XrayType
 GetXrayType(JSObject *obj)
@@ -467,17 +468,17 @@ bool JSXrayTraits::getOwnPropertyFromTar
             // Note - We're going add Xrays for Arrays/TypedArrays soon in
             // bug 987163, so we don't want to cause unnecessary compat churn
             // by making xrayedObj.arrayProp stop working temporarily, and then
             // start working again. At the same time, this is an important check,
             // and this patch wouldn't be as useful without it. So we just
             // forcibly override the behavior here for Arrays until bug 987163
             // lands.
             JSProtoKey key = IdentifyStandardInstanceOrPrototype(propObj);
-            if (key != JSProto_Array && key != JSProto_Uint8ClampedArray &&
+            if (key != JSProto_Uint8ClampedArray &&
                 key != JSProto_Int8Array && key != JSProto_Uint8Array &&
                 key != JSProto_Int16Array && key != JSProto_Uint16Array &&
                 key != JSProto_Int32Array && key != JSProto_Uint32Array &&
                 key != JSProto_Float32Array && key != JSProto_Float64Array)
             {
                 return SilentFailure(cx, id, "Value not Xrayable");
             }
         }
@@ -514,20 +515,28 @@ JSXrayTraits::resolveOwnProperty(JSConte
     // Call the common code.
     bool ok = XrayTraits::resolveOwnProperty(cx, jsWrapper, wrapper, holder,
                                              id, desc);
     if (!ok || desc.object())
         return ok;
 
     RootedObject target(cx, getTargetObject(wrapper));
     if (!isPrototype(holder)) {
-        // For object instances, we expose some properties from the underlying
-        // object, but only after filtering them carefully.
+        // For Object and Array instances, we expose some properties from the
+        // underlying object, but only after filtering them carefully.
+        //
+        // Note that, as far as JS observables go, Arrays are just Objects with
+        // a different prototype and a magic (own, non-configurable) |.length| that
+        // serves as a non-tight upper bound on |own| indexed properties. So while
+        // it's tempting to try to impose some sort of structure on what Arrays
+        // "should" look like over Xrays, the underlying object is squishy enough
+        // that it makes sense to just treat them like Objects for Xray purposes.
         switch (getProtoKey(holder)) {
           case JSProto_Object:
+          case JSProto_Array:
             {
                 JSAutoCompartment ac(cx, target);
                 if (!getOwnPropertyFromTargetIfSafe(cx, target, wrapper, id, desc))
                     return false;
             }
             return JS_WrapPropertyDescriptor(cx, desc);
 
           default:
@@ -664,18 +673,20 @@ JSXrayTraits::resolveOwnProperty(JSConte
 bool
 JSXrayTraits::delete_(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp)
 {
     RootedObject holder(cx, ensureHolder(cx, wrapper));
 
     // If we're using Object Xrays, we allow callers to attempt to delete any
     // property from the underlying object that they are able to resolve. Note
     // that this deleting may fail if the property is non-configurable.
-    bool isObjectInstance = getProtoKey(holder) == JSProto_Object && !isPrototype(holder);
-    if (isObjectInstance) {
+    JSProtoKey key = getProtoKey(holder);
+    bool isObjectOrArrayInstance = (key == JSProto_Object || key == JSProto_Array) &&
+                                   !isPrototype(holder);
+    if (isObjectOrArrayInstance) {
         RootedObject target(cx, getTargetObject(wrapper));
         JSAutoCompartment ac(cx, target);
         Rooted<JSPropertyDescriptor> desc(cx);
         if (!getOwnPropertyFromTargetIfSafe(cx, target, wrapper, id, &desc))
             return false;
         if (desc.object())
             return JS_DeletePropertyById2(cx, target, id, bp);
     }
@@ -690,44 +701,46 @@ JSXrayTraits::defineProperty(JSContext *
                                bool *defined)
 {
     *defined = false;
     RootedObject holder(cx, ensureHolder(cx, wrapper));
     if (!holder)
         return false;
 
 
-    // Object instances are special. For that case, we forward property
+    // Object and Array instances are special. For those cases, we forward property
     // definitions to the underlying object if the following conditions are met:
     // * The property being defined is a value-prop.
     // * The property being defined is either a primitive or subsumed by the target.
     // * As seen from the Xray, any existing property that we would overwrite is an
     //   |own| value-prop.
     //
-    // To avoid confusion, we disallow expandos on Object instances, and
+    // To avoid confusion, we disallow expandos on Object and Array instances, and
     // therefore raise an exception here if the above conditions aren't met.
-    bool isObjectInstance = getProtoKey(holder) == JSProto_Object && !isPrototype(holder);
-    if (isObjectInstance) {
+    JSProtoKey key = getProtoKey(holder);
+    bool isObjectOrArrayInstance = (key == JSProto_Object || key == JSProto_Array) &&
+                                   !isPrototype(holder);
+    if (isObjectOrArrayInstance) {
         RootedObject target(cx, getTargetObject(wrapper));
         if (desc.hasGetterOrSetter()) {
-            JS_ReportError(cx, "Not allowed to define accessor property on [Object] XrayWrapper");
+            JS_ReportError(cx, "Not allowed to define accessor property on [Object] or [Array] XrayWrapper");
             return false;
         }
         if (desc.value().isObject() &&
             !AccessCheck::subsumes(target, js::UncheckedUnwrap(&desc.value().toObject())))
         {
-            JS_ReportError(cx, "Not allowed to define cross-origin object as property on [Object] XrayWrapper");
+            JS_ReportError(cx, "Not allowed to define cross-origin object as property on [Object] or [Array] XrayWrapper");
             return false;
         }
         if (existingDesc.hasGetterOrSetter()) {
-            JS_ReportError(cx, "Not allowed to overwrite accessor property on [Object] XrayWrapper");
+            JS_ReportError(cx, "Not allowed to overwrite accessor property on [Object] or [Array] XrayWrapper");
             return false;
         }
         if (existingDesc.object() && existingDesc.object() != wrapper) {
-            JS_ReportError(cx, "Not allowed to shadow non-own Xray-resolved property on [Object] XrayWrapper");
+            JS_ReportError(cx, "Not allowed to shadow non-own Xray-resolved property on [Object] or [Array] XrayWrapper");
             return false;
         }
 
         JSAutoCompartment ac(cx, target);
         if (!JS_WrapPropertyDescriptor(cx, desc) ||
             !JS_DefinePropertyById(cx, target, id, desc.value(), desc.attributes(),
                                    JS_PropertyStub, JS_StrictPropertyStub))
         {
@@ -745,20 +758,21 @@ JSXrayTraits::enumerateNames(JSContext *
                              AutoIdVector &props)
 {
     RootedObject target(cx, getTargetObject(wrapper));
     RootedObject holder(cx, ensureHolder(cx, wrapper));
     if (!holder)
         return false;
 
     if (!isPrototype(holder)) {
-        // For object instances, we expose some properties from the underlying
+        // For Object and Array instances, we expose some properties from the underlying
         // object, but only after filtering them carefully.
         switch (getProtoKey(holder)) {
           case JSProto_Object:
+          case JSProto_Array:
             MOZ_ASSERT(props.empty());
             {
                 JSAutoCompartment ac(cx, target);
                 AutoIdVector targetProps(cx);
                 if (!js::GetPropertyNames(cx, target, flags | JSITER_OWNONLY, &targetProps))
                     return false;
                 // Loop over the properties, and only pass along the ones that
                 // we determine to be safe.
@@ -2309,18 +2323,32 @@ XrayWrapper<Base, Traits>::definePropert
         return false;
 
     // Note that the check here is intended to differentiate between own and
     // non-own properties, since the above lookup is not limited to own
     // properties. At present, this may not always do the right thing because
     // we often lie (sloppily) about where we found properties and set
     // desc.object() to |wrapper|. Once we fully fix our Xray prototype semantics,
     // this should work as intended.
-    if (existing_desc.object() == wrapper && existing_desc.isPermanent())
-        return true; // silently ignore attempt to overwrite native property
+    if (existing_desc.object() == wrapper && existing_desc.isPermanent()) {
+        // We have a non-configurable property. See if the caller is trying to
+        // re-configure it in any way other than making it non-writable.
+        if (existing_desc.hasGetterOrSetterObject() || desc.hasGetterOrSetterObject() ||
+            existing_desc.isEnumerable() != desc.isEnumerable() ||
+            (existing_desc.isReadonly() && !desc.isReadonly()))
+        {
+            // We should technically report non-configurability in strict mode, but
+            // doing that via JSAPI is a lot of trouble.
+            return true;
+        }
+        if (existing_desc.isReadonly()) {
+            // Same as the above for non-writability.
+            return true;
+        }
+    }
 
     bool defined = false;
     if (!Traits::singleton.defineProperty(cx, wrapper, id, desc, existing_desc, &defined))
         return false;
     if (defined)
         return true;
 
     // We're placing an expando. The expando objects live in the target
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -1568,17 +1568,17 @@ struct nsGenConInitializer {
   nsGenConInitializer(nsGenConNode* aNode, nsGenConList* aList,
                       void (nsCSSFrameConstructor::*aDirtyAll)())
     : mNode(aNode), mList(aList), mDirtyAll(aDirtyAll) {}
 };
 
 already_AddRefed<nsIContent>
 nsCSSFrameConstructor::CreateGenConTextNode(nsFrameConstructorState& aState,
                                             const nsString& aString,
-                                            nsCOMPtr<nsIDOMCharacterData>* aText,
+                                            nsRefPtr<nsTextNode>* aText,
                                             nsGenConInitializer* aInitializer)
 {
   nsRefPtr<nsTextNode> content = new nsTextNode(mDocument->NodeInfoManager());
   content->SetText(aString, false);
   if (aText) {
     *aText = content;
   }
   if (aInitializer) {
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -332,17 +332,17 @@ private:
                                   nsIFrame** aNewFrame);
 
   /**
    * Create a text node containing the given string. If aText is non-null
    * then we also set aText to the returned node.
    */
   already_AddRefed<nsIContent> CreateGenConTextNode(nsFrameConstructorState& aState,
                                                     const nsString& aString,
-                                                    nsCOMPtr<nsIDOMCharacterData>* aText,
+                                                    nsRefPtr<nsTextNode>* aText,
                                                     nsGenConInitializer* aInitializer);
 
   /**
    * Create a content node for the given generated content style.
    * The caller takes care of making it SetIsNativeAnonymousRoot, binding it
    * to the document, and creating frames for it.
    * @param aParentContent is the node that has the before/after style
    * @param aStyleContext is the 'before' or 'after' pseudo-element
--- a/layout/base/nsFrameManager.h
+++ b/layout/base/nsFrameManager.h
@@ -63,17 +63,19 @@ struct UndisplayedNode {
 /**
  * Frame manager interface. The frame manager serves two purposes:
  * <li>provides a service for mapping from content to frame and from
  * out-of-flow frame to placeholder frame.
  * <li>handles structural modifications to the frame model. If the frame model
  * lock can be acquired, then the changes are processed immediately; otherwise,
  * they're queued and processed later.
  *
- * Do not add virtual methods to this class, or bryner will punish you.
+ * Do not add virtual methods (a vtable pointer) or members to this class, or
+ * else you'll break the validity of the reinterpret_cast in nsIPresShell's
+ * FrameManager() method.
  */
 
 class nsFrameManager : public nsFrameManagerBase
 {
   typedef nsIFrame::ChildListID ChildListID;
 
 public:
   nsFrameManager(nsIPresShell *aPresShell, nsStyleSet* aStyleSet) {
@@ -150,16 +152,11 @@ public:
   /*
    * Add/restore state for one frame
    */
   void CaptureFrameStateFor(nsIFrame*              aFrame,
                                         nsILayoutHistoryState* aState);
 
   void RestoreFrameStateFor(nsIFrame*              aFrame,
                                         nsILayoutHistoryState* aState);
-
-  nsIPresShell* GetPresShell() const { return mPresShell; }
-  nsPresContext* GetPresContext() const {
-    return mPresShell->GetPresContext();
-  }
 };
 
 #endif
--- a/layout/base/nsGenConList.h
+++ b/layout/base/nsGenConList.h
@@ -6,18 +6,18 @@
 /* base class for nsCounterList and nsQuoteList */
 
 #ifndef nsGenConList_h___
 #define nsGenConList_h___
 
 #include "nsIFrame.h"
 #include "nsStyleStruct.h"
 #include "prclist.h"
-#include "nsIDOMCharacterData.h"
 #include "nsCSSPseudoElements.h"
+#include "nsTextNode.h"
 
 class nsGenConList;
 
 struct nsGenConNode : public PRCList {
   // The wrapper frame for all of the pseudo-element's content.  This
   // frame generally has useful style data and has the
   // NS_FRAME_GENERATED_CONTENT bit set (so we use it to track removal),
   // but does not necessarily for |nsCounterChangeNode|s.
@@ -25,17 +25,17 @@ struct nsGenConNode : public PRCList {
 
   // Index within the list of things specified by the 'content' property,
   // which is needed to do 'content: open-quote open-quote' correctly,
   // and needed for similar cases for counters.
   const int32_t mContentIndex;
 
   // null for 'content:no-open-quote', 'content:no-close-quote' and for
   // counter nodes for increments and resets (rather than uses)
-  nsCOMPtr<nsIDOMCharacterData> mText;
+  nsRefPtr<nsTextNode> mText;
 
   nsGenConNode(int32_t aContentIndex)
     : mPseudoFrame(nullptr)
     , mContentIndex(aContentIndex)
   {
   }
 
   /**
--- a/layout/style/forms.css
+++ b/layout/style/forms.css
@@ -920,16 +920,17 @@ input[type=number]::-moz-number-text {
   /* This pseudo-element is also an 'input' element (nested inside and
    * distinct from the <input type=number> element) so we need to prevent the
    * explicit setting of 'text-align' by the general CSS rule for 'input'
    * above. We want to inherit its value from its <input type=number>
    * ancestor, not have that general CSS rule reset it.
    */
   text-align: inherit;
   flex: 1;
+  min-width: 0;
   padding: 0;
   border: 0;
   margin: 0;
 }
 
 input[type=number]::-moz-number-spin-box {
   display: flex;
   flex-direction: column;
--- a/security/manager/boot/src/StaticHPKPins.h
+++ b/security/manager/boot/src/StaticHPKPins.h
@@ -666,337 +666,336 @@ struct TransportSecurityPreload {
   const bool mIsMoz;
   const int32_t mId;
   const StaticPinset *pinset;
 };
 
 /* Sort hostnames for binary search. */
 static const TransportSecurityPreload kPublicKeyPinningPreloadList[] = {
   { "accounts.firefox.com", true, true, false, 4, &kPinset_mozilla_fxa },
-  { "accounts.google.com", true, true, false, -1, &kPinset_google_root_pems },
+  { "accounts.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "addons.mozilla.net", true, false, true, 2, &kPinset_mozilla },
   { "addons.mozilla.org", true, false, true, 1, &kPinset_mozilla },
-  { "admin.google.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "android.com", true, true, false, -1, &kPinset_google_root_pems },
+  { "admin.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "android.com", true, false, false, -1, &kPinset_google_root_pems },
   { "api.twitter.com", true, false, false, -1, &kPinset_twitterCDN },
-  { "apis.google.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "appengine.google.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "appspot.com", true, true, false, -1, &kPinset_google_root_pems },
+  { "apis.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "appengine.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "appspot.com", true, false, false, -1, &kPinset_google_root_pems },
   { "aus4.mozilla.org", true, true, true, 3, &kPinset_mozilla },
   { "blog.torproject.org", true, true, false, -1, &kPinset_tor },
   { "business.twitter.com", true, false, false, -1, &kPinset_twitterCom },
   { "cdn.mozilla.net", true, false, true, -1, &kPinset_mozilla },
   { "cdn.mozilla.org", true, false, true, -1, &kPinset_mozilla },
-  { "chart.apis.google.com", true, true, false, -1, &kPinset_google_root_pems },
+  { "chart.apis.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "check.torproject.org", true, true, false, -1, &kPinset_tor },
-  { "checkout.google.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "chrome-devtools-frontend.appspot.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "chrome.google.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "chromiumcodereview.appspot.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "cloud.google.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "code.google.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "codereview.appspot.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "codereview.chromium.org", true, true, false, -1, &kPinset_google_root_pems },
+  { "checkout.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "chrome-devtools-frontend.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "chrome.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "chromiumcodereview.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "cloud.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "code.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "codereview.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "codereview.chromium.org", true, false, false, -1, &kPinset_google_root_pems },
   { "crypto.cat", false, true, false, -1, &kPinset_cryptoCat },
   { "dev.twitter.com", true, false, false, -1, &kPinset_twitterCom },
   { "dist.torproject.org", true, true, false, -1, &kPinset_tor },
-  { "dl.google.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "docs.google.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "doubleclick.net", true, true, false, -1, &kPinset_google_root_pems },
-  { "drive.google.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "encrypted.google.com", true, true, false, -1, &kPinset_google_root_pems },
+  { "dl.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "docs.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "doubleclick.net", true, false, false, -1, &kPinset_google_root_pems },
+  { "drive.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "encrypted.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "exclude-subdomains.pinning.example.com", false, false, false, 0, &kPinset_mozilla_test },
-  { "g.co", true, true, false, -1, &kPinset_google_root_pems },
-  { "glass.google.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "gmail.com", false, true, false, -1, &kPinset_google_root_pems },
-  { "goo.gl", true, true, false, -1, &kPinset_google_root_pems },
-  { "google-analytics.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.ac", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.ad", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.ae", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.af", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.ag", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.am", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.as", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.at", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.az", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.ba", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.be", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.bf", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.bg", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.bi", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.bj", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.bs", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.by", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.ca", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.cat", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.cc", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.cd", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.cf", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.cg", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.ch", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.ci", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.cl", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.cm", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.cn", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.co.ao", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.co.bw", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.co.ck", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.co.cr", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.co.hu", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.co.id", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.co.il", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.co.im", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.co.in", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.co.je", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.co.jp", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.co.ke", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.co.kr", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.co.ls", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.co.ma", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.co.mz", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.co.nz", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.co.th", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.co.tz", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.co.ug", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.co.uk", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.co.uz", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.co.ve", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.co.vi", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.co.za", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.co.zm", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.co.zw", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.af", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.ag", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.ai", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.ar", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.au", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.bd", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.bh", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.bn", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.bo", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.br", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.by", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.bz", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.cn", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.co", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.cu", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.cy", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.do", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.ec", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.eg", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.et", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.fj", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.ge", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.gh", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.gi", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.gr", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.gt", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.hk", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.iq", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.jm", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.jo", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.kh", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.kw", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.lb", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.ly", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.mt", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.mx", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.my", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.na", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.nf", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.ng", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.ni", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.np", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.nr", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.om", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.pa", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.pe", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.ph", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.pk", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.pl", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.pr", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.py", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.qa", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.ru", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.sa", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.sb", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.sg", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.sl", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.sv", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.tj", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.tn", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.tr", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.tw", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.ua", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.uy", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.vc", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.ve", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.com.vn", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.cv", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.cz", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.de", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.dj", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.dk", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.dm", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.dz", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.ee", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.es", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.fi", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.fm", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.fr", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.ga", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.ge", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.gg", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.gl", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.gm", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.gp", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.gr", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.gy", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.hk", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.hn", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.hr", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.ht", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.hu", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.ie", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.im", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.info", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.iq", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.is", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.it", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.it.ao", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.je", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.jo", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.jobs", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.jp", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.kg", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.ki", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.kz", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.la", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.li", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.lk", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.lt", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.lu", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.lv", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.md", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.me", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.mg", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.mk", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.ml", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.mn", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.ms", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.mu", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.mv", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.mw", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.ne", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.ne.jp", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.net", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.nl", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.no", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.nr", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.nu", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.off.ai", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.pk", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.pl", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.pn", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.ps", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.pt", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.ro", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.rs", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.ru", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.rw", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.sc", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.se", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.sh", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.si", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.sk", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.sm", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.sn", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.so", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.st", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.td", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.tg", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.tk", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.tl", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.tm", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.tn", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.to", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.tp", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.tt", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.us", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.uz", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.vg", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.vu", true, true, false, -1, &kPinset_google_root_pems },
-  { "google.ws", true, true, false, -1, &kPinset_google_root_pems },
-  { "googleadservices.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "googleapis.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "googlecode.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "googlecommerce.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "googlegroups.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "googlemail.com", false, true, false, -1, &kPinset_google_root_pems },
-  { "googleplex.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "googlesyndication.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "googletagmanager.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "googletagservices.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "googleusercontent.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "goto.google.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "groups.google.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "gstatic.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "history.google.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "hostedtalkgadget.google.com", true, true, false, -1, &kPinset_google_root_pems },
+  { "g.co", true, false, false, -1, &kPinset_google_root_pems },
+  { "glass.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "gmail.com", false, false, false, -1, &kPinset_google_root_pems },
+  { "goo.gl", true, false, false, -1, &kPinset_google_root_pems },
+  { "google-analytics.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ac", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ad", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ae", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.af", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ag", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.am", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.as", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.at", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.az", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ba", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.be", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.bf", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.bg", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.bi", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.bj", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.bs", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.by", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ca", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.cat", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.cc", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.cd", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.cf", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.cg", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ch", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ci", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.cl", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.cm", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.cn", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.ao", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.bw", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.ck", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.cr", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.hu", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.id", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.il", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.im", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.in", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.je", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.jp", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.ke", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.kr", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.ls", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.ma", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.mz", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.nz", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.th", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.tz", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.ug", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.uk", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.uz", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.ve", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.vi", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.za", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.zm", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.zw", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.af", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.ag", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.ai", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.ar", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.au", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.bd", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.bh", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.bn", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.bo", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.br", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.by", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.bz", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.cn", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.co", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.cu", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.cy", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.do", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.ec", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.eg", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.et", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.fj", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.ge", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.gh", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.gi", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.gr", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.gt", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.hk", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.iq", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.jm", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.jo", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.kh", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.kw", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.lb", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.ly", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.mt", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.mx", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.my", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.na", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.nf", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.ng", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.ni", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.np", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.nr", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.om", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.pa", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.pe", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.ph", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.pk", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.pl", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.pr", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.py", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.qa", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.ru", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.sa", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.sb", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.sg", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.sl", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.sv", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.tj", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.tn", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.tr", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.tw", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.ua", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.uy", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.vc", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.ve", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.vn", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.cv", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.cz", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.de", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.dj", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.dk", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.dm", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.dz", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ee", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.es", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.fi", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.fm", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.fr", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ga", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ge", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.gg", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.gl", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.gm", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.gp", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.gr", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.gy", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.hk", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.hn", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.hr", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ht", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.hu", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ie", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.im", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.info", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.iq", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.is", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.it", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.it.ao", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.je", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.jo", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.jobs", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.jp", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.kg", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ki", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.kz", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.la", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.li", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.lk", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.lt", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.lu", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.lv", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.md", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.me", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.mg", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.mk", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ml", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.mn", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ms", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.mu", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.mv", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.mw", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ne", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ne.jp", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.net", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.nl", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.no", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.nr", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.nu", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.off.ai", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.pk", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.pl", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.pn", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ps", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.pt", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ro", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.rs", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ru", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.rw", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.sc", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.se", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.sh", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.si", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.sk", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.sm", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.sn", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.so", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.st", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.td", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.tg", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.tk", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.tl", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.tm", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.tn", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.to", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.tt", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.us", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.uz", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.vg", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.vu", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ws", true, false, false, -1, &kPinset_google_root_pems },
+  { "googleadservices.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "googleapis.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "googlecode.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "googlecommerce.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "googlegroups.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "googlemail.com", false, false, false, -1, &kPinset_google_root_pems },
+  { "googleplex.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "googlesyndication.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "googletagmanager.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "googletagservices.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "googleusercontent.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "goto.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "groups.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "gstatic.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "history.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "hostedtalkgadget.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "include-subdomains.pinning.example.com", true, false, false, -1, &kPinset_mozilla_test },
   { "liberty.lavabit.com", true, true, false, -1, &kPinset_lavabit },
-  { "mail.google.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "market.android.com", true, true, false, -1, &kPinset_google_root_pems },
+  { "mail.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "market.android.com", true, false, false, -1, &kPinset_google_root_pems },
   { "media.mozilla.com", true, false, true, -1, &kPinset_mozilla },
   { "mobile.twitter.com", true, false, false, -1, &kPinset_twitterCom },
   { "oauth.twitter.com", true, false, false, -1, &kPinset_twitterCom },
   { "pinningtest.appspot.com", true, false, false, -1, &kPinset_test },
   { "platform.twitter.com", true, false, false, -1, &kPinset_twitterCDN },
-  { "play.google.com", false, true, false, -1, &kPinset_google_root_pems },
-  { "plus.google.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "plus.sandbox.google.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "profiles.google.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "script.google.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "security.google.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "sites.google.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "spreadsheets.google.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "ssl.google-analytics.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "talk.google.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "talkgadget.google.com", true, true, false, -1, &kPinset_google_root_pems },
+  { "play.google.com", false, false, false, -1, &kPinset_google_root_pems },
+  { "plus.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "plus.sandbox.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "profiles.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "script.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "security.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "sites.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "spreadsheets.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "ssl.google-analytics.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "talk.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "talkgadget.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "test-mode.pinning.example.com", true, true, false, -1, &kPinset_mozilla_test },
   { "tor2web.org", true, true, false, -1, &kPinset_tor2web },
   { "torproject.org", false, true, false, -1, &kPinset_tor },
-  { "translate.googleapis.com", true, true, false, -1, &kPinset_google_root_pems },
+  { "translate.googleapis.com", true, false, false, -1, &kPinset_google_root_pems },
   { "twimg.com", true, false, false, -1, &kPinset_twitterCDN },
   { "twitter.com", false, false, false, -1, &kPinset_twitterCom },
-  { "urchin.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "w-spotlight.appspot.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "wallet.google.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "webfilings-eu-mirror.appspot.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "webfilings-eu.appspot.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "webfilings-mirror-hrd.appspot.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "webfilings.appspot.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "wf-bigsky-master.appspot.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "wf-demo-eu.appspot.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "wf-demo-hrd.appspot.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "wf-dogfood-hrd.appspot.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "wf-pentest.appspot.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "wf-staging-hr.appspot.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "wf-training-hrd.appspot.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "wf-training-master.appspot.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "wf-trial-hrd.appspot.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "www.gmail.com", false, true, false, -1, &kPinset_google_root_pems },
-  { "www.googlemail.com", false, true, false, -1, &kPinset_google_root_pems },
+  { "urchin.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "w-spotlight.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "wallet.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "webfilings-eu-mirror.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "webfilings-eu.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "webfilings-mirror-hrd.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "webfilings.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "wf-bigsky-master.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "wf-demo-eu.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "wf-demo-hrd.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "wf-dogfood-hrd.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "wf-pentest.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "wf-staging-hr.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "wf-training-hrd.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "wf-training-master.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "wf-trial-hrd.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "www.gmail.com", false, false, false, -1, &kPinset_google_root_pems },
+  { "www.googlemail.com", false, false, false, -1, &kPinset_google_root_pems },
   { "www.torproject.org", true, true, false, -1, &kPinset_tor },
   { "www.twitter.com", true, false, false, -1, &kPinset_twitterCom },
-  { "xbrlsuccess.appspot.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "youtu.be", true, true, false, -1, &kPinset_google_root_pems },
-  { "youtube.com", true, true, false, -1, &kPinset_google_root_pems },
-  { "ytimg.com", true, true, false, -1, &kPinset_google_root_pems },
+  { "xbrlsuccess.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "youtu.be", true, false, false, -1, &kPinset_google_root_pems },
+  { "youtube.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "ytimg.com", true, false, false, -1, &kPinset_google_root_pems },
 };
 
-static const int kPublicKeyPinningPreloadListLength = 323;
+static const int kPublicKeyPinningPreloadListLength = 322;
 
 static const int32_t kUnknownId = -1;
 
-static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1412966638293000);
+static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1413412335521000);
--- a/security/manager/tools/PreloadedHPKPins.json
+++ b/security/manager/tools/PreloadedHPKPins.json
@@ -26,16 +26,19 @@
 {
   "chromium_data" : {
     "cert_file_url": "https://src.chromium.org/chrome/trunk/src/net/http/transport_security_state_static.certs",
     "json_file_url": "https://src.chromium.org/chrome/trunk/src/net/http/transport_security_state_static.json",
     "substitute_pinsets": {
       // Use the larger google_root_pems pinset instead of google
       "google": "google_root_pems"
     },
+    "production_pinsets": [
+      "google_root_pems"
+    ],
     "production_domains": [
       // Chrome's test domain.
       "pinningtest.appspot.com",
       // Twitter
       "api.twitter.com",
       "business.twitter.com",
       "dev.twitter.com",
       "mobile.twitter.com",
--- a/security/manager/tools/genHPKPStaticPins.js
+++ b/security/manager/tools/genHPKPStaticPins.js
@@ -314,21 +314,24 @@ function downloadAndParseChromePins(file
   let entries = chromePreloads.entries;
   entries.forEach(function(entry) {
     let pinsetName = cData.substitute_pinsets[entry.pins];
     if (!pinsetName) {
       pinsetName = entry.pins;
     }
     let isProductionDomain =
       (cData.production_domains.indexOf(entry.name) != -1);
+    let isProductionPinset =
+      (cData.production_pinsets.indexOf(pinsetName) != -1);
+    let isTestMode = !isProductionPinset && !isProductionDomain;
     if (entry.pins && chromeImportedPinsets[entry.pins]) {
       chromeImportedEntries.push({
         name: entry.name,
         include_subdomains: entry.include_subdomains,
-        test_mode: !isProductionDomain,
+        test_mode: isTestMode,
         is_moz: false,
         pins: pinsetName });
     }
   });
   return [ chromeImportedPinsets, chromeImportedEntries ];
 }
 
 // Returns a pair of maps [certNameToSKD, certSKDToName] between cert
--- a/testing/marionette/marionette-listener.js
+++ b/testing/marionette/marionette-listener.js
@@ -490,16 +490,17 @@ function executeScript(msg, directInject
 
   try {
     if (directInject) {
       if (importedScripts.exists()) {
         let stream = Components.classes["@mozilla.org/network/file-input-stream;1"].
                       createInstance(Components.interfaces.nsIFileInputStream);
         stream.init(importedScripts, -1, 0, 0);
         let data = NetUtil.readInputStreamToString(stream, stream.available());
+        stream.close();
         script = data + script;
       }
       let res = Cu.evalInSandbox(script, sandbox, "1.8", "dummy file" ,0);
       sendSyncMessage("Marionette:shareData",
                       {log: elementManager.wrapValue(marionetteLogObj.getLogs())});
       marionetteLogObj.clearLogs();
 
       if (res == undefined || res.passed == undefined) {
@@ -521,16 +522,17 @@ function executeScript(msg, directInject
 
       script = "let __marionetteFunc = function(){" + script + "};" +
                    "__marionetteFunc.apply(null, __marionetteParams);";
       if (importedScripts.exists()) {
         let stream = Components.classes["@mozilla.org/network/file-input-stream;1"].
                       createInstance(Components.interfaces.nsIFileInputStream);
         stream.init(importedScripts, -1, 0, 0);
         let data = NetUtil.readInputStreamToString(stream, stream.available());
+        stream.close();
         script = data + script;
       }
       let res = Cu.evalInSandbox(script, sandbox, "1.8", "dummy file", 0);
       sendSyncMessage("Marionette:shareData",
                       {log: elementManager.wrapValue(marionetteLogObj.getLogs())});
       marionetteLogObj.clearLogs();
       sendResponse({value: elementManager.wrapValue(res)}, asyncTestCommandId);
     }
@@ -657,16 +659,17 @@ function executeWithCallback(msg, useFin
 
   try {
     asyncTestRunning = true;
     if (importedScripts.exists()) {
       let stream = Cc["@mozilla.org/network/file-input-stream;1"].
                       createInstance(Ci.nsIFileInputStream);
       stream.init(importedScripts, -1, 0, 0);
       let data = NetUtil.readInputStreamToString(stream, stream.available());
+      stream.close();
       scriptSrc = data + scriptSrc;
     }
     Cu.evalInSandbox(scriptSrc, sandbox, "1.8", "dummy file", 0);
   } catch (e) {
     // 17 = JavascriptException
     let error = createStackMessage(e,
                                    "execute_async_script",
                                    msg.json.filename,
--- a/testing/marionette/marionette-server.js
+++ b/testing/marionette/marionette-server.js
@@ -711,16 +711,17 @@ MarionetteServerConnection.prototype = {
       return;
     }
 
     if (this.importedScripts.exists()) {
       let stream = Cc["@mozilla.org/network/file-input-stream;1"].
                     createInstance(Ci.nsIFileInputStream);
       stream.init(this.importedScripts, -1, 0, 0);
       let data = NetUtil.readInputStreamToString(stream, stream.available());
+      stream.close();
       script = data + script;
     }
 
     let res = Cu.evalInSandbox(script, sandbox, "1.8", "dummy file", 0);
 
     if (directInject && !async &&
         (res == undefined || res.passed == undefined)) {
       this.sendError("finish() not called", 500, null, command_id);
--- a/testing/specialpowers/content/specialpowersAPI.js
+++ b/testing/specialpowers/content/specialpowersAPI.js
@@ -71,16 +71,23 @@ function unwrapIfWrapped(x) {
 function wrapIfUnwrapped(x) {
   return isWrapper(x) ? x : wrapPrivileged(x);
 }
 
 function isXrayWrapper(x) {
   return Cu.isXrayWrapper(x);
 }
 
+function isObjectOrArray(obj) {
+  if (Object(obj) !== obj)
+    return false;
+  let className = Cu.getClassName(obj, true);
+  return className == 'Object' || className == 'Array';
+}
+
 function callGetOwnPropertyDescriptor(obj, name) {
   // Quickstubbed getters and setters are propertyOps, and don't get reified
   // until someone calls __lookupGetter__ or __lookupSetter__ on them (note
   // that there are special version of those functions for quickstubs, so
   // apply()ing Object.prototype.__lookupGetter__ isn't good enough). Try to
   // trigger reification before calling Object.getOwnPropertyDescriptor.
   //
   // See bug 764315.
@@ -100,19 +107,17 @@ function doApply(fun, invocant, args) {
   // SpecialPowers. So we waive [[Object]] instances when they're passed to a
   // SpecialPowers-wrapped callable.
   //
   // Note that the transitive nature of Xray waivers means that any property
   // pulled off such an object will also be waived, and so we'll get principal
   // clamping for Xrayed DOM objects reached from literals, so passing things
   // like {l : xoWin.location} won't work. Hopefully the rabbit hole doesn't
   // go that deep.
-  args = args.map(x => (Object(x) === x &&
-                        Cu.getClassName(x, true) == 'Object')
-                  ? Cu.waiveXrays(x) : x);
+  args = args.map(x => isObjectOrArray(x) ? Cu.waiveXrays(x) : x);
   return Function.prototype.apply.call(fun, invocant, args);
 }
 
 function wrapPrivileged(obj) {
 
   // Primitives pass straight through.
   if (!isWrappable(obj))
     return obj;
@@ -225,22 +230,23 @@ SpecialPowersHandler.prototype.doGetProp
   // compartment boundaries. However, there are some exceptions where we want
   // to use a waiver:
   //
   // * Xray adds some gunk to toString(), which has the potential to confuse
   //   consumers that aren't expecting Xray wrappers. Since toString() is a
   //   non-privileged method that returns only strings, we can just waive Xray
   //   for that case.
   //
-  // *  We implement Xrays to pure JS [[Object]] instances that filter out tricky
-  //    things like callables. This is the right thing for security in general,
-  //    but tends to break tests that try to pass object literals into
-  //    SpecialPowers. So we waive [[Object]] instances before inspecting properties.
+  // *  We implement Xrays to pure JS [[Object]] and [[Array]] instances that
+  //    filter out tricky things like callables. This is the right thing for
+  //    security in general, but tends to break tests that try to pass object
+  //    literals into SpecialPowers. So we waive [[Object]] and [[Array]]
+  //    instances before inspecting properties.
   var obj = this.wrappedObject;
-  if (name == 'toString' || Cu.getClassName(obj, true) == 'Object')
+  if (name == 'toString' || isObjectOrArray(obj))
     obj = XPCNativeWrapper.unwrap(obj);
 
   //
   // Call through to the wrapped object.
   //
   // Note that we have several cases here, each of which requires special handling.
   //
   var desc;
--- a/toolkit/devtools/server/actors/script.js
+++ b/toolkit/devtools/server/actors/script.js
@@ -3616,18 +3616,26 @@ DebuggerServer.ObjectActorPreviewers = {
 
     if (threadActor._gripDepth > 1) {
       return true;
     }
 
     let raw = obj.unsafeDereference();
     let items = aGrip.preview.items = [];
 
-    for (let [i, value] of Array.prototype.entries.call(raw)) {
-      if (Object.hasOwnProperty.call(raw, i)) {
+    for (let i = 0; i < length; ++i) {
+      // Array Xrays filter out various possibly-unsafe properties (like
+      // functions, and claim that the value is undefined instead. This
+      // is generally the right thing for privileged code accessing untrusted
+      // objects, but quite confusing for Object previews. So we manually
+      // override this protection by waiving Xrays on the array, and re-applying
+      // Xrays on any indexed value props that we pull off of it.
+      let desc = Object.getOwnPropertyDescriptor(Cu.waiveXrays(raw), i);
+      if (desc && !desc.get && !desc.set) {
+        let value = Cu.unwaiveXrays(desc.value);
         value = makeDebuggeeValueIfNeeded(obj, value);
         items.push(threadActor.createValueGrip(value));
       } else {
         items.push(null);
       }
 
       if (items.length == OBJECT_PREVIEW_MAX_ITEMS) {
         break;
@@ -3689,17 +3697,28 @@ DebuggerServer.ObjectActorPreviewers = {
     };
 
     if (threadActor._gripDepth > 1) {
       return true;
     }
 
     let raw = obj.unsafeDereference();
     let entries = aGrip.preview.entries = [];
-    for (let [key, value] of Map.prototype.entries.call(raw)) {
+    // We don't have Xrays to Iterators, so .entries returns [key, value]
+    // Arrays that live in content. But since we have Array Xrays,
+    // we'll deny access depending on the nature of those values. So we need
+    // to waive Xrays on those tuples (and re-apply them on the underlying
+    // values) until we fix bug 1023984.
+    //
+    // Even then though, we might want to continue waiving Xrays here for the
+    // same reason we do so for Arrays above - this filtering behavior is likely
+    // to be more confusing than beneficial in the case of Object previews.
+    for (let keyValuePair of Map.prototype.entries.call(raw)) {
+      let key = Cu.unwaiveXrays(Cu.waiveXrays(keyValuePair)[0]);
+      let value = Cu.unwaiveXrays(Cu.waiveXrays(keyValuePair)[1]);
       key = makeDebuggeeValueIfNeeded(obj, key);
       value = makeDebuggeeValueIfNeeded(obj, value);
       entries.push([threadActor.createValueGrip(key),
                     threadActor.createValueGrip(value)]);
       if (entries.length == OBJECT_PREVIEW_MAX_ITEMS) {
         break;
       }
     }
@@ -3793,17 +3812,19 @@ DebuggerServer.ObjectActorPreviewers.Obj
 
     if (threadActor._gripDepth > 1) {
       return true;
     }
 
     let raw = obj.unsafeDereference();
     let global = Cu.getGlobalForObject(DebuggerServer);
     let classProto = global[obj.class].prototype;
-    let safeView = classProto.subarray.call(raw, 0, OBJECT_PREVIEW_MAX_ITEMS);
+    // The Xray machinery for TypedArrays denies indexed access on the grounds
+    // that it's slow, and advises callers to do a structured clone instead.
+    let safeView = Cu.cloneInto(classProto.subarray.call(raw, 0, OBJECT_PREVIEW_MAX_ITEMS), global);
     let items = aGrip.preview.items = [];
     for (let i = 0; i < safeView.length; i++) {
       items.push(safeView[i]);
     }
 
     return true;
   },
 
--- a/toolkit/devtools/webconsole/utils.js
+++ b/toolkit/devtools/webconsole/utils.js
@@ -1743,26 +1743,30 @@ function JSTermHelpers(aOwner)
         output.push(name + ": " + valueString);
       }
     }
 
     return "  " + output.join("\n  ");
   };
 
   /**
-   * Print a string to the output, as-is.
+   * Print the String representation of a value to the output, as-is.
    *
-   * @param string aString
-   *        A string you want to output.
+   * @param any aValue
+   *        A value you want to output as a string.
    * @return void
    */
-  aOwner.sandbox.print = function JSTH_print(aString)
+  aOwner.sandbox.print = function JSTH_print(aValue)
   {
     aOwner.helperResult = { rawOutput: true };
-    return String(aString);
+    // Waiving Xrays here allows us to see a closer representation of the
+    // underlying object. This may execute arbitrary content code, but that
+    // code will run with content privileges, and the result will be rendered
+    // inert by coercing it to a String.
+    return String(Cu.waiveXrays(aValue));
   };
 }
 exports.JSTermHelpers = JSTermHelpers;
 
 
 /**
  * A ReflowObserver that listens for reflow events from the page.
  * Implements nsIReflowObserver.