Merge mozilla-inbound to mozilla-central. a=merge
authorCosmin Sabou <csabou@mozilla.com>
Fri, 05 Oct 2018 13:07:56 +0300
changeset 439778 863c5a0642a84831b8ff3cac737c8657c70b05f1
parent 439762 624bb3de2d559c1e6fbcab1a092dd1a202f1eef6 (current diff)
parent 439777 19b215c7a61e1ae5e018cbf22e0f18033f73ceef (diff)
child 439785 cee4d0a7b2557b5df896c9fffb05eb001ad3fcff
child 439787 6cb426898fc9702e8ae8640a627736dc57459f25
push id34787
push usercsabou@mozilla.com
push dateFri, 05 Oct 2018 10:08:34 +0000
treeherdermozilla-central@863c5a0642a8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone64.0a1
first release with
nightly linux32
863c5a0642a8 / 64.0a1 / 20181005102516 / files
nightly linux64
863c5a0642a8 / 64.0a1 / 20181005102516 / files
nightly mac
863c5a0642a8 / 64.0a1 / 20181005102516 / files
nightly win32
863c5a0642a8 / 64.0a1 / 20181005102516 / files
nightly win64
863c5a0642a8 / 64.0a1 / 20181005102516 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-inbound to mozilla-central. a=merge
gfx/webrender_bindings/RenderCompositorANGLE.cpp
gfx/webrender_bindings/src/bindings.rs
gfx/webrender_bindings/src/moz2d_renderer.rs
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -1466,19 +1466,19 @@ WorkerPrivate::GetDocument() const
 void
 WorkerPrivate::SetCSP(nsIContentSecurityPolicy* aCSP)
 {
   AssertIsOnMainThread();
   if (!aCSP) {
     return;
   }
   aCSP->EnsureEventTarget(mMainThreadEventTarget);
-  aCSP->SetEventListener(mCSPEventListener);
 
   mLoadInfo.mCSP = aCSP;
+  EnsureCSPEventListener();
 }
 
 nsresult
 WorkerPrivate::SetCSPFromHeaderValues(const nsACString& aCSPHeaderValue,
                                       const nsACString& aCSPReportOnlyHeaderValue)
 {
   AssertIsOnMainThread();
   MOZ_DIAGNOSTIC_ASSERT(!mLoadInfo.mCSP);
@@ -1488,17 +1488,16 @@ WorkerPrivate::SetCSPFromHeaderValues(co
 
   nsCOMPtr<nsIContentSecurityPolicy> csp;
   nsresult rv = mLoadInfo.mPrincipal->EnsureCSP(nullptr, getter_AddRefs(csp));
   if (!csp) {
     return NS_OK;
   }
 
   csp->EnsureEventTarget(mMainThreadEventTarget);
-  csp->SetEventListener(mCSPEventListener);
 
   // If there's a CSP header, apply it.
   if (!cspHeaderValue.IsEmpty()) {
     rv = CSP_AppendCSPFromHeader(csp, cspHeaderValue, false);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   // If there's a report-only CSP header, apply it.
   if (!cspROHeaderValue.IsEmpty()) {
@@ -1510,16 +1509,17 @@ WorkerPrivate::SetCSPFromHeaderValues(co
   bool evalAllowed = false;
   bool reportEvalViolations = false;
   rv = csp->GetAllowsEval(&reportEvalViolations, &evalAllowed);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mLoadInfo.mCSP = csp;
   mLoadInfo.mEvalAllowed = evalAllowed;
   mLoadInfo.mReportCSPViolations = reportEvalViolations;
+  EnsureCSPEventListener();
 
   return NS_OK;
 }
 
 void
 WorkerPrivate::SetReferrerPolicyFromHeaderValue(const nsACString& aReferrerPolicyHeaderValue)
 {
   NS_ConvertUTF8toUTF16 headerValue(aReferrerPolicyHeaderValue);
@@ -3440,19 +3440,21 @@ WorkerPrivate::EnsureClientSource()
   }
 
   return true;
 }
 
 bool
 WorkerPrivate::EnsureCSPEventListener()
 {
-  mCSPEventListener = WorkerCSPEventListener::Create(this);
-  if (NS_WARN_IF(!mCSPEventListener)) {
-    return false;
+  if (!mCSPEventListener) {
+    mCSPEventListener = WorkerCSPEventListener::Create(this);
+    if (NS_WARN_IF(!mCSPEventListener)) {
+      return false;
+    }
   }
 
   if (mLoadInfo.mCSP) {
     mLoadInfo.mCSP->SetEventListener(mCSPEventListener);
   }
 
   return true;
 }
--- a/gfx/layers/SyncObject.h
+++ b/gfx/layers/SyncObject.h
@@ -32,17 +32,17 @@ public:
 #endif
                                                               );
 
   virtual bool Init() = 0;
 
   virtual SyncHandle GetSyncHandle() = 0;
 
   // Return false for failed synchronization.
-  virtual bool Synchronize() = 0;
+  virtual bool Synchronize(bool aFallible = false) = 0;
 
 protected:
   SyncObjectHost() { }
 };
 
 class SyncObjectClient : public external::AtomicRefCounted<SyncObjectClient>
 {
 public:
--- a/gfx/layers/d3d11/TextureD3D11.cpp
+++ b/gfx/layers/d3d11/TextureD3D11.cpp
@@ -1711,32 +1711,34 @@ SyncObjectD3D11Host::Init()
 
 SyncHandle
 SyncObjectD3D11Host::GetSyncHandle()
 {
   return mSyncHandle;
 }
 
 bool
-SyncObjectD3D11Host::Synchronize()
+SyncObjectD3D11Host::Synchronize(bool aFallible)
 {
   HRESULT hr;
   AutoTextureLock lock(mKeyedMutex, hr, 10000);
 
   if (hr == WAIT_TIMEOUT) {
     hr = mDevice->GetDeviceRemovedReason();
-    if (hr == S_OK) {
+    if (hr != S_OK ) {
+      // Since the timeout is related to the driver-removed. Return false for
+      // error handling.
+      gfxCriticalNote << "GFX: D3D11 timeout with device-removed:" << gfx::hexa(hr);
+    } else if (aFallible) {
+      gfxCriticalNote << "GFX: D3D11 timeout on the D3D11 sync lock.";
+    } else {
       // There is no driver-removed event. Crash with this timeout.
       MOZ_CRASH("GFX: D3D11 normal status timeout");
     }
 
-    // Since the timeout is related to the driver-removed. Return false for
-    // error handling.
-    gfxCriticalNote << "GFX: D3D11 timeout with device-removed:" << gfx::hexa(hr);
-
     return false;
   }
   if (hr == WAIT_ABANDONED) {
     gfxCriticalNote << "GFX: AL_D3D11 abandoned sync";
   }
 
   return true;
 }
--- a/gfx/layers/d3d11/TextureD3D11.h
+++ b/gfx/layers/d3d11/TextureD3D11.h
@@ -493,17 +493,17 @@ class SyncObjectD3D11Host : public SyncO
 {
 public:
   explicit SyncObjectD3D11Host(ID3D11Device* aDevice);
 
   virtual bool Init() override;
 
   virtual SyncHandle GetSyncHandle() override;
 
-  virtual bool Synchronize() override;
+  virtual bool Synchronize(bool aFallible) override;
 
   IDXGIKeyedMutex* GetKeyedMutex() { return mKeyedMutex.get(); };
 
 private:
   virtual ~SyncObjectD3D11Host() { }
 
   SyncHandle mSyncHandle;
   RefPtr<ID3D11Device> mDevice;
--- a/gfx/layers/ipc/CompositorBridgeChild.cpp
+++ b/gfx/layers/ipc/CompositorBridgeChild.cpp
@@ -1092,19 +1092,17 @@ CompositorBridgeChild::DeallocPAPZCTreeM
 void
 CompositorBridgeChild::WillEndTransaction()
 {
   ResetShmemCounter();
 }
 
 PWebRenderBridgeChild*
 CompositorBridgeChild::AllocPWebRenderBridgeChild(const wr::PipelineId& aPipelineId,
-                                                  const LayoutDeviceIntSize&,
-                                                  TextureFactoryIdentifier*,
-                                                  wr::IdNamespace *aIdNamespace)
+                                                  const LayoutDeviceIntSize&)
 {
   WebRenderBridgeChild* child = new WebRenderBridgeChild(aPipelineId);
   child->AddIPDLReference();
   return child;
 }
 
 bool
 CompositorBridgeChild::DeallocPWebRenderBridgeChild(PWebRenderBridgeChild* aActor)
--- a/gfx/layers/ipc/CompositorBridgeChild.h
+++ b/gfx/layers/ipc/CompositorBridgeChild.h
@@ -205,19 +205,17 @@ public:
   bool DeallocPAPZCTreeManagerChild(PAPZCTreeManagerChild* aActor) override;
 
   PAPZChild* AllocPAPZChild(const LayersId& aLayersId) override;
   bool DeallocPAPZChild(PAPZChild* aActor) override;
 
   void WillEndTransaction();
 
   PWebRenderBridgeChild* AllocPWebRenderBridgeChild(const wr::PipelineId& aPipelineId,
-                                                    const LayoutDeviceIntSize&,
-                                                    TextureFactoryIdentifier*,
-                                                    wr::IdNamespace*) override;
+                                                    const LayoutDeviceIntSize&) override;
   bool DeallocPWebRenderBridgeChild(PWebRenderBridgeChild* aActor) override;
 
   wr::MaybeExternalImageId GetNextExternalImageId() override;
 
   wr::PipelineId GetNextPipelineId();
 
   // Must only be called from the main thread. Ensures that any paints from
   // previous frames have been flushed. The main thread blocks until the
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -1849,19 +1849,17 @@ CompositorBridgeParent::RecvAdoptChild(c
     }
     mApzUpdater->NotifyLayerTreeAdopted(child, oldApzUpdater);
   }
   return IPC_OK();
 }
 
 PWebRenderBridgeParent*
 CompositorBridgeParent::AllocPWebRenderBridgeParent(const wr::PipelineId& aPipelineId,
-                                                    const LayoutDeviceIntSize& aSize,
-                                                    TextureFactoryIdentifier* aTextureFactoryIdentifier,
-                                                    wr::IdNamespace* aIdNamespace)
+                                                    const LayoutDeviceIntSize& aSize)
 {
 #ifndef MOZ_BUILD_WEBRENDER
   // Extra guard since this in the parent process and we don't want a malicious
   // child process invoking this codepath before it's ready
   MOZ_RELEASE_ASSERT(false);
 #endif
   MOZ_ASSERT(wr::AsLayersId(aPipelineId) == mRootLayerTreeID);
   MOZ_ASSERT(!mWrBridge);
@@ -1880,38 +1878,34 @@ CompositorBridgeParent::AllocPWebRenderB
   if (mApzSampler) {
     // Same as for mApzUpdater, but for the sampler thread.
     mApzSampler->SetWebRenderWindowId(windowId);
   }
   RefPtr<wr::WebRenderAPI> api = wr::WebRenderAPI::Create(this, std::move(widget), windowId, aSize);
   if (!api) {
     mWrBridge = WebRenderBridgeParent::CreateDestroyed(aPipelineId);
     mWrBridge.get()->AddRef(); // IPDL reference
-    *aIdNamespace = mWrBridge->GetIdNamespace();
-    *aTextureFactoryIdentifier = TextureFactoryIdentifier(LayersBackend::LAYERS_NONE);
     return mWrBridge;
   }
   mAsyncImageManager = new AsyncImagePipelineManager(api->Clone());
   RefPtr<AsyncImagePipelineManager> asyncMgr = mAsyncImageManager;
   wr::TransactionBuilder txn;
   txn.SetRootPipeline(aPipelineId);
   api->SendTransaction(txn);
   RefPtr<CompositorAnimationStorage> animStorage = GetAnimationStorage();
   mWrBridge = new WebRenderBridgeParent(this, aPipelineId, mWidget, nullptr, std::move(api), std::move(asyncMgr), std::move(animStorage), mVsyncRate);
   mWrBridge.get()->AddRef(); // IPDL reference
 
-  *aIdNamespace = mWrBridge->GetIdNamespace();
   mCompositorScheduler = mWrBridge->CompositorScheduler();
   MOZ_ASSERT(mCompositorScheduler);
   { // scope lock
     MonitorAutoLock lock(*sIndirectLayerTreesLock);
     MOZ_ASSERT(sIndirectLayerTrees[mRootLayerTreeID].mWrBridge == nullptr);
     sIndirectLayerTrees[mRootLayerTreeID].mWrBridge = mWrBridge;
   }
-  *aTextureFactoryIdentifier = mWrBridge->GetTextureFactoryIdentifier();
   return mWrBridge;
 }
 
 bool
 CompositorBridgeParent::DeallocPWebRenderBridgeParent(PWebRenderBridgeParent* aActor)
 {
 #ifndef MOZ_BUILD_WEBRENDER
   // Extra guard since this in the parent process and we don't want a malicious
--- a/gfx/layers/ipc/CompositorBridgeParent.h
+++ b/gfx/layers/ipc/CompositorBridgeParent.h
@@ -482,19 +482,17 @@ public:
   }
 
   TimeDuration GetVsyncInterval() const {
     // the variable is called "rate" but really it's an interval
     return mVsyncRate;
   }
 
   PWebRenderBridgeParent* AllocPWebRenderBridgeParent(const wr::PipelineId& aPipelineId,
-                                                      const LayoutDeviceIntSize& aSize,
-                                                      TextureFactoryIdentifier* aTextureFactoryIdentifier,
-                                                      wr::IdNamespace* aIdNamespace) override;
+                                                      const LayoutDeviceIntSize& aSize) override;
   bool DeallocPWebRenderBridgeParent(PWebRenderBridgeParent* aActor) override;
   RefPtr<WebRenderBridgeParent> GetWebRenderBridgeParent() const;
   Maybe<TimeStamp> GetTestingTimeStamp() const;
 
   static CompositorBridgeParent* GetCompositorBridgeParentFromLayersId(const LayersId& aLayersId);
   static RefPtr<CompositorBridgeParent> GetCompositorBridgeParentFromWindowId(const wr::WindowId& aWindowId);
 
   /**
--- a/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
@@ -190,19 +190,17 @@ CrossProcessCompositorBridgeParent::Deal
 {
   RemoteContentController* controller = static_cast<RemoteContentController*>(aActor);
   controller->Release();
   return true;
 }
 
 PWebRenderBridgeParent*
 CrossProcessCompositorBridgeParent::AllocPWebRenderBridgeParent(const wr::PipelineId& aPipelineId,
-                                                                const LayoutDeviceIntSize& aSize,
-                                                                TextureFactoryIdentifier* aTextureFactoryIdentifier,
-                                                                wr::IdNamespace *aIdNamespace)
+                                                                const LayoutDeviceIntSize& aSize)
 {
 #ifndef MOZ_BUILD_WEBRENDER
   // Extra guard since this in the parent process and we don't want a malicious
   // child process invoking this codepath before it's ready
   MOZ_RELEASE_ASSERT(false);
 #endif
   LayersId layersId = wr::AsLayersId(aPipelineId);
   // Check to see if this child process has access to this layer tree.
@@ -230,35 +228,31 @@ CrossProcessCompositorBridgeParent::Allo
   }
 
   if (!root || !api) {
     // This could happen when this function is called after CompositorBridgeParent destruction.
     // This was observed during Tab move between different windows.
     NS_WARNING(nsPrintfCString("Created child without a matching parent? root %p", root.get()).get());
     WebRenderBridgeParent* parent = WebRenderBridgeParent::CreateDestroyed(aPipelineId);
     parent->AddRef(); // IPDL reference
-    *aIdNamespace = parent->GetIdNamespace();
-    *aTextureFactoryIdentifier = TextureFactoryIdentifier(LayersBackend::LAYERS_NONE);
     return parent;
   }
 
   api = api->Clone();
   RefPtr<AsyncImagePipelineManager> holder = root->AsyncImageManager();
   RefPtr<CompositorAnimationStorage> animStorage = cbp->GetAnimationStorage();
   WebRenderBridgeParent* parent = new WebRenderBridgeParent(
           this, aPipelineId, nullptr, root->CompositorScheduler(), std::move(api), std::move(holder), std::move(animStorage), cbp->GetVsyncInterval());
   parent->AddRef(); // IPDL reference
 
   { // scope lock
     MonitorAutoLock lock(*sIndirectLayerTreesLock);
     sIndirectLayerTrees[layersId].mCrossProcessParent = this;
     sIndirectLayerTrees[layersId].mWrBridge = parent;
   }
-  *aTextureFactoryIdentifier = parent->GetTextureFactoryIdentifier();
-  *aIdNamespace = parent->GetIdNamespace();
 
   return parent;
 }
 
 bool
 CrossProcessCompositorBridgeParent::DeallocPWebRenderBridgeParent(PWebRenderBridgeParent* aActor)
 {
 #ifndef MOZ_BUILD_WEBRENDER
--- a/gfx/layers/ipc/CrossProcessCompositorBridgeParent.h
+++ b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.h
@@ -140,19 +140,17 @@ public:
   bool DeallocPAPZCTreeManagerParent(PAPZCTreeManagerParent* aActor) override;
 
   PAPZParent* AllocPAPZParent(const LayersId& aLayersId) override;
   bool DeallocPAPZParent(PAPZParent* aActor) override;
 
   void UpdatePaintTime(LayerTransactionParent* aLayerTree, const TimeDuration& aPaintTime) override;
 
   PWebRenderBridgeParent* AllocPWebRenderBridgeParent(const wr::PipelineId& aPipelineId,
-                                                      const LayoutDeviceIntSize& aSize,
-                                                      TextureFactoryIdentifier* aTextureFactoryIdentifier,
-                                                      wr::IdNamespace* aIdNamespace) override;
+                                                      const LayoutDeviceIntSize& aSize) override;
   bool DeallocPWebRenderBridgeParent(PWebRenderBridgeParent* aActor) override;
 
   void ObserveLayersUpdate(LayersId aLayersId, LayersObserverEpoch aEpoch, bool aActive) override;
 
   bool IsRemote() const override {
     return true;
   }
 
--- a/gfx/layers/ipc/PCompositorBridge.ipdl
+++ b/gfx/layers/ipc/PCompositorBridge.ipdl
@@ -237,18 +237,17 @@ parent:
    */
   async AllPluginsCaptured();
 
   async PTexture(SurfaceDescriptor aSharedData, ReadLockDescriptor aReadLock, LayersBackend aBackend, TextureFlags aTextureFlags, LayersId id, uint64_t aSerial, MaybeExternalImageId aExternalImageId);
 
   sync SyncWithCompositor();
 
   // The pipelineId is the same as the layersId
-  sync PWebRenderBridge(PipelineId pipelineId, LayoutDeviceIntSize aSize)
-    returns (TextureFactoryIdentifier textureFactoryIdentifier, IdNamespace idNamespace);
+  async PWebRenderBridge(PipelineId pipelineId, LayoutDeviceIntSize aSize);
 
   sync CheckContentOnlyTDR(uint32_t sequenceNum)
     returns (bool isContentOnlyTDR);
 
 child:
   // Send back Compositor Frame Metrics from APZCs so tiled layers can
   // update progressively.
   async SharedCompositorFrameMetrics(Handle metrics, CrossProcessMutexHandle mutex, LayersId aLayersId, uint32_t aAPZCId);
--- a/gfx/layers/ipc/PWebRenderBridge.ipdl
+++ b/gfx/layers/ipc/PWebRenderBridge.ipdl
@@ -17,29 +17,33 @@ include protocol PTexture;
 using mozilla::layers::APZTestData from "mozilla/layers/APZTestData.h";
 using mozilla::layers::ScrollUpdatesMap from "FrameMetrics.h";
 using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h";
 using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
 using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h";
 using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h";
 using mozilla::wr::BuiltDisplayListDescriptor from "mozilla/webrender/webrender_ffi.h";
 using mozilla::wr::IdNamespace from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::MaybeIdNamespace from "mozilla/webrender/WebRenderTypes.h";
 using mozilla::layers::WebRenderScrollData from "mozilla/layers/WebRenderScrollData.h";
 using mozilla::layers::FocusTarget from "mozilla/layers/FocusTarget.h";
 using mozilla::layers::LayersObserverEpoch from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::TransactionId from "mozilla/layers/LayersTypes.h";
 
 namespace mozilla {
 namespace layers {
 
 sync protocol PWebRenderBridge
 {
   manager PCompositorBridge;
 
 parent:
+  sync EnsureConnected()
+    returns (TextureFactoryIdentifier textureFactoryIdentifier, MaybeIdNamespace maybeIdNamespace);
+
   async NewCompositable(CompositableHandle handle, TextureInfo info);
   async ReleaseCompositable(CompositableHandle compositable);
 
   async DeleteCompositorAnimations(uint64_t[] aIds);
   async SetDisplayList(IntSize aSize, WebRenderParentCommand[] commands, OpDestroy[] toDestroy, uint64_t fwdTransactionId, TransactionId transactionId,
                        LayoutSize aContentSize, ByteBuf aDL, BuiltDisplayListDescriptor aDLDesc,
                        WebRenderScrollData aScrollData,
                        OpUpdateResource[] aResourceUpdates, RefCountedShmem[] aSmallShmems, Shmem[] aLargeShmems,
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -258,16 +258,33 @@ WebRenderBridgeParent::CreateDestroyed(c
   return new WebRenderBridgeParent(aPipelineId);
 }
 
 WebRenderBridgeParent::~WebRenderBridgeParent()
 {
 }
 
 mozilla::ipc::IPCResult
+WebRenderBridgeParent::RecvEnsureConnected(TextureFactoryIdentifier* aTextureFactoryIdentifier,
+                                           MaybeIdNamespace* aMaybeIdNamespace)
+{
+  if (mDestroyed) {
+    *aTextureFactoryIdentifier = TextureFactoryIdentifier(LayersBackend::LAYERS_NONE);
+    *aMaybeIdNamespace = Nothing();
+    return IPC_OK();
+  }
+
+  MOZ_ASSERT(mIdNamespace.mHandle != 0);
+  *aTextureFactoryIdentifier = GetTextureFactoryIdentifier();
+  *aMaybeIdNamespace = Some(mIdNamespace);
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvShutdown()
 {
   return HandleShutdown();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvShutdownSync()
 {
--- a/gfx/layers/wr/WebRenderBridgeParent.h
+++ b/gfx/layers/wr/WebRenderBridgeParent.h
@@ -60,16 +60,19 @@ public:
 
   static WebRenderBridgeParent* CreateDestroyed(const wr::PipelineId& aPipelineId);
 
   wr::PipelineId PipelineId() { return mPipelineId; }
   already_AddRefed<wr::WebRenderAPI> GetWebRenderAPI() { return do_AddRef(mApi); }
   AsyncImagePipelineManager* AsyncImageManager() { return mAsyncImageManager; }
   CompositorVsyncScheduler* CompositorScheduler() { return mCompositorScheduler.get(); }
 
+  mozilla::ipc::IPCResult RecvEnsureConnected(TextureFactoryIdentifier* aTextureFactoryIdentifier,
+                                              MaybeIdNamespace* aMaybeIdNamespace) override;
+
   mozilla::ipc::IPCResult RecvNewCompositable(const CompositableHandle& aHandle,
                                               const TextureInfo& aInfo) override;
   mozilla::ipc::IPCResult RecvReleaseCompositable(const CompositableHandle& aHandle) override;
 
   mozilla::ipc::IPCResult RecvShutdown() override;
   mozilla::ipc::IPCResult RecvShutdownSync() override;
   mozilla::ipc::IPCResult RecvDeleteCompositorAnimations(InfallibleTArray<uint64_t>&& aIds) override;
   mozilla::ipc::IPCResult RecvUpdateResources(nsTArray<OpUpdateResource>&& aUpdates,
--- a/gfx/layers/wr/WebRenderLayerManager.cpp
+++ b/gfx/layers/wr/WebRenderLayerManager.cpp
@@ -56,35 +56,41 @@ bool
 WebRenderLayerManager::Initialize(PCompositorBridgeChild* aCBChild,
                                   wr::PipelineId aLayersId,
                                   TextureFactoryIdentifier* aTextureFactoryIdentifier)
 {
   MOZ_ASSERT(mWrChild == nullptr);
   MOZ_ASSERT(aTextureFactoryIdentifier);
 
   LayoutDeviceIntSize size = mWidget->GetClientSize();
-  TextureFactoryIdentifier textureFactoryIdentifier;
-  wr::IdNamespace id_namespace;
   PWebRenderBridgeChild* bridge = aCBChild->SendPWebRenderBridgeConstructor(aLayersId,
-                                                                            size,
-                                                                            &textureFactoryIdentifier,
-                                                                            &id_namespace);
+                                                                            size);
   if (!bridge) {
     // This should only fail if we attempt to access a layer we don't have
     // permission for, or more likely, the GPU process crashed again during
     // reinitialization. We can expect to be notified again to reinitialize
     // (which may or may not be using WebRender).
     gfxCriticalNote << "Failed to create WebRenderBridgeChild.";
     return false;
   }
 
+  TextureFactoryIdentifier textureFactoryIdentifier;
+  wr::MaybeIdNamespace idNamespace;
+  // Sync ipc
+  bridge->SendEnsureConnected(&textureFactoryIdentifier, &idNamespace);
+  if (textureFactoryIdentifier.mParentBackend == LayersBackend::LAYERS_NONE ||
+      idNamespace.isNothing()) {
+    gfxCriticalNote << "Failed to connect WebRenderBridgeChild.";
+    return false;
+  }
+
   mWrChild = static_cast<WebRenderBridgeChild*>(bridge);
   WrBridge()->SetWebRenderLayerManager(this);
   WrBridge()->IdentifyTextureHost(textureFactoryIdentifier);
-  WrBridge()->SetNamespace(id_namespace);
+  WrBridge()->SetNamespace(idNamespace.ref());
   *aTextureFactoryIdentifier = textureFactoryIdentifier;
   return true;
 }
 
 void
 WebRenderLayerManager::Destroy()
 {
   DoDestroy(/* aIsSync */ false);
--- a/gfx/webrender/res/brush_image.glsl
+++ b/gfx/webrender/res/brush_image.glsl
@@ -37,30 +37,16 @@ ImageBrushData fetch_image_data(int addr
     ImageBrushData data = ImageBrushData(
         raw_data[0],
         raw_data[1],
         raw_data[2].xy
     );
     return data;
 }
 
-#ifdef WR_FEATURE_ALPHA_PASS
-vec2 transform_point_snapped(
-    vec2 local_pos,
-    RectWithSize local_rect,
-    mat4 transform
-) {
-    vec2 snap_offset = compute_snap_offset(local_pos, transform, local_rect);
-    vec4 world_pos = transform * vec4(local_pos, 0.0, 1.0);
-    vec2 device_pos = world_pos.xy / world_pos.w * uDevicePixelRatio;
-
-    return device_pos + snap_offset;
-}
-#endif
-
 void brush_vs(
     VertexInfo vi,
     int prim_address,
     RectWithSize prim_rect,
     RectWithSize segment_rect,
     ivec3 user_data,
     mat4 transform,
     PictureTask pic_task,
@@ -90,20 +76,20 @@ void brush_vs(
         local_rect = segment_rect;
         stretch_size = local_rect.size;
 
         // Note: Here we can assume that texels in device
         //       space map to local space, due to how border-image
         //       works. That assumption may not hold if this
         //       is used for other purposes in the future.
         if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_X) != 0) {
-            stretch_size.x = (texel_rect.z - texel_rect.x) / uDevicePixelRatio;
+            stretch_size.x = (texel_rect.z - texel_rect.x) / pic_task.common_data.device_pixel_scale;
         }
         if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_Y) != 0) {
-            stretch_size.y = (texel_rect.w - texel_rect.y) / uDevicePixelRatio;
+            stretch_size.y = (texel_rect.w - texel_rect.y) / pic_task.common_data.device_pixel_scale;
         }
 
         uv0 = res.uv_rect.p0 + texel_rect.xy;
         uv1 = res.uv_rect.p0 + texel_rect.zw;
     }
 
     vUv.z = res.layer;
 
--- a/gfx/webrender/res/brush_mix_blend.glsl
+++ b/gfx/webrender/res/brush_mix_blend.glsl
@@ -7,28 +7,33 @@
 #include shared,prim_shared,brush
 
 varying vec3 vSrcUv;
 varying vec3 vBackdropUv;
 flat varying int vOp;
 
 #ifdef WR_VERTEX_SHADER
 
+//Note: this function is unsafe for `vi.world_pos.w <= 0.0`
+vec2 snap_device_pos(VertexInfo vi, float device_pixel_scale) {
+    return vi.world_pos.xy * device_pixel_scale / max(0.0, vi.world_pos.w) + vi.snap_offset;
+}
+
 void brush_vs(
     VertexInfo vi,
     int prim_address,
     RectWithSize local_rect,
     RectWithSize segment_rect,
     ivec3 user_data,
     mat4 transform,
     PictureTask pic_task,
     int brush_flags,
     vec4 unused
 ) {
-    vec2 snapped_device_pos = snap_device_pos(vi);
+    vec2 snapped_device_pos = snap_device_pos(vi, pic_task.common_data.device_pixel_scale);
     vec2 texture_size = vec2(textureSize(sPrevPassColor, 0));
     vOp = user_data.x;
 
     PictureTask src_task = fetch_picture_task(user_data.z);
     vec2 src_uv = snapped_device_pos +
                   src_task.common_data.task_rect.p0 -
                   src_task.content_origin;
     vSrcUv = vec3(src_uv / texture_size, src_task.common_data.texture_layer_index);
--- a/gfx/webrender/res/clip_shared.glsl
+++ b/gfx/webrender/res/clip_shared.glsl
@@ -58,31 +58,32 @@ ClipVertexInfo write_clip_tile_vertex(Re
                                       ClipArea area) {
     vec2 device_pos = area.screen_origin +
                       aPosition.xy * area.common_data.task_rect.size;
 
     if (clip_transform.is_axis_aligned && prim_transform.is_axis_aligned) {
         mat4 snap_mat = clip_transform.m * prim_transform.inv_m;
         vec4 snap_positions = compute_snap_positions(
             snap_mat,
-            local_clip_rect
+            local_clip_rect,
+            area.common_data.device_pixel_scale
         );
 
         vec2 snap_offsets = compute_snap_offset_impl(
             device_pos,
             snap_mat,
             local_clip_rect,
             RectWithSize(snap_positions.xy, snap_positions.zw - snap_positions.xy),
             snap_positions
         );
 
         device_pos -= snap_offsets;
     }
 
-    vec2 world_pos = device_pos / uDevicePixelRatio;
+    vec2 world_pos = device_pos / area.common_data.device_pixel_scale;
 
     vec4 pos = prim_transform.m * vec4(world_pos, 0.0, 1.0);
     pos.xyz /= pos.w;
 
     vec4 p = get_node_pos(pos.xy, clip_transform);
     vec3 local_pos = p.xyw * pos.w;
 
     vec4 vertex_pos = vec4(
--- a/gfx/webrender/res/cs_blur.glsl
+++ b/gfx/webrender/res/cs_blur.glsl
@@ -27,17 +27,17 @@ struct BlurTask {
     float blur_radius;
 };
 
 BlurTask fetch_blur_task(int address) {
     RenderTaskData task_data = fetch_render_task_data(address);
 
     BlurTask task = BlurTask(
         task_data.common_data,
-        task_data.data1.x
+        task_data.user_data.x
     );
 
     return task;
 }
 
 void main(void) {
     BlurTask blur_task = fetch_blur_task(aBlurRenderTaskAddress);
     RenderTaskCommonData src_task = fetch_render_task_common_data(aBlurSourceTaskAddress);
--- a/gfx/webrender/res/debug_color.glsl
+++ b/gfx/webrender/res/debug_color.glsl
@@ -7,17 +7,17 @@
 varying vec4 vColor;
 
 #ifdef WR_VERTEX_SHADER
 in vec4 aColor;
 
 void main(void) {
     vColor = vec4(aColor.rgb * aColor.a, aColor.a);
     vec4 pos = vec4(aPosition, 1.0);
-    pos.xy = floor(pos.xy * uDevicePixelRatio + 0.5) / uDevicePixelRatio;
+    pos.xy = floor(pos.xy + 0.5);
     gl_Position = uTransform * pos;
 }
 #endif
 
 #ifdef WR_FRAGMENT_SHADER
 void main(void) {
     oFragColor = vColor;
 }
--- a/gfx/webrender/res/debug_font.glsl
+++ b/gfx/webrender/res/debug_font.glsl
@@ -10,17 +10,17 @@ varying vec4 vColor;
 #ifdef WR_VERTEX_SHADER
 in vec4 aColor;
 in vec2 aColorTexCoord;
 
 void main(void) {
     vColor = aColor;
     vColorTexCoord = aColorTexCoord;
     vec4 pos = vec4(aPosition, 1.0);
-    pos.xy = floor(pos.xy * uDevicePixelRatio + 0.5) / uDevicePixelRatio;
+    pos.xy = floor(pos.xy + 0.5);
     gl_Position = uTransform * pos;
 }
 #endif
 
 #ifdef WR_FRAGMENT_SHADER
 void main(void) {
     float alpha = texture(sColor0, vec3(vColorTexCoord.xy, 0.0)).r;
     oFragColor = vColor * alpha;
--- a/gfx/webrender/res/prim_shared.glsl
+++ b/gfx/webrender/res/prim_shared.glsl
@@ -84,21 +84,16 @@ PrimitiveHeader fetch_prim_header(int in
 }
 
 struct VertexInfo {
     vec2 local_pos;
     vec2 snap_offset;
     vec4 world_pos;
 };
 
-//Note: this function is unsafe for `vi.world_pos.w <= 0.0`
-vec2 snap_device_pos(VertexInfo vi) {
-    return vi.world_pos.xy * uDevicePixelRatio / max(0.0, vi.world_pos.w) + vi.snap_offset;
-}
-
 VertexInfo write_vertex(RectWithSize instance_rect,
                         RectWithSize local_clip_rect,
                         float z,
                         Transform transform,
                         PictureTask task,
                         RectWithSize snap_rect) {
 
     // Select the corner of the local rect that we are processing.
@@ -106,24 +101,25 @@ VertexInfo write_vertex(RectWithSize ins
 
     // Clamp to the two local clip rects.
     vec2 clamped_local_pos = clamp_rect(local_pos, local_clip_rect);
 
     /// Compute the snapping offset.
     vec2 snap_offset = compute_snap_offset(
         clamped_local_pos,
         transform.m,
-        snap_rect
+        snap_rect,
+        task.common_data.device_pixel_scale
     );
 
     // Transform the current vertex to world space.
     vec4 world_pos = transform.m * vec4(clamped_local_pos, 0.0, 1.0);
 
     // Convert the world positions to device pixel space.
-    vec2 device_pos = world_pos.xy * uDevicePixelRatio;
+    vec2 device_pos = world_pos.xy * task.common_data.device_pixel_scale;
 
     // Apply offsets for the render task to get correct screen location.
     vec2 final_offset = snap_offset - task.content_origin + task.common_data.task_rect.p0;
 
     gl_Position = uTransform * vec4(device_pos + final_offset * world_pos.w, z * world_pos.w, world_pos.w);
 
     VertexInfo vi = VertexInfo(
         clamped_local_pos,
@@ -190,17 +186,17 @@ VertexInfo write_transform_vertex(RectWi
     vec2 local_pos = local_segment_rect.p0 + local_segment_rect.size * aPosition.xy;
 
     // Convert the world positions to device pixel space.
     vec2 task_offset = task.common_data.task_rect.p0 - task.content_origin;
 
     // Transform the current vertex to the world cpace.
     vec4 world_pos = transform.m * vec4(local_pos, 0.0, 1.0);
     vec4 final_pos = vec4(
-        world_pos.xy * uDevicePixelRatio + task_offset * world_pos.w,
+        world_pos.xy * task.common_data.device_pixel_scale + task_offset * world_pos.w,
         z * world_pos.w,
         world_pos.w
     );
 
     gl_Position = uTransform * final_pos;
 
     init_transform_vs(mix(
         vec4(prim_rect.p0, prim_rect.p1),
@@ -213,17 +209,17 @@ VertexInfo write_transform_vertex(RectWi
         vec2(0.0),
         world_pos
     );
 
     return vi;
 }
 
 void write_clip(vec4 world_pos, vec2 snap_offset, ClipArea area) {
-    vec2 uv = world_pos.xy * uDevicePixelRatio +
+    vec2 uv = world_pos.xy * area.common_data.device_pixel_scale +
         world_pos.w * (snap_offset + area.common_data.task_rect.p0 - area.screen_origin);
     vClipMaskUvBounds = vec4(
         area.common_data.task_rect.p0,
         area.common_data.task_rect.p0 + area.common_data.task_rect.size
     );
     vClipMaskUv = vec4(uv, area.common_data.texture_layer_index, world_pos.w);
 }
 #endif //WR_VERTEX_SHADER
--- a/gfx/webrender/res/ps_split_composite.glsl
+++ b/gfx/webrender/res/ps_split_composite.glsl
@@ -68,17 +68,17 @@ void main(void) {
                        dest_task.content_origin;
 
     vec2 local_pos = bilerp(geometry.local[0], geometry.local[1],
                             geometry.local[3], geometry.local[2],
                             aPosition.y, aPosition.x);
     vec4 world_pos = transform.m * vec4(local_pos, 0.0, 1.0);
 
     vec4 final_pos = vec4(
-        dest_origin * world_pos.w + world_pos.xy * uDevicePixelRatio,
+        dest_origin * world_pos.w + world_pos.xy * dest_task.common_data.device_pixel_scale,
         world_pos.w * ci.z,
         world_pos.w
     );
 
     write_clip(
         world_pos,
         vec2(0.0),
         clip_area
--- a/gfx/webrender/res/ps_text_run.glsl
+++ b/gfx/webrender/res/ps_text_run.glsl
@@ -76,17 +76,17 @@ VertexInfo write_text_vertex(RectWithSiz
 #ifdef WR_FEATURE_GLYPH_TRANSFORM
     bool remove_subpx_offset = true;
 #else
     bool remove_subpx_offset = transform.is_axis_aligned;
 #endif
     // Compute the snapping offset only if the scroll node transform is axis-aligned.
     if (remove_subpx_offset) {
         // Transform from local space to device space.
-        float device_scale = uDevicePixelRatio / transform.m[3].w;
+        float device_scale = task.common_data.device_pixel_scale / transform.m[3].w;
         mat2 device_transform = mat2(transform.m) * device_scale;
 
         // Ensure the transformed text offset does not contain a subpixel translation
         // such that glyph snapping is stable for equivalent glyph subpixel positions.
         vec2 device_text_pos = device_transform * text_offset + transform.m[3].xy * device_scale;
         snap_offset = floor(device_text_pos + 0.5) - device_text_pos;
 
         // Snap the glyph offset to a device pixel, using an appropriate bias depending
@@ -125,17 +125,17 @@ VertexInfo write_text_vertex(RectWithSiz
     vec2 local_pos = glyph_rect.p0 + glyph_rect.size * aPosition.xy;
 #endif
 
     // Clamp to the local clip rect.
     local_pos = clamp_rect(local_pos, local_clip_rect);
 
     // Map the clamped local space corner into device space.
     vec4 world_pos = transform.m * vec4(local_pos, 0.0, 1.0);
-    vec2 device_pos = world_pos.xy / world_pos.w * uDevicePixelRatio;
+    vec2 device_pos = world_pos.xy / world_pos.w * task.common_data.device_pixel_scale;
 
     // Apply offsets for the render task to get correct screen location.
     vec2 final_pos = device_pos -
                      task.content_origin +
                      task.common_data.task_rect.p0;
 
     gl_Position = uTransform * vec4(final_pos, z, 1.0);
 
@@ -167,25 +167,25 @@ void main(void) {
         color_mode = uMode;
     }
 
     Glyph glyph = fetch_glyph(ph.specific_prim_address, glyph_index);
     GlyphResource res = fetch_glyph_resource(resource_address);
 
 #ifdef WR_FEATURE_GLYPH_TRANSFORM
     // Transform from local space to glyph space.
-    mat2 glyph_transform = mat2(transform.m) * uDevicePixelRatio;
+    mat2 glyph_transform = mat2(transform.m) * task.common_data.device_pixel_scale;
 
     // Compute the glyph rect in glyph space.
     RectWithSize glyph_rect = RectWithSize(res.offset + glyph_transform * (text.offset + glyph.offset),
                                            res.uv_rect.zw - res.uv_rect.xy);
 
 #else
     // Scale from glyph space to local space.
-    float scale = res.scale / uDevicePixelRatio;
+    float scale = res.scale / task.common_data.device_pixel_scale;
 
     // Compute the glyph rect in local space.
     RectWithSize glyph_rect = RectWithSize(scale * res.offset + text.offset + glyph.offset,
                                            scale * (res.uv_rect.zw - res.uv_rect.xy));
 #endif
 
     vec2 snap_bias;
     // In subpixel mode, the subpixel offset has already been
--- a/gfx/webrender/res/render_task.glsl
+++ b/gfx/webrender/res/render_task.glsl
@@ -6,42 +6,44 @@
 #ifdef WR_VERTEX_SHADER
 #define VECS_PER_RENDER_TASK        2U
 
 uniform HIGHP_SAMPLER_FLOAT sampler2D sRenderTasks;
 
 struct RenderTaskCommonData {
     RectWithSize task_rect;
     float texture_layer_index;
+    float device_pixel_scale;
 };
 
 struct RenderTaskData {
     RenderTaskCommonData common_data;
-    vec3 data1;
+    vec2 user_data;
 };
 
 RenderTaskData fetch_render_task_data(int index) {
     ivec2 uv = get_fetch_uv(index, VECS_PER_RENDER_TASK);
 
     vec4 texel0 = TEXEL_FETCH(sRenderTasks, uv, 0, ivec2(0, 0));
     vec4 texel1 = TEXEL_FETCH(sRenderTasks, uv, 0, ivec2(1, 0));
 
     RectWithSize task_rect = RectWithSize(
         texel0.xy,
         texel0.zw
     );
 
     RenderTaskCommonData common_data = RenderTaskCommonData(
         task_rect,
-        texel1.x
+        texel1.x,
+        texel1.y
     );
 
     RenderTaskData data = RenderTaskData(
         common_data,
-        texel1.yzw
+        texel1.zw
     );
 
     return data;
 }
 
 RenderTaskCommonData fetch_render_task_common_data(int index) {
     ivec2 uv = get_fetch_uv(index, VECS_PER_RENDER_TASK);
 
@@ -50,17 +52,18 @@ RenderTaskCommonData fetch_render_task_c
 
     RectWithSize task_rect = RectWithSize(
         texel0.xy,
         texel0.zw
     );
 
     RenderTaskCommonData data = RenderTaskCommonData(
         task_rect,
-        texel1.x
+        texel1.x,
+        texel1.y
     );
 
     return data;
 }
 
 #define PIC_TYPE_IMAGE          1
 #define PIC_TYPE_TEXT_SHADOW    2
 
@@ -74,43 +77,40 @@ struct PictureTask {
     vec2 content_origin;
 };
 
 PictureTask fetch_picture_task(int address) {
     RenderTaskData task_data = fetch_render_task_data(address);
 
     PictureTask task = PictureTask(
         task_data.common_data,
-        task_data.data1.xy
+        task_data.user_data
     );
 
     return task;
 }
 
 #define CLIP_TASK_EMPTY 0x7FFF
 
 struct ClipArea {
     RenderTaskCommonData common_data;
     vec2 screen_origin;
-    bool local_space;
 };
 
 ClipArea fetch_clip_area(int index) {
     ClipArea area;
 
     if (index >= CLIP_TASK_EMPTY) {
         RectWithSize rect = RectWithSize(vec2(0.0), vec2(0.0));
 
-        area.common_data = RenderTaskCommonData(rect, 0.0);
+        area.common_data = RenderTaskCommonData(rect, 0.0, 1.0);
         area.screen_origin = vec2(0.0);
-        area.local_space = false;
     } else {
         RenderTaskData task_data = fetch_render_task_data(index);
 
         area.common_data = task_data.common_data;
-        area.screen_origin = task_data.data1.xy;
-        area.local_space = task_data.data1.z == 0.0;
+        area.screen_origin = task_data.user_data;
     }
 
     return area;
 }
 
 #endif //WR_VERTEX_SHADER
--- a/gfx/webrender/res/shared.glsl
+++ b/gfx/webrender/res/shared.glsl
@@ -25,17 +25,16 @@
 //======================================================================================
 #ifdef WR_VERTEX_SHADER
     // A generic uniform that shaders can optionally use to configure
     // an operation mode for this batch.
     uniform int uMode;
 
     // Uniform inputs
     uniform mat4 uTransform;       // Orthographic projection
-    uniform float uDevicePixelRatio;
 
     // Attribute inputs
     in vec3 aPosition;
 
     // get_fetch_uv is a macro to work around a macOS Intel driver parsing bug.
     // TODO: convert back to a function once the driver issues are resolved, if ever.
     // https://github.com/servo/webrender/pull/623
     // https://github.com/servo/servo/issues/13953
--- a/gfx/webrender/res/snap.glsl
+++ b/gfx/webrender/res/snap.glsl
@@ -1,29 +1,33 @@
 /* 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/. */
 
 #ifdef WR_VERTEX_SHADER
 
-vec4 compute_snap_positions(mat4 transform, RectWithSize snap_rect) {
+vec4 compute_snap_positions(
+    mat4 transform,
+    RectWithSize snap_rect,
+    float device_pixel_scale
+) {
     // Ensure that the snap rect is at *least* one device pixel in size.
     // TODO(gw): It's not clear to me that this is "correct". Specifically,
     //           how should it interact with sub-pixel snap rects when there
     //           is a transform with scale present? But it does fix
     //           the test cases we have in Servo that are failing without it
     //           and seem better than not having this at all.
-    snap_rect.size = max(snap_rect.size, vec2(1.0 / uDevicePixelRatio));
+    snap_rect.size = max(snap_rect.size, vec2(1.0 / device_pixel_scale));
 
     // Transform the snap corners to the world space.
     vec4 world_snap_p0 = transform * vec4(snap_rect.p0, 0.0, 1.0);
     vec4 world_snap_p1 = transform * vec4(snap_rect.p0 + snap_rect.size, 0.0, 1.0);
     // Snap bounds in world coordinates, adjusted for pixel ratio. XY = top left, ZW = bottom right
-    vec4 world_snap = uDevicePixelRatio * vec4(world_snap_p0.xy, world_snap_p1.xy) /
-                                          vec4(world_snap_p0.ww, world_snap_p1.ww);
+    vec4 world_snap = device_pixel_scale * vec4(world_snap_p0.xy, world_snap_p1.xy) /
+                                           vec4(world_snap_p0.ww, world_snap_p1.ww);
     return world_snap;
 }
 
 vec2 compute_snap_offset_impl(
     vec2 reference_pos,
     mat4 transform,
     RectWithSize snap_rect,
     RectWithSize reference_rect,
@@ -38,20 +42,22 @@ vec2 compute_snap_offset_impl(
     /// Compute the actual world offset for this vertex needed to make it snap.
     return mix(snap_offsets.xy, snap_offsets.zw, normalized_snap_pos);
 }
 
 // Compute a snapping offset in world space (adjusted to pixel ratio),
 // given local position on the transform and a snap rectangle.
 vec2 compute_snap_offset(vec2 local_pos,
                          mat4 transform,
-                         RectWithSize snap_rect) {
+                         RectWithSize snap_rect,
+                         float device_pixel_scale) {
     vec4 snap_positions = compute_snap_positions(
         transform,
-        snap_rect
+        snap_rect,
+        device_pixel_scale
     );
 
     vec2 snap_offsets = compute_snap_offset_impl(
         local_pos,
         transform,
         snap_rect,
         snap_rect,
         snap_positions
--- a/gfx/webrender/src/clip.rs
+++ b/gfx/webrender/src/clip.rs
@@ -1224,21 +1224,19 @@ fn add_clip_node_to_current_chain(
     let clip_spatial_node = &clip_scroll_tree.spatial_nodes[clip_spatial_node_index.0];
     let ref_spatial_node = &clip_scroll_tree.spatial_nodes[spatial_node_index.0];
 
     // Determine the most efficient way to convert between coordinate
     // systems of the primitive and clip node.
     let conversion = if spatial_node_index == clip_spatial_node_index {
         Some(ClipSpaceConversion::Local)
     } else if ref_spatial_node.coordinate_system_id == clip_spatial_node.coordinate_system_id {
-        let scale_offset = ref_spatial_node
-            .coordinate_system_relative_scale_offset
-            .difference(
-                &clip_spatial_node.coordinate_system_relative_scale_offset
-            );
+        let scale_offset = ref_spatial_node.coordinate_system_relative_scale_offset
+            .inverse()
+            .accumulate(&clip_spatial_node.coordinate_system_relative_scale_offset);
         Some(ClipSpaceConversion::ScaleOffset(scale_offset))
     } else {
         let xf = clip_scroll_tree.get_relative_transform(
             clip_spatial_node_index,
             ROOT_SPATIAL_NODE_INDEX,
         );
 
         xf.map(|xf| {
--- a/gfx/webrender/src/debug_render.rs
+++ b/gfx/webrender/src/debug_render.rs
@@ -100,20 +100,29 @@ pub struct DebugRenderer {
     tri_vao: VAO,
     line_vertices: Vec<DebugColorVertex>,
     line_vao: VAO,
     color_program: Program,
 }
 
 impl DebugRenderer {
     pub fn new(device: &mut Device) -> Result<Self, ShaderError> {
-        let font_program = device.create_program("debug_font", "", &DESC_FONT)?;
+        let font_program = device.create_program_linked(
+            "debug_font",
+            "",
+            &DESC_FONT,
+        )?;
+        device.bind_program(&font_program);
         device.bind_shader_samplers(&font_program, &[("sColor0", DebugSampler::Font)]);
 
-        let color_program = device.create_program("debug_color", "", &DESC_COLOR)?;
+        let color_program = device.create_program_linked(
+            "debug_color",
+            "",
+            &DESC_COLOR,
+        )?;
 
         let font_vao = device.create_vao(&DESC_FONT);
         let line_vao = device.create_vao(&DESC_COLOR);
         let tri_vao = device.create_vao(&DESC_COLOR);
 
         let mut font_texture = device.create_texture(TextureTarget::Array, ImageFormat::R8);
         device.init_texture(
             &mut font_texture,
--- a/gfx/webrender/src/device/gl.rs
+++ b/gfx/webrender/src/device/gl.rs
@@ -521,21 +521,34 @@ impl Texture {
 }
 
 impl Drop for Texture {
     fn drop(&mut self) {
         debug_assert!(thread::panicking() || self.id == 0);
     }
 }
 
+/// Temporary state retained by a program when it
+/// is created, discarded when it is linked.
+struct ProgramInitState {
+    base_filename: String,
+    sources: ProgramSources,
+}
+
 pub struct Program {
     id: gl::GLuint,
     u_transform: gl::GLint,
-    u_device_pixel_ratio: gl::GLint,
     u_mode: gl::GLint,
+    init_state: Option<ProgramInitState>,
+}
+
+impl Program {
+    pub fn is_initialized(&self) -> bool {
+        self.init_state.is_none()
+    }
 }
 
 impl Drop for Program {
     fn drop(&mut self) {
         debug_assert!(
             thread::panicking() || self.id == 0,
             "renderer::deinit not called"
         );
@@ -1031,18 +1044,142 @@ impl Device {
         debug_assert!(self.inside_frame);
 
         if self.bound_draw_fbo != fbo_id {
             self.bound_draw_fbo = fbo_id;
             fbo_id.bind(self.gl(), FBOTarget::Draw);
         }
     }
 
+    /// Link a program, attaching the supplied vertex format.
+    /// Ideally, this should be run some time after the program
+    /// is created. This gives some drivers time to compile the
+    /// shader on a background thread, before blocking due to
+    /// an API call accessing the shader.
+    pub fn link_program(
+        &mut self,
+        program: &mut Program,
+        descriptor: &VertexDescriptor,
+    ) -> Result<(), ShaderError> {
+        if let Some(init_state) = program.init_state.take() {
+            let mut build_program = true;
+
+            // See if we hit the binary shader cache
+            if let Some(ref cached_programs) = self.cached_programs {
+                if let Some(binary) = cached_programs.binaries.borrow().get(&init_state.sources) {
+                    let mut link_status = [0];
+                    unsafe {
+                        self.gl.get_program_iv(program.id, gl::LINK_STATUS, &mut link_status);
+                    }
+                    if link_status[0] == 0 {
+                        let error_log = self.gl.get_program_info_log(program.id);
+                        error!(
+                          "Failed to load a program object with a program binary: {} renderer {}\n{}",
+                          &init_state.base_filename,
+                          self.renderer_name,
+                          error_log
+                        );
+                        if let Some(ref program_cache_handler) = cached_programs.program_cache_handler {
+                            program_cache_handler.notify_program_binary_failed(&binary);
+                        }
+                    } else {
+                        build_program = false;
+                    }
+                }
+            }
+
+            // If not, we need to do a normal compile + link pass.
+            if build_program {
+                // Compile the vertex shader
+                let vs_id =
+                    match Device::compile_shader(&*self.gl, &init_state.base_filename, gl::VERTEX_SHADER, &init_state.sources.vs_source) {
+                        Ok(vs_id) => vs_id,
+                        Err(err) => return Err(err),
+                    };
+
+                // Compile the fragment shader
+                let fs_id =
+                    match Device::compile_shader(&*self.gl, &init_state.base_filename, gl::FRAGMENT_SHADER, &init_state.sources.fs_source) {
+                        Ok(fs_id) => fs_id,
+                        Err(err) => {
+                            self.gl.delete_shader(vs_id);
+                            return Err(err);
+                        }
+                    };
+
+                // Attach shaders
+                self.gl.attach_shader(program.id, vs_id);
+                self.gl.attach_shader(program.id, fs_id);
+
+                // Bind vertex attributes
+                for (i, attr) in descriptor
+                    .vertex_attributes
+                    .iter()
+                    .chain(descriptor.instance_attributes.iter())
+                    .enumerate()
+                {
+                    self.gl
+                        .bind_attrib_location(program.id, i as gl::GLuint, attr.name);
+                }
+
+                if self.cached_programs.is_some() {
+                    self.gl.program_parameter_i(program.id, gl::PROGRAM_BINARY_RETRIEVABLE_HINT, gl::TRUE as gl::GLint);
+                }
+
+                // Link!
+                self.gl.link_program(program.id);
+
+                // GL recommends detaching and deleting shaders once the link
+                // is complete (whether successful or not). This allows the driver
+                // to free any memory associated with the parsing and compilation.
+                self.gl.detach_shader(program.id, vs_id);
+                self.gl.detach_shader(program.id, fs_id);
+                self.gl.delete_shader(vs_id);
+                self.gl.delete_shader(fs_id);
+
+                let mut link_status = [0];
+                unsafe {
+                    self.gl.get_program_iv(program.id, gl::LINK_STATUS, &mut link_status);
+                }
+                if link_status[0] == 0 {
+                    let error_log = self.gl.get_program_info_log(program.id);
+                    error!(
+                        "Failed to link shader program: {}\n{}",
+                        &init_state.base_filename,
+                        error_log
+                    );
+                    self.gl.delete_program(program.id);
+                    return Err(ShaderError::Link(init_state.base_filename.clone(), error_log));
+                }
+
+                if let Some(ref cached_programs) = self.cached_programs {
+                    if !cached_programs.binaries.borrow().contains_key(&init_state.sources) {
+                        let (buffer, format) = self.gl.get_program_binary(program.id);
+                        if buffer.len() > 0 {
+                            let program_binary = Arc::new(ProgramBinary::new(buffer, format, &init_state.sources));
+                            if let Some(ref program_cache_handler) = cached_programs.program_cache_handler {
+                                program_cache_handler.notify_binary_added(&program_binary);
+                            }
+                            cached_programs.binaries.borrow_mut().insert(init_state.sources, program_binary);
+                        }
+                    }
+                }
+            }
+
+            // If we get here, the link succeeded, so get the uniforms.
+            program.u_transform = self.gl.get_uniform_location(program.id, "uTransform");
+            program.u_mode = self.gl.get_uniform_location(program.id, "uMode");
+        }
+
+        Ok(())
+    }
+
     pub fn bind_program(&mut self, program: &Program) {
         debug_assert!(self.inside_frame);
+        debug_assert!(program.init_state.is_none());
 
         if self.bound_program != program.id {
             self.gl.use_program(program.id);
             self.bound_program = program.id;
             self.program_mode_id = UniformLocation(program.u_mode);
         }
     }
 
@@ -1426,21 +1563,35 @@ impl Device {
         external.id = 0;
     }
 
     pub fn delete_program(&mut self, mut program: Program) {
         self.gl.delete_program(program.id);
         program.id = 0;
     }
 
+    /// Create a shader program and link it immediately.
+    pub fn create_program_linked(
+        &mut self,
+        base_filename: &str,
+        features: &str,
+        descriptor: &VertexDescriptor,
+    ) -> Result<Program, ShaderError> {
+        let mut program = self.create_program(base_filename, features)?;
+        self.link_program(&mut program, descriptor)?;
+        Ok(program)
+    }
+
+    /// Create a shader program. This does minimal amount of work
+    /// to start loading a binary shader. The main part of the
+    /// work is done in link_program.
     pub fn create_program(
         &mut self,
         base_filename: &str,
         features: &str,
-        descriptor: &VertexDescriptor,
     ) -> Result<Program, ShaderError> {
         debug_assert!(self.inside_frame);
 
         let gl_version_string = get_shader_version(&*self.gl);
 
         let (vs_source, fs_source) = build_shader_strings(
             gl_version_string,
             features,
@@ -1448,141 +1599,49 @@ impl Device {
             &self.resource_override_path,
         );
 
         let sources = ProgramSources::new(self.renderer_name.clone(), vs_source, fs_source);
 
         // Create program
         let pid = self.gl.create_program();
 
-        let mut loaded = false;
-
+        // Attempt to load a cached binary if possible.
         if let Some(ref cached_programs) = self.cached_programs {
-            if let Some(binary) = cached_programs.binaries.borrow().get(&sources)
-            {
+            if let Some(binary) = cached_programs.binaries.borrow().get(&sources) {
                 self.gl.program_binary(pid, binary.format, &binary.binary);
-
-                let mut link_status = [0];
-                unsafe {
-                    self.gl.get_program_iv(pid, gl::LINK_STATUS, &mut link_status);
-                }
-                if link_status[0] == 0 {
-                    let error_log = self.gl.get_program_info_log(pid);
-                    error!(
-                      "Failed to load a program object with a program binary: {} renderer {}\n{}",
-                      base_filename,
-                      self.renderer_name,
-                      error_log
-                    );
-                    if let Some(ref program_cache_handler) = cached_programs.program_cache_handler {
-                        program_cache_handler.notify_program_binary_failed(&binary);
-                    }
-                } else {
-                    loaded = true;
-                }
             }
         }
 
-        if loaded == false {
-            // Compile the vertex shader
-            let vs_id =
-                match Device::compile_shader(&*self.gl, base_filename, gl::VERTEX_SHADER, &sources.vs_source) {
-                    Ok(vs_id) => vs_id,
-                    Err(err) => return Err(err),
-                };
-
-            // Compiler the fragment shader
-            let fs_id =
-                match Device::compile_shader(&*self.gl, base_filename, gl::FRAGMENT_SHADER, &sources.fs_source) {
-                    Ok(fs_id) => fs_id,
-                    Err(err) => {
-                        self.gl.delete_shader(vs_id);
-                        return Err(err);
-                    }
-                };
-
-            // Attach shaders
-            self.gl.attach_shader(pid, vs_id);
-            self.gl.attach_shader(pid, fs_id);
-
-            // Bind vertex attributes
-            for (i, attr) in descriptor
-                .vertex_attributes
-                .iter()
-                .chain(descriptor.instance_attributes.iter())
-                .enumerate()
-            {
-                self.gl
-                    .bind_attrib_location(pid, i as gl::GLuint, attr.name);
-            }
-
-            if self.cached_programs.is_some() {
-                self.gl.program_parameter_i(pid, gl::PROGRAM_BINARY_RETRIEVABLE_HINT, gl::TRUE as gl::GLint);
-            }
-
-            // Link!
-            self.gl.link_program(pid);
-
-            // GL recommends detaching and deleting shaders once the link
-            // is complete (whether successful or not). This allows the driver
-            // to free any memory associated with the parsing and compilation.
-            self.gl.detach_shader(pid, vs_id);
-            self.gl.detach_shader(pid, fs_id);
-            self.gl.delete_shader(vs_id);
-            self.gl.delete_shader(fs_id);
-
-            let mut link_status = [0];
-            unsafe {
-                self.gl.get_program_iv(pid, gl::LINK_STATUS, &mut link_status);
-            }
-            if link_status[0] == 0 {
-                let error_log = self.gl.get_program_info_log(pid);
-                error!(
-                    "Failed to link shader program: {}\n{}",
-                    base_filename,
-                    error_log
-                );
-                self.gl.delete_program(pid);
-                return Err(ShaderError::Link(base_filename.to_string(), error_log));
-            }
-        }
-
-        if let Some(ref cached_programs) = self.cached_programs {
-            if !cached_programs.binaries.borrow().contains_key(&sources) {
-                let (buffer, format) = self.gl.get_program_binary(pid);
-                if buffer.len() > 0 {
-                    let program_binary = Arc::new(ProgramBinary::new(buffer, format, &sources));
-                    if let Some(ref program_cache_handler) = cached_programs.program_cache_handler {
-                        program_cache_handler.notify_binary_added(&program_binary);
-                    }
-                    cached_programs.binaries.borrow_mut().insert(sources, program_binary);
-                }
-            }
-        }
+        // Set up the init state that will be used in link_program.
+        let init_state = Some(ProgramInitState {
+            base_filename: base_filename.to_owned(),
+            sources,
+        });
 
         let u_transform = self.gl.get_uniform_location(pid, "uTransform");
-        let u_device_pixel_ratio = self.gl.get_uniform_location(pid, "uDevicePixelRatio");
         let u_mode = self.gl.get_uniform_location(pid, "uMode");
 
         let program = Program {
             id: pid,
             u_transform,
-            u_device_pixel_ratio,
             u_mode,
+            init_state,
         };
 
-        self.bind_program(&program);
-
         Ok(program)
     }
 
     pub fn bind_shader_samplers<S>(&mut self, program: &Program, bindings: &[(&'static str, S)])
     where
         S: Into<TextureSlot> + Copy,
     {
+        // bind_program() must be called before calling bind_shader_samplers
+        assert_eq!(self.bound_program, program.id);
+
         for binding in bindings {
             let u_location = self.gl.get_uniform_location(program.id, binding.0);
             if u_location != -1 {
                 self.bind_program(program);
                 self.gl
                     .uniform_1i(u_location, binding.1.into().0 as gl::GLint);
             }
         }
@@ -1596,18 +1655,16 @@ impl Device {
     pub fn set_uniforms(
         &self,
         program: &Program,
         transform: &Transform3D<f32>,
     ) {
         debug_assert!(self.inside_frame);
         self.gl
             .uniform_matrix_4fv(program.u_transform, false, &transform.to_row_major_array());
-        self.gl
-            .uniform_1f(program.u_device_pixel_ratio, self.device_pixel_ratio);
     }
 
     pub fn switch_mode(&self, mode: i32) {
         debug_assert!(self.inside_frame);
         self.gl.uniform_1i(self.program_mode_id.0, mode);
     }
 
     pub fn create_pbo(&mut self) -> PBO {
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -394,17 +394,17 @@ impl FrameBuilder {
 
             if let RenderPassKind::OffScreen { ref texture_cache, .. } = pass.kind {
                 has_texture_cache_tasks |= !texture_cache.is_empty();
             }
         }
 
         let gpu_cache_frame_id = gpu_cache.end_frame(gpu_cache_profile);
 
-        render_tasks.write_task_data();
+        render_tasks.write_task_data(device_pixel_scale);
 
         resource_cache.end_frame();
 
         Frame {
             window_size: self.window_size,
             inner_rect: self.screen_rect,
             device_pixel_ratio: device_pixel_scale.0,
             background_color: self.background_color,
--- a/gfx/webrender/src/gpu_glyph_renderer.rs
+++ b/gfx/webrender/src/gpu_glyph_renderer.rs
@@ -8,17 +8,17 @@ use api::{DeviceIntPoint, DeviceIntRect,
 use api::{ImageFormat, TextureTarget};
 use debug_colors;
 use device::{Device, Texture, TextureFilter, VAO};
 use euclid::{Point2D, Size2D, Transform3D, TypedVector2D, Vector2D};
 use internal_types::RenderTargetInfo;
 use pathfinder_gfx_utils::ShelfBinPacker;
 use profiler::GpuProfileTag;
 use renderer::{self, ImageBufferKind, Renderer, RendererError, RendererStats};
-use renderer::{TextureSampler, VertexArrayKind};
+use renderer::{TextureSampler, VertexArrayKind, ShaderPrecacheFlags};
 use shade::{LazilyCompiledShader, ShaderKind};
 use tiling::GlyphJob;
 
 // The area lookup table in uncompressed grayscale TGA format (TGA image format 3).
 static AREA_LUT_TGA_BYTES: &'static [u8] = include_bytes!("../res/area-lut.tga");
 
 const HORIZONTAL_BIN_PADDING: i32 = 3;
 
@@ -37,17 +37,17 @@ pub struct GpuGlyphRenderer {
     pub vector_cover_vao: VAO,
 
     // These are Pathfinder shaders, used for rendering vector graphics.
     vector_stencil: LazilyCompiledShader,
     vector_cover: LazilyCompiledShader,
 }
 
 impl GpuGlyphRenderer {
-    pub fn new(device: &mut Device, prim_vao: &VAO, precache_shaders: bool)
+    pub fn new(device: &mut Device, prim_vao: &VAO, precache_flags: ShaderPrecacheFlags)
                -> Result<GpuGlyphRenderer, RendererError> {
         // Make sure the area LUT is uncompressed grayscale TGA, 8bpp.
         debug_assert!(AREA_LUT_TGA_BYTES[2] == 3);
         debug_assert!(AREA_LUT_TGA_BYTES[16] == 8);
         let area_lut_width = (AREA_LUT_TGA_BYTES[12] as u32) |
             ((AREA_LUT_TGA_BYTES[13] as u32) << 8);
         let area_lut_height = (AREA_LUT_TGA_BYTES[14] as u32) |
             ((AREA_LUT_TGA_BYTES[15] as u32) << 8);
@@ -69,24 +69,24 @@ impl GpuGlyphRenderer {
                                                                     prim_vao);
 
         // Load Pathfinder vector graphics shaders.
         let vector_stencil = try!{
             LazilyCompiledShader::new(ShaderKind::VectorStencil,
                                       "pf_vector_stencil",
                                       &[ImageBufferKind::Texture2D.get_feature_string()],
                                       device,
-                                      precache_shaders)
+                                      precache_flags)
         };
         let vector_cover = try!{
             LazilyCompiledShader::new(ShaderKind::VectorCover,
                                       "pf_vector_cover",
                                       &[ImageBufferKind::Texture2D.get_feature_string()],
                                       device,
-                                      precache_shaders)
+                                      precache_flags)
         };
 
         Ok(GpuGlyphRenderer {
             area_lut_texture,
             vector_stencil_vao,
             vector_cover_vao,
             vector_stencil,
             vector_cover,
--- a/gfx/webrender/src/lib.rs
+++ b/gfx/webrender/src/lib.rs
@@ -181,12 +181,12 @@ pub extern crate webrender_api;
 
 #[doc(hidden)]
 pub use device::{build_shader_strings, ReadPixelsFormat, UploadMethod, VertexUsageHint};
 pub use device::{ProgramBinary, ProgramCache, ProgramCacheObserver, ProgramSources};
 pub use frame_builder::ChasePrimitive;
 pub use renderer::{AsyncPropertySampler, CpuProfile, DebugFlags, OutputImageHandler, RendererKind};
 pub use renderer::{ExternalImage, ExternalImageHandler, ExternalImageSource, GpuProfile};
 pub use renderer::{GraphicsApi, GraphicsApiInfo, PipelineInfo, Renderer, RendererOptions};
-pub use renderer::{RendererStats, SceneBuilderHooks, ThreadListener};
+pub use renderer::{RendererStats, SceneBuilderHooks, ThreadListener, ShaderPrecacheFlags};
 pub use renderer::MAX_VERTEX_TEXTURE_WIDTH;
 pub use webrender_api as api;
 pub use resource_cache::intersect_for_tile;
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -126,17 +126,18 @@ impl<F, T> SpaceMapper<F, T> where F: fm
             let target_spatial_node = &spatial_nodes[target_node_index.0];
             self.current_target_spatial_node_index = target_node_index;
 
             self.kind = if self.ref_spatial_node_index == target_node_index {
                 CoordinateSpaceMapping::Local
             } else if ref_spatial_node.coordinate_system_id == target_spatial_node.coordinate_system_id {
                 CoordinateSpaceMapping::ScaleOffset(
                     ref_spatial_node.coordinate_system_relative_scale_offset
-                        .difference(
+                        .inverse()
+                        .accumulate(
                             &target_spatial_node.coordinate_system_relative_scale_offset
                         )
                 )
             } else {
                 let transform = clip_scroll_tree.get_relative_transform(
                     target_node_index,
                     self.ref_spatial_node_index,
                 ).expect("bug: should have already been culled");
@@ -514,16 +515,68 @@ impl BrushSegment {
             local_rect,
             clip_task_id: BrushSegmentTaskId::Opaque,
             may_need_clip_mask,
             edge_flags,
             extra_data,
             brush_flags,
         }
     }
+
+    pub fn update_clip_task(
+        &mut self,
+        clip_chain: Option<&ClipChainInstance>,
+        prim_bounding_rect: WorldRect,
+        root_spatial_node_index: SpatialNodeIndex,
+        pic_state: &mut PictureState,
+        frame_context: &FrameBuildingContext,
+        frame_state: &mut FrameBuildingState,
+    ) {
+        match clip_chain {
+            Some(clip_chain) => {
+                if !clip_chain.needs_mask ||
+                   (!self.may_need_clip_mask && !clip_chain.has_non_local_clips) {
+                    self.clip_task_id = BrushSegmentTaskId::Opaque;
+                    return;
+                }
+
+                let (device_rect, _, _) = match get_raster_rects(
+                    clip_chain.pic_clip_rect,
+                    &pic_state.map_pic_to_raster,
+                    &pic_state.map_raster_to_world,
+                    prim_bounding_rect,
+                    frame_context.device_pixel_scale,
+                ) {
+                    Some(info) => info,
+                    None => {
+                        self.clip_task_id = BrushSegmentTaskId::Empty;
+                        return;
+                    }
+                };
+
+                let clip_task = RenderTask::new_mask(
+                    device_rect.to_i32(),
+                    clip_chain.clips_range,
+                    root_spatial_node_index,
+                    frame_state.clip_store,
+                    frame_state.gpu_cache,
+                    frame_state.resource_cache,
+                    frame_state.render_tasks,
+                    &mut frame_state.resources.clip_data_store,
+                );
+
+                let clip_task_id = frame_state.render_tasks.add(clip_task);
+                pic_state.tasks.push(clip_task_id);
+                self.clip_task_id = BrushSegmentTaskId::RenderTaskId(clip_task_id);
+            }
+            None => {
+                self.clip_task_id = BrushSegmentTaskId::Empty;
+            }
+        }
+    }
 }
 
 pub type BrushSegmentVec = SmallVec<[BrushSegment; 8]>;
 
 #[derive(Debug)]
 pub struct BrushSegmentDescriptor {
     pub segments: BrushSegmentVec,
 }
@@ -2230,78 +2283,60 @@ impl Primitive {
             frame_state,
         );
 
         let segment_desc = match brush.segment_desc {
             Some(ref mut description) => description,
             None => return false,
         };
 
-        for segment in &mut segment_desc.segments {
-            // Build a clip chain for the smaller segment rect. This will
-            // often manage to eliminate most/all clips, and sometimes
-            // clip the segment completely.
-            let segment_clip_chain = frame_state
-                .clip_store
-                .build_clip_chain_instance(
-                    self.metadata.clip_chain_id,
-                    segment.local_rect,
-                    self.metadata.local_clip_rect,
-                    prim_context.spatial_node_index,
-                    &pic_state.map_local_to_pic,
-                    &pic_state.map_pic_to_world,
-                    &frame_context.clip_scroll_tree,
-                    frame_state.gpu_cache,
-                    frame_state.resource_cache,
-                    frame_context.device_pixel_scale,
-                    &frame_context.world_rect,
-                    clip_node_collector,
-                    &mut frame_state.resources.clip_data_store,
-                );
-
-            match segment_clip_chain {
-                Some(segment_clip_chain) => {
-                    if !segment_clip_chain.needs_mask ||
-                       (!segment.may_need_clip_mask && !segment_clip_chain.has_non_local_clips) {
-                        segment.clip_task_id = BrushSegmentTaskId::Opaque;
-                        continue;
-                    }
-
-                    let (device_rect, _, _) = match get_raster_rects(
-                        segment_clip_chain.pic_clip_rect,
-                        &pic_state.map_pic_to_raster,
-                        &pic_state.map_raster_to_world,
-                        prim_bounding_rect,
-                        frame_context.device_pixel_scale,
-                    ) {
-                        Some(info) => info,
-                        None => {
-                            segment.clip_task_id = BrushSegmentTaskId::Empty;
-                            continue;
-                        }
-                    };
-
-                    let clip_task = RenderTask::new_mask(
-                        device_rect.to_i32(),
-                        segment_clip_chain.clips_range,
-                        root_spatial_node_index,
-                        frame_state.clip_store,
+        // If we only built 1 segment, there is no point in re-running
+        // the clip chain builder. Instead, just use the clip chain
+        // instance that was built for the main primitive. This is a
+        // significant optimization for the common case.
+        if segment_desc.segments.len() == 1 {
+            segment_desc.segments[0].update_clip_task(
+                Some(prim_clip_chain),
+                prim_bounding_rect,
+                root_spatial_node_index,
+                pic_state,
+                frame_context,
+                frame_state,
+            );
+        } else {
+            for segment in &mut segment_desc.segments {
+                // Build a clip chain for the smaller segment rect. This will
+                // often manage to eliminate most/all clips, and sometimes
+                // clip the segment completely.
+                let segment_clip_chain = frame_state
+                    .clip_store
+                    .build_clip_chain_instance(
+                        self.metadata.clip_chain_id,
+                        segment.local_rect,
+                        self.metadata.local_clip_rect,
+                        prim_context.spatial_node_index,
+                        &pic_state.map_local_to_pic,
+                        &pic_state.map_pic_to_world,
+                        &frame_context.clip_scroll_tree,
                         frame_state.gpu_cache,
                         frame_state.resource_cache,
-                        frame_state.render_tasks,
+                        frame_context.device_pixel_scale,
+                        &frame_context.world_rect,
+                        clip_node_collector,
                         &mut frame_state.resources.clip_data_store,
                     );
 
-                    let clip_task_id = frame_state.render_tasks.add(clip_task);
-                    pic_state.tasks.push(clip_task_id);
-                    segment.clip_task_id = BrushSegmentTaskId::RenderTaskId(clip_task_id);
-                }
-                None => {
-                    segment.clip_task_id = BrushSegmentTaskId::Empty;
-                }
+                segment.update_clip_task(
+                    segment_clip_chain.as_ref(),
+                    prim_bounding_rect,
+                    root_spatial_node_index,
+                    pic_state,
+                    frame_context,
+                    frame_state,
+                );
             }
         }
 
         true
     }
 
     // Returns true if the primitive *might* need a clip mask. If
     // false, there is no need to even check for clip masks for
--- a/gfx/webrender/src/render_task.rs
+++ b/gfx/webrender/src/render_task.rs
@@ -1,26 +1,27 @@
 /* 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/. */
 
-use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceSize, DeviceIntSideOffsets, ImageDescriptor, ImageFormat};
+use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceSize, DeviceIntSideOffsets};
+use api::{DevicePixelScale, ImageDescriptor, ImageFormat};
 #[cfg(feature = "pathfinder")]
 use api::FontRenderMode;
 use border::BorderCacheKey;
 use box_shadow::{BoxShadowCacheKey};
 use clip::{ClipDataStore, ClipItem, ClipStore, ClipNodeRange};
 use clip_scroll_tree::SpatialNodeIndex;
 use device::TextureFilter;
 #[cfg(feature = "pathfinder")]
 use euclid::{TypedPoint2D, TypedVector2D};
 use freelist::{FreeList, FreeListHandle, WeakFreeListHandle};
 use glyph_rasterizer::GpuGlyphCacheKey;
 use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
-use gpu_types::{BorderInstance, ImageSource, RasterizationSpace, UvRectKind};
+use gpu_types::{BorderInstance, ImageSource, UvRectKind};
 use internal_types::{CacheTextureId, FastHashMap, SavedTargetIndex};
 #[cfg(feature = "pathfinder")]
 use pathfinder_partitioner::mesh::Mesh;
 use picture::PictureCacheKey;
 use prim_store::{PrimitiveIndex, ImageCacheKey};
 #[cfg(feature = "debugger")]
 use print_tree::{PrintTreePrinter};
 use render_backend::FrameId;
@@ -126,19 +127,19 @@ impl RenderTaskTree {
         }
     }
 
     pub fn get_task_address(&self, id: RenderTaskId) -> RenderTaskAddress {
         debug_assert_eq!(self.frame_id, id.1);
         RenderTaskAddress(id.0)
     }
 
-    pub fn write_task_data(&mut self) {
+    pub fn write_task_data(&mut self, device_pixel_scale: DevicePixelScale) {
         for task in &self.tasks {
-            self.task_data.push(task.write_task_data());
+            self.task_data.push(task.write_task_data(device_pixel_scale));
         }
     }
 
     pub fn save_target(&mut self) -> SavedTargetIndex {
         let id = self.next_saved;
         self.next_saved.0 += 1;
         id
     }
@@ -704,64 +705,55 @@ impl RenderTask {
                 UvRectKind::Rect
             }
         }
     }
 
     // Write (up to) 8 floats of data specific to the type
     // of render task that is provided to the GPU shaders
     // via a vertex texture.
-    pub fn write_task_data(&self) -> RenderTaskData {
+    pub fn write_task_data(&self, device_pixel_scale: DevicePixelScale) -> RenderTaskData {
         // NOTE: The ordering and layout of these structures are
         //       required to match both the GPU structures declared
         //       in prim_shared.glsl, and also the uses in submit_batch()
         //       in renderer.rs.
         // TODO(gw): Maybe there's a way to make this stuff a bit
         //           more type-safe. Although, it will always need
         //           to be kept in sync with the GLSL code anyway.
 
         let data = match self.kind {
             RenderTaskKind::Picture(ref task) => {
                 // Note: has to match `PICTURE_TYPE_*` in shaders
                 [
                     task.content_origin.x as f32,
                     task.content_origin.y as f32,
-                    0.0,
                 ]
             }
             RenderTaskKind::CacheMask(ref task) => {
                 [
                     task.actual_rect.origin.x as f32,
                     task.actual_rect.origin.y as f32,
-                    RasterizationSpace::Screen as i32 as f32,
-                ]
-            }
-            RenderTaskKind::ClipRegion(..) => {
-                [
-                    0.0,
-                    0.0,
-                    RasterizationSpace::Local as i32 as f32,
                 ]
             }
             RenderTaskKind::VerticalBlur(ref task) |
             RenderTaskKind::HorizontalBlur(ref task) => {
                 [
                     task.blur_std_deviation,
                     0.0,
-                    0.0,
                 ]
             }
             RenderTaskKind::Glyph(_) => {
-                [1.0, 0.0, 0.0]
+                [1.0, 0.0]
             }
+            RenderTaskKind::ClipRegion(..) |
             RenderTaskKind::Readback(..) |
             RenderTaskKind::Scaling(..) |
             RenderTaskKind::Border(..) |
             RenderTaskKind::Blit(..) => {
-                [0.0; 3]
+                [0.0; 2]
             }
         };
 
         let (mut target_rect, target_index) = self.get_target_rect();
         // The primitives inside a fixed-location render task
         // are already placed to their corresponding positions,
         // so the shader doesn't need to shift by the origin.
         if let RenderTaskLocation::Fixed(_) = self.location {
@@ -770,19 +762,19 @@ impl RenderTask {
 
         RenderTaskData {
             data: [
                 target_rect.origin.x as f32,
                 target_rect.origin.y as f32,
                 target_rect.size.width as f32,
                 target_rect.size.height as f32,
                 target_index.0 as f32,
+                device_pixel_scale.0,
                 data[0],
                 data[1],
-                data[2],
             ]
         }
     }
 
     pub fn get_texture_address(&self, gpu_cache: &GpuCache) -> GpuCacheAddress {
         match self.kind {
             RenderTaskKind::Picture(ref info) => {
                 gpu_cache.get_address(&info.uv_rect_handle)
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -703,17 +703,17 @@ impl CpuProfile {
     }
 }
 
 #[cfg(not(feature = "pathfinder"))]
 pub struct GpuGlyphRenderer;
 
 #[cfg(not(feature = "pathfinder"))]
 impl GpuGlyphRenderer {
-    fn new(_: &mut Device, _: &VAO, _: bool) -> Result<GpuGlyphRenderer, RendererError> {
+    fn new(_: &mut Device, _: &VAO, _: ShaderPrecacheFlags) -> Result<GpuGlyphRenderer, RendererError> {
         Ok(GpuGlyphRenderer)
     }
 }
 
 #[cfg(not(feature = "pathfinder"))]
 struct StenciledGlyphPage;
 
 /// A Texture that has been initialized by the `device` module and is ready to
@@ -1019,18 +1019,21 @@ struct GpuCacheTexture {
     bus: GpuCacheBus,
 }
 
 impl GpuCacheTexture {
     fn new(device: &mut Device, use_scatter: bool) -> Result<Self, RendererError> {
         let texture = device.create_texture(TextureTarget::Default, ImageFormat::RGBAF32);
 
         let bus = if use_scatter {
-            let program = device
-                .create_program("gpu_cache_update", "", &desc::GPU_CACHE_UPDATE)?;
+            let program = device.create_program_linked(
+                "gpu_cache_update",
+                "",
+                &desc::GPU_CACHE_UPDATE,
+            )?;
             let buf_position = device.create_vbo();
             let buf_value = device.create_vbo();
             //Note: the vertex attributes have to be supplied in the same order
             // as for program creation, but each assigned to a different stream.
             let vao = device.create_custom_vao(&[
                 buf_position.stream_with(&desc::GPU_CACHE_UPDATE.vertex_attributes[0..1]),
                 buf_value   .stream_with(&desc::GPU_CACHE_UPDATE.vertex_attributes[1..2]),
             ]);
@@ -1709,17 +1712,17 @@ impl Renderer {
 
         let prim_vao = device.create_vao(&desc::PRIM_INSTANCES);
         device.bind_vao(&prim_vao);
         device.update_vao_indices(&prim_vao, &quad_indices, VertexUsageHint::Static);
         device.update_vao_main_vertices(&prim_vao, &quad_vertices, VertexUsageHint::Static);
 
         let gpu_glyph_renderer = try!(GpuGlyphRenderer::new(&mut device,
                                                             &prim_vao,
-                                                            options.precache_shaders));
+                                                            options.precache_flags));
 
         let blur_vao = device.create_vao_with_new_instances(&desc::BLUR, &prim_vao);
         let clip_vao = device.create_vao_with_new_instances(&desc::CLIP, &prim_vao);
         let border_vao = device.create_vao_with_new_instances(&desc::BORDER, &prim_vao);
         let scale_vao = device.create_vao_with_new_instances(&desc::SCALE, &prim_vao);
         let texture_cache_upload_pbo = device.create_pbo();
 
         let texture_resolver = TextureResolver::new(&mut device);
@@ -4420,23 +4423,38 @@ pub trait AsyncPropertySampler {
     /// (i.e. that will trigger a render). The list of frame messages returned
     /// are processed as though they were part of the original transaction.
     fn sample(&self) -> Vec<FrameMsg>;
     /// This is called exactly once, when the render backend thread is about to
     /// terminate.
     fn deregister(&self);
 }
 
+/// Flags that control how shaders are pre-cached, if at all.
+bitflags! {
+    #[derive(Default)]
+    pub struct ShaderPrecacheFlags: u32 {
+        /// Needed for const initialization
+        const EMPTY                 = 0;
+
+        /// Only start async compile
+        const ASYNC_COMPILE         = 1 << 2;
+
+        /// Do a full compile/link during startup
+        const FULL_COMPILE          = 1 << 3;
+    }
+}
+
 pub struct RendererOptions {
     pub device_pixel_ratio: f32,
     pub resource_override_path: Option<PathBuf>,
     pub enable_aa: bool,
     pub enable_dithering: bool,
     pub max_recorded_profiles: usize,
-    pub precache_shaders: bool,
+    pub precache_flags: ShaderPrecacheFlags,
     pub renderer_kind: RendererKind,
     pub enable_subpixel_aa: bool,
     pub clear_color: Option<ColorF>,
     pub enable_clear_scissor: bool,
     pub max_texture_size: Option<u32>,
     pub scatter_gpu_cache_updates: bool,
     pub upload_method: UploadMethod,
     pub workers: Option<Arc<ThreadPool>>,
@@ -4459,17 +4477,17 @@ impl Default for RendererOptions {
     fn default() -> Self {
         RendererOptions {
             device_pixel_ratio: 1.0,
             resource_override_path: None,
             enable_aa: true,
             enable_dithering: true,
             debug_flags: DebugFlags::empty(),
             max_recorded_profiles: 0,
-            precache_shaders: false,
+            precache_flags: ShaderPrecacheFlags::empty(),
             renderer_kind: RendererKind::Native,
             enable_subpixel_aa: false,
             clear_color: Some(ColorF::new(1.0, 1.0, 1.0, 1.0)),
             enable_clear_scissor: true,
             max_texture_size: None,
             // Scattered GPU cache updates haven't met a test that would show their superiority yet.
             scatter_gpu_cache_updates: false,
             // This is best as `Immediate` on Angle, or `Pixelbuffer(Dynamic)` on GL,
--- a/gfx/webrender/src/resource_cache.rs
+++ b/gfx/webrender/src/resource_cache.rs
@@ -1438,20 +1438,21 @@ impl ResourceCache {
             return;
         }
 
         self.blob_image_handler
             .as_mut()
             .unwrap()
             .prepare_resources(&self.resources, &self.missing_blob_images);
 
+        let is_low_priority = false;
         let rasterized_blobs = self.blob_image_rasterizer
             .as_mut()
             .unwrap()
-            .rasterize(&self.missing_blob_images);
+            .rasterize(&self.missing_blob_images, is_low_priority);
 
         self.add_rasterized_blob_images(rasterized_blobs);
 
         self.missing_blob_images.clear();
     }
 
     fn update_texture_cache(&mut self, gpu_cache: &mut GpuCache) {
         for request in self.pending_image_requests.drain() {
@@ -1879,17 +1880,17 @@ impl ResourceCache {
                             },
                             dirty_rect: None,
                         }
                     ];
 
                     let blob_handler = self.blob_image_handler.as_mut().unwrap();
                     blob_handler.prepare_resources(&self.resources, blob_request_params);
                     let mut rasterizer = blob_handler.create_blob_rasterizer();
-                    let (_, result) = rasterizer.rasterize(blob_request_params).pop().unwrap();
+                    let (_, result) = rasterizer.rasterize(blob_request_params, false).pop().unwrap();
                     let result = result.expect("Blob rasterization failed");
 
                     assert_eq!(result.rasterized_rect.size, desc.size);
                     assert_eq!(result.data.len(), desc.compute_total_size() as usize);
 
                     num_blobs += 1;
                     #[cfg(feature = "png")]
                     CaptureConfig::save_png(
--- a/gfx/webrender/src/scene_builder.rs
+++ b/gfx/webrender/src/scene_builder.rs
@@ -442,20 +442,21 @@ impl SceneBuilder {
                 built_scene = Some(BuiltScene {
                     scene: new_scene,
                     frame_builder,
                     clip_scroll_tree,
                 });
             }
         }
 
+        let is_low_priority = false;
         let blob_requests = replace(&mut txn.blob_requests, Vec::new());
         let mut rasterized_blobs = txn.blob_rasterizer.as_mut().map_or(
             Vec::new(),
-            |rasterizer| rasterizer.rasterize(&blob_requests),
+            |rasterizer| rasterizer.rasterize(&blob_requests, is_low_priority),
         );
         rasterized_blobs.append(&mut txn.rasterized_blobs);
 
         drain_filter(
             &mut txn.notifications,
             |n| { n.when() == Checkpoint::SceneBuilt },
             |n| { n.notify(); },
         );
@@ -565,19 +566,20 @@ impl LowPrioritySceneBuilder {
                     break;
                 }
             }
         }
     }
 
     fn process_transaction(&mut self, mut txn: Box<Transaction>) -> Box<Transaction> {
         let blob_requests = replace(&mut txn.blob_requests, Vec::new());
+        let is_low_priority = true;
         let mut more_rasterized_blobs = txn.blob_rasterizer.as_mut().map_or(
             Vec::new(),
-            |rasterizer| rasterizer.rasterize(&blob_requests),
+            |rasterizer| rasterizer.rasterize(&blob_requests, is_low_priority),
         );
         txn.rasterized_blobs.append(&mut more_rasterized_blobs);
 
         if self.simulate_slow_ms > 0 {
             thread::sleep(Duration::from_millis(self.simulate_slow_ms as u64));
         }
 
         txn
--- a/gfx/webrender/src/shade.rs
+++ b/gfx/webrender/src/shade.rs
@@ -9,17 +9,17 @@ use api::{
 use batch::{BatchKey, BatchKind, BrushBatchKind};
 use device::{Device, Program, ShaderError};
 use euclid::{Transform3D};
 use glyph_rasterizer::GlyphFormat;
 use renderer::{
     desc,
     MAX_VERTEX_TEXTURE_WIDTH,
     BlendMode, DebugFlags, ImageBufferKind, RendererError, RendererOptions,
-    TextureSampler, VertexArrayKind,
+    TextureSampler, VertexArrayKind, ShaderPrecacheFlags,
 };
 
 use gleam::gl::GlType;
 use time::precise_time_ns;
 
 
 impl ImageBufferKind {
     pub(crate) fn get_feature_string(&self) -> &'static str {
@@ -74,35 +74,31 @@ pub struct LazilyCompiledShader {
 }
 
 impl LazilyCompiledShader {
     pub(crate) fn new(
         kind: ShaderKind,
         name: &'static str,
         features: &[&'static str],
         device: &mut Device,
-        precache: bool,
+        precache_flags: ShaderPrecacheFlags,
     ) -> Result<Self, ShaderError> {
         let mut shader = LazilyCompiledShader {
             program: None,
             name,
             kind,
             features: features.to_vec(),
         };
 
-        if precache {
+        if precache_flags.intersects(ShaderPrecacheFlags::ASYNC_COMPILE | ShaderPrecacheFlags::FULL_COMPILE) {
             let t0 = precise_time_ns();
-            let program = shader.get(device)?;
+            shader.get_internal(device, precache_flags)?;
             let t1 = precise_time_ns();
-            device.bind_program(program);
-            device.draw_triangles_u16(0, 3);
-            let t2 = precise_time_ns();
-            debug!("[C: {:.1} ms D: {:.1} ms] Precache {} {:?}",
+            debug!("[C: {:.1} ms ] Precache {} {:?}",
                 (t1 - t0) as f64 / 1000000.0,
-                (t2 - t1) as f64 / 1000000.0,
                 name,
                 features
             );
         }
 
         Ok(shader)
     }
 
@@ -118,51 +114,115 @@ impl LazilyCompiledShader {
                 renderer_errors.push(RendererError::from(e));
                 return;
             }
         };
         device.bind_program(program);
         device.set_uniforms(program, projection);
     }
 
-    fn get(&mut self, device: &mut Device) -> Result<&Program, ShaderError> {
+    fn get_internal(
+        &mut self,
+        device: &mut Device,
+        precache_flags: ShaderPrecacheFlags,
+    ) -> Result<&mut Program, ShaderError> {
         if self.program.is_none() {
             let program = match self.kind {
                 ShaderKind::Primitive | ShaderKind::Brush | ShaderKind::Text => {
                     create_prim_shader(self.name,
                                        device,
-                                       &self.features,
-                                       VertexArrayKind::Primitive)
+                                       &self.features)
                 }
-                ShaderKind::Cache(format) => {
+                ShaderKind::Cache(..) => {
                     create_prim_shader(self.name,
                                        device,
-                                       &self.features,
-                                       format)
+                                       &self.features)
                 }
                 ShaderKind::VectorStencil => {
                     create_prim_shader(self.name,
                                        device,
-                                       &self.features,
-                                       VertexArrayKind::VectorStencil)
+                                       &self.features)
                 }
                 ShaderKind::VectorCover => {
                     create_prim_shader(self.name,
                                        device,
-                                       &self.features,
-                                       VertexArrayKind::VectorCover)
+                                       &self.features)
                 }
                 ShaderKind::ClipCache => {
                     create_clip_shader(self.name, device)
                 }
             };
             self.program = Some(program?);
         }
 
-        Ok(self.program.as_ref().unwrap())
+        let program = self.program.as_mut().unwrap();
+
+        if precache_flags.contains(ShaderPrecacheFlags::FULL_COMPILE) && !program.is_initialized() {
+            let vertex_format = match self.kind {
+                ShaderKind::Primitive |
+                ShaderKind::Brush |
+                ShaderKind::Text => VertexArrayKind::Primitive,
+                ShaderKind::Cache(format) => format,
+                ShaderKind::VectorStencil => VertexArrayKind::VectorStencil,
+                ShaderKind::VectorCover => VertexArrayKind::VectorCover,
+                ShaderKind::ClipCache => VertexArrayKind::Clip,
+            };
+
+            let vertex_descriptor = match vertex_format {
+                VertexArrayKind::Primitive => &desc::PRIM_INSTANCES,
+                VertexArrayKind::Blur => &desc::BLUR,
+                VertexArrayKind::Clip => &desc::CLIP,
+                VertexArrayKind::VectorStencil => &desc::VECTOR_STENCIL,
+                VertexArrayKind::VectorCover => &desc::VECTOR_COVER,
+                VertexArrayKind::Border => &desc::BORDER,
+                VertexArrayKind::Scale => &desc::SCALE,
+            };
+
+            device.link_program(program, vertex_descriptor)?;
+            device.bind_program(program);
+            match self.kind {
+                ShaderKind::ClipCache => {
+                    device.bind_shader_samplers(
+                        &program,
+                        &[
+                            ("sColor0", TextureSampler::Color0),
+                            ("sTransformPalette", TextureSampler::TransformPalette),
+                            ("sRenderTasks", TextureSampler::RenderTasks),
+                            ("sGpuCache", TextureSampler::GpuCache),
+                            ("sPrimitiveHeadersF", TextureSampler::PrimitiveHeadersF),
+                            ("sPrimitiveHeadersI", TextureSampler::PrimitiveHeadersI),
+                        ],
+                    );
+                }
+                _ => {
+                    device.bind_shader_samplers(
+                        &program,
+                        &[
+                            ("sColor0", TextureSampler::Color0),
+                            ("sColor1", TextureSampler::Color1),
+                            ("sColor2", TextureSampler::Color2),
+                            ("sDither", TextureSampler::Dither),
+                            ("sPrevPassAlpha", TextureSampler::PrevPassAlpha),
+                            ("sPrevPassColor", TextureSampler::PrevPassColor),
+                            ("sTransformPalette", TextureSampler::TransformPalette),
+                            ("sRenderTasks", TextureSampler::RenderTasks),
+                            ("sGpuCache", TextureSampler::GpuCache),
+                            ("sPrimitiveHeadersF", TextureSampler::PrimitiveHeadersF),
+                            ("sPrimitiveHeadersI", TextureSampler::PrimitiveHeadersI),
+                        ],
+                    );
+                }
+            }
+        }
+
+        Ok(program)
+    }
+
+    fn get(&mut self, device: &mut Device) -> Result<&mut Program, ShaderError> {
+        self.get_internal(device, ShaderPrecacheFlags::FULL_COMPILE)
     }
 
     fn deinit(self, device: &mut Device) {
         if let Some(program) = self.program {
             device.delete_program(program);
         }
     }
 }
@@ -185,64 +245,64 @@ struct BrushShader {
     debug_overdraw: LazilyCompiledShader,
 }
 
 impl BrushShader {
     fn new(
         name: &'static str,
         device: &mut Device,
         features: &[&'static str],
-        precache: bool,
+        precache_flags: ShaderPrecacheFlags,
         dual_source: bool,
     ) -> Result<Self, ShaderError> {
         let opaque = LazilyCompiledShader::new(
             ShaderKind::Brush,
             name,
             features,
             device,
-            precache,
+            precache_flags,
         )?;
 
         let mut alpha_features = features.to_vec();
         alpha_features.push(ALPHA_FEATURE);
 
         let alpha = LazilyCompiledShader::new(
             ShaderKind::Brush,
             name,
             &alpha_features,
             device,
-            precache,
+            precache_flags,
         )?;
 
         let dual_source = if dual_source {
             let mut dual_source_features = alpha_features.to_vec();
             dual_source_features.push(DUAL_SOURCE_FEATURE);
 
             let shader = LazilyCompiledShader::new(
                 ShaderKind::Brush,
                 name,
                 &dual_source_features,
                 device,
-                precache,
+                precache_flags,
             )?;
 
             Some(shader)
         } else {
             None
         };
 
         let mut debug_overdraw_features = features.to_vec();
         debug_overdraw_features.push(DEBUG_OVERDRAW_FEATURE);
 
         let debug_overdraw = LazilyCompiledShader::new(
             ShaderKind::Brush,
             name,
             &debug_overdraw_features,
             device,
-            precache,
+            precache_flags,
         )?;
 
         Ok(BrushShader {
             opaque,
             alpha,
             dual_source,
             debug_overdraw,
         })
@@ -282,46 +342,46 @@ pub struct TextShader {
     debug_overdraw: LazilyCompiledShader,
 }
 
 impl TextShader {
     fn new(
         name: &'static str,
         device: &mut Device,
         features: &[&'static str],
-        precache: bool,
+        precache_flags: ShaderPrecacheFlags,
     ) -> Result<Self, ShaderError> {
         let simple = LazilyCompiledShader::new(
             ShaderKind::Text,
             name,
             features,
             device,
-            precache,
+            precache_flags,
         )?;
 
         let mut glyph_transform_features = features.to_vec();
         glyph_transform_features.push("GLYPH_TRANSFORM");
 
         let glyph_transform = LazilyCompiledShader::new(
             ShaderKind::Text,
             name,
             &glyph_transform_features,
             device,
-            precache,
+            precache_flags,
         )?;
 
         let mut debug_overdraw_features = features.to_vec();
         debug_overdraw_features.push("DEBUG_OVERDRAW");
 
         let debug_overdraw = LazilyCompiledShader::new(
             ShaderKind::Text,
             name,
             &debug_overdraw_features,
             device,
-            precache,
+            precache_flags,
         )?;
 
         Ok(TextShader { simple, glyph_transform, debug_overdraw })
     }
 
     pub fn get(
         &mut self,
         glyph_format: GlyphFormat,
@@ -344,88 +404,40 @@ impl TextShader {
         self.debug_overdraw.deinit(device);
     }
 }
 
 fn create_prim_shader(
     name: &'static str,
     device: &mut Device,
     features: &[&'static str],
-    vertex_format: VertexArrayKind,
 ) -> Result<Program, ShaderError> {
     let mut prefix = format!(
         "#define WR_MAX_VERTEX_TEXTURE_WIDTH {}U\n",
         MAX_VERTEX_TEXTURE_WIDTH
     );
 
     for feature in features {
         prefix.push_str(&format!("#define WR_FEATURE_{}\n", feature));
     }
 
     debug!("PrimShader {}", name);
 
-    let vertex_descriptor = match vertex_format {
-        VertexArrayKind::Primitive => desc::PRIM_INSTANCES,
-        VertexArrayKind::Blur => desc::BLUR,
-        VertexArrayKind::Clip => desc::CLIP,
-        VertexArrayKind::VectorStencil => desc::VECTOR_STENCIL,
-        VertexArrayKind::VectorCover => desc::VECTOR_COVER,
-        VertexArrayKind::Border => desc::BORDER,
-        VertexArrayKind::Scale => desc::SCALE,
-    };
-
-    let program = device.create_program(name, &prefix, &vertex_descriptor);
-
-    if let Ok(ref program) = program {
-        device.bind_shader_samplers(
-            program,
-            &[
-                ("sColor0", TextureSampler::Color0),
-                ("sColor1", TextureSampler::Color1),
-                ("sColor2", TextureSampler::Color2),
-                ("sDither", TextureSampler::Dither),
-                ("sPrevPassAlpha", TextureSampler::PrevPassAlpha),
-                ("sPrevPassColor", TextureSampler::PrevPassColor),
-                ("sTransformPalette", TextureSampler::TransformPalette),
-                ("sRenderTasks", TextureSampler::RenderTasks),
-                ("sGpuCache", TextureSampler::GpuCache),
-                ("sPrimitiveHeadersF", TextureSampler::PrimitiveHeadersF),
-                ("sPrimitiveHeadersI", TextureSampler::PrimitiveHeadersI),
-            ],
-        );
-    }
-
-    program
+    device.create_program(name, &prefix)
 }
 
 fn create_clip_shader(name: &'static str, device: &mut Device) -> Result<Program, ShaderError> {
     let prefix = format!(
         "#define WR_MAX_VERTEX_TEXTURE_WIDTH {}U\n",
         MAX_VERTEX_TEXTURE_WIDTH
     );
 
     debug!("ClipShader {}", name);
 
-    let program = device.create_program(name, &prefix, &desc::CLIP);
-
-    if let Ok(ref program) = program {
-        device.bind_shader_samplers(
-            program,
-            &[
-                ("sColor0", TextureSampler::Color0),
-                ("sTransformPalette", TextureSampler::TransformPalette),
-                ("sRenderTasks", TextureSampler::RenderTasks),
-                ("sGpuCache", TextureSampler::GpuCache),
-                ("sPrimitiveHeadersF", TextureSampler::PrimitiveHeadersF),
-                ("sPrimitiveHeadersI", TextureSampler::PrimitiveHeadersI),
-            ],
-        );
-    }
-
-    program
+    device.create_program(name, &prefix)
 }
 
 // NB: If you add a new shader here, make sure to deinitialize it
 // in `Shaders::deinit()` below.
 pub struct Shaders {
     // These are "cache shaders". These shaders are used to
     // draw intermediate results to cache targets. The results
     // of these shaders are then used by the primitive shaders.
@@ -467,147 +479,144 @@ pub struct Shaders {
 }
 
 impl Shaders {
     pub fn new(
         device: &mut Device,
         gl_type: GlType,
         options: &RendererOptions,
     ) -> Result<Self, ShaderError> {
-        // needed for the precache fake draws
-        let dummy_vao = if options.precache_shaders {
-            let vao = device.create_custom_vao(&[]);
-            device.bind_custom_vao(&vao);
-            Some(vao)
-        } else {
-            None
-        };
-
         let brush_solid = BrushShader::new(
             "brush_solid",
             device,
             &[],
-            options.precache_shaders,
+            options.precache_flags,
             false,
         )?;
 
         let brush_blend = BrushShader::new(
             "brush_blend",
             device,
             &[],
-            options.precache_shaders,
+            options.precache_flags,
             false,
         )?;
 
         let brush_mix_blend = BrushShader::new(
             "brush_mix_blend",
             device,
             &[],
-            options.precache_shaders,
+            options.precache_flags,
             false,
         )?;
 
         let brush_radial_gradient = BrushShader::new(
             "brush_radial_gradient",
             device,
             if options.enable_dithering {
                &[DITHERING_FEATURE]
             } else {
                &[]
             },
-            options.precache_shaders,
+            options.precache_flags,
             false,
         )?;
 
         let brush_linear_gradient = BrushShader::new(
             "brush_linear_gradient",
             device,
             if options.enable_dithering {
                &[DITHERING_FEATURE]
             } else {
                &[]
             },
-            options.precache_shaders,
+            options.precache_flags,
             false,
         )?;
 
         let cs_blur_a8 = LazilyCompiledShader::new(
             ShaderKind::Cache(VertexArrayKind::Blur),
             "cs_blur",
             &["ALPHA_TARGET"],
             device,
-            options.precache_shaders,
+            options.precache_flags,
         )?;
 
         let cs_blur_rgba8 = LazilyCompiledShader::new(
             ShaderKind::Cache(VertexArrayKind::Blur),
             "cs_blur",
             &["COLOR_TARGET"],
             device,
-            options.precache_shaders,
+            options.precache_flags,
         )?;
 
         let cs_clip_rectangle = LazilyCompiledShader::new(
             ShaderKind::ClipCache,
             "cs_clip_rectangle",
             &[],
             device,
-            options.precache_shaders,
+            options.precache_flags,
         )?;
 
         let cs_clip_box_shadow = LazilyCompiledShader::new(
             ShaderKind::ClipCache,
             "cs_clip_box_shadow",
             &[],
             device,
-            options.precache_shaders,
+            options.precache_flags,
         )?;
 
         let cs_clip_line = LazilyCompiledShader::new(
             ShaderKind::ClipCache,
             "cs_clip_line",
             &[],
             device,
-            options.precache_shaders,
+            options.precache_flags,
         )?;
 
         let cs_clip_image = LazilyCompiledShader::new(
             ShaderKind::ClipCache,
             "cs_clip_image",
             &[],
             device,
-            options.precache_shaders,
+            options.precache_flags,
         )?;
 
         let cs_scale_a8 = LazilyCompiledShader::new(
             ShaderKind::Cache(VertexArrayKind::Scale),
             "cs_scale",
             &["ALPHA_TARGET"],
             device,
-            options.precache_shaders,
+            options.precache_flags,
         )?;
 
         let cs_scale_rgba8 = LazilyCompiledShader::new(
             ShaderKind::Cache(VertexArrayKind::Scale),
             "cs_scale",
             &["COLOR_TARGET"],
             device,
-            options.precache_shaders,
+            options.precache_flags,
         )?;
 
         let ps_text_run = TextShader::new("ps_text_run",
             device,
             &[],
-            options.precache_shaders,
+            options.precache_flags,
         )?;
 
+        let dual_source_precache_flags = if options.disable_dual_source_blending {
+            ShaderPrecacheFlags::empty()
+        } else {
+            options.precache_flags
+        };
+
         let ps_text_run_dual_source = TextShader::new("ps_text_run",
             device,
             &[DUAL_SOURCE_FEATURE],
-            options.precache_shaders && !options.disable_dual_source_blending,
+            dual_source_precache_flags,
         )?;
 
         // All image configuration.
         let mut image_features = Vec::new();
         let mut brush_image = Vec::new();
         // PrimitiveShader is not clonable. Use push() to initialize the vec.
         for _ in 0 .. IMAGE_BUFFER_KINDS.len() {
             brush_image.push(None);
@@ -617,17 +626,17 @@ impl Shaders {
                 let feature_string = IMAGE_BUFFER_KINDS[buffer_kind].get_feature_string();
                 if feature_string != "" {
                     image_features.push(feature_string);
                 }
                 brush_image[buffer_kind] = Some(BrushShader::new(
                     "brush_image",
                     device,
                     &image_features,
-                    options.precache_shaders,
+                    options.precache_flags,
                     !options.disable_dual_source_blending,
                 )?);
             }
             image_features.clear();
         }
 
         // All yuv_image configuration.
         let mut yuv_features = Vec::new();
@@ -653,17 +662,17 @@ impl Shaders {
                         if feature_string != "" {
                             yuv_features.push(feature_string);
                         }
 
                         let shader = BrushShader::new(
                             "brush_yuv_image",
                             device,
                             &yuv_features,
-                            options.precache_shaders,
+                            options.precache_flags,
                             false,
                         )?;
                         let index = Self::get_yuv_shader_index(
                             *image_buffer_kind,
                             *format_kind,
                             *color_space_kind,
                         );
                         brush_yuv_image[index] = Some(shader);
@@ -673,39 +682,35 @@ impl Shaders {
             }
         }
 
         let cs_border_segment = LazilyCompiledShader::new(
             ShaderKind::Cache(VertexArrayKind::Border),
             "cs_border_segment",
              &[],
              device,
-             options.precache_shaders,
+             options.precache_flags,
         )?;
 
         let cs_border_solid = LazilyCompiledShader::new(
             ShaderKind::Cache(VertexArrayKind::Border),
             "cs_border_solid",
             &[],
             device,
-            options.precache_shaders,
+            options.precache_flags,
         )?;
 
         let ps_split_composite = LazilyCompiledShader::new(
             ShaderKind::Primitive,
             "ps_split_composite",
             &[],
             device,
-            options.precache_shaders,
+            options.precache_flags,
         )?;
 
-        if let Some(vao) = dummy_vao {
-            device.delete_custom_vao(vao);
-        }
-
         Ok(Shaders {
             cs_blur_a8,
             cs_blur_rgba8,
             cs_border_segment,
             cs_border_solid,
             cs_scale_a8,
             cs_scale_rgba8,
             brush_solid,
--- a/gfx/webrender/src/util.rs
+++ b/gfx/webrender/src/util.rs
@@ -78,20 +78,22 @@ impl ScaleOffset {
             offset: Vector2D::new(
                 -self.offset.x / self.scale.x,
                 -self.offset.y / self.scale.y,
             ),
         }
     }
 
     pub fn offset(&self, offset: Vector2D<f32>) -> Self {
-        ScaleOffset {
-            scale: self.scale,
-            offset: self.offset + offset,
-        }
+        self.accumulate(
+            &ScaleOffset {
+                scale: Vector2D::new(1.0, 1.0),
+                offset,
+            }
+        )
     }
 
     // Produce a ScaleOffset that includes both self
     // and other. The 'self' ScaleOffset is applied
     // after other.
     pub fn accumulate(&self, other: &ScaleOffset) -> Self {
         ScaleOffset {
             scale: Vector2D::new(
@@ -100,30 +102,16 @@ impl ScaleOffset {
             ),
             offset: Vector2D::new(
                 self.offset.x + self.scale.x * other.offset.x,
                 self.offset.y + self.scale.y * other.offset.y,
             ),
         }
     }
 
-    // Find the difference between two ScaleOffset types.
-    pub fn difference(&self, other: &ScaleOffset) -> Self {
-        ScaleOffset {
-            scale: Vector2D::new(
-                other.scale.x / self.scale.x,
-                other.scale.y / self.scale.y,
-            ),
-            offset: Vector2D::new(
-                (other.offset.x - self.offset.x) / self.scale.x,
-                (other.offset.y - self.offset.y) / self.scale.y,
-            ),
-        }
-    }
-
     pub fn map_rect<F, T>(&self, rect: &TypedRect<f32, F>) -> TypedRect<f32, T> {
         TypedRect::new(
             TypedPoint2D::new(
                 rect.origin.x * self.scale.x + self.offset.x,
                 rect.origin.y * self.scale.y + self.offset.y,
             ),
             TypedSize2D::new(
                 rect.size.width * self.scale.x,
@@ -373,30 +361,101 @@ pub fn extract_inner_rect_safe<U>(
 ) -> Option<TypedRect<f32, U>> {
     // value of `k==1.0` is used for extraction of the corner rectangles
     // see `SEGMENT_CORNER_*` in `clip_shared.glsl`
     extract_inner_rect_impl(rect, radii, 1.0)
 }
 
 #[cfg(test)]
 pub mod test {
+    use api::{LayoutTransform, LayoutVector3D};
     use super::*;
     use euclid::{Point2D, Angle, Transform3D};
     use std::f32::consts::PI;
 
     #[test]
     fn inverse_project() {
         let m0 = Transform3D::identity();
         let p0 = Point2D::new(1.0, 2.0);
         // an identical transform doesn't need any inverse projection
         assert_eq!(m0.inverse_project(&p0), Some(p0));
         let m1 = Transform3D::create_rotation(0.0, 1.0, 0.0, Angle::radians(PI / 3.0));
         // rotation by 60 degrees would imply scaling of X component by a factor of 2
         assert_eq!(m1.inverse_project(&p0), Some(Point2D::new(2.0, 2.0)));
     }
+
+    fn validate_convert(xref: &LayoutTransform) {
+        let so = ScaleOffset::from_transform(xref).unwrap();
+        let xf = so.to_transform();
+        assert!(xref.approx_eq(&xf));
+    }
+
+    #[test]
+    fn scale_offset_convert() {
+        let xref = LayoutTransform::create_translation(130.0, 200.0, 0.0);
+        validate_convert(&xref);
+
+        let xref = LayoutTransform::create_scale(13.0, 8.0, 1.0);
+        validate_convert(&xref);
+
+        let xref = LayoutTransform::create_scale(0.5, 0.5, 1.0)
+                        .pre_translate(LayoutVector3D::new(124.0, 38.0, 0.0));
+        validate_convert(&xref);
+
+        let xref = LayoutTransform::create_translation(50.0, 240.0, 0.0)
+                        .pre_mul(&LayoutTransform::create_scale(30.0, 11.0, 1.0));
+        validate_convert(&xref);
+    }
+
+    fn validate_inverse(xref: &LayoutTransform) {
+        let s0 = ScaleOffset::from_transform(xref).unwrap();
+        let s1 = s0.inverse().accumulate(&s0);
+        assert!((s1.scale.x - 1.0).abs() < NEARLY_ZERO &&
+                (s1.scale.y - 1.0).abs() < NEARLY_ZERO &&
+                s1.offset.x.abs() < NEARLY_ZERO &&
+                s1.offset.y.abs() < NEARLY_ZERO,
+                "{:?}",
+                s1);
+    }
+
+    #[test]
+    fn scale_offset_inverse() {
+        let xref = LayoutTransform::create_translation(130.0, 200.0, 0.0);
+        validate_inverse(&xref);
+
+        let xref = LayoutTransform::create_scale(13.0, 8.0, 1.0);
+        validate_inverse(&xref);
+
+        let xref = LayoutTransform::create_scale(0.5, 0.5, 1.0)
+                        .pre_translate(LayoutVector3D::new(124.0, 38.0, 0.0));
+        validate_inverse(&xref);
+
+        let xref = LayoutTransform::create_translation(50.0, 240.0, 0.0)
+                        .pre_mul(&LayoutTransform::create_scale(30.0, 11.0, 1.0));
+        validate_inverse(&xref);
+    }
+
+    fn validate_accumulate(x0: &LayoutTransform, x1: &LayoutTransform) {
+        let x = x0.pre_mul(x1);
+
+        let s0 = ScaleOffset::from_transform(x0).unwrap();
+        let s1 = ScaleOffset::from_transform(x1).unwrap();
+
+        let s = s0.accumulate(&s1).to_transform();
+
+        assert!(x.approx_eq(&s), "{:?}\n{:?}", x, s);
+    }
+
+    #[test]
+    fn scale_offset_accumulate() {
+        let x0 = LayoutTransform::create_translation(130.0, 200.0, 0.0);
+        let x1 = LayoutTransform::create_scale(7.0, 3.0, 1.0);
+
+        validate_accumulate(&x0, &x1);
+    }
 }
 
 pub trait MaxRect {
     fn max_rect() -> Self;
 }
 
 impl MaxRect for DeviceIntRect {
     fn max_rect() -> Self {
--- a/gfx/webrender/tests/angle_shader_validation.rs
+++ b/gfx/webrender/tests/angle_shader_validation.rs
@@ -14,17 +14,17 @@ const VERTEX_SHADER: u32 = 0x8B31;
 struct Shader {
     name: &'static str,
     features: &'static [&'static str],
 }
 
 const SHADER_PREFIX: &str = "#define WR_MAX_VERTEX_TEXTURE_WIDTH 1024U\n";
 
 const BRUSH_FEATURES: &[&str] = &["", "ALPHA_PASS"];
-const CLIP_FEATURES: &[&str] = &["TRANSFORM"];
+const CLIP_FEATURES: &[&str] = &[""];
 const CACHE_FEATURES: &[&str] = &[""];
 const GRADIENT_FEATURES: &[&str] = &[ "", "DITHERING", "ALPHA_PASS", "DITHERING,ALPHA_PASS" ];
 const PRIM_FEATURES: &[&str] = &[""];
 
 const SHADERS: &[Shader] = &[
     // Clip mask shaders
     Shader {
         name: "cs_clip_rectangle",
--- a/gfx/webrender_api/src/image.rs
+++ b/gfx/webrender_api/src/image.rs
@@ -338,17 +338,24 @@ pub trait BlobImageHandler: Send {
     /// A hook to let the handler clean up any state related a given namespace before the
     /// resource cache deletes them.
     fn clear_namespace(&mut self, namespace: IdNamespace);
 }
 
 /// A group of rasterization requests to execute synchronously on the scene builder thread.
 pub trait AsyncBlobImageRasterizer : Send {
     /// Rasterize the requests.
-    fn rasterize(&mut self, requests: &[BlobImageParams]) -> Vec<(BlobImageRequest, BlobImageResult)>;
+    ///
+    /// Gecko uses te priority hint to schedule work in a way that minimizes the risk
+    /// of high priority work being blocked by (or enqued behind) low priority work.
+    fn rasterize(
+        &mut self,
+        requests: &[BlobImageParams],
+        low_priority: bool
+    ) -> Vec<(BlobImageRequest, BlobImageResult)>;
 }
 
 
 /// Input parameters for the BlobImageRasterizer.
 #[derive(Copy, Clone, Debug)]
 pub struct BlobImageParams {
     /// A key that identifies the blob image rasterization request.
     pub request: BlobImageRequest,
--- a/gfx/webrender_bindings/RenderCompositorANGLE.cpp
+++ b/gfx/webrender_bindings/RenderCompositorANGLE.cpp
@@ -309,17 +309,17 @@ RenderCompositorANGLE::BeginFrame()
   }
 
   if (!MakeCurrent()) {
     gfxCriticalNote << "Failed to make render context current, can't draw.";
     return false;
   }
 
   if (mSyncObject) {
-    if (!mSyncObject->Synchronize()) {
+    if (!mSyncObject->Synchronize(/* aFallible */ true)) {
       // It's timeout or other error. Handle the device-reset here.
       RenderThread::Get()->HandleDeviceReset("SyncObject", /* aNotify */ true);
       return false;
     }
   }
   return true;
 }
 
--- a/gfx/webrender_bindings/WebRenderTypes.h
+++ b/gfx/webrender_bindings/WebRenderTypes.h
@@ -38,16 +38,17 @@ typedef wr::WrWindowId WindowId;
 typedef wr::WrPipelineId PipelineId;
 typedef wr::WrImageKey ImageKey;
 typedef wr::WrFontKey FontKey;
 typedef wr::WrFontInstanceKey FontInstanceKey;
 typedef wr::WrEpoch Epoch;
 typedef wr::WrExternalImageId ExternalImageId;
 typedef wr::WrDebugFlags DebugFlags;
 
+typedef mozilla::Maybe<mozilla::wr::IdNamespace> MaybeIdNamespace;
 typedef mozilla::Maybe<mozilla::wr::WrImageMask> MaybeImageMask;
 typedef Maybe<ExternalImageId> MaybeExternalImageId;
 
 typedef Maybe<FontInstanceOptions> MaybeFontInstanceOptions;
 typedef Maybe<FontInstancePlatformOptions> MaybeFontInstancePlatformOptions;
 
 /* Generate a brand new window id and return it. */
 WindowId NewWindowId();
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-9536249e3ed920a920346f6cc0a79473cad16099
+3c3f9a4e919b81639f078d7bd101012de61b9396
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -9,16 +9,17 @@ use gleam::gl;
 
 use webrender::api::*;
 use webrender::{ReadPixelsFormat, Renderer, RendererOptions, ThreadListener};
 use webrender::{ExternalImage, ExternalImageHandler, ExternalImageSource};
 use webrender::DebugFlags;
 use webrender::{ApiRecordingReceiver, BinaryRecorder};
 use webrender::{AsyncPropertySampler, PipelineInfo, SceneBuilderHooks};
 use webrender::{UploadMethod, VertexUsageHint};
+use webrender::ShaderPrecacheFlags;
 use thread_profiler::register_thread_with_profiler;
 use moz2d_renderer::Moz2dBlobImageHandler;
 use program_cache::{WrProgramCache, remove_disk_cache};
 use app_units::Au;
 use rayon;
 use euclid::SideOffsets2D;
 use nsstring::nsAString;
 
@@ -962,16 +963,22 @@ pub extern "C" fn wr_window_new(window_i
     };
 
     let upload_method = if unsafe { is_glcontext_angle(gl_context) } {
         UploadMethod::Immediate
     } else {
         UploadMethod::PixelBuffer(VertexUsageHint::Dynamic)
     };
 
+    let precache_flags = if env_var_to_bool("MOZ_WR_PRECACHE_SHADERS") {
+        ShaderPrecacheFlags::FULL_COMPILE
+    } else {
+        ShaderPrecacheFlags::empty()
+    };
+
     let opts = RendererOptions {
         enable_aa: true,
         enable_subpixel_aa: true,
         support_low_priority_transactions,
         recorder: recorder,
         blob_image_handler: Some(Box::new(Moz2dBlobImageHandler::new(workers.clone()))),
         workers: Some(workers.clone()),
         thread_listener: Some(Box::new(GeckoProfilerThreadListener::new())),
@@ -988,17 +995,17 @@ pub extern "C" fn wr_window_new(window_i
             }
         },
         renderer_id: Some(window_id.0),
         upload_method,
         scene_builder_hooks: Some(Box::new(APZCallbacks::new(window_id))),
         sampler: Some(Box::new(SamplerCallback::new(window_id))),
         max_texture_size: Some(8192), // Moz2D doesn't like textures bigger than this
         clear_color: Some(ColorF::new(0.0, 0.0, 0.0, 0.0)),
-        precache_shaders: env_var_to_bool("MOZ_WR_PRECACHE_SHADERS"),
+        precache_flags,
         namespace_alloc_by_client: true,
         ..Default::default()
     };
 
     let notifier = Box::new(CppNotifier {
         window_id: window_id,
     });
     let (renderer, sender) = match Renderer::new(gl, notifier, opts) {
new file mode 100644
--- /dev/null
+++ b/gfx/webrender_bindings/src/bindings.rs.rej
@@ -0,0 +1,11 @@
+--- bindings.rs
++++ bindings.rs
+@@ -1017,7 +1023,7 @@
+         sampler: Some(Box::new(SamplerCallback::new(window_id))),
+         max_texture_size: Some(8192), // Moz2D doesn't like textures bigger than this
+         clear_color: Some(ColorF::new(0.0, 0.0, 0.0, 0.0)),
+-        precache_shaders: env_var_to_bool("MOZ_WR_PRECACHE_SHADERS"),
++        precache_flags,
+         ..Default::default()
+     };
+ 
--- a/gfx/webrender_bindings/src/moz2d_renderer.rs
+++ b/gfx/webrender_bindings/src/moz2d_renderer.rs
@@ -441,18 +441,18 @@ struct BlobCommand {
 struct Moz2dBlobRasterizer {
     /// Pool of rasterizers.
     workers: Arc<ThreadPool>,
     /// Blobs to rasterize.
     blob_commands: HashMap<ImageKey, BlobCommand>,
 }
 
 impl AsyncBlobImageRasterizer for Moz2dBlobRasterizer {
-
-    fn rasterize(&mut self, requests: &[BlobImageParams]) -> Vec<(BlobImageRequest, BlobImageResult)> {
+   
+    fn rasterize(&mut self, requests: &[BlobImageParams], _low_priority: bool) -> Vec<(BlobImageRequest, BlobImageResult)> {
         // All we do here is spin up our workers to callback into gecko to replay the drawing commands.
 
         struct Job {
             request: BlobImageRequest,
             descriptor: BlobImageDescriptor,
             commands: Arc<BlobImageData>,
             dirty_rect: Option<DeviceUintRect>,
             tile_size: Option<TileSize>,
--- a/gfx/wrench/src/blob.rs
+++ b/gfx/wrench/src/blob.rs
@@ -178,17 +178,21 @@ struct Command {
     dirty_rect: Option<DeviceUintRect>
 }
 
 struct Rasterizer {
     image_cmds: HashMap<ImageKey, (ColorU, Option<TileSize>)>,
 }
 
 impl AsyncBlobImageRasterizer for Rasterizer {
-    fn rasterize(&mut self, requests: &[BlobImageParams]) -> Vec<(BlobImageRequest, BlobImageResult)> {
+    fn rasterize(
+        &mut self,
+        requests: &[BlobImageParams],
+        _low_priority: bool
+    ) -> Vec<(BlobImageRequest, BlobImageResult)> {
         let requests: Vec<Command> = requests.into_iter().map(
             |item| {
                 let (color, tile_size) = self.image_cmds[&item.request.key];
 
                 let tile = item.request.tile.map(|tile| (tile_size.unwrap(), tile));
 
                 Command {
                     request: item.request,
--- a/gfx/wrench/src/wrench.rs
+++ b/gfx/wrench/src/wrench.rs
@@ -15,17 +15,17 @@ use json_frame_writer::JsonFrameWriter;
 use ron_frame_writer::RonFrameWriter;
 use std::collections::HashMap;
 use std::path::PathBuf;
 use std::sync::{Arc, Mutex};
 use std::sync::mpsc::Receiver;
 use time;
 use webrender;
 use webrender::api::*;
-use webrender::{DebugFlags, RendererStats};
+use webrender::{DebugFlags, RendererStats, ShaderPrecacheFlags};
 use yaml_frame_writer::YamlFrameWriterReceiver;
 use {WindowWrapper, NotifierEvent};
 
 // TODO(gw): This descriptor matches what we currently support for fonts
 //           but is quite a mess. We should at least document and
 //           use better types for things like the style and stretch.
 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub enum FontDescriptor {
@@ -200,25 +200,31 @@ impl Wrench {
                 &PathBuf::from("wr-record.bin"),
             )) as Box<webrender::ApiRecordingReceiver>,
         });
 
         let mut debug_flags = DebugFlags::ECHO_DRIVER_MESSAGES;
         debug_flags.set(DebugFlags::DISABLE_BATCHING, no_batch);
         let callbacks = Arc::new(Mutex::new(blob::BlobCallbacks::new()));
 
+        let precache_flags = if precache_shaders {
+            ShaderPrecacheFlags::FULL_COMPILE
+        } else {
+            ShaderPrecacheFlags::empty()
+        };
+
         let opts = webrender::RendererOptions {
             device_pixel_ratio: dp_ratio,
             resource_override_path: shader_override_path,
             recorder,
             enable_subpixel_aa: !no_subpixel_aa,
             debug_flags,
             enable_clear_scissor: !no_scissor,
             max_recorded_profiles: 16,
-            precache_shaders,
+            precache_flags,
             blob_image_handler: Some(Box::new(blob::CheckerboardRenderer::new(callbacks.clone()))),
             disable_dual_source_blending,
             chase_primitive,
             ..Default::default()
         };
 
         // put an Awakened event into the queue to kick off the first frame
         if let Some(ref elp) = proxy {
--- a/ipc/ipdl/sync-messages.ini
+++ b/ipc/ipdl/sync-messages.ini
@@ -975,18 +975,16 @@ description =
 [PCompositorBridge::WaitOnTransactionProcessed]
 description = bug 1364626
 [PCompositorBridge::StartFrameTimeRecording]
 description =
 [PCompositorBridge::StopFrameTimeRecording]
 description =
 [PCompositorBridge::SyncWithCompositor]
 description =
-[PCompositorBridge::PWebRenderBridge]
-description =
 [PCompositorBridge::CheckContentOnlyTDR]
 description =
 [PCompositorWidget::EnterPresentLock]
 description =
 platform = win
 [PCompositorWidget::LeavePresentLock]
 description =
 platform = win
@@ -1016,16 +1014,18 @@ description =
 [PLayerTransaction::GetTextureFactoryIdentifier]
 description = bug 1350634
 [PUiCompositorController::Pause]
 description =
 [PUiCompositorController::Resume]
 description =
 [PUiCompositorController::ResumeAndResize]
 description =
+[PWebRenderBridge::EnsureConnected]
+description =
 [PWebRenderBridge::GetSnapshot]
 description =
 [PWebRenderBridge::SetTestSampleTime]
 description = test only
 [PWebRenderBridge::LeaveTestMode]
 description = test only
 [PWebRenderBridge::GetAnimationValue]
 description = test only
--- a/js/src/builtin/JSON.cpp
+++ b/js/src/builtin/JSON.cpp
@@ -67,36 +67,76 @@ InfallibleQuote(RangedPtr<const SrcCharT
         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,  '\\', // rest are all zeros
     };
 
     /* Step 1. */
     *dstPtr++ = '"';
 
+    auto ToLowerHex = [](uint8_t u) {
+        MOZ_ASSERT(u <= 0xF);
+        return "0123456789abcdef"[u];
+    };
+
     /* Step 2. */
     while (srcBegin != srcEnd) {
-        SrcCharT c = *srcBegin++;
-        size_t escapeIndex = c % sizeof(escapeLookup);
-        Latin1Char escaped = escapeLookup[escapeIndex];
-        if (MOZ_LIKELY((escapeIndex != size_t(c)) || !escaped)) {
+        const SrcCharT c = *srcBegin++;
+
+        // Handle the Latin-1 cases.
+        if (MOZ_LIKELY(c < sizeof(escapeLookup))) {
+            Latin1Char escaped = escapeLookup[c];
+
+            // Directly copy non-escaped code points.
+            if (escaped == 0) {
+                *dstPtr++ = c;
+                continue;
+            }
+
+            // Escape the rest, elaborating Unicode escapes when needed.
+            *dstPtr++ = '\\';
+            *dstPtr++ = escaped;
+            if (escaped == 'u') {
+                *dstPtr++ = '0';
+                *dstPtr++ = '0';
+
+                uint8_t x = c >> 4;
+                MOZ_ASSERT(x < 10);
+                *dstPtr++ = '0' + x;
+
+                *dstPtr++ = ToLowerHex(c & 0xF);
+            }
+
+            continue;
+        }
+
+        // Non-ASCII non-surrogates are directly copied.
+        if (!unicode::IsSurrogate(c)) {
             *dstPtr++ = c;
             continue;
         }
+
+        // So too for complete surrogate pairs.
+        if (MOZ_LIKELY(unicode::IsLeadSurrogate(c) &&
+                       srcBegin < srcEnd &&
+                       unicode::IsTrailSurrogate(*srcBegin)))
+        {
+            *dstPtr++ = c;
+            *dstPtr++ = *srcBegin++;
+            continue;
+        }
+
+        // But lone surrogates are Unicode-escaped.
+        char32_t as32 = char32_t(c);
         *dstPtr++ = '\\';
-        *dstPtr++ = escaped;
-        if (escaped == 'u') {
-            MOZ_ASSERT(c < ' ');
-            MOZ_ASSERT((c >> 4) < 10);
-            uint8_t x = c >> 4, y = c % 16;
-            *dstPtr++ = '0';
-            *dstPtr++ = '0';
-            *dstPtr++ = '0' + x;
-            *dstPtr++ = y < 10 ? '0' + y : 'a' + (y - 10);
-        }
+        *dstPtr++ = 'u';
+        *dstPtr++ = ToLowerHex(as32 >> 12);
+        *dstPtr++ = ToLowerHex((as32 >> 8) & 0xF);
+        *dstPtr++ = ToLowerHex((as32 >> 4) & 0xF);
+        *dstPtr++ = ToLowerHex(as32 & 0xF);
     }
 
     /* Steps 3-4. */
     *dstPtr++ = '"';
     return dstPtr;
 }
 
 template <typename SrcCharT, typename CharVectorT>
--- a/js/src/tests/non262/JSON/stringify-special-escapes.js
+++ b/js/src/tests/non262/JSON/stringify-special-escapes.js
@@ -214,14 +214,64 @@ assertEq(JSON.stringify("\\u0019Q"), '"\
 assertEq(JSON.stringify("\\u001AQ"), '"\\\\u001AQ"');
 assertEq(JSON.stringify("\\u001BQ"), '"\\\\u001BQ"');
 assertEq(JSON.stringify("\\u001CQ"), '"\\\\u001CQ"');
 assertEq(JSON.stringify("\\u001DQ"), '"\\\\u001DQ"');
 assertEq(JSON.stringify("\\u001EQ"), '"\\\\u001EQ"');
 assertEq(JSON.stringify("\\u001FQ"), '"\\\\u001FQ"');
 assertEq(JSON.stringify("\\u0020Q"), '"\\\\u0020Q"');
 
+// https://tc39.github.io/proposal-well-formed-stringify/
+
+assertEq(JSON.stringify("\ud7ff"), '"\ud7ff"');
+assertEq(JSON.stringify("\ud800"), '"\\ud800"');
+assertEq(JSON.stringify("\ud937"), '"\\ud937"');
+assertEq(JSON.stringify("\uda20"), '"\\uda20"');
+assertEq(JSON.stringify("\udbff"), '"\\udbff"');
+
+assertEq(JSON.stringify("\udc00"), '"\\udc00"');
+assertEq(JSON.stringify("\udddd"), '"\\udddd"');
+assertEq(JSON.stringify("\udeaf"), '"\\udeaf"');
+assertEq(JSON.stringify("\udfff"), '"\\udfff"');
+assertEq(JSON.stringify("\ue000"), '"\ue000"');
+
+assertEq(JSON.stringify("\ud7ffa"), '"\ud7ffa"');
+assertEq(JSON.stringify("\ud800a"), '"\\ud800a"');
+assertEq(JSON.stringify("\ud937a"), '"\\ud937a"');
+assertEq(JSON.stringify("\uda20a"), '"\\uda20a"');
+assertEq(JSON.stringify("\udbffa"), '"\\udbffa"');
+
+assertEq(JSON.stringify("\udc00a"), '"\\udc00a"');
+assertEq(JSON.stringify("\udddda"), '"\\udddda"');
+assertEq(JSON.stringify("\udeafa"), '"\\udeafa"');
+assertEq(JSON.stringify("\udfffa"), '"\\udfffa"');
+assertEq(JSON.stringify("\ue000a"), '"\ue000a"');
+
+assertEq(JSON.stringify("\ud7ff\ud800"), '"\ud7ff\\ud800"');
+assertEq(JSON.stringify("\ud800\ud800"), '"\\ud800\\ud800"');
+assertEq(JSON.stringify("\ud937\ud800"), '"\\ud937\\ud800"');
+assertEq(JSON.stringify("\uda20\ud800"), '"\\uda20\\ud800"');
+assertEq(JSON.stringify("\udbff\ud800"), '"\\udbff\\ud800"');
+
+assertEq(JSON.stringify("\udc00\ud800"), '"\\udc00\\ud800"');
+assertEq(JSON.stringify("\udddd\ud800"), '"\\udddd\\ud800"');
+assertEq(JSON.stringify("\udeaf\ud800"), '"\\udeaf\\ud800"');
+assertEq(JSON.stringify("\udfff\ud800"), '"\\udfff\\ud800"');
+assertEq(JSON.stringify("\ue000\ud800"), '"\ue000\\ud800"');
+
+assertEq(JSON.stringify("\ud7ff\udc00"), '"\ud7ff\\udc00"');
+assertEq(JSON.stringify("\ud800\udc00"), '"\ud800\udc00"');
+assertEq(JSON.stringify("\ud937\udc00"), '"\ud937\udc00"');
+assertEq(JSON.stringify("\uda20\udc00"), '"\uda20\udc00"');
+assertEq(JSON.stringify("\udbff\udc00"), '"\udbff\udc00"');
+
+assertEq(JSON.stringify("\udc00\udc00"), '"\\udc00\\udc00"');
+assertEq(JSON.stringify("\udddd\udc00"), '"\\udddd\\udc00"');
+assertEq(JSON.stringify("\udeaf\udc00"), '"\\udeaf\\udc00"');
+assertEq(JSON.stringify("\udfff\udc00"), '"\\udfff\\udc00"');
+assertEq(JSON.stringify("\ue000\udc00"), '"\ue000\\udc00"');
+
 /******************************************************************************/
 
 if (typeof reportCompare === "function")
   reportCompare(true, true);
 
 print("Tests complete");
--- a/js/src/tests/non262/RegExp/split-trace.js
+++ b/js/src/tests/non262/RegExp/split-trace.js
@@ -176,17 +176,17 @@ reset();
 flags = "u";
 expectedFlags = "uy";
 target = "-\uD83D\uDC38\uDC38\uD83D";
 execResult        = [    null, null, null, null ];
 lastIndexResult   = [ ,  ,     ,     ,     ,    ];
 lastIndexExpected = [ 0, 1,    3,    4,         ];
 ret = RegExp.prototype[Symbol.split].call(myRegExp, target);
 assertEq(arraySetterObserved, false);
-assertEq(JSON.stringify(ret), `["-\uD83D\uDC38\uDC38\uD83D"]`);
+assertEq(JSON.stringify(ret), `["-\uD83D\uDC38\\udc38\\ud83d"]`);
 assertEq(log,
          "get:constructor," +
          "get:species," +
          "get:flags," +
          "call:constructor," +
          "set:lastIndex,get:exec,call:exec," +
          "set:lastIndex,get:exec,call:exec," +
          "set:lastIndex,get:exec,call:exec," +
@@ -198,17 +198,17 @@ flags = "u";
 expectedFlags = "uy";
 target = "-\uD83D\uDC38\uDC38\uD83D";
 var E = P(["", "X"]);
 execResult        = [    E, E, E, E, E, E, E ];
 lastIndexResult   = [ ,  0, 1, 1, 3, 3, 4, 4 ];
 lastIndexExpected = [ 0, 1, 1, 3, 3, 4, 4,   ];
 ret = RegExp.prototype[Symbol.split].call(myRegExp, target);
 assertEq(arraySetterObserved, false);
-assertEq(JSON.stringify(ret), `["-","X","\uD83D\uDC38","X","\uDC38","X","\uD83D"]`);
+assertEq(JSON.stringify(ret), `["-","X","\uD83D\uDC38","X","\\udc38","X","\\ud83d"]`);
 assertEq(log,
          "get:constructor," +
          "get:species," +
          "get:flags," +
          "call:constructor," +
          "set:lastIndex,get:exec,call:exec,get:lastIndex," +
          "set:lastIndex,get:exec,call:exec,get:lastIndex," +
          "get:result[length]," +
--- a/js/src/tests/non262/TypedArray/sort_snans.js
+++ b/js/src/tests/non262/TypedArray/sort_snans.js
@@ -29,16 +29,23 @@ function testFloat32NaNRanges(start, end
     floatView.sort();
     assertDeepEq(floatView, NaNArray);
 }
 
 // Test every skipNth value in some range n, where start <= n <= end
 // and startHi, startLow and endHi, endLow should be 32-bit integers which,
 // when combined (Hi + Low), form Float64 NaNs.
 function testFloat64NaNRanges(startHi, startLow, endHi, endLow) {
+
+    // Swap on big endian platforms
+    if (new Uint32Array(new Uint8Array([1,2,3,4]).buffer)[0] === 0x01020304) {
+	[startHi, startLow] = [startLow, startHi];
+	[endHi, endLow] = [endLow, endHi];
+    }
+
     let skipN = 10e6;
 
     let sampleSizeHi  = Math.floor((endHi - startHi)/skipN);
     let sampleSizeLow = Math.floor((endLow - startLow)/skipN);
 
     let NaNArray   = new Float64Array(getNaNArray(sampleSizeHi + sampleSizeLow));
     let buffer     = new ArrayBuffer(8 * (sampleSizeHi + sampleSizeLow));
     let uintView   = new Uint32Array(buffer);
--- a/js/src/vm/JSAtom.cpp
+++ b/js/src/vm/JSAtom.cpp
@@ -1071,17 +1071,18 @@ js::XDRAtom(XDRState<mode>* xdr, Mutable
     // non-align loads of 16bits characters.
     if (!latin1) {
         MOZ_TRY(xdr->codeAlign(sizeof(char16_t)));
     }
 
     if (mode == XDR_ENCODE) {
         JS::AutoCheckCannotGC nogc;
         if (latin1) {
-            return xdr->codeChars(atomp->latin1Chars(nogc), length);
+            return xdr->codeChars(const_cast<JS::Latin1Char*>(atomp->latin1Chars(nogc)),
+                                  length);
         }
         return xdr->codeChars(const_cast<char16_t*>(atomp->twoByteChars(nogc)), length);
     }
 
     MOZ_ASSERT(mode == XDR_DECODE);
     /* Avoid JSString allocation for already existing atoms. See bug 321985. */
     JSContext* cx = xdr->cx();
     JSAtom* atom;
--- a/js/src/vm/JSScript.cpp
+++ b/js/src/vm/JSScript.cpp
@@ -2288,16 +2288,18 @@ ScriptSource::xdrFinalizeEncoder(JS::Tra
 
 template<XDRMode mode>
 XDRResult
 ScriptSource::performXDR(XDRState<mode>* xdr)
 {
     struct CompressedLengthMatcher
     {
         size_t match(Uncompressed&) {
+            // Return 0 for uncompressed source so that |if (compressedLength)|
+            // can be used to distinguish compressed and uncompressed source.
             return 0;
         }
 
         size_t match(Compressed& c) {
             return c.raw.length();
         }
 
         size_t match(BinAST&) {
@@ -2336,60 +2338,84 @@ ScriptSource::performXDR(XDRState<mode>*
     uint8_t hasBinSource = hasBinASTSource();
     MOZ_TRY(xdr->codeUint8(&hasBinSource));
 
     uint8_t retrievable = sourceRetrievable_;
     MOZ_TRY(xdr->codeUint8(&retrievable));
     sourceRetrievable_ = retrievable;
 
     if ((hasSource || hasBinSource) && !sourceRetrievable_) {
-        uint32_t len = 0;
+        uint32_t uncompressedLength = 0;
         if (mode == XDR_ENCODE) {
-            len = length();
+            uncompressedLength = length();
         }
-        MOZ_TRY(xdr->codeUint32(&len));
-
+        MOZ_TRY(xdr->codeUint32(&uncompressedLength));
+
+        // A compressed length of 0 indicates source is uncompressed.
         uint32_t compressedLength;
         if (mode == XDR_ENCODE) {
             CompressedLengthMatcher m;
             compressedLength = data.match(m);
         }
         MOZ_TRY(xdr->codeUint32(&compressedLength));
 
-        size_t byteLen = hasBinSource ? len : compressedLength ? compressedLength : (len * sizeof(char16_t));
         if (mode == XDR_DECODE) {
-            auto bytes = xdr->cx()->template make_pod_array<char>(Max<size_t>(byteLen, 1));
-            if (!bytes) {
-                return xdr->fail(JS::TranscodeResult_Throw);
-            }
-            MOZ_TRY(xdr->codeBytes(bytes.get(), byteLen));
-
             if (hasBinSource) {
 #if defined(JS_BUILD_BINAST)
-                if (!setBinASTSource(xdr->cx(), std::move(bytes), len)) {
+                auto bytes =
+                    xdr->cx()->template make_pod_array<char>(Max<size_t>(uncompressedLength, 1));
+                if (!bytes) {
+                    return xdr->fail(JS::TranscodeResult_Throw);
+                }
+                MOZ_TRY(xdr->codeBytes(bytes.get(), uncompressedLength));
+
+                if (!setBinASTSource(xdr->cx(), std::move(bytes), uncompressedLength)) {
                     return xdr->fail(JS::TranscodeResult_Throw);
                 }
 #else
                 MOZ_ASSERT(mode != XDR_ENCODE);
                 return xdr->fail(JS::TranscodeResult_Throw);
 #endif /* JS_BUILD_BINAST */
             } else if (compressedLength) {
-                if (!setCompressedSource(xdr->cx(), std::move(bytes), byteLen, len)) {
+                auto bytes =
+                    xdr->cx()->template make_pod_array<char>(Max<size_t>(compressedLength, 1));
+                if (!bytes) {
+                    return xdr->fail(JS::TranscodeResult_Throw);
+                }
+                MOZ_TRY(xdr->codeBytes(bytes.get(), compressedLength));
+
+                if (!setCompressedSource(xdr->cx(), std::move(bytes), compressedLength,
+                                         uncompressedLength))
+                {
                     return xdr->fail(JS::TranscodeResult_Throw);
                 }
             } else {
-                UniqueTwoByteChars source(reinterpret_cast<char16_t*>(bytes.release()));
-                if (!setSource(xdr->cx(), std::move(source), len)) {
+                auto sourceChars =
+                    xdr->cx()->template make_pod_array<char16_t>(Max<size_t>(uncompressedLength,
+                                                                             1));
+                if (!sourceChars) {
+                    return xdr->fail(JS::TranscodeResult_Throw);
+                }
+                MOZ_TRY(xdr->codeChars(sourceChars.get(), uncompressedLength));
+
+                if (!setSource(xdr->cx(), std::move(sourceChars), uncompressedLength)) {
                     return xdr->fail(JS::TranscodeResult_Throw);
                 }
             }
         } else {
-            RawDataMatcher rdm;
-            void* p = data.match(rdm);
-            MOZ_TRY(xdr->codeBytes(p, byteLen));
+            if (hasBinSource) {
+                void* bytes = data.match(RawDataMatcher());
+                MOZ_TRY(xdr->codeBytes(bytes, uncompressedLength));
+            } else if (compressedLength) {
+                void* bytes = data.match(RawDataMatcher());
+                MOZ_TRY(xdr->codeBytes(bytes, compressedLength));
+            } else {
+                char16_t* sourceChars = static_cast<char16_t*>(data.match(RawDataMatcher()));
+                MOZ_TRY(xdr->codeChars(sourceChars, uncompressedLength));
+            }
         }
 
         uint8_t hasMetadata = !!binASTMetadata_;
         MOZ_TRY(xdr->codeUint8(&hasMetadata));
         if (hasMetadata) {
 #if defined(JS_BUILD_BINAST)
             uint32_t numBinKinds;
             uint32_t numStrings;
@@ -2515,17 +2541,20 @@ ScriptSource::performXDR(XDRState<mode>*
         // Note: If the decoder has an option, then the filename is defined by
         // the CompileOption from the document.
         MOZ_ASSERT_IF(mode == XDR_DECODE && xdr->hasOptions(), filename());
         if (mode == XDR_DECODE && !xdr->hasOptions() && !setFilename(xdr->cx(), fn)) {
             return xdr->fail(JS::TranscodeResult_Throw);
         }
 
         // Note the content of sources decoded when recording or replaying.
-        if (mode == XDR_DECODE && hasSourceText() && mozilla::recordreplay::IsRecordingOrReplaying()) {
+        if (mode == XDR_DECODE &&
+            hasSourceText() &&
+            mozilla::recordreplay::IsRecordingOrReplaying())
+        {
             UncompressedSourceCache::AutoHoldEntry holder;
             ScriptSource::PinnedChars chars(xdr->cx(), this, holder, 0, length());
             if (!chars.get()) {
                 return xdr->fail(JS::TranscodeResult_Throw);
             }
             mozilla::recordreplay::NoteContentParse(this, filename(), "application/javascript",
                                                     chars.get(), length());
         }
--- a/js/src/vm/Xdr.cpp
+++ b/js/src/vm/Xdr.cpp
@@ -4,30 +4,36 @@
  * 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 "vm/Xdr.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/ScopeExit.h"
+#include "mozilla/Utf8.h"
 
+#include <algorithm> // std::transform
 #include <string.h>
+#include <type_traits> // std::is_same
+#include <utility> // std::move
 
 #include "jsapi.h"
 #include "jsutil.h"
 
 #include "vm/Debugger.h"
 #include "vm/EnvironmentObject.h"
 #include "vm/JSContext.h"
 #include "vm/JSScript.h"
 #include "vm/TraceLogging.h"
 
 using namespace js;
+
 using mozilla::ArrayEqual;
+using mozilla::Utf8Unit;
 
 template<XDRMode mode>
 LifoAlloc&
 XDRState<mode>::lifoAlloc() const {
     return buf.cx()->tempLifoAlloc();
 }
 
 #ifdef DEBUG
@@ -40,31 +46,53 @@ XDRCoderBase::validateResultCode(JSConte
         return true;
     }
     return cx->isExceptionPending() == bool(code == JS::TranscodeResult_Throw);
 }
 #endif
 
 template<XDRMode mode>
 XDRResult
-XDRState<mode>::codeChars(const Latin1Char* chars, size_t nchars)
+XDRState<mode>::codeChars(Latin1Char* chars, size_t nchars)
 {
-    static_assert(sizeof(Latin1Char) == sizeof(uint8_t), "Latin1Char must fit in 1 byte");
+    static_assert(sizeof(Latin1Char) == 1,
+                  "Latin1Char must be 1 byte for nchars below to be the "
+                  "proper count of bytes");
+    static_assert(std::is_same<Latin1Char, unsigned char>::value,
+                  "Latin1Char must be unsigned char to C++-safely reinterpret "
+                  "the bytes generically copied below as Latin1Char");
+    return codeBytes(chars, nchars);
+}
 
-    MOZ_ASSERT(mode == XDR_ENCODE);
-
-    if (nchars == 0) {
+template<XDRMode mode>
+XDRResult
+XDRState<mode>::codeChars(Utf8Unit* units, size_t count)
+{
+    if (count == 0) {
         return Ok();
     }
-    uint8_t* ptr = buf.write(nchars);
-    if (!ptr) {
-        return fail(JS::TranscodeResult_Throw);
+
+    if (mode == XDR_ENCODE) {
+        uint8_t* ptr = buf.write(count);
+        if (!ptr) {
+            return fail(JS::TranscodeResult_Throw);
+        }
+
+        std::transform(units, units + count,
+                       ptr, [](const Utf8Unit& unit) { return unit.toUint8(); });
+    } else {
+        const uint8_t* ptr = buf.read(count);
+        if (!ptr) {
+            return fail(JS::TranscodeResult_Failure_BadDecode);
+        }
+
+        std::transform(ptr, ptr + count,
+                       units, [](const uint8_t& value) { return Utf8Unit(value); });
     }
 
-    mozilla::PodCopy(ptr, chars, nchars);
     return Ok();
 }
 
 template<XDRMode mode>
 XDRResult
 XDRState<mode>::codeChars(char16_t* chars, size_t nchars)
 {
     if (nchars == 0) {
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef vm_Xdr_h
 #define vm_Xdr_h
 
 #include "mozilla/EndianUtils.h"
 #include "mozilla/TypeTraits.h"
+#include "mozilla/Utf8.h"
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "NamespaceImports.h"
 
 #include "js/CompileOptions.h"
 #include "js/Transcoding.h"
 #include "js/TypeDecls.h"
@@ -483,17 +484,19 @@ class XDRState : public XDRCoderBase
             if (!ptr || ptr[len] != '\0') {
                 return fail(JS::TranscodeResult_Failure_BadDecode);
             }
             *sp = reinterpret_cast<const char*>(ptr);
         }
         return Ok();
     }
 
-    XDRResult codeChars(const JS::Latin1Char* chars, size_t nchars);
+    XDRResult codeChars(JS::Latin1Char* chars, size_t nchars);
+    XDRResult codeChars(mozilla::Utf8Unit* units, size_t nchars);
+
     XDRResult codeChars(char16_t* chars, size_t nchars);
 
     XDRResult codeFunction(JS::MutableHandleFunction objp,
                            HandleScriptSourceObject sourceObject = nullptr);
     XDRResult codeScript(MutableHandleScript scriptp);
 };
 
 using XDREncoder = XDRState<XDR_ENCODE>;
--- a/mfbt/Assertions.h
+++ b/mfbt/Assertions.h
@@ -216,26 +216,43 @@ MOZ_NoReturn(int aLine)
 }
 
 #  define MOZ_REALLY_CRASH(line) \
      do { \
        __debugbreak(); \
        MOZ_NoReturn(line); \
      } while (false)
 #else
+
+/*
+ * MOZ_CRASH_WRITE_ADDR is the address to be used when performing a forced
+ * crash. NULL is preferred however if for some reason NULL cannot be used
+ * this makes choosing another value possible.
+ *
+ * In the case of UBSan certain checks, bounds specifically, cause the compiler
+ * to emit the 'ud2' instruction when storing to 0x0. This causes forced
+ * crashes to manifest as ILL (at an arbitrary address) instead of the expected
+ * SEGV at 0x0.
+ */
+#  ifdef MOZ_UBSAN
+#    define MOZ_CRASH_WRITE_ADDR 0x1
+#  else
+#    define MOZ_CRASH_WRITE_ADDR NULL
+#  endif
+
 #  ifdef __cplusplus
 #    define MOZ_REALLY_CRASH(line) \
        do { \
-         *((volatile int*) NULL) = line; \
+         *((volatile int*) MOZ_CRASH_WRITE_ADDR) = line; \
          ::abort(); \
        } while (false)
 #  else
 #    define MOZ_REALLY_CRASH(line) \
        do { \
-         *((volatile int*) NULL) = line; \
+         *((volatile int*) MOZ_CRASH_WRITE_ADDR) = line; \
          abort(); \
        } while (false)
 #  endif
 #endif
 
 /*
  * MOZ_CRASH([explanation-string]) crashes the program, plain and simple, in a
  * Breakpad-compatible way, in both debug and release builds.