Merge graphics to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Thu, 27 Apr 2017 11:22:35 -0700
changeset 569628 c5c2b95e9d7b3c56cab54b3b8ca3ad11ee343646
parent 569501 abe5868346c7abb5b0bdf76f29bc3d9f839461f5 (current diff)
parent 569627 29f8f98fb7baf586f1b953e48c6eec235a7515aa (diff)
child 569629 1d487745871670f6c2b218a2cefd0f1c7ed1e7cd
child 569665 db3b433211bd79d153f4e3dd074cad407d454ff2
child 569700 ffdedb9c5aadf033a6a230bbc0338bc0e0760db0
child 569703 be4f389d6b343a4c912d68b5dba40d09bf6ebb92
child 570321 dd472c5b6214721ebef518e1584a8f4774c4dfbd
child 570879 5082d6fff817cae8abb8d662291dc1301dc5807e
child 570880 43bd3630a946b57e861a3297519ccb6258df51be
push id56240
push usermozilla@buttercookie.de
push dateThu, 27 Apr 2017 18:44:39 +0000
reviewersmerge
milestone55.0a1
Merge graphics to central, a=merge MozReview-Commit-ID: JY76sgXJHM0
gfx/webrender/src/CLIPPING.md
gfx/webrender/src/batch_builder.rs
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -5262,16 +5262,28 @@ ContentParent::RecvClassifyLocal(const U
   if (!uriClassifier) {
     return IPC_FAIL_NO_REASON(this);
   }
   *aRv = uriClassifier->ClassifyLocalWithTables(uri, aTables, *aResults);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
+ContentParent::RecvAllocPipelineId(RefPtr<AllocPipelineIdPromise>&& aPromise)
+{
+  GPUProcessManager* pm = GPUProcessManager::Get();
+  if (!pm) {
+    aPromise->Reject(PromiseRejectReason::HandlerRejected, __func__);
+    return IPC_OK();
+  }
+  aPromise->Resolve(wr::AsPipelineId(pm->AllocateLayerTreeId()), __func__);
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
 ContentParent::RecvFileCreationRequest(const nsID& aID,
                                        const nsString& aFullPath,
                                        const nsString& aType,
                                        const nsString& aName,
                                        const bool& aLastModifiedPassed,
                                        const int64_t& aLastModified,
                                        const bool& aExistenceCheck,
                                        const bool& aIsFromNsIFile)
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -641,16 +641,19 @@ public:
   DeallocPURLClassifierParent(PURLClassifierParent* aActor) override;
 
   virtual mozilla::ipc::IPCResult
   RecvClassifyLocal(const URIParams& aURI,
                     const nsCString& aTables,
                     nsresult* aRv,
                     nsTArray<nsCString>* aResults) override;
 
+  virtual mozilla::ipc::IPCResult
+  RecvAllocPipelineId(RefPtr<AllocPipelineIdPromise>&& aPromise) override;
+
   // Use the PHangMonitor channel to ask the child to repaint a tab.
   void ForceTabPaint(TabParent* aTabParent, uint64_t aLayerObserverEpoch);
 
   nsresult TransmitPermissionsFor(nsIChannel* aChannel);
 
   nsresult TransmitPermissionsForPrincipal(nsIPrincipal* aPrincipal);
 
 protected:
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -59,16 +59,17 @@ include ServiceWorkerConfiguration;
 include GraphicsMessages;
 include ProfilerTypes;
 include MemoryReportTypes;
 
 // Workaround to prevent error if PContentChild.cpp & PContentBridgeParent.cpp
 // are put into different UnifiedProtocolsXX.cpp files.
 // XXX Remove this once bug 1069073 is fixed
 include "mozilla/dom/PContentBridgeParent.h";
+include "mozilla/layers/WebRenderMessageUtils.h";
 
 using GeoPosition from "nsGeoPositionIPCSerialiser.h";
 using AlertNotificationType from "mozilla/AlertNotificationIPCSerializer.h";
 
 using struct ChromePackage from "mozilla/chrome/RegistryMessageUtils.h";
 using struct SubstitutionMapping from "mozilla/chrome/RegistryMessageUtils.h";
 using struct OverrideMapping from "mozilla/chrome/RegistryMessageUtils.h";
 using base::ChildPrivileges from "base/process_util.h";
@@ -91,16 +92,17 @@ using mozilla::OriginAttributes from "mo
 using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
 using mozilla::layers::CompositorOptions from "mozilla/layers/CompositorOptions.h";
 using struct mozilla::dom::FlyWebPublishOptions from "mozilla/dom/FlyWebPublishOptionsIPCSerializer.h";
 using mozilla::Telemetry::Accumulation from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::KeyedAccumulation from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::ScalarAction from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::KeyedScalarAction from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::ChildEventData from "mozilla/TelemetryComms.h";
+using mozilla::wr::PipelineId from "mozilla/webrender/WebRenderTypes.h";
 
 union ChromeRegistryItem
 {
     ChromePackage;
     OverrideMapping;
     SubstitutionMapping;
 };
 
@@ -1105,16 +1107,18 @@ parent:
 
     sync GetA11yContentId() returns (uint32_t aContentId);
     async A11yHandlerControl(uint32_t aPid,
                              IHandlerControlHolder aHandlerControl);
 
     async AddMemoryReport(MemoryReport aReport);
     async FinishMemoryReport(uint32_t aGeneration);
 
+    async AllocPipelineId() returns (PipelineId pipelineId);
+
 both:
      async AsyncMessage(nsString aMessage, CpowEntry[] aCpows,
                         Principal aPrincipal, ClonedMessageData aData);
 
     /**
      * Notify `push-subscription-modified` observers in the parent and child.
      */
     async NotifyPushSubscriptionModifiedObservers(nsCString scope,
--- a/dom/plugins/test/reftest/reftest.list
+++ b/dom/plugins/test/reftest/reftest.list
@@ -1,13 +1,13 @@
 # basic sanity checking
 random-if(!haveTestPlugin) != plugin-sanity.html about:blank
 fails-if(!haveTestPlugin) == plugin-sanity.html div-sanity.html
 fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,160000) == plugin-alpha-zindex.html div-alpha-zindex.html
-fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,164000) fails-if(webrender) == plugin-alpha-opacity.html div-alpha-opacity.html
+fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,164000) == plugin-alpha-opacity.html div-alpha-opacity.html
 random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) == windowless-clipping-1.html windowless-clipping-1-ref.html # bug 631832
 # fuzzy because of anti-aliasing in dashed border
 fuzzy(16,256) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) == border-padding-1.html border-padding-1-ref.html # bug 629430
 fuzzy(16,256) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) == border-padding-2.html border-padding-2-ref.html # bug 629430
 skip-if(!webrender) pref(layers.advanced.border-layers,1) fuzzy(16,256) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) == border-padding-2.html border-padding-2-ref.html
 fuzzy(16,256) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) skip-if(!haveTestPlugin||Android) == border-padding-3.html border-padding-3-ref.html # bug 629430 # bug 773482
 # The following two "pluginproblemui-direction" tests are unreliable on all platforms. They should be re-written or replaced.
 #random-if(cocoaWidget||d2d||/^Windows\x20NT\x205\.1/.test(http.oscpu)) fails-if(!haveTestPlugin&&!Android) == pluginproblemui-direction-1.html pluginproblemui-direction-1-ref.html # bug 567367
--- a/gfx/2d/UnscaledFontFreeType.cpp
+++ b/gfx/2d/UnscaledFontFreeType.cpp
@@ -2,22 +2,56 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "UnscaledFontFreeType.h"
 
 #include FT_TRUETYPE_TABLES_H
 
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
 namespace mozilla {
 namespace gfx {
 
 bool
 UnscaledFontFreeType::GetFontFileData(FontFileDataOutput aDataCallback, void* aBaton)
 {
+  if (!mFile.empty()) {
+    int fd = open(mFile.c_str(), O_RDONLY);
+    if (fd < 0) {
+      return false;
+    }
+    struct stat buf;
+    if (fstat(fd, &buf) < 0 ||
+        // Don't erroneously read directories as files.
+        !S_ISREG(buf.st_mode) ||
+        // Verify the file size fits in a uint32_t.
+        buf.st_size <= 0 ||
+        off_t(uint32_t(buf.st_size)) != buf.st_size) {
+      close(fd);
+      return false;
+    }
+    uint32_t length = buf.st_size;
+    uint8_t* fontData =
+      reinterpret_cast<uint8_t*>(
+        mmap(nullptr, length, PROT_READ, MAP_PRIVATE, fd, 0));
+    close(fd);
+    if (fontData == MAP_FAILED) {
+      return false;
+    }
+    aDataCallback(fontData, length, mIndex, aBaton);
+    munmap(fontData, length);
+    return true;
+  }
+
   bool success = false;
   FT_ULong length = 0;
   // Request the SFNT file. This may not always succeed for all font types.
   if (FT_Load_Sfnt_Table(mFace, 0, 0, nullptr, &length) == FT_Err_Ok) {
     uint8_t* fontData = new uint8_t[length];
     if (FT_Load_Sfnt_Table(mFace, 0, 0, fontData, &length) == FT_Err_Ok) {
       aDataCallback(fontData, length, 0, aBaton);
       success = true;
--- a/gfx/doc/README.webrender
+++ b/gfx/doc/README.webrender
@@ -74,9 +74,9 @@ there is another crate in m-c called moz
 the same folder to store its rust dependencies. If one of the libraries that is
 required by both mozjs_sys and webrender is updated without updating the other
 project's Cargo.lock file, that results in build bustage.
 This means that any time you do this sort of manual update of packages, you need
 to make sure that mozjs_sys also has its Cargo.lock file updated if needed, hence
 the need to run the cargo update command in js/src as well. Hopefully this will
 be resolved soon.
 
-Latest Commit: 3dd68f54e12bd5abf8ef41de4d4ec851620f7e4e
+Latest Commit: 1437cc124696ecc95b726dffa17f918bb6ea5af1
--- a/gfx/ipc/GfxMessageUtils.h
+++ b/gfx/ipc/GfxMessageUtils.h
@@ -290,16 +290,24 @@ struct ParamTraits<mozilla::layers::Text
 
 template <>
 struct ParamTraits<mozilla::layers::DiagnosticTypes>
   : public BitFlagsEnumSerializer<
              mozilla::layers::DiagnosticTypes,
              mozilla::layers::DiagnosticTypes::ALL_BITS>
 {};
 
+template <>
+struct ParamTraits<mozilla::layers::ScrollDirection>
+  : public ContiguousEnumSerializer<
+            mozilla::layers::ScrollDirection,
+            mozilla::layers::ScrollDirection::NONE,
+            mozilla::layers::ScrollDirection::SENTINEL>
+{};
+
 /*
 template <>
 struct ParamTraits<mozilla::PixelFormat>
   : public EnumSerializer<mozilla::PixelFormat,
                           SurfaceFormat::A8R8G8B8_UINT32,
                           SurfaceFormat::UNKNOWN>
 {};
 */
--- a/gfx/layers/AnimationHelper.cpp
+++ b/gfx/layers/AnimationHelper.cpp
@@ -7,33 +7,42 @@
 #include "AnimationHelper.h"
 #include "mozilla/ComputedTimingFunction.h" // for ComputedTimingFunction
 #include "mozilla/dom/AnimationEffectReadOnlyBinding.h" // for dom::FillMode
 #include "mozilla/dom/KeyframeEffectBinding.h" // for dom::IterationComposite
 #include "mozilla/dom/KeyframeEffectReadOnly.h" // for dom::KeyFrameEffectReadOnly
 #include "mozilla/layers/CompositorThread.h" // for CompositorThreadHolder
 #include "mozilla/layers/LayerAnimationUtils.h" // for TimingFunctionToComputedTimingFunction
 #include "mozilla/StyleAnimationValue.h" // for StyleAnimationValue, etc
+#include "nsDisplayList.h"              // for nsDisplayTransform, etc
 
 namespace mozilla {
 namespace layers {
 
 struct StyleAnimationValueCompositePair {
   StyleAnimationValue mValue;
   dom::CompositeOperation mComposite;
 };
 
 void
 CompositorAnimationStorage::Clear()
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
 
   mAnimatedValues.Clear();
   mAnimations.Clear();
+}
 
+void
+CompositorAnimationStorage::ClearById(const uint64_t& aId)
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+
+  mAnimatedValues.Remove(aId);
+  mAnimations.Remove(aId);
 }
 
 AnimatedValue*
 CompositorAnimationStorage::GetAnimatedValue(const uint64_t& aId) const
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   return mAnimatedValues.Get(aId);
 }
@@ -126,17 +135,17 @@ SampleValue(float aPortion, const layers
     StyleAnimationValue::Interpolate(aAnimation.property(),
                                      startValue, endValue,
                                      aPortion, interpolatedValue);
   MOZ_ASSERT(uncomputeResult, "could not uncompute value");
   return interpolatedValue;
 }
 
 bool
-AnimationHelper::SampleAnimationForEachNode(TimeStamp aPoint,
+AnimationHelper::SampleAnimationForEachNode(TimeStamp aTime,
                            AnimationArray& aAnimations,
                            InfallibleTArray<AnimData>& aAnimationData,
                            StyleAnimationValue& aAnimationValue,
                            bool& aHasInEffectAnimations)
 {
   bool activeAnimations = false;
 
   if (aAnimations.IsEmpty()) {
@@ -152,17 +161,17 @@ AnimationHelper::SampleAnimationForEachN
 
     MOZ_ASSERT(!animation.startTime().IsNull() ||
                animation.isNotPlaying(),
                "Failed to resolve start time of play-pending animations");
     // If the animation is not currently playing , e.g. paused or
     // finished, then use the hold time to stay at the same position.
     TimeDuration elapsedDuration = animation.isNotPlaying()
       ? animation.holdTime()
-      : (aPoint - animation.startTime())
+      : (aTime - animation.startTime())
           .MultDouble(animation.playbackRate());
     TimingParams timing;
     timing.mDuration.emplace(animation.duration());
     timing.mDelay = animation.delay();
     timing.mEndDelay = animation.endDelay();
     timing.mIterations = animation.iterations();
     timing.mIterationStart = animation.iterationStart();
     timing.mDirection =
@@ -473,10 +482,90 @@ AnimationHelper::GetNextCompositorAnimat
   ++sNextId;
 
   uint32_t procId = static_cast<uint32_t>(base::GetCurrentProcId());
   uint64_t nextId = procId;
   nextId = nextId << 32 | sNextId;
   return nextId;
 }
 
+void
+AnimationHelper::SampleAnimations(CompositorAnimationStorage* aStorage,
+                                  TimeStamp aTime)
+{
+  MOZ_ASSERT(aStorage);
+
+  // Do nothing if there are no compositor animations
+  if (!aStorage->AnimationsCount()) {
+    return;
+  }
+
+  //Sample the animations in CompositorAnimationStorage
+  for (auto iter = aStorage->ConstAnimationsTableIter();
+       !iter.Done(); iter.Next()) {
+    bool hasInEffectAnimations = false;
+    AnimationArray* animations = iter.UserData();
+    StyleAnimationValue animationValue;
+    InfallibleTArray<AnimData> animationData;
+    AnimationHelper::SetAnimations(*animations,
+                                   animationData,
+                                   animationValue);
+    AnimationHelper::SampleAnimationForEachNode(aTime,
+                                                *animations,
+                                                animationData,
+                                                animationValue,
+                                                hasInEffectAnimations);
+
+    if (!hasInEffectAnimations) {
+      continue;
+    }
+
+    // Store the AnimatedValue
+    Animation& animation = animations->LastElement();
+    switch (animation.property()) {
+      case eCSSProperty_opacity: {
+        aStorage->SetAnimatedValue(iter.Key(),
+                                   animationValue.GetFloatValue());
+        break;
+      }
+      case eCSSProperty_transform: {
+        nsCSSValueSharedList* list = animationValue.GetCSSValueSharedListValue();
+        const TransformData& transformData = animation.data().get_TransformData();
+        nsPoint origin = transformData.origin();
+        // we expect all our transform data to arrive in device pixels
+        gfx::Point3D transformOrigin = transformData.transformOrigin();
+        nsDisplayTransform::FrameTransformProperties props(list,
+                                                           transformOrigin);
+
+        gfx::Matrix4x4 transform =
+          nsDisplayTransform::GetResultingTransformMatrix(props, origin,
+                                                          transformData.appUnitsPerDevPixel(),
+                                                          0, &transformData.bounds());
+        gfx::Matrix4x4 frameTransform = transform;
+
+        //TODO how do we support this without layer information
+        // If our parent layer is a perspective layer, then the offset into reference
+        // frame coordinates is already on that layer. If not, then we need to ask
+        // for it to be added here.
+        // if (!aLayer->GetParent() ||
+        //     !aLayer->GetParent()->GetTransformIsPerspective()) {
+        //   nsLayoutUtils::PostTranslate(transform, origin,
+        //                                transformData.appUnitsPerDevPixel(),
+        //                                true);
+        // }
+
+        // if (ContainerLayer* c = aLayer->AsContainerLayer()) {
+        //   transform.PostScale(c->GetInheritedXScale(), c->GetInheritedYScale(), 1);
+        // }
+
+        aStorage->SetAnimatedValue(iter.Key(),
+                                   Move(transform), Move(frameTransform),
+                                   transformData);
+        break;
+      }
+      default:
+        MOZ_ASSERT_UNREACHABLE("Unhandled animated property");
+    }
+  }
+}
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/AnimationHelper.h
+++ b/gfx/layers/AnimationHelper.h
@@ -97,51 +97,113 @@ public:
   void SetAnimatedValue(uint64_t aId, const float& aOpacity);
 
   /**
    * Return the animated value if a given id can map to its animated value
    */
   AnimatedValue* GetAnimatedValue(const uint64_t& aId) const;
 
   /**
+   * Return the iterator of animated value table
+   */
+  AnimatedValueTable::Iterator ConstAnimatedValueTableIter() const
+  {
+    return mAnimatedValues.ConstIter();
+  }
+
+  uint32_t AnimatedValueCount() const
+  {
+    return mAnimatedValues.Count();
+  }
+
+  /**
    * Set the animations based on the unique id
    */
   void SetAnimations(uint64_t aId, const AnimationArray& aAnimations);
 
   /**
    * Return the animations if a given id can map to its animations
    */
   AnimationArray* GetAnimations(const uint64_t& aId) const;
 
   /**
+   * Return the iterator of animations table
+   */
+  AnimationsTable::Iterator ConstAnimationsTableIter() const
+  {
+    return mAnimations.ConstIter();
+  }
+
+  uint32_t AnimationsCount() const
+  {
+    return mAnimations.Count();
+  }
+
+  /**
    * Clear AnimatedValues and Animations data
    */
   void Clear();
 
+  void ClearById(const uint64_t& aId);
 private:
   ~CompositorAnimationStorage() { Clear(); };
 
 private:
   AnimatedValueTable mAnimatedValues;
   AnimationsTable mAnimations;
 };
 
 class AnimationHelper
 {
 public:
+
+
+  /*
+   * TODO Bug 1356509 Once we decouple the compositor animations and layers
+   * in parent side, the API will be called inside SampleAnimations.
+   * Before this, we expose this API for AsyncCompositionManager.
+   *
+   * Sample animations based on a given time stamp for a element(layer) with
+   * its animation data.
+   * Returns true if there exists compositor animation, and stores corresponding
+   * animated value in |aAnimationValue|.
+   */
   static bool
-  SampleAnimationForEachNode(TimeStamp aPoint,
+  SampleAnimationForEachNode(TimeStamp aTime,
                              AnimationArray& aAnimations,
                              InfallibleTArray<AnimData>& aAnimationData,
                              StyleAnimationValue& aAnimationValue,
                              bool& aHasInEffectAnimations);
-
+  /*
+   * TODO Bug 1356509 Once we decouple the compositor animations and layers
+   * in parent side, the API will be called inside SampleAnimations.
+   * Before this, we expose this API for AsyncCompositionManager.
+   *
+   * Populates AnimData stuctures into |aAnimData| based on |aAnimations|
+   */
   static void
   SetAnimations(AnimationArray& aAnimations,
                 InfallibleTArray<AnimData>& aAnimData,
                 StyleAnimationValue& aBaseAnimationStyle);
+
+  /*
+   * Get a unique id to represent the compositor animation between child
+   * and parent side. This id will be used as a key to store animation
+   * data in the CompositorAnimationStorage per compositor.
+   * Each layer on the content side calls this when it gets new animation
+   * data.
+   */
   static uint64_t GetNextCompositorAnimationsId();
+
+  /*
+   * Sample animation based a given time stamp |aTime| and the animation
+   * data inside CompositorAnimationStorage |aStorage|. The animated values
+   * after sampling will be stored in CompositorAnimationStorage as well.
+   */
+  static void
+  SampleAnimations(CompositorAnimationStorage* aStorage,
+                   TimeStamp aTime);
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // mozilla_layers_AnimationHelper_h
--- a/gfx/layers/LayerMetricsWrapper.h
+++ b/gfx/layers/LayerMetricsWrapper.h
@@ -349,16 +349,28 @@ public:
     MOZ_ASSERT(IsValid());
 
     if (AtBottomLayer()) {
       return mLayer->AsRefLayer();
     }
     return nullptr;
   }
 
+  Maybe<uint64_t> GetReferentId() const
+  {
+    MOZ_ASSERT(IsValid());
+
+    if (AtBottomLayer()) {
+      return mLayer->AsRefLayer()
+           ? Some(mLayer->AsRefLayer()->GetReferentId())
+           : Nothing();
+    }
+    return Nothing();
+  }
+
   Maybe<ParentLayerIntRect> GetClipRect() const
   {
     MOZ_ASSERT(IsValid());
 
     Maybe<ParentLayerIntRect> result;
 
     // The layer can have a clip rect and a scrolled clip, which are considered
     // to apply only to the bottommost LayerMetricsWrapper.
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -691,16 +691,27 @@ Layer::GetLocalTransform()
 
 const LayerToParentLayerMatrix4x4
 Layer::GetLocalTransformTyped()
 {
   return ViewAs<LayerToParentLayerMatrix4x4>(GetLocalTransform());
 }
 
 bool
+Layer::HasOpacityAnimation() const
+{
+  for (uint32_t i = 0; i < mAnimations.Length(); i++) {
+    if (mAnimations[i].property() == eCSSProperty_opacity) {
+      return true;
+    }
+  }
+  return false;
+}
+
+bool
 Layer::HasTransformAnimation() const
 {
   for (uint32_t i = 0; i < mAnimations.Length(); i++) {
     if (mAnimations[i].property() == eCSSProperty_transform) {
       return true;
     }
   }
   return false;
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -1436,16 +1436,17 @@ public:
   AnimationArray& GetAnimations() { return mAnimations; }
   uint64_t GetCompositorAnimationsId() { return mCompositorAnimationsId; }
   InfallibleTArray<AnimData>& GetAnimationData() { return mAnimationData; }
 
   uint64_t GetAnimationGeneration() { return mAnimationGeneration; }
   void SetAnimationGeneration(uint64_t aCount) { mAnimationGeneration = aCount; }
 
   bool HasTransformAnimation() const;
+  bool HasOpacityAnimation() const;
 
   StyleAnimationValue GetBaseAnimationStyle() const
   {
     return mBaseAnimationStyle;
   }
 
   /**
    * Returns the local transform for this layer: either mTransform or,
--- a/gfx/layers/LayersTypes.h
+++ b/gfx/layers/LayersTypes.h
@@ -297,15 +297,16 @@ public:
   }
 private:
   uint64_t mHandle;
 };
 
 enum class ScrollDirection : uint32_t {
   NONE,
   VERTICAL,
-  HORIZONTAL
+  HORIZONTAL,
+  SENTINEL /* for IPC serialization */
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif /* GFX_LAYERSTYPES_H */
new file mode 100644
--- /dev/null
+++ b/gfx/layers/UpdateImageHelper.h
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_UPDATEIMAGEHELPER_H
+#define GFX_UPDATEIMAGEHELPER_H
+
+#include "Layers.h"
+#include "mozilla/layers/ImageClient.h"
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/TextureClientRecycleAllocator.h"
+#include "mozilla/layers/TextureWrapperImage.h"
+#include "mozilla/gfx/Types.h"
+
+namespace mozilla {
+namespace layers {
+
+class UpdateImageHelper
+{
+public:
+  UpdateImageHelper(ImageContainer* aImageContainer, ImageClient* aImageClient, gfx::IntSize aImageSize) :
+    mImageContainer(aImageContainer),
+    mImageClient(aImageClient),
+    mImageSize(aImageSize),
+    mIsLocked(false)
+  {
+    mTexture = mImageClient->GetTextureClientRecycler()->CreateOrRecycle(gfx::SurfaceFormat::B8G8R8A8,
+                                                                         mImageSize,
+                                                                         BackendSelector::Content,
+                                                                         TextureFlags::DEFAULT);
+    if (!mTexture) {
+      return;
+    }
+
+    mIsLocked = mTexture->Lock(OpenMode::OPEN_WRITE_ONLY);
+    if (!mIsLocked) {
+      return;
+    }
+  }
+
+  ~UpdateImageHelper()
+  {
+    if (mIsLocked) {
+      mTexture->Unlock();
+      mIsLocked = false;
+    }
+  }
+
+  already_AddRefed<gfx::DrawTarget> GetDrawTarget()
+  {
+    RefPtr<gfx::DrawTarget> target = mTexture->BorrowDrawTarget();
+    return target.forget();
+  }
+
+  bool UpdateImage()
+  {
+    if (!mTexture) {
+      return false;
+    }
+
+    if (mIsLocked) {
+      mTexture->Unlock();
+      mIsLocked = false;
+    }
+
+    RefPtr<TextureWrapperImage> image = new TextureWrapperImage(mTexture,
+                                                                gfx::IntRect(gfx::IntPoint(0, 0), mImageSize));
+    mImageContainer->SetCurrentImageInTransaction(image);
+    return mImageClient->UpdateImage(mImageContainer, /* unused */0);
+  }
+
+private:
+  RefPtr<ImageContainer> mImageContainer;
+  RefPtr<ImageClient> mImageClient;
+  gfx::IntSize mImageSize;
+  RefPtr<TextureClient> mTexture;
+  bool mIsLocked;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif  // GFX_UPDATEIMAGEHELPER_H
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -17,16 +17,17 @@
 #include "mozilla/gfx/GPUParent.h"      // for GPUParent
 #include "mozilla/gfx/Logging.h"        // for gfx::TreeLog
 #include "mozilla/gfx/Point.h"          // for Point
 #include "mozilla/layers/APZThreadUtils.h"  // for AssertOnCompositorThread, etc
 #include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
 #include "mozilla/layers/AsyncDragMetrics.h" // for AsyncDragMetrics
 #include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent, etc
 #include "mozilla/layers/LayerMetricsWrapper.h"
+#include "mozilla/layers/WebRenderScrollDataWrapper.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/mozalloc.h"           // for operator new
 #include "mozilla/TouchEvents.h"
 #include "mozilla/Preferences.h"        // for Preferences
 #include "mozilla/EventStateManager.h"  // for WheelPrefs
 #include "nsDebug.h"                    // for NS_WARNING
 #include "nsPoint.h"                    // for nsIntPoint
 #include "nsThreadUtils.h"              // for NS_IsMainThread
@@ -211,22 +212,22 @@ APZCTreeManager::GetFrameTime()
 
 void
 APZCTreeManager::SetAllowedTouchBehavior(uint64_t aInputBlockId,
                                          const nsTArray<TouchBehaviorFlags> &aValues)
 {
   mInputQueue->SetAllowedTouchBehavior(aInputBlockId, aValues);
 }
 
-void
-APZCTreeManager::UpdateHitTestingTree(uint64_t aRootLayerTreeId,
-                                      Layer* aRoot,
-                                      bool aIsFirstPaint,
-                                      uint64_t aOriginatingLayersId,
-                                      uint32_t aPaintSequenceNumber)
+template<class ScrollNode> void // ScrollNode is a LayerMetricsWrapper or a WebRenderScrollDataWrapper
+APZCTreeManager::UpdateHitTestingTreeImpl(uint64_t aRootLayerTreeId,
+                                          const ScrollNode& aRoot,
+                                          bool aIsFirstPaint,
+                                          uint64_t aOriginatingLayersId,
+                                          uint32_t aPaintSequenceNumber)
 {
   APZThreadUtils::AssertOnCompositorThread();
 
   MutexAutoLock lock(mTreeLock);
 
   // For testing purposes, we log some data to the APZTestData associated with
   // the layers id that originated this update.
   APZTestData* testData = nullptr;
@@ -266,21 +267,20 @@ APZCTreeManager::UpdateHitTestingTree(ui
     std::stack<gfx::TreeAutoIndent> indents;
     std::stack<gfx::Matrix4x4> ancestorTransforms;
     HitTestingTreeNode* parent = nullptr;
     HitTestingTreeNode* next = nullptr;
     uint64_t layersId = aRootLayerTreeId;
     ancestorTransforms.push(Matrix4x4());
 
     mApzcTreeLog << "[start]\n";
-    LayerMetricsWrapper root(aRoot);
     mTreeLock.AssertCurrentThreadOwns();
 
-    ForEachNode<ReverseIterator>(root,
-        [&](LayerMetricsWrapper aLayerMetrics)
+    ForEachNode<ReverseIterator>(aRoot,
+        [&](ScrollNode aLayerMetrics)
         {
           mApzcTreeLog << aLayerMetrics.Name() << '\t';
 
           HitTestingTreeNode* node = PrepareNodeForLayer(aLayerMetrics,
                 aLayerMetrics.Metrics(), layersId, ancestorTransforms.top(),
                 parent, next, state);
           MOZ_ASSERT(node);
           AsyncPanZoomController* apzc = node->GetApzc();
@@ -303,20 +303,20 @@ APZCTreeManager::UpdateHitTestingTree(ui
           }
           ancestorTransforms.push(currentTransform);
 
           // Note that |node| at this point will not have any children, otherwise we
           // we would have to set next to node->GetFirstChild().
           MOZ_ASSERT(!node->GetFirstChild());
           parent = node;
           next = nullptr;
-          layersId = (aLayerMetrics.AsRefLayer() ? aLayerMetrics.AsRefLayer()->GetReferentId() : layersId);
+          layersId = aLayerMetrics.GetReferentId().valueOr(layersId);
           indents.push(gfx::TreeAutoIndent(mApzcTreeLog));
         },
-        [&](LayerMetricsWrapper aLayerMetrics)
+        [&](ScrollNode aLayerMetrics)
         {
           next = parent;
           parent = parent->GetParent();
           layersId = next->GetLayersId();
           ancestorTransforms.pop();
           indents.pop();
         });
 
@@ -335,38 +335,62 @@ APZCTreeManager::UpdateHitTestingTree(ui
 
 #if ENABLE_APZCTM_LOGGING
   // Make the hit-test tree line up with the layer dump
   printf_stderr("APZCTreeManager (%p)\n", this);
   mRootNode->Dump("  ");
 #endif
 }
 
+void
+APZCTreeManager::UpdateHitTestingTree(uint64_t aRootLayerTreeId,
+                                      Layer* aRoot,
+                                      bool aIsFirstPaint,
+                                      uint64_t aOriginatingLayersId,
+                                      uint32_t aPaintSequenceNumber)
+{
+  LayerMetricsWrapper root(aRoot);
+  UpdateHitTestingTreeImpl(aRootLayerTreeId, root, aIsFirstPaint,
+                           aOriginatingLayersId, aPaintSequenceNumber);
+}
+
+void
+APZCTreeManager::UpdateHitTestingTree(uint64_t aRootLayerTreeId,
+                                      const WebRenderScrollData& aScrollData,
+                                      bool aIsFirstPaint,
+                                      uint64_t aOriginatingLayersId,
+                                      uint32_t aPaintSequenceNumber)
+{
+  WebRenderScrollDataWrapper wrapper(&aScrollData);
+  UpdateHitTestingTreeImpl(aRootLayerTreeId, wrapper, aIsFirstPaint,
+                           aOriginatingLayersId, aPaintSequenceNumber);
+}
+
 // Compute the clip region to be used for a layer with an APZC. This function
 // is only called for layers which actually have scrollable metrics and an APZC.
-static ParentLayerIntRegion
+template<class ScrollNode> static ParentLayerIntRegion
 ComputeClipRegion(GeckoContentController* aController,
-                  const LayerMetricsWrapper& aLayer)
+                  const ScrollNode& aLayer)
 {
   ParentLayerIntRegion clipRegion;
   if (aLayer.GetClipRect()) {
     clipRegion = *aLayer.GetClipRect();
   } else {
     // if there is no clip on this layer (which should only happen for the
     // root scrollable layer in a process, or for some of the LayerMetrics
     // expansions of a multi-metrics layer), fall back to using the comp
     // bounds which should be equivalent.
     clipRegion = RoundedToInt(aLayer.Metrics().GetCompositionBounds());
   }
 
   return clipRegion;
 }
 
-void
-APZCTreeManager::PrintAPZCInfo(const LayerMetricsWrapper& aLayer,
+template<class ScrollNode> void
+APZCTreeManager::PrintAPZCInfo(const ScrollNode& aLayer,
                                const AsyncPanZoomController* apzc)
 {
   const FrameMetrics& metrics = aLayer.Metrics();
   mApzcTreeLog << "APZC " << apzc->GetGuid()
                << "\tcb=" << metrics.GetCompositionBounds()
                << "\tsr=" << metrics.GetScrollableRect()
                << (aLayer.IsScrollInfoLayer() ? "\tscrollinfo" : "")
                << (apzc->HasScrollgrab() ? "\tscrollgrab" : "") << "\t"
@@ -384,18 +408,18 @@ APZCTreeManager::AttachNodeToTree(HitTes
     aParent->SetLastChild(aNode);
   } else {
     MOZ_ASSERT(!mRootNode);
     mRootNode = aNode;
     aNode->MakeRoot();
   }
 }
 
-static EventRegions
-GetEventRegions(const LayerMetricsWrapper& aLayer)
+template<class ScrollNode> static EventRegions
+GetEventRegions(const ScrollNode& aLayer)
 {
   if (aLayer.IsScrollInfoLayer()) {
     ParentLayerIntRect compositionBounds(RoundedToInt(aLayer.Metrics().GetCompositionBounds()));
     nsIntRegion hitRegion(compositionBounds.ToUnknownRect());
     EventRegions eventRegions(hitRegion);
     eventRegions.mDispatchToContentHitRegion = eventRegions.mHitRegion;
     return eventRegions;
   }
@@ -417,19 +441,19 @@ APZCTreeManager::RecycleOrCreateNode(Tre
       node->RecycleWith(aApzc, aLayersId);
       return node.forget();
     }
   }
   RefPtr<HitTestingTreeNode> node = new HitTestingTreeNode(aApzc, false, aLayersId);
   return node.forget();
 }
 
-static EventRegionsOverride
+template<class ScrollNode> static EventRegionsOverride
 GetEventRegionsOverride(HitTestingTreeNode* aParent,
-                       const LayerMetricsWrapper& aLayer)
+                       const ScrollNode& aLayer)
 {
   // Make it so that if the flag is set on the layer tree, it automatically
   // propagates to all the nodes in the corresponding subtree rooted at that
   // layer in the hit-test tree. This saves having to walk up the tree every
   // we want to see if a hit-test node is affected by this flag.
   EventRegionsOverride result = aLayer.GetEventRegionsOverride();
   if (aParent) {
     result |= aParent->GetEventRegionsOverride();
@@ -455,18 +479,18 @@ APZCTreeManager::StartScrollbarDrag(cons
 void
 APZCTreeManager::NotifyScrollbarDragRejected(const ScrollableLayerGuid& aGuid) const
 {
   const LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(aGuid.mLayersId);
   MOZ_ASSERT(state && state->mController);
   state->mController->NotifyAsyncScrollbarDragRejected(aGuid.mScrollId);
 }
 
-HitTestingTreeNode*
-APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer,
+template<class ScrollNode> HitTestingTreeNode*
+APZCTreeManager::PrepareNodeForLayer(const ScrollNode& aLayer,
                                      const FrameMetrics& aMetrics,
                                      uint64_t aLayersId,
                                      const gfx::Matrix4x4& aAncestorTransform,
                                      HitTestingTreeNode* aParent,
                                      HitTestingTreeNode* aNextSibling,
                                      TreeBuildingState& aState)
 {
   mTreeLock.AssertCurrentThreadOwns();
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -35,16 +35,17 @@ class APZCTreeManagerParent;
 class CompositorBridgeParent;
 class OverscrollHandoffChain;
 struct OverscrollHandoffState;
 struct FlingHandoffState;
 class LayerMetricsWrapper;
 class InputQueue;
 class GeckoContentController;
 class HitTestingTreeNode;
+class WebRenderScrollData;
 
 /**
  * ****************** NOTE ON LOCK ORDERING IN APZ **************************
  *
  * There are two kinds of locks used by APZ: APZCTreeManager::mTreeLock
  * ("the tree lock") and AsyncPanZoomController::mMonitor ("APZC locks").
  *
  * To avoid deadlock, we impose a lock ordering between these locks, which is:
@@ -127,16 +128,28 @@ public:
    */
   void UpdateHitTestingTree(uint64_t aRootLayerTreeId,
                             Layer* aRoot,
                             bool aIsFirstPaint,
                             uint64_t aOriginatingLayersId,
                             uint32_t aPaintSequenceNumber);
 
   /**
+   * Same as the above UpdateHitTestingTree, except slightly modified to take
+   * the scrolling data passed over PWebRenderBridge instead of the raw layer
+   * tree. This version is used when WebRender is enabled because we don't have
+   * shadow layers in that scenario.
+   */
+  void UpdateHitTestingTree(uint64_t aRootLayerTreeId,
+                            const WebRenderScrollData& aScrollData,
+                            bool aIsFirstPaint,
+                            uint64_t aOriginatingLayersId,
+                            uint32_t aPaintSequenceNumber);
+
+  /**
    * Walk the tree of APZCs and flushes the repaint requests for all the APZCS
    * corresponding to the given layers id. Finally, sends a flush complete
    * notification to the GeckoContentController for the layers id.
    */
   void FlushApzRepaints(uint64_t aLayersId);
 
   /**
    * General handler for incoming input events. Manipulates the frame metrics
@@ -435,16 +448,23 @@ public:
    * is occurring such as when the toolbar is being hidden/shown in Fennec.
    * This function can be called to have the y axis' velocity queue updated.
    */
   void ProcessTouchVelocity(uint32_t aTimestampMs, float aSpeedY) override;
 private:
   typedef bool (*GuidComparator)(const ScrollableLayerGuid&, const ScrollableLayerGuid&);
 
   /* Helpers */
+  template<class ScrollNode>
+  void UpdateHitTestingTreeImpl(uint64_t aRootLayerTreeId,
+                                const ScrollNode& aRoot,
+                                bool aIsFirstPaint,
+                                uint64_t aOriginatingLayersId,
+                                uint32_t aPaintSequenceNumber);
+
   void AttachNodeToTree(HitTestingTreeNode* aNode,
                         HitTestingTreeNode* aParent,
                         HitTestingTreeNode* aNextSibling);
   already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const ScrollableLayerGuid& aGuid);
   already_AddRefed<HitTestingTreeNode> GetTargetNode(const ScrollableLayerGuid& aGuid,
                                                      GuidComparator aComparator);
   HitTestingTreeNode* FindTargetNode(HitTestingTreeNode* aNode,
                                      const ScrollableLayerGuid& aGuid,
@@ -465,25 +485,27 @@ private:
   nsEventStatus ProcessTouchInput(MultiTouchInput& aInput,
                                   ScrollableLayerGuid* aOutTargetGuid,
                                   uint64_t* aOutInputBlockId);
   void FlushRepaintsToClearScreenToGeckoTransform();
 
   already_AddRefed<HitTestingTreeNode> RecycleOrCreateNode(TreeBuildingState& aState,
                                                            AsyncPanZoomController* aApzc,
                                                            uint64_t aLayersId);
-  HitTestingTreeNode* PrepareNodeForLayer(const LayerMetricsWrapper& aLayer,
+  template<class ScrollNode>
+  HitTestingTreeNode* PrepareNodeForLayer(const ScrollNode& aLayer,
                                           const FrameMetrics& aMetrics,
                                           uint64_t aLayersId,
                                           const gfx::Matrix4x4& aAncestorTransform,
                                           HitTestingTreeNode* aParent,
                                           HitTestingTreeNode* aNextSibling,
                                           TreeBuildingState& aState);
 
-  void PrintAPZCInfo(const LayerMetricsWrapper& aLayer,
+  template<class ScrollNode>
+  void PrintAPZCInfo(const ScrollNode& aLayer,
                      const AsyncPanZoomController* apzc);
 
   void NotifyScrollbarDragRejected(const ScrollableLayerGuid& aGuid) const;
 
 protected:
   /* The input queue where input events are held until we know enough to
    * figure out where they're going. Protected so gtests can access it.
    */
--- a/gfx/layers/apz/test/gtest/APZTestCommon.h
+++ b/gfx/layers/apz/test/gtest/APZTestCommon.h
@@ -263,16 +263,19 @@ public:
       EXPECT_EQ(PANNING, mState);
       break;
     case ScrollDirection::HORIZONTAL:
       EXPECT_EQ(PANNING_LOCKED_X, mState);
       break;
     case ScrollDirection::VERTICAL:
       EXPECT_EQ(PANNING_LOCKED_Y, mState);
       break;
+    case ScrollDirection::SENTINEL:
+      MOZ_ASSERT(false, "Invalid value");
+      break;
     }
   }
 
   void AdvanceAnimationsUntilEnd(const TimeDuration& aIncrement = TimeDuration::FromMilliseconds(10)) {
     while (AdvanceAnimations(mcc->Time())) {
       mcc->AdvanceBy(aIncrement);
     }
   }
--- a/gfx/layers/client/ImageClient.h
+++ b/gfx/layers/client/ImageClient.h
@@ -65,16 +65,18 @@ public:
   virtual void FlushAllImages() {}
 
   virtual void RemoveTexture(TextureClient* aTexture) override;
 
   virtual ImageClientSingle* AsImageClientSingle() { return nullptr; }
 
   static already_AddRefed<TextureClient> CreateTextureClientForImage(Image* aImage, KnowsCompositor* aForwarder);
 
+  uint32_t GetLastUpdateGenerationCounter() { return mLastUpdateGenerationCounter; }
+
 protected:
   ImageClient(CompositableForwarder* aFwd, TextureFlags aFlags,
               CompositableType aType);
 
   ClientLayer* mLayer;
   CompositableType mType;
   uint32_t mLastUpdateGenerationCounter;
 };
@@ -96,16 +98,18 @@ public:
   virtual bool AddTextureClient(TextureClient* aTexture) override;
 
   virtual TextureInfo GetTextureInfo() const override;
 
   virtual void FlushAllImages() override;
 
   ImageClientSingle* AsImageClientSingle() override { return this; }
 
+  bool IsEmpty() { return mBuffers.IsEmpty(); }
+
 protected:
   struct Buffer {
     RefPtr<TextureClient> mTextureClient;
     int32_t mImageSerial;
   };
   nsTArray<Buffer> mBuffers;
 };
 
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -887,21 +887,25 @@ TextureClient::InitIPDLActor(Compositabl
   }
   MOZ_ASSERT(!mActor || mActor->mDestroyed, "Cannot use a texture on several IPC channels.");
 
   SurfaceDescriptor desc;
   if (!ToSurfaceDescriptor(desc)) {
     return false;
   }
 
+  // Try external image id allocation.
+  mExternalImageId = aForwarder->GetTextureForwarder()->GetNextExternalImageId();
+
   PTextureChild* actor = aForwarder->GetTextureForwarder()->CreateTexture(
     desc,
     aForwarder->GetCompositorBackendType(),
     GetFlags(),
-    mSerial);
+    mSerial,
+    mExternalImageId);
   if (!actor) {
     gfxCriticalNote << static_cast<int32_t>(desc.type()) << ", "
                     << static_cast<int32_t>(aForwarder->GetCompositorBackendType()) << ", "
                     << static_cast<uint32_t>(GetFlags())
                     << ", " << mSerial;
     return false;
   }
 
@@ -943,21 +947,25 @@ TextureClient::InitIPDLActor(KnowsCompos
   }
   MOZ_ASSERT(!mActor || mActor->mDestroyed, "Cannot use a texture on several IPC channels.");
 
   SurfaceDescriptor desc;
   if (!ToSurfaceDescriptor(desc)) {
     return false;
   }
 
+  // Try external image id allocation.
+  mExternalImageId = aForwarder->GetTextureForwarder()->GetNextExternalImageId();
+
   PTextureChild* actor = fwd->CreateTexture(
     desc,
     aForwarder->GetCompositorBackendType(),
     GetFlags(),
-    mSerial);
+    mSerial,
+    mExternalImageId);
   if (!actor) {
     gfxCriticalNote << static_cast<int32_t>(desc.type()) << ", "
                     << static_cast<int32_t>(aForwarder->GetCompositorBackendType()) << ", "
                     << static_cast<uint32_t>(GetFlags())
                     << ", " << mSerial;
     return false;
   }
 
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -20,16 +20,17 @@
 #include "mozilla/ipc/Shmem.h"          // for Shmem
 #include "mozilla/layers/AtomicRefCountedWithFinalize.h"
 #include "mozilla/layers/CompositorTypes.h"  // for TextureFlags, etc
 #include "mozilla/layers/ISurfaceAllocator.h"
 #include "mozilla/layers/LayersTypes.h"
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor
 #include "mozilla/mozalloc.h"           // for operator delete
 #include "mozilla/gfx/CriticalSection.h"
+#include "mozilla/webrender/WebRenderTypes.h"
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsISupportsImpl.h"            // for TextureImage::AddRef, etc
 #include "GfxTexturesReporter.h"
 #include "pratom.h"
 #include "nsThreadUtils.h"
 
 class gfxImageSurface;
 struct ID3D11Device;
@@ -741,16 +742,23 @@ protected:
   bool mWorkaroundAnnoyingSharedSurfaceOwnershipIssues;
 
   RefPtr<TextureReadbackSink> mReadbackSink;
 
   uint64_t mFwdTransactionId;
 
   // Serial id of TextureClient. It is unique in current process.
   const uint64_t mSerial;
+
+  // External image id. It is unique if it is allocated.
+  // The id is allocated in TextureClient::InitIPDLActor().
+  // Its allocation is supported by
+  // CompositorBridgeChild and ImageBridgeChild for now.
+  wr::MaybeExternalImageId mExternalImageId;
+
   // Used to assign serial ids of TextureClient.
   static mozilla::Atomic<uint64_t> sSerialCounter;
 
   friend class TextureChild;
   friend void TestTextureClientSurface(TextureClient*, gfxImageSurface*);
   friend void TestTextureClientYCbCr(TextureClient*, PlanarYCbCrData&);
   friend already_AddRefed<TextureHost> CreateTextureHostWithBackend(
     TextureClient*, LayersBackend&);
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -643,17 +643,17 @@ ApplyAnimatedValue(Layer* aLayer,
     default:
       MOZ_ASSERT_UNREACHABLE("Unhandled animated property");
   }
 }
 
 static AnimationProcessTypes
 SampleAnimations(Layer* aLayer,
                  CompositorAnimationStorage* aStorage,
-                 TimeStamp aPoint,
+                 TimeStamp aTime,
                  uint64_t* aLayerAreaAnimated)
 {
   // This tracks the first-encountered RefLayer in the layer tree. Since we are
   // doing a depth-first traversal, it is set to a non-null value if and only if
   // the currently-being-traversed node has a RefLayer ancestor. In the case of
   // nested RefLayers it points to the rootmost RefLayer.
   RefLayer* ancestorRefLayer = nullptr;
 
@@ -667,17 +667,17 @@ SampleAnimations(Layer* aLayer,
       [&] (Layer* layer)
       {
         if (!ancestorRefLayer) {
           ancestorRefLayer = layer->AsRefLayer();
         }
 
         bool hasInEffectAnimations = false;
         StyleAnimationValue animationValue = layer->GetBaseAnimationStyle();
-        if (AnimationHelper::SampleAnimationForEachNode(aPoint,
+        if (AnimationHelper::SampleAnimationForEachNode(aTime,
                                                         layer->GetAnimations(),
                                                         layer->GetAnimationData(),
                                                         animationValue,
                                                         hasInEffectAnimations)) {
           animProcess |= (ancestorRefLayer ? AnimationProcessTypes::eContent
                                            : AnimationProcessTypes::eChrome);
         }
         if (hasInEffectAnimations) {
--- a/gfx/layers/composite/ImageComposite.cpp
+++ b/gfx/layers/composite/ImageComposite.cpp
@@ -1,36 +1,35 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ImageComposite.h"
 
-// this is also defined in ImageHost.cpp
-#define BIAS_TIME_MS 1.0
-
 namespace mozilla {
 
 using namespace gfx;
 
 namespace layers {
 
+/* static */ const float ImageComposite::BIAS_TIME_MS = 1.0f;
+
 ImageComposite::ImageComposite()
   : mLastFrameID(-1)
   , mLastProducerID(-1)
   , mBias(BIAS_NONE)
 {}
 
 ImageComposite::~ImageComposite()
 {
 }
 
-static TimeStamp
-GetBiasedTime(const TimeStamp& aInput, ImageComposite::Bias aBias)
+/* static */ TimeStamp
+ImageComposite::GetBiasedTime(const TimeStamp& aInput, ImageComposite::Bias aBias)
 {
   switch (aBias) {
   case ImageComposite::BIAS_NEGATIVE:
     return aInput - TimeDuration::FromMilliseconds(BIAS_TIME_MS);
   case ImageComposite::BIAS_POSITIVE:
     return aInput + TimeDuration::FromMilliseconds(BIAS_TIME_MS);
   default:
     return aInput;
@@ -115,10 +114,8 @@ const ImageComposite::TimedImage* ImageC
 ImageComposite::TimedImage* ImageComposite::ChooseImage()
 {
   int index = ChooseImageIndex();
   return index >= 0 ? &mImages[index] : nullptr;
 }
 
 } // namespace layers
 } // namespace mozilla
-
-#undef BIAS_TIME_MS
--- a/gfx/layers/composite/ImageComposite.h
+++ b/gfx/layers/composite/ImageComposite.h
@@ -15,16 +15,18 @@ namespace mozilla {
 namespace layers {
 
 /**
  * Implements Image selection logic.
  */
 class ImageComposite
 {
 public:
+  static const float BIAS_TIME_MS;
+
   explicit ImageComposite();
   ~ImageComposite();
 
   int32_t GetFrameID()
   {
     const TimedImage* img = ChooseImage();
     return img ? img->mFrameID : -1;
   }
@@ -42,16 +44,18 @@ public:
     // Don't apply bias to frame times
     BIAS_NONE,
     // Apply a negative bias to frame times to keep them before the vsync time
     BIAS_NEGATIVE,
     // Apply a positive bias to frame times to keep them after the vsync time
     BIAS_POSITIVE,
   };
 
+  static TimeStamp GetBiasedTime(const TimeStamp& aInput, ImageComposite::Bias aBias);
+
 protected:
   static Bias UpdateBias(const TimeStamp& aCompositionTime,
                          const TimeStamp& aCompositedImageTime,
                          const TimeStamp& aNextImageTime, // may be null
                          ImageComposite::Bias aBias);
 
   virtual TimeStamp GetCompositionTime() const = 0;
 
--- a/gfx/layers/composite/ImageHost.cpp
+++ b/gfx/layers/composite/ImageHost.cpp
@@ -11,19 +11,16 @@
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/Effects.h"     // for TexturedEffect, Effect, etc
 #include "mozilla/layers/LayerManagerComposite.h"     // for TexturedEffect, Effect, etc
 #include "nsAString.h"
 #include "nsDebug.h"                    // for NS_WARNING, NS_ASSERTION
 #include "nsPrintfCString.h"            // for nsPrintfCString
 #include "nsString.h"                   // for nsAutoCString
 
-// this is also defined in ImageComposite.cpp
-#define BIAS_TIME_MS 1.0
-
 namespace mozilla {
 
 using namespace gfx;
 
 namespace layers {
 
 class ISurfaceAllocator;
 
@@ -471,10 +468,8 @@ ImageHost::GenEffect(const gfx::Sampling
   return CreateTexturedEffect(mCurrentTextureHost,
                               mCurrentTextureSource,
                               aSamplingFilter,
                               isAlphaPremultiplied);
 }
 
 } // namespace layers
 } // namespace mozilla
-
-#undef BIAS_TIME_MS
--- a/gfx/layers/composite/TextureHost.cpp
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -60,17 +60,17 @@ namespace layers {
 
 /**
  * TextureParent is the host-side IPDL glue between TextureClient and TextureHost.
  * It is an IPDL actor just like LayerParent, CompositableParent, etc.
  */
 class TextureParent : public ParentActor<PTextureParent>
 {
 public:
-  explicit TextureParent(HostIPCAllocator* aAllocator, uint64_t aSerial);
+  explicit TextureParent(HostIPCAllocator* aAllocator, uint64_t aSerial, const wr::MaybeExternalImageId& aExternalImageId);
 
   ~TextureParent();
 
   bool Init(const SurfaceDescriptor& aSharedData,
             const LayersBackend& aLayersBackend,
             const TextureFlags& aFlags);
 
   void NotifyNotUsed(uint64_t aTransactionId);
@@ -82,16 +82,17 @@ public:
   virtual void Destroy() override;
 
   uint64_t GetSerial() const { return mSerial; }
 
   HostIPCAllocator* mSurfaceAllocator;
   RefPtr<TextureHost> mTextureHost;
   // mSerial is unique in TextureClient's process.
   const uint64_t mSerial;
+  wr::MaybeExternalImageId mExternalImageId;
 };
 
 static bool
 WrapWithWebRenderTextureHost(ISurfaceAllocator* aDeallocator,
                              LayersBackend aBackend,
                              TextureFlags aFlags)
 {
   if (!gfxVars::UseWebRender() ||
@@ -104,26 +105,27 @@ WrapWithWebRenderTextureHost(ISurfaceAll
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 PTextureParent*
 TextureHost::CreateIPDLActor(HostIPCAllocator* aAllocator,
                              const SurfaceDescriptor& aSharedData,
                              LayersBackend aLayersBackend,
                              TextureFlags aFlags,
-                             uint64_t aSerial)
+                             uint64_t aSerial,
+                             const wr::MaybeExternalImageId& aExternalImageId)
 {
   if (aSharedData.type() == SurfaceDescriptor::TSurfaceDescriptorBuffer &&
       aSharedData.get_SurfaceDescriptorBuffer().data().type() == MemoryOrShmem::Tuintptr_t &&
       !aAllocator->IsSameProcess())
   {
     NS_ERROR("A client process is trying to peek at our address space using a MemoryTexture!");
     return nullptr;
   }
-  TextureParent* actor = new TextureParent(aAllocator, aSerial);
+  TextureParent* actor = new TextureParent(aAllocator, aSerial, aExternalImageId);
   if (!actor->Init(aSharedData, aLayersBackend, aFlags)) {
     delete actor;
     return nullptr;
   }
   return actor;
 }
 
 // static
@@ -191,17 +193,18 @@ already_AddRefed<TextureHost> CreateText
                                                      ISurfaceAllocator* aDeallocator,
                                                      LayersBackend aBackend,
                                                      TextureFlags aFlags);
 
 already_AddRefed<TextureHost>
 TextureHost::Create(const SurfaceDescriptor& aDesc,
                     ISurfaceAllocator* aDeallocator,
                     LayersBackend aBackend,
-                    TextureFlags aFlags)
+                    TextureFlags aFlags,
+                    wr::MaybeExternalImageId& aExternalImageId)
 {
   RefPtr<TextureHost> result;
 
   switch (aDesc.type()) {
     case SurfaceDescriptor::TSurfaceDescriptorBuffer:
     case SurfaceDescriptor::TSurfaceDescriptorDIB:
     case SurfaceDescriptor::TSurfaceDescriptorFileMapping:
     case SurfaceDescriptor::TSurfaceDescriptorGPUVideo:
@@ -238,17 +241,18 @@ TextureHost::Create(const SurfaceDescrip
       result = CreateTextureHostD3D11(aDesc, aDeallocator, aBackend, aFlags);
       break;
 #endif
     default:
       MOZ_CRASH("GFX: Unsupported Surface type host");
   }
 
   if (WrapWithWebRenderTextureHost(aDeallocator, aBackend, aFlags)) {
-    result = new WebRenderTextureHost(aDesc, aFlags, result);
+    MOZ_ASSERT(aExternalImageId.isSome());
+    result = new WebRenderTextureHost(aDesc, aFlags, result, aExternalImageId.ref());
   }
 
   return result.forget();
 }
 
 already_AddRefed<TextureHost>
 CreateBackendIndependentTextureHost(const SurfaceDescriptor& aDesc,
                                     ISurfaceAllocator* aDeallocator,
@@ -1075,19 +1079,20 @@ size_t MemoryTextureHost::GetBufferSize(
 {
   // MemoryTextureHost just trusts that the buffer size is large enough to read
   // anything we need to. That's because MemoryTextureHost has to trust the buffer
   // pointer anyway, so the security model here is just that MemoryTexture's
   // are restricted to same-process clients.
   return std::numeric_limits<size_t>::max();
 }
 
-TextureParent::TextureParent(HostIPCAllocator* aSurfaceAllocator, uint64_t aSerial)
+TextureParent::TextureParent(HostIPCAllocator* aSurfaceAllocator, uint64_t aSerial, const wr::MaybeExternalImageId& aExternalImageId)
 : mSurfaceAllocator(aSurfaceAllocator)
 , mSerial(aSerial)
+, mExternalImageId(aExternalImageId)
 {
   MOZ_COUNT_CTOR(TextureParent);
 }
 
 TextureParent::~TextureParent()
 {
   MOZ_COUNT_DTOR(TextureParent);
 }
@@ -1104,17 +1109,18 @@ TextureParent::NotifyNotUsed(uint64_t aT
 bool
 TextureParent::Init(const SurfaceDescriptor& aSharedData,
                     const LayersBackend& aBackend,
                     const TextureFlags& aFlags)
 {
   mTextureHost = TextureHost::Create(aSharedData,
                                      mSurfaceAllocator,
                                      aBackend,
-                                     aFlags);
+                                     aFlags,
+                                     mExternalImageId);
   if (mTextureHost) {
     mTextureHost->mActor = this;
   }
 
   return !!mTextureHost;
 }
 
 void
--- a/gfx/layers/composite/TextureHost.h
+++ b/gfx/layers/composite/TextureHost.h
@@ -16,16 +16,17 @@
 #include "mozilla/gfx/Point.h"          // for IntSize, IntPoint
 #include "mozilla/gfx/Types.h"          // for SurfaceFormat, etc
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/CompositorTypes.h"  // for TextureFlags, etc
 #include "mozilla/layers/LayersTypes.h"  // for LayerRenderState, etc
 #include "mozilla/layers/LayersSurfaces.h"
 #include "mozilla/mozalloc.h"           // for operator delete
 #include "mozilla/UniquePtr.h"          // for UniquePtr
+#include "mozilla/webrender/WebRenderTypes.h"
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsDebug.h"                    // for NS_RUNTIMEABORT
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
 #include "nsRegion.h"                   // for nsIntRegion
 #include "nsTraceRefcnt.h"              // for MOZ_COUNT_CTOR, etc
 #include "nscore.h"                     // for nsACString
 #include "mozilla/layers/AtomicRefCountedWithFinalize.h"
 #include "mozilla/gfx/Rect.h"
@@ -381,17 +382,18 @@ protected:
 public:
   /**
    * Factory method.
    */
   static already_AddRefed<TextureHost> Create(
     const SurfaceDescriptor& aDesc,
     ISurfaceAllocator* aDeallocator,
     LayersBackend aBackend,
-    TextureFlags aFlags);
+    TextureFlags aFlags,
+    wr::MaybeExternalImageId& aExternalImageId);
 
   /**
    * Lock the texture host for compositing.
    */
   virtual bool Lock() { return true; }
   /**
    * Unlock the texture host after compositing. Lock() and Unlock() should be
    * called in pair.
@@ -514,17 +516,18 @@ public:
    * exposed to the rest of the code base. CreateIPDLActor and DestroyIPDLActor
    * are for use with the managing IPDL protocols only (so that they can
    * implement AllocPTextureParent and DeallocPTextureParent).
    */
   static PTextureParent* CreateIPDLActor(HostIPCAllocator* aAllocator,
                                          const SurfaceDescriptor& aSharedData,
                                          LayersBackend aLayersBackend,
                                          TextureFlags aFlags,
-                                         uint64_t aSerial);
+                                         uint64_t aSerial,
+                                         const wr::MaybeExternalImageId& aExternalImageId);
   static bool DestroyIPDLActor(PTextureParent* actor);
 
   /**
    * Destroy the TextureChild/Parent pair.
    */
   static bool SendDeleteIPDLActor(PTextureParent* actor);
 
   static void ReceivedDestroy(PTextureParent* actor);
--- a/gfx/layers/ipc/CompositorBridgeChild.cpp
+++ b/gfx/layers/ipc/CompositorBridgeChild.cpp
@@ -880,17 +880,18 @@ CompositorBridgeChild::SendAllPluginsCap
   return PCompositorBridgeChild::SendAllPluginsCaptured();
 }
 
 PTextureChild*
 CompositorBridgeChild::AllocPTextureChild(const SurfaceDescriptor&,
                                           const LayersBackend&,
                                           const TextureFlags&,
                                           const uint64_t&,
-                                          const uint64_t& aSerial)
+                                          const uint64_t& aSerial,
+                                          const wr::MaybeExternalImageId& aExternalImageId)
 {
   return TextureClient::CreateIPDLActor();
 }
 
 bool
 CompositorBridgeChild::DeallocPTextureChild(PTextureChild* actor)
 {
   return TextureClient::DestroyIPDLActor(actor);
@@ -1050,19 +1051,20 @@ CompositorBridgeChild::GetTileLockAlloca
   return mSectionAllocator;
 }
 
 
 PTextureChild*
 CompositorBridgeChild::CreateTexture(const SurfaceDescriptor& aSharedData,
                                      LayersBackend aLayersBackend,
                                      TextureFlags aFlags,
-                                     uint64_t aSerial)
+                                     uint64_t aSerial,
+                                     wr::MaybeExternalImageId& aExternalImageId)
 {
-  return PCompositorBridgeChild::SendPTextureConstructor(aSharedData, aLayersBackend, aFlags, 0 /* FIXME? */, aSerial);
+  return PCompositorBridgeChild::SendPTextureConstructor(aSharedData, aLayersBackend, aFlags, 0 /* FIXME? */, aSerial, aExternalImageId);
 }
 
 bool
 CompositorBridgeChild::AllocUnsafeShmem(size_t aSize,
                                    ipc::SharedMemory::SharedMemoryType aType,
                                    ipc::Shmem* aShmem)
 {
   ShmemAllocated(this);
@@ -1180,23 +1182,23 @@ CompositorBridgeChild::AllocPWebRenderBr
 bool
 CompositorBridgeChild::DeallocPWebRenderBridgeChild(PWebRenderBridgeChild* aActor)
 {
   WebRenderBridgeChild* child = static_cast<WebRenderBridgeChild*>(aActor);
   child->ReleaseIPDLReference();
   return true;
 }
 
-uint64_t
+wr::MaybeExternalImageId
 CompositorBridgeChild::GetNextExternalImageId()
 {
   static uint32_t sNextID = 1;
   ++sNextID;
   MOZ_RELEASE_ASSERT(sNextID != UINT32_MAX);
 
   uint64_t imageId = mNamespace;
   imageId = imageId << 32 | sNextID;
-  return imageId;
+  return Some(wr::ToExternalImageId(imageId));
 }
 
 } // namespace layers
 } // namespace mozilla
 
--- a/gfx/layers/ipc/CompositorBridgeChild.h
+++ b/gfx/layers/ipc/CompositorBridgeChild.h
@@ -8,16 +8,17 @@
 #define mozilla_layers_CompositorBridgeChild_h
 
 #include "base/basictypes.h"            // for DISALLOW_EVIL_CONSTRUCTORS
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT_HELPER2
 #include "mozilla/Attributes.h"         // for override
 #include "mozilla/ipc/ProtocolUtils.h"
 #include "mozilla/layers/PCompositorBridgeChild.h"
 #include "mozilla/layers/TextureForwarder.h" // for TextureForwarder
+#include "mozilla/webrender/WebRenderTypes.h"
 #include "nsClassHashtable.h"           // for nsClassHashtable
 #include "nsCOMPtr.h"                   // for nsCOMPtr
 #include "nsHashKeys.h"                 // for nsUint64HashKey
 #include "nsISupportsImpl.h"            // for NS_INLINE_DECL_REFCOUNTING
 #include "ThreadSafeRefcountingWithMainThreadDestruction.h"
 #include "nsWeakReference.h"
 
 namespace mozilla {
@@ -117,26 +118,28 @@ public:
 
   virtual mozilla::ipc::IPCResult
   RecvHideAllPlugins(const uintptr_t& aParentWidget) override;
 
   virtual PTextureChild* AllocPTextureChild(const SurfaceDescriptor& aSharedData,
                                             const LayersBackend& aLayersBackend,
                                             const TextureFlags& aFlags,
                                             const uint64_t& aId,
-                                            const uint64_t& aSerial) override;
+                                            const uint64_t& aSerial,
+                                            const wr::MaybeExternalImageId& aExternalImageId) override;
 
   virtual bool DeallocPTextureChild(PTextureChild* actor) override;
 
   virtual mozilla::ipc::IPCResult
   RecvParentAsyncMessages(InfallibleTArray<AsyncParentMessageData>&& aMessages) override;
   virtual PTextureChild* CreateTexture(const SurfaceDescriptor& aSharedData,
                                        LayersBackend aLayersBackend,
                                        TextureFlags aFlags,
-                                       uint64_t aSerial) override;
+                                       uint64_t aSerial,
+                                       wr::MaybeExternalImageId& aExternalImageId) override;
 
   virtual void HandleFatalError(const char* aName, const char* aMsg) const override;
 
   /**
    * Request that the parent tell us when graphics are ready on GPU.
    * When we get that message, we bounce it to the TabParent via
    * the TabChild
    * @param tabChild The object to bounce the note to.  Non-NULL.
@@ -229,17 +232,17 @@ public:
                                                     TextureFactoryIdentifier*,
                                                     uint32_t*) override;
   bool DeallocPWebRenderBridgeChild(PWebRenderBridgeChild* aActor) override;
 
   uint64_t DeviceResetSequenceNumber() const {
     return mDeviceResetSequenceNumber;
   }
 
-  uint64_t GetNextExternalImageId();
+  wr::MaybeExternalImageId GetNextExternalImageId() override;
 
 private:
   // Private destructor, to discourage deletion outside of Release():
   virtual ~CompositorBridgeChild();
 
   void InitIPDL();
   void DeallocPCompositorBridgeChild() override;
 
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -1596,17 +1596,17 @@ CompositorBridgeParent::AllocPWebRenderB
                                                     TextureFactoryIdentifier* aTextureFactoryIdentifier,
                                                     uint32_t* aIdNamespace)
 {
 #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(aPipelineId.mHandle == mRootLayerTreeID);
+  MOZ_ASSERT(wr::AsUint64(aPipelineId) == mRootLayerTreeID);
   MOZ_ASSERT(!mWrBridge);
   MOZ_ASSERT(!mCompositor);
   MOZ_ASSERT(!mCompositorScheduler);
 
 
   MOZ_ASSERT(mWidget);
   RefPtr<widget::CompositorWidget> widget = mWidget;
   RefPtr<wr::WebRenderAPI> api = wr::WebRenderAPI::Create(
@@ -1616,19 +1616,18 @@ CompositorBridgeParent::AllocPWebRenderB
   api->SetRootPipeline(aPipelineId);
   mWrBridge = new WebRenderBridgeParent(this, aPipelineId, mWidget, nullptr, Move(api), Move(holder));
   *aIdNamespace = mWrBridge->GetIdNameSpace();
 
   mCompositorScheduler = mWrBridge->CompositorScheduler();
   MOZ_ASSERT(mCompositorScheduler);
   mWrBridge.get()->AddRef(); // IPDL reference
   MonitorAutoLock lock(*sIndirectLayerTreesLock);
-  auto pipelineHandle = aPipelineId.mHandle;
-  MOZ_ASSERT(sIndirectLayerTrees[pipelineHandle].mWrBridge == nullptr);
-  sIndirectLayerTrees[pipelineHandle].mWrBridge = mWrBridge;
+  MOZ_ASSERT(sIndirectLayerTrees[mRootLayerTreeID].mWrBridge == nullptr);
+  sIndirectLayerTrees[mRootLayerTreeID].mWrBridge = mWrBridge;
   *aTextureFactoryIdentifier = mWrBridge->GetTextureFactoryIdentifier();
   return mWrBridge;
 }
 
 bool
 CompositorBridgeParent::DeallocPWebRenderBridgeParent(PWebRenderBridgeParent* aActor)
 {
 #ifndef MOZ_BUILD_WEBRENDER
@@ -2107,19 +2106,20 @@ CompositorBridgeParent::GetGeckoContentC
   return state ? state->mController.get() : nullptr;
 }
 
 PTextureParent*
 CompositorBridgeParent::AllocPTextureParent(const SurfaceDescriptor& aSharedData,
                                             const LayersBackend& aLayersBackend,
                                             const TextureFlags& aFlags,
                                             const uint64_t& aId,
-                                            const uint64_t& aSerial)
+                                            const uint64_t& aSerial,
+                                            const wr::MaybeExternalImageId& aExternalImageId)
 {
-  return TextureHost::CreateIPDLActor(this, aSharedData, aLayersBackend, aFlags, aSerial);
+  return TextureHost::CreateIPDLActor(this, aSharedData, aLayersBackend, aFlags, aSerial, aExternalImageId);
 }
 
 bool
 CompositorBridgeParent::DeallocPTextureParent(PTextureParent* actor)
 {
   return TextureHost::DestroyIPDLActor(actor);
 }
 
--- a/gfx/layers/ipc/CompositorBridgeParent.h
+++ b/gfx/layers/ipc/CompositorBridgeParent.h
@@ -245,17 +245,18 @@ public:
                                       const uint64_t& aInputBlockId,
                                       const nsTArray<ScrollableLayerGuid>& aTargets) override;
   virtual AsyncCompositionManager* GetCompositionManager(LayerTransactionParent* aLayerTree) override { return mCompositionManager; }
 
   virtual PTextureParent* AllocPTextureParent(const SurfaceDescriptor& aSharedData,
                                               const LayersBackend& aLayersBackend,
                                               const TextureFlags& aFlags,
                                               const uint64_t& aId,
-                                              const uint64_t& aSerial) override;
+                                              const uint64_t& aSerial,
+                                              const wr::MaybeExternalImageId& aExternalImageId) override;
   virtual bool DeallocPTextureParent(PTextureParent* actor) override;
 
   virtual bool IsSameProcess() const override;
 
 
   PCompositorWidgetParent* AllocPCompositorWidgetParent(const CompositorWidgetInitData& aInitData) override;
   bool DeallocPCompositorWidgetParent(PCompositorWidgetParent* aActor) override;
 
--- a/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
@@ -184,53 +184,53 @@ CrossProcessCompositorBridgeParent::Allo
                                                                 TextureFactoryIdentifier* aTextureFactoryIdentifier,
                                                                 uint32_t *aIdNamespace)
 {
 #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
+  uint64_t layersId = wr::AsUint64(aPipelineId);
   // Check to see if this child process has access to this layer tree.
-  if (!LayerTreeOwnerTracker::Get()->IsMapped(aPipelineId.mHandle, OtherPid())) {
+  if (!LayerTreeOwnerTracker::Get()->IsMapped(layersId, OtherPid())) {
     NS_ERROR("Unexpected layers id in AllocPAPZCTreeManagerParent; dropping message...");
     return nullptr;
   }
 
-  auto pipelineHandle = aPipelineId.mHandle;
   MonitorAutoLock lock(*sIndirectLayerTreesLock);
-  MOZ_ASSERT(sIndirectLayerTrees.find(pipelineHandle) != sIndirectLayerTrees.end());
-  MOZ_ASSERT(sIndirectLayerTrees[pipelineHandle].mWrBridge == nullptr);
-  CompositorBridgeParent* cbp = sIndirectLayerTrees[pipelineHandle].mParent;
+  MOZ_ASSERT(sIndirectLayerTrees.find(layersId) != sIndirectLayerTrees.end());
+  MOZ_ASSERT(sIndirectLayerTrees[layersId].mWrBridge == nullptr);
+  CompositorBridgeParent* cbp = sIndirectLayerTrees[layersId].mParent;
   WebRenderBridgeParent* root = sIndirectLayerTrees[cbp->RootLayerTreeId()].mWrBridge.get();
 
   WebRenderBridgeParent* parent = nullptr;
   RefPtr<wr::WebRenderAPI> api = root->GetWebRenderAPI();
   RefPtr<WebRenderCompositableHolder> holder = root->CompositableHolder();
   parent = new WebRenderBridgeParent(this, aPipelineId, nullptr, root->CompositorScheduler(), Move(api), Move(holder));
 
   parent->AddRef(); // IPDL reference
-  sIndirectLayerTrees[pipelineHandle].mCrossProcessParent = this;
-  sIndirectLayerTrees[pipelineHandle].mWrBridge = parent;
+  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
   // 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
   WebRenderBridgeParent* parent = static_cast<WebRenderBridgeParent*>(aActor);
-  EraseLayerState(parent->PipelineId().mHandle);
+  EraseLayerState(wr::AsUint64(parent->PipelineId()));
   parent->Release(); // IPDL reference
   return true;
 }
 
 mozilla::ipc::IPCResult
 CrossProcessCompositorBridgeParent::RecvNotifyChildCreated(const uint64_t& child,
                                                            CompositorOptions* aOptions)
 {
@@ -504,17 +504,18 @@ CrossProcessCompositorBridgeParent::~Cro
   MOZ_ASSERT(IToplevelProtocol::GetTransport());
 }
 
 PTextureParent*
 CrossProcessCompositorBridgeParent::AllocPTextureParent(const SurfaceDescriptor& aSharedData,
                                                         const LayersBackend& aLayersBackend,
                                                         const TextureFlags& aFlags,
                                                         const uint64_t& aId,
-                                                        const uint64_t& aSerial)
+                                                        const uint64_t& aSerial,
+                                                        const wr::MaybeExternalImageId& aExternalImageId)
 {
   CompositorBridgeParent::LayerTreeState* state = nullptr;
 
   LayerTreeMap::iterator itr = sIndirectLayerTrees.find(aId);
   if (sIndirectLayerTrees.end() != itr) {
     state = &itr->second;
   }
 
@@ -527,17 +528,17 @@ CrossProcessCompositorBridgeParent::Allo
     // kill the content process. Instead, we signal that the underlying
     // TextureHost should not attempt to access the compositor.
     flags |= TextureFlags::INVALID_COMPOSITOR;
   } else if (state->mLayerManager && state->mLayerManager->GetCompositor() &&
              aLayersBackend != state->mLayerManager->GetCompositor()->GetBackendType()) {
     gfxDevCrash(gfx::LogReason::PAllocTextureBackendMismatch) << "Texture backend is wrong";
   }
 
-  return TextureHost::CreateIPDLActor(this, aSharedData, aLayersBackend, aFlags, aSerial);
+  return TextureHost::CreateIPDLActor(this, aSharedData, aLayersBackend, aFlags, aSerial, aExternalImageId);
 }
 
 bool
 CrossProcessCompositorBridgeParent::DeallocPTextureParent(PTextureParent* actor)
 {
   return TextureHost::DestroyIPDLActor(actor);
 }
 
--- a/gfx/layers/ipc/CrossProcessCompositorBridgeParent.h
+++ b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.h
@@ -125,17 +125,18 @@ public:
   void DidComposite(uint64_t aId,
                     TimeStamp& aCompositeStart,
                     TimeStamp& aCompositeEnd);
 
   virtual PTextureParent* AllocPTextureParent(const SurfaceDescriptor& aSharedData,
                                               const LayersBackend& aLayersBackend,
                                               const TextureFlags& aFlags,
                                               const uint64_t& aId,
-                                              const uint64_t& aSerial) override;
+                                              const uint64_t& aSerial,
+                                              const wr::MaybeExternalImageId& aExternalImageId) override;
 
   virtual bool DeallocPTextureParent(PTextureParent* actor) override;
 
   virtual bool IsSameProcess() const override;
 
   PCompositorWidgetParent* AllocPCompositorWidgetParent(const CompositorWidgetInitData& aInitData) override {
     // Not allowed.
     return nullptr;
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -968,17 +968,18 @@ ImageBridgeChild::DeallocShmem(ipc::Shme
   task.Wait();
   return result;
 }
 
 PTextureChild*
 ImageBridgeChild::AllocPTextureChild(const SurfaceDescriptor&,
                                      const LayersBackend&,
                                      const TextureFlags&,
-                                     const uint64_t& aSerial)
+                                     const uint64_t& aSerial,
+                                     const wr::MaybeExternalImageId& aExternalImageId)
 {
   MOZ_ASSERT(CanSend());
   return TextureClient::CreateIPDLActor();
 }
 
 bool
 ImageBridgeChild::DeallocPTextureChild(PTextureChild* actor)
 {
@@ -1039,20 +1040,21 @@ ImageBridgeChild::RecvDidComposite(Infal
   }
   return IPC_OK();
 }
 
 PTextureChild*
 ImageBridgeChild::CreateTexture(const SurfaceDescriptor& aSharedData,
                                 LayersBackend aLayersBackend,
                                 TextureFlags aFlags,
-                                uint64_t aSerial)
+                                uint64_t aSerial,
+                                wr::MaybeExternalImageId& aExternalImageId)
 {
   MOZ_ASSERT(CanSend());
-  return SendPTextureConstructor(aSharedData, aLayersBackend, aFlags, aSerial);
+  return SendPTextureConstructor(aSharedData, aLayersBackend, aFlags, aSerial, aExternalImageId);
 }
 
 static bool
 IBCAddOpDestroy(CompositableTransaction* aTxn, const OpDestroy& op)
 {
   if (aTxn->Finished()) {
     return false;
   }
@@ -1151,22 +1153,22 @@ ImageBridgeChild::CanSend() const
 }
 
 void
 ImageBridgeChild::HandleFatalError(const char* aName, const char* aMsg) const
 {
   dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aName, aMsg, OtherPid());
 }
 
-uint64_t
+wr::MaybeExternalImageId
 ImageBridgeChild::GetNextExternalImageId()
 {
   static uint32_t sNextID = 1;
   ++sNextID;
   MOZ_RELEASE_ASSERT(sNextID != UINT32_MAX);
 
   uint64_t imageId = mNamespace;
   imageId = imageId << 32 | sNextID;
-  return imageId;
+  return Some(wr::ToExternalImageId(imageId));
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/ipc/ImageBridgeChild.h
+++ b/gfx/layers/ipc/ImageBridgeChild.h
@@ -12,16 +12,17 @@
 #include "mozilla/Atomics.h"
 #include "mozilla/RefPtr.h"             // for already_AddRefed
 #include "mozilla/ipc/SharedMemory.h"   // for SharedMemory, etc
 #include "mozilla/layers/CanvasClient.h"
 #include "mozilla/layers/CompositableForwarder.h"
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/layers/PImageBridgeChild.h"
 #include "mozilla/Mutex.h"
+#include "mozilla/webrender/WebRenderTypes.h"
 #include "nsDebug.h"                    // for NS_RUNTIMEABORT
 #include "nsIObserver.h"
 #include "nsRegion.h"                   // for nsIntRegion
 #include "mozilla/gfx/Rect.h"
 #include "mozilla/ReentrantMonitor.h"   // for ReentrantMonitor, etc
 
 class MessageLoop;
 
@@ -164,17 +165,21 @@ public:
    *
    * Can be called from any thread.
    */
   virtual MessageLoop * GetMessageLoop() const override;
 
   virtual base::ProcessId GetParentPid() const override { return OtherPid(); }
 
   virtual PTextureChild*
-  AllocPTextureChild(const SurfaceDescriptor& aSharedData, const LayersBackend& aLayersBackend, const TextureFlags& aFlags, const uint64_t& aSerial) override;
+  AllocPTextureChild(const SurfaceDescriptor& aSharedData,
+                     const LayersBackend& aLayersBackend,
+                     const TextureFlags& aFlags,
+                     const uint64_t& aSerial,
+                     const wr::MaybeExternalImageId& aExternalImageId) override;
 
   virtual bool
   DeallocPTextureChild(PTextureChild* actor) override;
 
   PMediaSystemResourceManagerChild*
   AllocPMediaSystemResourceManagerChild() override;
   bool
   DeallocPMediaSystemResourceManagerChild(PMediaSystemResourceManagerChild* aActor) override;
@@ -321,30 +326,31 @@ public:
    * If used outside the ImageBridgeChild thread, it will proxy a synchronous
    * call on the ImageBridgeChild thread.
    */
   virtual bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override;
 
   virtual PTextureChild* CreateTexture(const SurfaceDescriptor& aSharedData,
                                        LayersBackend aLayersBackend,
                                        TextureFlags aFlags,
-                                       uint64_t aSerial) override;
+                                       uint64_t aSerial,
+                                       wr::MaybeExternalImageId& aExternalImageId) override;
 
   virtual bool IsSameProcess() const override;
 
   virtual void UpdateFwdTransactionId() override { ++mFwdTransactionId; }
   virtual uint64_t GetFwdTransactionId() override { return mFwdTransactionId; }
 
   bool InForwarderThread() override {
     return InImageBridgeChildThread();
   }
 
   virtual void HandleFatalError(const char* aName, const char* aMsg) const override;
 
-  uint64_t GetNextExternalImageId();
+  virtual wr::MaybeExternalImageId GetNextExternalImageId() override;
 
 protected:
   explicit ImageBridgeChild(uint32_t aNamespace);
   bool DispatchAllocShmemInternal(size_t aSize,
                                   SharedMemory::SharedMemoryType aType,
                                   Shmem* aShmem,
                                   bool aUnsafe);
 
--- a/gfx/layers/ipc/ImageBridgeParent.cpp
+++ b/gfx/layers/ipc/ImageBridgeParent.cpp
@@ -240,19 +240,20 @@ ImageBridgeParent::RecvReleaseCompositab
   ReleaseCompositable(aHandle);
   return IPC_OK();
 }
 
 PTextureParent*
 ImageBridgeParent::AllocPTextureParent(const SurfaceDescriptor& aSharedData,
                                        const LayersBackend& aLayersBackend,
                                        const TextureFlags& aFlags,
-                                       const uint64_t& aSerial)
+                                       const uint64_t& aSerial,
+                                       const wr::MaybeExternalImageId& aExternalImageId)
 {
-  return TextureHost::CreateIPDLActor(this, aSharedData, aLayersBackend, aFlags, aSerial);
+  return TextureHost::CreateIPDLActor(this, aSharedData, aLayersBackend, aFlags, aSerial, aExternalImageId);
 }
 
 bool
 ImageBridgeParent::DeallocPTextureParent(PTextureParent* actor)
 {
   return TextureHost::DestroyIPDLActor(actor);
 }
 
--- a/gfx/layers/ipc/ImageBridgeParent.h
+++ b/gfx/layers/ipc/ImageBridgeParent.h
@@ -72,17 +72,18 @@ public:
   virtual mozilla::ipc::IPCResult RecvImageBridgeThreadId(const PlatformThreadId& aThreadId) override;
   virtual mozilla::ipc::IPCResult RecvInitReadLocks(ReadLockArray&& aReadLocks) override;
   virtual mozilla::ipc::IPCResult RecvUpdate(EditArray&& aEdits, OpDestroyArray&& aToDestroy,
                                           const uint64_t& aFwdTransactionId) override;
 
   virtual PTextureParent* AllocPTextureParent(const SurfaceDescriptor& aSharedData,
                                               const LayersBackend& aLayersBackend,
                                               const TextureFlags& aFlags,
-                                              const uint64_t& aSerial) override;
+                                              const uint64_t& aSerial,
+                                              const wr::MaybeExternalImageId& aExternalImageId) override;
   virtual bool DeallocPTextureParent(PTextureParent* actor) override;
 
   virtual mozilla::ipc::IPCResult RecvNewCompositable(const CompositableHandle& aHandle,
                                                       const TextureInfo& aInfo) override;
   virtual mozilla::ipc::IPCResult RecvReleaseCompositable(const CompositableHandle& aHandle) override;
 
   PMediaSystemResourceManagerParent* AllocPMediaSystemResourceManagerParent() override;
   bool DeallocPMediaSystemResourceManagerParent(PMediaSystemResourceManagerParent* aActor) override;
--- a/gfx/layers/ipc/PCompositorBridge.ipdl
+++ b/gfx/layers/ipc/PCompositorBridge.ipdl
@@ -11,16 +11,17 @@ include PlatformWidgetTypes;
 include protocol PAPZ;
 include protocol PAPZCTreeManager;
 include protocol PBrowser;
 include protocol PCompositorWidget;
 include protocol PLayerTransaction;
 include protocol PTexture;
 include protocol PWebRenderBridge;
 include "mozilla/GfxMessageUtils.h";
+include "mozilla/layers/WebRenderMessageUtils.h";
 
 using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
 using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
 using struct mozilla::layers::FrameMetrics from "FrameMetrics.h";
 using mozilla::layers::FrameMetrics::ViewID from "FrameMetrics.h";
 using mozilla::layers::MaybeZoomConstraints from "FrameMetrics.h";
 using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h";
 using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h";
@@ -32,16 +33,17 @@ using mozilla::LayoutDeviceIntPoint from
 using mozilla::LayoutDeviceIntRegion from "Units.h";
 using mozilla::LayoutDeviceIntSize from "Units.h";
 using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
 using class mozilla::layers::FrameUniformityData from "mozilla/layers/FrameUniformityData.h";
 using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h";
 using mozilla::layers::CompositorOptions from "mozilla/layers/CompositorOptions.h";
 using mozilla::wr::PipelineId from "mozilla/webrender/WebRenderTypes.h";
 using base::ProcessId from "base/process.h";
+using mozilla::wr::MaybeExternalImageId from "mozilla/webrender/WebRenderTypes.h";
 
 namespace mozilla {
 namespace layers {
 
 
 /**
  * The PCompositorBridge protocol is a top-level protocol for the compositor.
  * There is an instance of the protocol for each compositor, plus one for each
@@ -240,17 +242,17 @@ parent:
   // We visualize this information in the APZ minimap.
   async NotifyApproximatelyVisibleRegion(ScrollableLayerGuid guid, CSSIntRegion region);
 
   /**
    * Sent when the child has finished CaptureAllPlugins.
    */
   async AllPluginsCaptured();
 
-  async PTexture(SurfaceDescriptor aSharedData, LayersBackend aBackend, TextureFlags aTextureFlags, uint64_t id, uint64_t aSerial);
+  async PTexture(SurfaceDescriptor aSharedData, LayersBackend aBackend, TextureFlags aTextureFlags, uint64_t 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, uint32_t idNamespace); //XXX: use the WrIdNamespace type
 
 child:
--- a/gfx/layers/ipc/PImageBridge.ipdl
+++ b/gfx/layers/ipc/PImageBridge.ipdl
@@ -5,20 +5,22 @@
 
 include LayersSurfaces;
 include LayersMessages;
 include protocol PTexture;
 include ProtocolTypes;
 include protocol PMediaSystemResourceManager;
 
 include "mozilla/GfxMessageUtils.h";
+include "mozilla/layers/WebRenderMessageUtils.h";
 
 using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h";
 using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h";
 using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h";
+using mozilla::wr::MaybeExternalImageId from "mozilla/webrender/WebRenderTypes.h";
 
 using PlatformThreadId from "base/platform_thread.h";
 
 namespace mozilla {
 namespace layers {
 
 /**
  * The PImageBridge protocol is used to allow isolated threads or processes to push
@@ -49,17 +51,17 @@ parent:
   // in a state in which it can't send asynchronous messages
   // so as to not race with the channel getting closed.
   // In the child side, the Closing the channel does not happen right after WillClose,
   // it is scheduled in the ImageBridgeChild's message queue in order to ensure
   // that all of the messages from the parent side have been received and processed
   // before sending closing the channel.
   sync WillClose();
 
-  async PTexture(SurfaceDescriptor aSharedData, LayersBackend aBackend, TextureFlags aTextureFlags, uint64_t aSerial);
+  async PTexture(SurfaceDescriptor aSharedData, LayersBackend aBackend, TextureFlags aTextureFlags, uint64_t aSerial, MaybeExternalImageId aExternalImageId);
   async PMediaSystemResourceManager();
 
   sync NewCompositable(CompositableHandle aHandle, TextureInfo aInfo);
   async ReleaseCompositable(CompositableHandle aHandle);
 };
 
 
 } // namespace
--- a/gfx/layers/ipc/PWebRenderBridge.ipdl
+++ b/gfx/layers/ipc/PWebRenderBridge.ipdl
@@ -12,20 +12,22 @@ include "mozilla/layers/WebRenderMessage
 
 include WebRenderMessages;
 include protocol PCompositorBridge;
 include protocol PTexture;
 
 using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h";
 using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h";
 using mozilla::wr::ByteBuffer from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::ExternalImageId from "mozilla/webrender/WebRenderTypes.h";
 using mozilla::wr::ImageKey from "mozilla/webrender/WebRenderTypes.h";
 using mozilla::wr::FontKey from "mozilla/webrender/WebRenderTypes.h";
 using WrBuiltDisplayListDescriptor from "mozilla/webrender/webrender_ffi.h";
 using WrAuxiliaryListsDescriptor from "mozilla/webrender/webrender_ffi.h";
+using mozilla::layers::WebRenderScrollData from "mozilla/layers/WebRenderScrollData.h";
 
 namespace mozilla {
 namespace layers {
 
 sync protocol PWebRenderBridge
 {
   manager PCompositorBridge;
 
@@ -45,23 +47,25 @@ parent:
                      SurfaceFormat aFormat, ByteBuffer aBytes);
   sync UpdateImage(ImageKey aImageKey, IntSize aSize,
                    SurfaceFormat aFormat, ByteBuffer aBytes);
   sync DeleteImage(ImageKey aImageKey);
   async AddRawFont(FontKey aFontKey, ByteBuffer aBytes, uint32_t aFontIndex);
   async DeleteFont(FontKey aFontKey);
   async DPBegin(IntSize aSize);
   async DPEnd(IntSize aSize, WebRenderParentCommand[] commands, OpDestroy[] toDestroy, uint64_t fwdTransactionId, uint64_t transactionId,
-              ByteBuffer aDL, WrBuiltDisplayListDescriptor aDLDesc, ByteBuffer aAux, WrAuxiliaryListsDescriptor aAuxDesc);
+              ByteBuffer aDL, WrBuiltDisplayListDescriptor aDLDesc, ByteBuffer aAux, WrAuxiliaryListsDescriptor aAuxDesc,
+              WebRenderScrollData aScrollData);
   sync DPSyncEnd(IntSize aSize, WebRenderParentCommand[] commands, OpDestroy[] toDestroy, uint64_t fwdTransactionId, uint64_t transactionId,
-                 ByteBuffer aDL, WrBuiltDisplayListDescriptor aDLDesc, ByteBuffer aAux, WrAuxiliaryListsDescriptor aAuxDesc);
+                 ByteBuffer aDL, WrBuiltDisplayListDescriptor aDLDesc, ByteBuffer aAux, WrAuxiliaryListsDescriptor aAuxDesc,
+                 WebRenderScrollData aScrollData);
   sync DPGetSnapshot(PTexture texture);
-  async AddExternalImageId(uint64_t aImageId, CompositableHandle aHandle);
-  async AddExternalImageIdForCompositable(uint64_t aImageId, CompositableHandle aHandle);
-  async RemoveExternalImageId(uint64_t aImageId);
+  async AddExternalImageId(ExternalImageId aImageId, CompositableHandle aHandle);
+  async AddExternalImageIdForCompositable(ExternalImageId aImageId, CompositableHandle aHandle);
+  async RemoveExternalImageId(ExternalImageId aImageId);
   async SetLayerObserverEpoch(uint64_t layerObserverEpoch);
   async ClearCachedResources();
   // Schedule a composite if one isn't already scheduled.
   async ForceComposite();
 
   async Shutdown();
 child:
   async __delete__();
--- a/gfx/layers/ipc/TextureForwarder.h
+++ b/gfx/layers/ipc/TextureForwarder.h
@@ -46,16 +46,18 @@ public:
   virtual base::ProcessId GetParentPid() const = 0;
 
   virtual MessageLoop* GetMessageLoop() const = 0;
 
   virtual FixedSizeSmallShmemSectionAllocator* GetTileLockAllocator() { return nullptr; }
 
   virtual void CancelWaitForRecycle(uint64_t aTextureId) = 0;
 
+  virtual wr::MaybeExternalImageId GetNextExternalImageId() { return Nothing(); }
+
 protected:
   virtual ~LayersIPCChannel() {}
 };
 
 /**
  * An abstract interface for classes that can allocate PTexture objects
  * across IPDL. Currently a sub-class of LayersIPCChannel for simplicity
  * since all our implementations use both, but could be independant if needed.
@@ -64,15 +66,16 @@ class TextureForwarder : public LayersIP
 public:
   /**
    * Create a TextureChild/Parent pair as as well as the TextureHost on the parent side.
    */
   virtual PTextureChild* CreateTexture(
     const SurfaceDescriptor& aSharedData,
     LayersBackend aLayersBackend,
     TextureFlags aFlags,
-    uint64_t aSerial) = 0;
+    uint64_t aSerial,
+    wr::MaybeExternalImageId& aExternalImageId) = 0;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif
--- a/gfx/layers/ipc/VideoBridgeChild.cpp
+++ b/gfx/layers/ipc/VideoBridgeChild.cpp
@@ -102,17 +102,18 @@ VideoBridgeChild::DeallocPVideoBridgeChi
 {
   mIPDLSelfRef = nullptr;
 }
 
 PTextureChild*
 VideoBridgeChild::CreateTexture(const SurfaceDescriptor& aSharedData,
                                 LayersBackend aLayersBackend,
                                 TextureFlags aFlags,
-                                uint64_t aSerial)
+                                uint64_t aSerial,
+                                wr::MaybeExternalImageId& aExternalImageId)
 {
   MOZ_ASSERT(CanSend());
   return SendPTextureConstructor(aSharedData, aLayersBackend, aFlags, aSerial);
 }
 
 bool VideoBridgeChild::IsSameProcess() const
 {
   return OtherPid() == base::GetCurrentProcId();
--- a/gfx/layers/ipc/VideoBridgeChild.h
+++ b/gfx/layers/ipc/VideoBridgeChild.h
@@ -43,17 +43,18 @@ public:
                   mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
                   mozilla::ipc::Shmem* aShmem) override;
   bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override;
 
   // TextureForwarder
   PTextureChild* CreateTexture(const SurfaceDescriptor& aSharedData,
                                LayersBackend aLayersBackend,
                                TextureFlags aFlags,
-                               uint64_t aSerial) override;
+                               uint64_t aSerial,
+                               wr::MaybeExternalImageId& aExternalImageId) override;
 
   // ClientIPCAllocator
   base::ProcessId GetParentPid() const override { return OtherPid(); }
   MessageLoop * GetMessageLoop() const override { return mMessageLoop; }
   void CancelWaitForRecycle(uint64_t aTextureId) override { MOZ_ASSERT(false, "NO RECYCLING HERE"); }
 
   // ISurfaceAllocator
   bool IsSameProcess() const override;
--- a/gfx/layers/ipc/VideoBridgeParent.cpp
+++ b/gfx/layers/ipc/VideoBridgeParent.cpp
@@ -57,17 +57,17 @@ VideoBridgeParent::DeallocPVideoBridgePa
 
 PTextureParent*
 VideoBridgeParent::AllocPTextureParent(const SurfaceDescriptor& aSharedData,
                                        const LayersBackend& aLayersBackend,
                                        const TextureFlags& aFlags,
                                        const uint64_t& aSerial)
 {
   PTextureParent* parent =
-    TextureHost::CreateIPDLActor(this, aSharedData, aLayersBackend, aFlags, aSerial);
+    TextureHost::CreateIPDLActor(this, aSharedData, aLayersBackend, aFlags, aSerial, Nothing());
   mTextureMap[aSerial] = parent;
   return parent;
 }
 
 bool
 VideoBridgeParent::DeallocPTextureParent(PTextureParent* actor)
 {
   mTextureMap.erase(TextureHost::GetTextureSerial(actor));
--- a/gfx/layers/ipc/WebRenderMessages.ipdlh
+++ b/gfx/layers/ipc/WebRenderMessages.ipdlh
@@ -4,25 +4,36 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include LayersSurfaces;
 include LayersMessages;
 include protocol PTexture;
 
+using mozilla::wr::ExternalImageId from "mozilla/webrender/WebRenderTypes.h";
 using mozilla::wr::ImageKey from "mozilla/webrender/WebRenderTypes.h";
 
 namespace mozilla {
 namespace layers {
 
 struct OpAddExternalImage {
-  uint64_t externalImageId;
+  ExternalImageId externalImageId;
   ImageKey key;
 };
 
+struct OpAddCompositorAnimations {
+  CompositorAnimations data;
+};
+
+struct OpRemoveCompositorAnimations {
+  uint64_t id;
+};
+
 union WebRenderParentCommand {
   OpAddExternalImage;
   CompositableOperation;
+  OpAddCompositorAnimations;
+  OpRemoveCompositorAnimations;
 };
 
 } // namespace
 } // namespace
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -193,24 +193,29 @@ EXPORTS.mozilla.layers += [
     'opengl/TextureHostOGL.h',
     'PersistentBufferProvider.h',
     'RenderTrace.h',
     'SourceSurfaceSharedData.h',
     'SourceSurfaceVolatileData.h',
     'TextureSourceProvider.h',
     'TextureWrapperImage.h',
     'TransactionIdAllocator.h',
+    'UpdateImageHelper.h',
+    'wr/StackingContextHelper.h',
     'wr/WebRenderBridgeChild.h',
     'wr/WebRenderBridgeParent.h',
     'wr/WebRenderCompositableHolder.h',
     'wr/WebRenderDisplayItemLayer.h',
     'wr/WebRenderImageHost.h',
+    'wr/WebRenderLayer.h',
     'wr/WebRenderLayerManager.h',
     'wr/WebRenderLayersLogging.h',
     'wr/WebRenderMessageUtils.h',
+    'wr/WebRenderScrollData.h',
+    'wr/WebRenderScrollDataWrapper.h',
     'wr/WebRenderTextureHost.h',
 ]
 
 if CONFIG['MOZ_X11']:
     EXPORTS.mozilla.layers += [
         'basic/TextureClientX11.h',
         'basic/X11TextureSourceBasic.h',
         'composite/X11TextureHost.h',
@@ -384,28 +389,31 @@ UNIFIED_SOURCES += [
     'ReadbackProcessor.cpp',
     'RenderTrace.cpp',
     'RotatedBuffer.cpp',
     'ShareableCanvasLayer.cpp',
     'SourceSurfaceSharedData.cpp',
     'SourceSurfaceVolatileData.cpp',
     'TextureSourceProvider.cpp',
     'TextureWrapperImage.cpp',
+    'wr/StackingContextHelper.cpp',
     'wr/WebRenderBridgeChild.cpp',
     'wr/WebRenderBridgeParent.cpp',
     'wr/WebRenderCanvasLayer.cpp',
     'wr/WebRenderColorLayer.cpp',
     'wr/WebRenderCompositableHolder.cpp',
     'wr/WebRenderContainerLayer.cpp',
     'wr/WebRenderDisplayItemLayer.cpp',
     'wr/WebRenderImageHost.cpp',
     'wr/WebRenderImageLayer.cpp',
+    'wr/WebRenderLayer.cpp',
     'wr/WebRenderLayerManager.cpp',
     'wr/WebRenderLayersLogging.cpp',
     'wr/WebRenderPaintedLayer.cpp',
+    'wr/WebRenderScrollData.cpp',
     'wr/WebRenderTextLayer.cpp',
     # XXX here are some unified build error.
     #'wr/WebRenderTextureHost.cpp'
 ]
 
 SOURCES += [
     'basic/BasicImageLayer.cpp',
     'ImageContainer.cpp',
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/StackingContextHelper.cpp
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/StackingContextHelper.h"
+
+#include "mozilla/layers/WebRenderLayer.h"
+
+namespace mozilla {
+namespace layers {
+
+StackingContextHelper::StackingContextHelper(wr::DisplayListBuilder& aBuilder,
+                                             WebRenderLayer* aLayer,
+                                             const Maybe<gfx::Matrix4x4>& aTransform)
+  : mBuilder(&aBuilder)
+{
+  LayerRect scBounds = aLayer->RelativeToParent(aLayer->BoundsForStackingContext());
+  Layer* layer = aLayer->GetLayer();
+  gfx::Matrix4x4 transform = aTransform.valueOr(layer->GetTransform());
+  mBuilder->PushStackingContext(wr::ToWrRect(scBounds),
+                                1.0f,
+                                transform,
+                                wr::ToWrMixBlendMode(layer->GetMixBlendMode()));
+  mOrigin = aLayer->Bounds().TopLeft();
+}
+
+StackingContextHelper::StackingContextHelper(wr::DisplayListBuilder& aBuilder,
+                                             WebRenderLayer* aLayer,
+                                             uint64_t aAnimationsId,
+                                             float* aOpacityPtr,
+                                             gfx::Matrix4x4* aTransformPtr)
+  : mBuilder(&aBuilder)
+{
+  LayerRect scBounds = aLayer->RelativeToParent(aLayer->BoundsForStackingContext());
+  mBuilder->PushStackingContext(wr::ToWrRect(scBounds),
+                                aAnimationsId,
+                                aOpacityPtr,
+                                aTransformPtr,
+                                wr::ToWrMixBlendMode(aLayer->GetLayer()->GetMixBlendMode()));
+  mOrigin = aLayer->Bounds().TopLeft();
+}
+
+StackingContextHelper::~StackingContextHelper()
+{
+  mBuilder->PopStackingContext();
+}
+
+WrRect
+StackingContextHelper::ToRelativeWrRect(const LayerRect& aRect)
+{
+  return wr::ToWrRect(aRect - mOrigin);
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/StackingContextHelper.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_STACKINGCONTEXTHELPER_H
+#define GFX_STACKINGCONTEXTHELPER_H
+
+#include "mozilla/Attributes.h"
+#include "mozilla/gfx/MatrixFwd.h"
+#include "mozilla/webrender/WebRenderAPI.h"
+#include "mozilla/webrender/WebRenderTypes.h"
+#include "Units.h"
+
+namespace mozilla {
+namespace layers {
+
+class WebRenderLayer;
+
+/**
+ * This is a helper class that pushes/pops a stacking context, and manages
+ * some of the coordinate space transformations needed.
+ */
+class MOZ_RAII StackingContextHelper
+{
+public:
+  // Pushes a stacking context onto the provided DisplayListBuilder. It uses
+  // the transform if provided, otherwise takes the transform from the layer.
+  // It also takes the mix-blend-mode and bounds from the layer, and uses 1.0
+  // for the opacity.
+  StackingContextHelper(wr::DisplayListBuilder& aBuilder,
+                        WebRenderLayer* aLayer,
+                        const Maybe<gfx::Matrix4x4>& aTransform = Nothing());
+  // Alternate constructor which invokes the version of PushStackingContext
+  // for animations.
+  StackingContextHelper(wr::DisplayListBuilder& aBuilder,
+                        WebRenderLayer* aLayer,
+                        uint64_t aAnimationsId,
+                        float* aOpacityPtr,
+                        gfx::Matrix4x4* aTransformPtr);
+  // Pops the stacking context
+  ~StackingContextHelper();
+
+  // When this StackingContextHelper is in scope, this function can be used
+  // to convert a rect from the layer system's coordinate space to a WrRect
+  // that is relative to the stacking context. This is useful because most
+  // things that are pushed inside the stacking context need to be relative
+  // to the stacking context.
+  WrRect ToRelativeWrRect(const LayerRect& aRect);
+
+private:
+  wr::DisplayListBuilder* mBuilder;
+  LayerPoint mOrigin;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_STACKINGCONTEXTHELPER_H */
--- a/gfx/layers/wr/WebRenderBridgeChild.cpp
+++ b/gfx/layers/wr/WebRenderBridgeChild.cpp
@@ -60,96 +60,104 @@ WebRenderBridgeChild::AddWebRenderParent
   MOZ_ASSERT(mIsInTransaction);
   mParentCommands.AppendElements(aCommands);
 }
 
 bool
 WebRenderBridgeChild::DPBegin(const gfx::IntSize& aSize)
 {
   MOZ_ASSERT(!mDestroyed);
-  MOZ_ASSERT(!mIsInTransaction);
 
   UpdateFwdTransactionId();
   this->SendDPBegin(aSize);
   mIsInTransaction = true;
   mReadLockSequenceNumber = 0;
   mReadLocks.AppendElement();
   return true;
 }
 
 void
-WebRenderBridgeChild::DPEnd(wr::DisplayListBuilder &aBuilder, const gfx::IntSize& aSize, bool aIsSync, uint64_t aTransactionId)
+WebRenderBridgeChild::ClearReadLocks()
 {
-  MOZ_ASSERT(!mDestroyed);
-  MOZ_ASSERT(mIsInTransaction);
-
   for (nsTArray<ReadLockInit>& locks : mReadLocks) {
     if (locks.Length()) {
       if (!SendInitReadLocks(locks)) {
         NS_WARNING("WARNING: sending read locks failed!");
         return;
       }
     }
   }
 
+  mReadLocks.Clear();
+}
+
+void
+WebRenderBridgeChild::DPEnd(wr::DisplayListBuilder &aBuilder,
+                            const gfx::IntSize& aSize,
+                            bool aIsSync,
+                            uint64_t aTransactionId,
+                            const WebRenderScrollData& aScrollData)
+{
+  MOZ_ASSERT(!mDestroyed);
+  MOZ_ASSERT(mIsInTransaction);
+
   wr::BuiltDisplayList dl = aBuilder.Finalize();
   ByteBuffer dlData(Move(dl.dl));
   ByteBuffer auxData(Move(dl.aux));
 
   if (aIsSync) {
     this->SendDPSyncEnd(aSize, mParentCommands, mDestroyedActors, GetFwdTransactionId(), aTransactionId,
-                        dlData, dl.dl_desc, auxData, dl.aux_desc);
+                        dlData, dl.dl_desc, auxData, dl.aux_desc, aScrollData);
   } else {
     this->SendDPEnd(aSize, mParentCommands, mDestroyedActors, GetFwdTransactionId(), aTransactionId,
-                    dlData, dl.dl_desc, auxData, dl.aux_desc);
+                    dlData, dl.dl_desc, auxData, dl.aux_desc, aScrollData);
   }
 
   mParentCommands.Clear();
   mDestroyedActors.Clear();
-  mReadLocks.Clear();
   mIsInTransaction = false;
 }
 
-uint64_t
+wr::ExternalImageId
 WebRenderBridgeChild::GetNextExternalImageId()
 {
-  return GetCompositorBridgeChild()->GetNextExternalImageId();
+  wr::MaybeExternalImageId id = GetCompositorBridgeChild()->GetNextExternalImageId();
+  MOZ_RELEASE_ASSERT(id.isSome());
+  return id.value();
 }
 
-uint64_t
+wr::ExternalImageId
 WebRenderBridgeChild::AllocExternalImageId(const CompositableHandle& aHandle)
 {
   MOZ_ASSERT(!mDestroyed);
 
-  uint64_t imageId = GetNextExternalImageId();
+  wr::ExternalImageId imageId = GetNextExternalImageId();
   SendAddExternalImageId(imageId, aHandle);
   return imageId;
 }
 
-uint64_t
+wr::ExternalImageId
 WebRenderBridgeChild::AllocExternalImageIdForCompositable(CompositableClient* aCompositable)
 {
   MOZ_ASSERT(!mDestroyed);
   MOZ_ASSERT(aCompositable->IsConnected());
 
-  uint64_t imageId = GetNextExternalImageId();
+  wr::ExternalImageId imageId = GetNextExternalImageId();
   SendAddExternalImageIdForCompositable(imageId, aCompositable->GetIPCHandle());
   return imageId;
 }
 
 void
-WebRenderBridgeChild::DeallocExternalImageId(uint64_t aImageId)
+WebRenderBridgeChild::DeallocExternalImageId(wr::ExternalImageId& aImageId)
 {
   if (mDestroyed) {
     // This can happen if the IPC connection was torn down, because, e.g.
     // the GPU process died.
     return;
   }
-
-  MOZ_ASSERT(aImageId);
   SendRemoveExternalImageId(aImageId);
 }
 
 struct FontFileData
 {
   wr::ByteBuffer mFontBuffer;
   uint32_t mFontIndex;
 };
@@ -165,17 +173,17 @@ WriteFontFileData(const uint8_t* aData, 
   }
   memcpy(data->mFontBuffer.mData, aData, aLength);
 
   data->mFontIndex = aIndex;
 }
 
 void
 WebRenderBridgeChild::PushGlyphs(wr::DisplayListBuilder& aBuilder, const nsTArray<GlyphArray>& aGlyphs,
-                                 gfx::ScaledFont* aFont, const gfx::Point& aOffset, const gfx::Rect& aBounds,
+                                 gfx::ScaledFont* aFont, const LayerPoint& aOffset, const gfx::Rect& aBounds,
                                  const gfx::Rect& aClip)
 {
   MOZ_ASSERT(aFont);
   MOZ_ASSERT(!aGlyphs.IsEmpty());
 
   WrFontKey key = GetFontKeyForScaledFont(aFont);
   MOZ_ASSERT(key.mNamespace && key.mHandle);
 
--- a/gfx/layers/wr/WebRenderBridgeChild.h
+++ b/gfx/layers/wr/WebRenderBridgeChild.h
@@ -55,29 +55,31 @@ class WebRenderBridgeChild final : publi
 
 public:
   explicit WebRenderBridgeChild(const wr::PipelineId& aPipelineId);
 
   void AddWebRenderParentCommand(const WebRenderParentCommand& aCmd);
   void AddWebRenderParentCommands(const nsTArray<WebRenderParentCommand>& aCommands);
 
   bool DPBegin(const  gfx::IntSize& aSize);
-  void DPEnd(wr::DisplayListBuilder &aBuilder, const gfx::IntSize& aSize, bool aIsSync, uint64_t aTransactionId);
+  void DPEnd(wr::DisplayListBuilder &aBuilder, const gfx::IntSize& aSize,
+             bool aIsSync, uint64_t aTransactionId,
+             const WebRenderScrollData& aScrollData);
 
   CompositorBridgeChild* GetCompositorBridgeChild();
 
   wr::PipelineId GetPipeline() { return mPipelineId; }
 
   // KnowsCompositor
   TextureForwarder* GetTextureForwarder() override;
   LayersIPCActor* GetLayersIPCActor() override;
 
-  uint64_t AllocExternalImageId(const CompositableHandle& aHandle);
-  uint64_t AllocExternalImageIdForCompositable(CompositableClient* aCompositable);
-  void DeallocExternalImageId(uint64_t aImageId);
+  wr::ExternalImageId AllocExternalImageId(const CompositableHandle& aHandle);
+  wr::ExternalImageId AllocExternalImageIdForCompositable(CompositableClient* aCompositable);
+  void DeallocExternalImageId(wr::ExternalImageId& aImageId);
 
   /**
    * Clean this up, finishing with SendShutDown() which will cause __delete__
    * to be sent from the parent side.
    */
   void Destroy();
   bool IPCOpen() const { return mIPCOpen && !mDestroyed; }
   bool IsDestroyed() const { return mDestroyed; }
@@ -85,29 +87,30 @@ public:
   uint32_t GetNextResourceId() { return ++mResourceId; }
   uint32_t GetNamespace() { return mIdNamespace; }
   void SetNamespace(uint32_t aIdNamespace)
   {
     mIdNamespace = aIdNamespace;
   }
 
   void PushGlyphs(wr::DisplayListBuilder& aBuilder, const nsTArray<GlyphArray>& aGlyphs,
-                  gfx::ScaledFont* aFont, const gfx::Point& aOffset, const gfx::Rect& aBounds,
+                  gfx::ScaledFont* aFont, const LayerPoint& aOffset, const gfx::Rect& aBounds,
                   const gfx::Rect& aClip);
 
   wr::FontKey GetFontKeyForScaledFont(gfx::ScaledFont* aScaledFont);
 
   void RemoveExpiredFontKeys();
+  void ClearReadLocks();
 
 private:
   friend class CompositorBridgeChild;
 
   ~WebRenderBridgeChild() {}
 
-  uint64_t GetNextExternalImageId();
+  wr::ExternalImageId GetNextExternalImageId();
 
   // CompositableForwarder
   void Connect(CompositableClient* aCompositable,
                ImageContainer* aImageContainer = nullptr) override;
   void UseTiledLayerBuffer(CompositableClient* aCompositable,
                            const SurfaceDescriptorTiles& aTiledDescriptor) override;
   void UpdateTextureRegion(CompositableClient* aCompositable,
                            const ThebesBufferData& aThebesBufferData,
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -2,28 +2,33 @@
 /* vim: set sw=4 ts=8 et tw=80 : */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/layers/WebRenderBridgeParent.h"
 
 #include "CompositableHost.h"
+#include "gfxPrefs.h"
 #include "GLContext.h"
 #include "GLContextProvider.h"
 #include "mozilla/Range.h"
+#include "mozilla/layers/AnimationHelper.h"
+#include "mozilla/layers/APZCTreeManager.h"
 #include "mozilla/layers/Compositor.h"
 #include "mozilla/layers/CompositorBridgeParent.h"
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/CompositorVsyncScheduler.h"
 #include "mozilla/layers/ImageBridgeParent.h"
 #include "mozilla/layers/ImageDataSerializer.h"
 #include "mozilla/layers/TextureHost.h"
 #include "mozilla/layers/WebRenderCompositableHolder.h"
+#include "mozilla/layers/WebRenderImageHost.h"
 #include "mozilla/layers/WebRenderTextureHost.h"
+#include "mozilla/TimeStamp.h"
 #include "mozilla/webrender/RenderThread.h"
 #include "mozilla/widget/CompositorWidget.h"
 
 bool is_in_main_thread()
 {
   return NS_IsMainThread();
 }
 
@@ -213,17 +218,17 @@ WebRenderBridgeParent::RecvAddRawFont(co
                                       const ByteBuffer& aBuffer,
                                       const uint32_t& aFontIndex)
 {
   if (mDestroyed) {
     return IPC_OK();
   }
   MOZ_ASSERT(mApi);
   auto slice = aBuffer.AsSlice();
-  mApi->AddRawFont(aFontKey, slice);
+  mApi->AddRawFont(aFontKey, slice, aFontIndex);
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvDeleteFont(const wr::FontKey& aFontKey)
 {
   if (mDestroyed) {
@@ -277,17 +282,18 @@ void
 WebRenderBridgeParent::HandleDPEnd(const gfx::IntSize& aSize,
                                  InfallibleTArray<WebRenderParentCommand>&& aCommands,
                                  InfallibleTArray<OpDestroy>&& aToDestroy,
                                  const uint64_t& aFwdTransactionId,
                                  const uint64_t& aTransactionId,
                                  const ByteBuffer& dl,
                                  const WrBuiltDisplayListDescriptor& dlDesc,
                                  const ByteBuffer& aux,
-                                 const WrAuxiliaryListsDescriptor& auxDesc)
+                                 const WrAuxiliaryListsDescriptor& auxDesc,
+                                 const WebRenderScrollData& aScrollData)
 {
   UpdateFwdTransactionId(aFwdTransactionId);
   AutoClearReadLocks clearLocks(mReadLocks);
 
   if (mDestroyed) {
     for (const auto& op : aToDestroy) {
       DestroyActor(op);
     }
@@ -296,76 +302,131 @@ WebRenderBridgeParent::HandleDPEnd(const
   // This ensures that destroy operations are always processed. It is not safe
   // to early-return from RecvDPEnd without doing so.
   AutoWebRenderBridgeParentAsyncMessageSender autoAsyncMessageSender(this, &aToDestroy);
 
   ++mWrEpoch; // Update webrender epoch
   ProcessWebRenderCommands(aSize, aCommands, wr::NewEpoch(mWrEpoch),
                            dl, dlDesc, aux, auxDesc);
   HoldPendingTransactionId(mWrEpoch, aTransactionId);
+
+  mScrollData = aScrollData;
+  UpdateAPZ();
+}
+
+void
+WebRenderBridgeParent::UpdateAPZ()
+{
+  if (!mCompositorBridge) {
+    return;
+  }
+
+  CompositorBridgeParent* cbp;
+  uint64_t rootLayersId;
+  WebRenderBridgeParent* rootWrbp;
+  if (mWidget) {
+    // This WebRenderBridgeParent is attached to the root
+    // CompositorBridgeParent.
+    cbp = static_cast<CompositorBridgeParent*>(mCompositorBridge);
+    rootLayersId = wr::AsUint64(mPipelineId);
+    rootWrbp = this;
+  } else {
+    // This WebRenderBridgeParent is attached to a
+    // CrossProcessCompositorBridgeParent so we have an extra level of
+    // indirection to unravel.
+    uint64_t layersId = wr::AsUint64(mPipelineId);
+    CompositorBridgeParent::LayerTreeState* lts =
+        CompositorBridgeParent::GetIndirectShadowTree(layersId);
+    MOZ_ASSERT(lts);
+    cbp = lts->mParent;
+    rootLayersId = cbp->RootLayerTreeId();
+    lts = CompositorBridgeParent::GetIndirectShadowTree(rootLayersId);
+    MOZ_ASSERT(lts);
+    rootWrbp = lts->mWrBridge.get();
+  }
+
+  MOZ_ASSERT(cbp);
+  if (!rootWrbp) {
+    return;
+  }
+  if (RefPtr<APZCTreeManager> apzc = cbp->GetAPZCTreeManager()) {
+    apzc->UpdateHitTestingTree(rootLayersId, rootWrbp->GetScrollData(),
+        mScrollData.IsFirstPaint(), wr::AsUint64(mPipelineId),
+        /* TODO: propagate paint sequence number */ 0);
+  }
+}
+
+const WebRenderScrollData&
+WebRenderBridgeParent::GetScrollData() const
+{
+  MOZ_ASSERT(mozilla::layers::CompositorThreadHolder::IsInCompositorThread());
+  return mScrollData;
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvDPEnd(const gfx::IntSize& aSize,
                                  InfallibleTArray<WebRenderParentCommand>&& aCommands,
                                  InfallibleTArray<OpDestroy>&& aToDestroy,
                                  const uint64_t& aFwdTransactionId,
                                  const uint64_t& aTransactionId,
                                  const ByteBuffer& dl,
                                  const WrBuiltDisplayListDescriptor& dlDesc,
                                  const ByteBuffer& aux,
-                                 const WrAuxiliaryListsDescriptor& auxDesc)
+                                 const WrAuxiliaryListsDescriptor& auxDesc,
+                                 const WebRenderScrollData& aScrollData)
 {
   HandleDPEnd(aSize, Move(aCommands), Move(aToDestroy), aFwdTransactionId, aTransactionId,
-              dl, dlDesc, aux, auxDesc);
+              dl, dlDesc, aux, auxDesc, aScrollData);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvDPSyncEnd(const gfx::IntSize &aSize,
                                      InfallibleTArray<WebRenderParentCommand>&& aCommands,
                                      InfallibleTArray<OpDestroy>&& aToDestroy,
                                      const uint64_t& aFwdTransactionId,
                                      const uint64_t& aTransactionId,
                                      const ByteBuffer& dl,
                                      const WrBuiltDisplayListDescriptor& dlDesc,
                                      const ByteBuffer& aux,
-                                     const WrAuxiliaryListsDescriptor& auxDesc)
+                                     const WrAuxiliaryListsDescriptor& auxDesc,
+                                     const WebRenderScrollData& aScrollData)
 {
   HandleDPEnd(aSize, Move(aCommands), Move(aToDestroy), aFwdTransactionId, aTransactionId,
-              dl, dlDesc, aux, auxDesc);
+              dl, dlDesc, aux, auxDesc, aScrollData);
   return IPC_OK();
 }
 
 void
 WebRenderBridgeParent::ProcessWebRenderCommands(const gfx::IntSize &aSize,
                                                 InfallibleTArray<WebRenderParentCommand>& aCommands, const wr::Epoch& aEpoch,
                                                 const ByteBuffer& dl,
                                                 const WrBuiltDisplayListDescriptor& dlDesc,
                                                 const ByteBuffer& aux,
                                                 const WrAuxiliaryListsDescriptor& auxDesc)
 {
+  mCompositableHolder->SetCompositionTime(TimeStamp::Now());
 
   for (InfallibleTArray<WebRenderParentCommand>::index_type i = 0; i < aCommands.Length(); ++i) {
     const WebRenderParentCommand& cmd = aCommands[i];
     switch (cmd.type()) {
       case WebRenderParentCommand::TOpAddExternalImage: {
         const OpAddExternalImage& op = cmd.get_OpAddExternalImage();
         wr::ImageKey key = op.key();
-        MOZ_ASSERT(mExternalImageIds.Get(op.externalImageId()).get());
+        MOZ_ASSERT(mExternalImageIds.Get(wr::AsUint64(op.externalImageId())).get());
         MOZ_ASSERT(!mActiveKeys.Get(wr::AsUint64(key), nullptr));
         mActiveKeys.Put(wr::AsUint64(key), key);
 
-        RefPtr<CompositableHost> host = mExternalImageIds.Get(op.externalImageId());
+        RefPtr<WebRenderImageHost> host = mExternalImageIds.Get(wr::AsUint64(op.externalImageId()));
         if (!host) {
           NS_ERROR("CompositableHost does not exist");
           break;
         }
         // XXX select Texture for video in CompositeToTarget().
-        TextureHost* texture = host->GetAsTextureHost();
+        TextureHost* texture = host->GetAsTextureHostForComposite();
         if (!texture) {
           NS_ERROR("TextureHost does not exist");
           break;
         }
         WebRenderTextureHost* wrTexture = texture->AsWebRenderTextureHost();
         if (wrTexture) {
           if (wrTexture->IsWrappingNativeHandle()) {
             // XXX only for MacIOSurface right now.
@@ -407,16 +468,41 @@ WebRenderBridgeParent::ProcessWebRenderC
         break;
       }
       case WebRenderParentCommand::TCompositableOperation: {
         if (!ReceiveCompositableUpdate(cmd.get_CompositableOperation())) {
           NS_ERROR("ReceiveCompositableUpdate failed");
         }
         break;
       }
+      case WebRenderParentCommand::TOpAddCompositorAnimations: {
+        const OpAddCompositorAnimations& op = cmd.get_OpAddCompositorAnimations();
+        CompositorAnimations data(Move(op.data()));
+        if (data.animations().Length()) {
+          uint64_t id = mWidget ? 0 : wr::AsUint64(mPipelineId);
+          CompositorAnimationStorage* storage =
+            mCompositorBridge->GetAnimationStorage(id);
+          if (storage) {
+            storage->SetAnimations(data.id(), data.animations());
+          }
+        }
+        break;
+      }
+      case WebRenderParentCommand::TOpRemoveCompositorAnimations: {
+        const OpRemoveCompositorAnimations& op = cmd.get_OpRemoveCompositorAnimations();
+        if (op.id()) {
+          uint64_t id = mWidget ? 0 : wr::AsUint64(mPipelineId);
+          CompositorAnimationStorage* storage =
+            mCompositorBridge->GetAnimationStorage(id);
+          if (storage) {
+            storage->ClearById(op.id());
+          }
+        }
+        break;
+      }
       default: {
         // other commands are handle on the child
         break;
       }
     }
   }
   if (mWidget) {
     LayoutDeviceIntSize size = mWidget->GetClientSize();
@@ -480,74 +566,82 @@ WebRenderBridgeParent::RecvDPGetSnapshot
   }
 
   mApi->Readback(size, buffer, buffer_size);
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
-WebRenderBridgeParent::RecvAddExternalImageId(const uint64_t& aImageId,
+WebRenderBridgeParent::RecvAddExternalImageId(const ExternalImageId& aImageId,
                                               const CompositableHandle& aHandle)
 {
   if (mDestroyed) {
     return IPC_OK();
   }
 
-  MOZ_ASSERT(!mExternalImageIds.Get(aImageId).get());
+  MOZ_ASSERT(!mExternalImageIds.Get(wr::AsUint64(aImageId)).get());
 
   ImageBridgeParent* imageBridge = ImageBridgeParent::GetInstance(OtherPid());
   if (!imageBridge) {
      return IPC_FAIL_NO_REASON(this);
   }
   RefPtr<CompositableHost> host = imageBridge->FindCompositable(aHandle);
   if (!host) {
     NS_ERROR("CompositableHost not found in the map!");
     return IPC_FAIL_NO_REASON(this);
   }
   MOZ_ASSERT(host->AsWebRenderImageHost());
-  if (!host->AsWebRenderImageHost()) {
+  WebRenderImageHost* wrHost = host->AsWebRenderImageHost();
+  if (!wrHost) {
     NS_ERROR("Incompatible CompositableHost");
     return IPC_OK();
   }
 
-  mExternalImageIds.Put(aImageId, host);
+  wrHost->SetWrCompositableHolder(mCompositableHolder);
+  mExternalImageIds.Put(wr::AsUint64(aImageId), wrHost);
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
-WebRenderBridgeParent::RecvAddExternalImageIdForCompositable(const uint64_t& aImageId,
+WebRenderBridgeParent::RecvAddExternalImageIdForCompositable(const ExternalImageId& aImageId,
                                                              const CompositableHandle& aHandle)
 {
   if (mDestroyed) {
     return IPC_OK();
   }
-  MOZ_ASSERT(!mExternalImageIds.Get(aImageId).get());
+  MOZ_ASSERT(!mExternalImageIds.Get(wr::AsUint64(aImageId)).get());
 
   RefPtr<CompositableHost> host = FindCompositable(aHandle);
   MOZ_ASSERT(host->AsWebRenderImageHost());
-  if (!host->AsWebRenderImageHost()) {
+  WebRenderImageHost* wrHost = host->AsWebRenderImageHost();
+  if (!wrHost) {
     NS_ERROR("Incompatible CompositableHost");
     return IPC_OK();
   }
 
-  mExternalImageIds.Put(aImageId, host);
+  wrHost->SetWrCompositableHolder(mCompositableHolder);
+  mExternalImageIds.Put(wr::AsUint64(aImageId), wrHost);
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
-WebRenderBridgeParent::RecvRemoveExternalImageId(const uint64_t& aImageId)
+WebRenderBridgeParent::RecvRemoveExternalImageId(const ExternalImageId& aImageId)
 {
   if (mDestroyed) {
     return IPC_OK();
   }
-  MOZ_ASSERT(mExternalImageIds.Get(aImageId).get());
-  mExternalImageIds.Remove(aImageId);
+  MOZ_ASSERT(mExternalImageIds.Get(wr::AsUint64(aImageId)).get());
+  WebRenderImageHost* wrHost = mExternalImageIds.Get(wr::AsUint64(aImageId)).get();
+  if (wrHost) {
+    wrHost->SetWrCompositableHolder(nullptr);
+  }
+  mExternalImageIds.Remove(wr::AsUint64(aImageId));
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvSetLayerObserverEpoch(const uint64_t& aLayerObserverEpoch)
 {
   mChildLayerObserverEpoch = aLayerObserverEpoch;
@@ -573,22 +667,68 @@ WebRenderBridgeParent::RecvForceComposit
 
 void
 WebRenderBridgeParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   Destroy();
 }
 
 void
+WebRenderBridgeParent::SampleAnimations(nsTArray<WrOpacityProperty>& aOpacityArray,
+                                        nsTArray<WrTransformProperty>& aTransformArray)
+{
+  uint64_t id = mWidget ? 0 : wr::AsUint64(mPipelineId);
+  CompositorAnimationStorage* storage =
+    mCompositorBridge->GetAnimationStorage(id);
+
+  AnimationHelper::SampleAnimations(storage,
+                                    mCompositorScheduler->
+                                      GetLastComposeTime());
+
+  // return the animated data if has
+  if (storage->AnimatedValueCount()) {
+    for(auto iter = storage->ConstAnimatedValueTableIter();
+        !iter.Done(); iter.Next()) {
+      AnimatedValue * value = iter.UserData();
+      if (value->mType == AnimatedValue::TRANSFORM) {
+        aTransformArray.AppendElement(
+          wr::ToWrTransformProperty(iter.Key(), value->mTransform.mTransformInDevSpace));
+      } else if (value->mType == AnimatedValue::OPACITY) {
+        aOpacityArray.AppendElement(
+          wr::ToWrOpacityProperty(iter.Key(), value->mOpacity));
+      }
+    }
+  }
+}
+
+void
 WebRenderBridgeParent::CompositeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect)
 {
   if (mPaused) {
     return;
   }
+
+  if (gfxPrefs::WebRenderOMTAEnabled()) {
+    nsTArray<WrOpacityProperty> opacityArray;
+    nsTArray<WrTransformProperty> transformArray;
+    SampleAnimations(opacityArray, transformArray);
+
+    if (!transformArray.IsEmpty() || !opacityArray.IsEmpty()) {
+      mApi->GenerateFrame(opacityArray, transformArray);
+      ScheduleComposition();
+      return;
+    }
+  }
+
   mApi->GenerateFrame();
+
+  // XXX Enable it when async video is supported.
+  // if (!mCompositableHolder->GetCompositeUntilTime().IsNull()) {
+  //   ScheduleComposition();
+  // }
 }
 
 void
 WebRenderBridgeParent::HoldPendingTransactionId(uint32_t aWrEpoch, uint64_t aTransactionId)
 {
   // The transaction ID might get reset to 1 if the page gets reloaded, see
   // https://bugzilla.mozilla.org/show_bug.cgi?id=1145295#c41
   // Otherwise, it should be continually increasing.
@@ -699,16 +839,19 @@ WebRenderBridgeParent::ClearResources()
     }
     if (!mKeysToDelete.empty()) {
       // XXX Sync wait.
       mApi->WaitFlushed();
       DeleteOldImages();
     }
   }
   mCompositableHolder->RemovePipeline(mPipelineId);
+  for (auto iter = mExternalImageIds.Iter(); !iter.Done(); iter.Next()) {
+    iter.Data()->SetWrCompositableHolder(nullptr);
+  }
   mExternalImageIds.Clear();
 
   if (mWidget && mCompositorScheduler) {
     mCompositorScheduler->Destroy();
   }
   mCompositorScheduler = nullptr;
   mApi = nullptr;
   mCompositorBridge = nullptr;
--- a/gfx/layers/wr/WebRenderBridgeParent.h
+++ b/gfx/layers/wr/WebRenderBridgeParent.h
@@ -32,16 +32,17 @@ class WebRenderAPI;
 }
 
 namespace layers {
 
 class Compositor;
 class CompositorBridgeParentBase;
 class CompositorVsyncScheduler;
 class WebRenderCompositableHolder;
+class WebRenderImageHost;
 
 class WebRenderBridgeParent final : public PWebRenderBridgeParent
                                   , public CompositorVsyncSchedulerOwner
                                   , public CompositableParentManager
 {
 public:
   WebRenderBridgeParent(CompositorBridgeParentBase* aCompositorBridge,
                         const wr::PipelineId& aPipelineId,
@@ -86,33 +87,35 @@ public:
   mozilla::ipc::IPCResult RecvDPEnd(const gfx::IntSize& aSize,
                                     InfallibleTArray<WebRenderParentCommand>&& aCommands,
                                     InfallibleTArray<OpDestroy>&& aToDestroy,
                                     const uint64_t& aFwdTransactionId,
                                     const uint64_t& aTransactionId,
                                     const ByteBuffer& dl,
                                     const WrBuiltDisplayListDescriptor& dlDesc,
                                     const ByteBuffer& aux,
-                                    const WrAuxiliaryListsDescriptor& auxDesc) override;
+                                    const WrAuxiliaryListsDescriptor& auxDesc,
+                                    const WebRenderScrollData& aScrollData) override;
   mozilla::ipc::IPCResult RecvDPSyncEnd(const gfx::IntSize& aSize,
                                         InfallibleTArray<WebRenderParentCommand>&& aCommands,
                                         InfallibleTArray<OpDestroy>&& aToDestroy,
                                         const uint64_t& aFwdTransactionId,
                                         const uint64_t& aTransactionId,
                                         const ByteBuffer& dl,
                                         const WrBuiltDisplayListDescriptor& dlDesc,
                                         const ByteBuffer& aux,
-                                        const WrAuxiliaryListsDescriptor& auxDesc) override;
+                                        const WrAuxiliaryListsDescriptor& auxDesc,
+                                        const WebRenderScrollData& aScrollData) override;
   mozilla::ipc::IPCResult RecvDPGetSnapshot(PTextureParent* aTexture) override;
 
-  mozilla::ipc::IPCResult RecvAddExternalImageId(const uint64_t& aImageId,
+  mozilla::ipc::IPCResult RecvAddExternalImageId(const ExternalImageId& aImageId,
                                                  const CompositableHandle& aHandle) override;
-  mozilla::ipc::IPCResult RecvAddExternalImageIdForCompositable(const uint64_t& aImageId,
+  mozilla::ipc::IPCResult RecvAddExternalImageIdForCompositable(const ExternalImageId& aImageId,
                                                                 const CompositableHandle& aHandle) override;
-  mozilla::ipc::IPCResult RecvRemoveExternalImageId(const uint64_t& aImageId) override;
+  mozilla::ipc::IPCResult RecvRemoveExternalImageId(const ExternalImageId& aImageId) override;
   mozilla::ipc::IPCResult RecvSetLayerObserverEpoch(const uint64_t& aLayerObserverEpoch) override;
 
   mozilla::ipc::IPCResult RecvClearCachedResources() override;
   mozilla::ipc::IPCResult RecvForceComposite() override;
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
   void SetWebRenderProfilerEnabled(bool aEnabled);
 
@@ -153,16 +156,19 @@ public:
     aNotifications->AppendElements(Move(mImageCompositeNotifications));
   }
 
   uint32_t GetIdNameSpace()
   {
     return mIdNameSpace;
   }
 
+  void UpdateAPZ();
+  const WebRenderScrollData& GetScrollData() const;
+
 private:
   virtual ~WebRenderBridgeParent();
 
   void DeleteOldImages();
   void ProcessWebRenderCommands(const gfx::IntSize &aSize, InfallibleTArray<WebRenderParentCommand>& commands, const wr::Epoch& aEpoch,
                                     const ByteBuffer& dl,
                                     const WrBuiltDisplayListDescriptor& dlDesc,
                                     const ByteBuffer& aux,
@@ -174,17 +180,21 @@ private:
   void HandleDPEnd(const gfx::IntSize& aSize,
                    InfallibleTArray<WebRenderParentCommand>&& aCommands,
                    InfallibleTArray<OpDestroy>&& aToDestroy,
                    const uint64_t& aFwdTransactionId,
                    const uint64_t& aTransactionId,
                    const ByteBuffer& dl,
                    const WrBuiltDisplayListDescriptor& dlDesc,
                    const ByteBuffer& aux,
-                   const WrAuxiliaryListsDescriptor& auxDesc);
+                   const WrAuxiliaryListsDescriptor& auxDesc,
+                   const WebRenderScrollData& aScrollData);
+
+  void SampleAnimations(nsTArray<WrOpacityProperty>& aOpacityArray,
+                        nsTArray<WrTransformProperty>& aTransformArray);
 
 private:
   struct PendingTransactionId {
     PendingTransactionId(wr::Epoch aEpoch, uint64_t aId)
       : mEpoch(aEpoch)
       , mId(aId)
     {}
     wr::Epoch mEpoch;
@@ -195,32 +205,35 @@ private:
   wr::PipelineId mPipelineId;
   RefPtr<widget::CompositorWidget> mWidget;
   RefPtr<wr::WebRenderAPI> mApi;
   RefPtr<WebRenderCompositableHolder> mCompositableHolder;
   RefPtr<CompositorVsyncScheduler> mCompositorScheduler;
   std::vector<wr::ImageKey> mKeysToDelete;
   // XXX How to handle active keys of non-ExternalImages?
   nsDataHashtable<nsUint64HashKey, wr::ImageKey> mActiveKeys;
-  nsDataHashtable<nsUint64HashKey, RefPtr<CompositableHost>> mExternalImageIds;
+  nsDataHashtable<nsUint64HashKey, RefPtr<WebRenderImageHost>> mExternalImageIds;
   nsTArray<ImageCompositeNotificationInfo> mImageCompositeNotifications;
 
   // These fields keep track of the latest layer observer epoch values in the child and the
   // parent. mChildLayerObserverEpoch is the latest epoch value received from the child.
   // mParentLayerObserverEpoch is the latest epoch value that we have told TabParent about
   // (via ObserveLayerUpdate).
   uint64_t mChildLayerObserverEpoch;
   uint64_t mParentLayerObserverEpoch;
 
   std::queue<PendingTransactionId> mPendingTransactionIds;
   uint32_t mWrEpoch;
   uint32_t mIdNameSpace;
 
   bool mPaused;
   bool mDestroyed;
 
+  // Can only be accessed on the compositor thread.
+  WebRenderScrollData mScrollData;
+
   static uint32_t sIdNameSpace;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // mozilla_layers_WebRenderBridgeParent_h
--- a/gfx/layers/wr/WebRenderCanvasLayer.cpp
+++ b/gfx/layers/wr/WebRenderCanvasLayer.cpp
@@ -7,32 +7,33 @@
 
 #include "AsyncCanvasRenderer.h"
 #include "gfxPrefs.h"
 #include "gfxUtils.h"
 #include "GLContext.h"
 #include "GLScreenBuffer.h"
 #include "LayersLogging.h"
 #include "mozilla/gfx/2D.h"
+#include "mozilla/layers/StackingContextHelper.h"
 #include "mozilla/layers/TextureClientSharedSurface.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
 #include "PersistentBufferProvider.h"
 #include "SharedSurface.h"
 #include "SharedSurfaceGL.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 
 namespace mozilla {
 namespace layers {
 
 WebRenderCanvasLayer::~WebRenderCanvasLayer()
 {
   MOZ_COUNT_DTOR(WebRenderCanvasLayer);
 
-  if (mExternalImageId) {
-    WrBridge()->DeallocExternalImageId(mExternalImageId);
+  if (mExternalImageId.isSome()) {
+    WrBridge()->DeallocExternalImageId(mExternalImageId.ref());
   }
 }
 
 void
 WebRenderCanvasLayer::Initialize(const Data& aData)
 {
   ShareableCanvasLayer::Initialize(aData);
 
@@ -45,58 +46,50 @@ WebRenderCanvasLayer::Initialize(const D
   screen->Morph(Move(factory));
 }
 
 void
 WebRenderCanvasLayer::RenderLayer(wr::DisplayListBuilder& aBuilder)
 {
   UpdateCompositableClient();
 
-  if (!mExternalImageId) {
-    mExternalImageId = WrBridge()->AllocExternalImageIdForCompositable(mCanvasClient);
+  if (mExternalImageId.isNothing()) {
+    mExternalImageId = Some(WrBridge()->AllocExternalImageIdForCompositable(mCanvasClient));
   }
 
-  MOZ_ASSERT(mExternalImageId);
-
-  gfx::Matrix4x4 transform = GetTransform();
+  Maybe<gfx::Matrix4x4> transform;
   const bool needsYFlip = (mOriginPos == gl::OriginPos::BottomLeft);
   if (needsYFlip) {
-    transform.PreTranslate(0, mBounds.height, 0).PreScale(1, -1, 1);
+    transform = Some(GetTransform().PreTranslate(0, mBounds.height, 0).PreScale(1, -1, 1));
   }
 
-  gfx::Rect relBounds = GetWrRelBounds();
-  gfx::Rect rect = RelativeToVisible(gfx::Rect(0, 0, mBounds.width, mBounds.height));
+  StackingContextHelper sc(aBuilder, this, transform);
+
+  LayerRect rect(0, 0, mBounds.width, mBounds.height);
+  DumpLayerInfo("CanvasLayer", rect);
 
-  gfx::Rect clipRect = GetWrClipRect(rect);
+  LayerRect clipRect = ClipRect().valueOr(rect);
   Maybe<WrImageMask> mask = BuildWrMaskLayer(true);
-  WrClipRegion clip = aBuilder.BuildClipRegion(wr::ToWrRect(clipRect), mask.ptrOr(nullptr));
+  WrClipRegion clip = aBuilder.BuildClipRegion(
+      sc.ToRelativeWrRect(clipRect),
+      mask.ptrOr(nullptr));
 
   wr::ImageRendering filter = wr::ToImageRendering(mSamplingFilter);
-  wr::MixBlendMode mixBlendMode = wr::ToWrMixBlendMode(GetMixBlendMode());
 
-  DumpLayerInfo("CanvasLayer", rect);
   if (gfxPrefs::LayersDump()) {
     printf_stderr("CanvasLayer %p texture-filter=%s\n",
                   this->GetLayer(),
                   Stringify(filter).c_str());
   }
 
-  WrImageKey key;
-  key.mNamespace = WrBridge()->GetNamespace();
-  key.mHandle = WrBridge()->GetNextResourceId();
-  WrBridge()->AddWebRenderParentCommand(OpAddExternalImage(mExternalImageId, key));
+  WrImageKey key = GetImageKey();
+  WrBridge()->AddWebRenderParentCommand(OpAddExternalImage(mExternalImageId.value(), key));
   Manager()->AddImageKeyForDiscard(key);
 
-  aBuilder.PushStackingContext(wr::ToWrRect(relBounds),
-                               1.0f,
-                               //GetAnimations(),
-                               transform,
-                               mixBlendMode);
-  aBuilder.PushImage(wr::ToWrRect(rect), clip, filter, key);
-  aBuilder.PopStackingContext();
+  aBuilder.PushImage(sc.ToRelativeWrRect(rect), clip, filter, key);
 }
 
 void
 WebRenderCanvasLayer::AttachCompositable()
 {
   mCanvasClient->Connect();
 }
 
--- a/gfx/layers/wr/WebRenderCanvasLayer.h
+++ b/gfx/layers/wr/WebRenderCanvasLayer.h
@@ -1,33 +1,33 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GFX_WEBRENDERCANVASLAYER_H
 #define GFX_WEBRENDERCANVASLAYER_H
 
+#include "mozilla/layers/WebRenderLayer.h"
+#include "mozilla/layers/WebRenderLayerManager.h"
 #include "ShareableCanvasLayer.h"
-#include "WebRenderLayerManager.h"
 
 namespace mozilla {
 namespace gfx {
 class SourceSurface;
 }; // namespace gfx
 
 namespace layers {
 
 class WebRenderCanvasLayer : public WebRenderLayer,
                              public ShareableCanvasLayer
 {
 public:
   explicit WebRenderCanvasLayer(WebRenderLayerManager* aLayerManager)
     : ShareableCanvasLayer(aLayerManager, static_cast<WebRenderLayer*>(this))
-    , mExternalImageId(0)
   {
     MOZ_COUNT_CTOR(WebRenderCanvasLayer);
   }
 
   virtual void Initialize(const Data& aData) override;
 
   virtual CompositableForwarder* GetForwarder() override;
 
@@ -40,15 +40,15 @@ protected:
     return static_cast<WebRenderLayerManager*>(mManager);
   }
 
 public:
   Layer* GetLayer() override { return this; }
   void RenderLayer(wr::DisplayListBuilder& aBuilder) override;
 
 protected:
-  uint64_t mExternalImageId;
+  wr::MaybeExternalImageId mExternalImageId;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // GFX_WEBRENDERCANVASLAYER_H
--- a/gfx/layers/wr/WebRenderColorLayer.cpp
+++ b/gfx/layers/wr/WebRenderColorLayer.cpp
@@ -4,41 +4,35 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebRenderColorLayer.h"
 
 #include "gfxPrefs.h"
 #include "LayersLogging.h"
 #include "mozilla/webrender/webrender_ffi.h"
 #include "mozilla/webrender/WebRenderTypes.h"
+#include "mozilla/layers/StackingContextHelper.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
 
 namespace mozilla {
 namespace layers {
 
 using namespace mozilla::gfx;
 
 void
 WebRenderColorLayer::RenderLayer(wr::DisplayListBuilder& aBuilder)
 {
-  gfx::Matrix4x4 transform = GetTransform();
-  gfx::Rect relBounds = GetWrRelBounds();
-  gfx::Rect rect = GetWrBoundsRect();
+  StackingContextHelper sc(aBuilder, this);
 
-  gfx::Rect clipRect = GetWrClipRect(rect);
-  Maybe<WrImageMask> mask = BuildWrMaskLayer(true);
-  WrClipRegion clip = aBuilder.BuildClipRegion(wr::ToWrRect(clipRect), mask.ptrOr(nullptr));
-
-  wr::MixBlendMode mixBlendMode = wr::ToWrMixBlendMode(GetMixBlendMode());
-
+  LayerRect rect = Bounds();
   DumpLayerInfo("ColorLayer", rect);
 
-  aBuilder.PushStackingContext(wr::ToWrRect(relBounds),
-                              1.0f,
-                              //GetAnimations(),
-                              transform,
-                              mixBlendMode);
-  aBuilder.PushRect(wr::ToWrRect(rect), clip, wr::ToWrColor(mColor));
-  aBuilder.PopStackingContext();
+  LayerRect clipRect = ClipRect().valueOr(rect);
+  Maybe<WrImageMask> mask = BuildWrMaskLayer(true);
+  WrClipRegion clip = aBuilder.BuildClipRegion(
+      sc.ToRelativeWrRect(clipRect),
+      mask.ptrOr(nullptr));
+
+  aBuilder.PushRect(sc.ToRelativeWrRect(rect), clip, wr::ToWrColor(mColor));
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderColorLayer.h
+++ b/gfx/layers/wr/WebRenderColorLayer.h
@@ -2,17 +2,18 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GFX_WEBRENDERCOLORLAYER_H
 #define GFX_WEBRENDERCOLORLAYER_H
 
 #include "Layers.h"
-#include "WebRenderLayerManager.h"
+#include "mozilla/layers/WebRenderLayer.h"
+#include "mozilla/layers/WebRenderLayerManager.h"
 
 namespace mozilla {
 namespace layers {
 
 class WebRenderColorLayer : public WebRenderLayer,
                             public ColorLayer {
 public:
   explicit WebRenderColorLayer(WebRenderLayerManager* aLayerManager)
--- a/gfx/layers/wr/WebRenderCompositableHolder.h
+++ b/gfx/layers/wr/WebRenderCompositableHolder.h
@@ -34,16 +34,36 @@ protected:
   ~WebRenderCompositableHolder();
 
 public:
   void AddPipeline(const wr::PipelineId& aPipelineId);
   void RemovePipeline(const wr::PipelineId& aPipelineId);
   void HoldExternalImage(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch, WebRenderTextureHost* aTexture);
   void Update(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch);
 
+  TimeStamp GetCompositionTime() const {
+    return mCompositionTime;
+  }
+  void SetCompositionTime(TimeStamp aTimeStamp) {
+    mCompositionTime = aTimeStamp;
+    if (!mCompositionTime.IsNull() && !mCompositeUntilTime.IsNull() &&
+        mCompositionTime >= mCompositeUntilTime) {
+      mCompositeUntilTime = TimeStamp();
+    }
+  }
+  void CompositeUntil(TimeStamp aTimeStamp) {
+    if (mCompositeUntilTime.IsNull() ||
+        mCompositeUntilTime < aTimeStamp) {
+      mCompositeUntilTime = aTimeStamp;
+    }
+  }
+  TimeStamp GetCompositeUntilTime() const {
+    return mCompositeUntilTime;
+  }
+
 private:
 
   struct ForwardingTextureHost {
     ForwardingTextureHost(const wr::Epoch& aEpoch, TextureHost* aTexture)
       : mEpoch(aEpoch)
       , mTexture(aTexture)
     {}
     wr::Epoch mEpoch;
@@ -51,14 +71,22 @@ private:
   };
 
   struct PipelineTexturesHolder {
     // Holds forwarding WebRenderTextureHosts.
     std::queue<ForwardingTextureHost> mTextureHosts;
   };
 
   nsClassHashtable<nsUint64HashKey, PipelineTexturesHolder> mPipelineTexturesHolders;
+
+  // Render time for the current composition.
+  TimeStamp mCompositionTime;
+
+  // When nonnull, during rendering, some compositable indicated that it will
+  // change its rendering at this time. In order not to miss it, we composite
+  // on every vsync until this time occurs (this is the latest such time).
+  TimeStamp mCompositeUntilTime;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif /* MOZILLA_GFX_WEBRENDERCOMPOSITABLE_HOLDER_H */
--- a/gfx/layers/wr/WebRenderContainerLayer.cpp
+++ b/gfx/layers/wr/WebRenderContainerLayer.cpp
@@ -3,59 +3,67 @@
  * 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 "WebRenderContainerLayer.h"
 
 #include <inttypes.h>
 #include "gfxPrefs.h"
 #include "LayersLogging.h"
+#include "mozilla/layers/StackingContextHelper.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 
 namespace mozilla {
 namespace layers {
 
 void
 WebRenderContainerLayer::RenderLayer(wr::DisplayListBuilder& aBuilder)
 {
   nsTArray<LayerPolygon> children = SortChildrenBy3DZOrder(SortMode::WITHOUT_GEOMETRY);
 
   gfx::Matrix4x4 transform = GetTransform();
-  gfx::Rect relBounds = GetWrRelBounds();
-  gfx::Rect overflow(0, 0, relBounds.width, relBounds.height);
+  gfx::Matrix4x4* maybeTransform = &transform;
+  float opacity = GetLocalOpacity();
+  float* maybeOpacity = &opacity;
+  uint64_t animationsId = 0;
+
+  if (gfxPrefs::WebRenderOMTAEnabled() &&
+      GetAnimations().Length()) {
+    MOZ_ASSERT(GetCompositorAnimationsId());
+
+    animationsId = GetCompositorAnimationsId();
+    CompositorAnimations anim;
+    anim.animations() = GetAnimations();
+    anim.id() = animationsId;
+    WrBridge()->AddWebRenderParentCommand(OpAddCompositorAnimations(anim));
+
+    if (!HasOpacityAnimation()) {
+      maybeOpacity = nullptr;
+    }
+    if (!HasTransformAnimation()) {
+      maybeTransform = nullptr;
+    }
+  }
+
+  StackingContextHelper sc(aBuilder, this, animationsId, maybeOpacity, maybeTransform);
+
+  LayerRect rect = Bounds();
+  DumpLayerInfo("ContainerLayer", rect);
 
   Maybe<WrImageMask> mask = BuildWrMaskLayer(true);
-
-  wr::MixBlendMode mixBlendMode = wr::ToWrMixBlendMode(GetMixBlendMode());
+  aBuilder.PushClip(sc.ToRelativeWrRect(rect), mask.ptrOr(nullptr));
 
-  if (gfxPrefs::LayersDump()) {
-    printf_stderr("ContainerLayer %p using bounds=%s, overflow=%s, transform=%s, mix-blend-mode=%s\n",
-                  this->GetLayer(),
-                  Stringify(relBounds).c_str(),
-                  Stringify(overflow).c_str(),
-                  Stringify(transform).c_str(),
-                  Stringify(mixBlendMode).c_str());
-  }
-  aBuilder.PushStackingContext(wr::ToWrRect(relBounds),
-                               GetLocalOpacity(),
-                               //GetLayer()->GetAnimations(),
-                               transform,
-                               mixBlendMode);
-  aBuilder.PushScrollLayer(wr::ToWrRect(overflow),
-                           wr::ToWrRect(overflow),
-                           mask.ptrOr(nullptr));
   for (LayerPolygon& child : children) {
     if (child.layer->IsBackfaceHidden()) {
       continue;
     }
     ToWebRenderLayer(child.layer)->RenderLayer(aBuilder);
   }
-  aBuilder.PopScrollLayer();
-  aBuilder.PopStackingContext();
+  aBuilder.PopClip();
 }
 
 void
 WebRenderRefLayer::RenderLayer(wr::DisplayListBuilder& aBuilder)
 {
   gfx::Matrix4x4 transform;// = GetTransform();
   gfx::Rect relBounds = TransformedVisibleBoundsRelativeToParent();
 
--- a/gfx/layers/wr/WebRenderContainerLayer.h
+++ b/gfx/layers/wr/WebRenderContainerLayer.h
@@ -1,18 +1,20 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GFX_WEBRENDERCONTAINERLAYER_H
 #define GFX_WEBRENDERCONTAINERLAYER_H
 
+#include "gfxPrefs.h"
 #include "Layers.h"
-#include "WebRenderLayerManager.h"
+#include "mozilla/layers/WebRenderLayer.h"
+#include "mozilla/layers/WebRenderLayerManager.h"
 
 namespace mozilla {
 namespace layers {
 
 class WebRenderContainerLayer : public WebRenderLayer,
                                 public ContainerLayer
 {
 public:
@@ -20,16 +22,23 @@ public:
     : ContainerLayer(aManager, static_cast<WebRenderLayer*>(this))
   {
     MOZ_COUNT_CTOR(WebRenderContainerLayer);
   }
 
 protected:
   virtual ~WebRenderContainerLayer()
   {
+
+    if (gfxPrefs::WebRenderOMTAEnabled() &&
+        GetAnimations().Length()) {
+      mManager->AsWebRenderLayerManager()->
+        AddCompositorAnimationsIdForDiscard(GetCompositorAnimationsId());
+    }
+
     ContainerLayer::RemoveAllChildren();
     MOZ_COUNT_DTOR(WebRenderContainerLayer);
   }
 
 public:
   Layer* GetLayer() override { return this; }
   void RenderLayer(wr::DisplayListBuilder& aBuilder) override;
 
--- a/gfx/layers/wr/WebRenderDisplayItemLayer.cpp
+++ b/gfx/layers/wr/WebRenderDisplayItemLayer.cpp
@@ -6,59 +6,82 @@
 #include "WebRenderDisplayItemLayer.h"
 
 #include "LayersLogging.h"
 #include "mozilla/webrender/webrender_ffi.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
 #include "nsDisplayList.h"
 #include "mozilla/gfx/Matrix.h"
+#include "mozilla/layers/UpdateImageHelper.h"
+#include "UnitTransforms.h"
 
 namespace mozilla {
 namespace layers {
 
+WebRenderDisplayItemLayer::~WebRenderDisplayItemLayer()
+{
+  MOZ_COUNT_DTOR(WebRenderDisplayItemLayer);
+  if (mKey.isSome()) {
+    WrManager()->AddImageKeyForDiscard(mKey.value());
+  }
+  if (mExternalImageId.isSome()) {
+    WrBridge()->DeallocExternalImageId(mExternalImageId.ref());
+  }
+}
+
 void
 WebRenderDisplayItemLayer::RenderLayer(wr::DisplayListBuilder& aBuilder)
 {
   if (mVisibleRegion.IsEmpty()) {
     return;
   }
 
   Maybe<WrImageMask> mask = BuildWrMaskLayer(false);
   WrImageMask* imageMask = mask.ptrOr(nullptr);
   if (imageMask) {
     gfx::Rect rect = TransformedVisibleBoundsRelativeToParent();
-    gfx::Rect overflow(0.0, 0.0, rect.width, rect.height);
-    aBuilder.PushScrollLayer(wr::ToWrRect(rect),
-                             wr::ToWrRect(overflow),
-                             imageMask);
+    gfx::Rect clip(0.0, 0.0, rect.width, rect.height);
+    aBuilder.PushClip(wr::ToWrRect(clip), imageMask);
   }
 
   if (mItem) {
     wr::DisplayListBuilder builder(WrBridge()->GetPipeline());
     // We might have recycled this layer. Throw away the old commands.
     mParentCommands.Clear();
     mItem->CreateWebRenderCommands(builder, mParentCommands, this);
     mBuiltDisplayList = builder.Finalize();
+  } else {
+    // else we have an empty transaction and just use the
+    // old commands.
+    WebRenderLayerManager* manager = static_cast<WebRenderLayerManager*>(Manager());
+    MOZ_ASSERT(manager);
+
+    // Since our recording relies on our parent layer's transform and stacking context
+    // If this layer or our parent changed, this empty transaction won't work.
+    if (manager->IsMutatedLayer(this) || manager->IsMutatedLayer(GetParent())) {
+      manager->SetTransactionIncomplete();
+      return;
+    }
   }
-  // else we have an empty transaction and just use the
-  // old commands.
 
   aBuilder.PushBuiltDisplayList(Move(mBuiltDisplayList));
   WrBridge()->AddWebRenderParentCommands(mParentCommands);
 
   if (imageMask) {
-    aBuilder.PopScrollLayer();
+    aBuilder.PopClip();
   }
 }
 
 Maybe<wr::ImageKey>
 WebRenderDisplayItemLayer::SendImageContainer(ImageContainer* aContainer,
                                               nsTArray<layers::WebRenderParentCommand>& aParentCommands)
 {
+  MOZ_ASSERT(aContainer);
+
   if (mImageContainer != aContainer) {
     AutoLockImage autoLock(aContainer);
     Image* image = autoLock.GetImage();
     if (!image) {
       return Nothing();
     }
 
     if (!mImageClient) {
@@ -66,32 +89,98 @@ WebRenderDisplayItemLayer::SendImageCont
                                                     WrBridge(),
                                                     TextureFlags::DEFAULT);
       if (!mImageClient) {
         return Nothing();
       }
       mImageClient->Connect();
     }
 
-    if (!mExternalImageId) {
+    if (mExternalImageId.isNothing()) {
       MOZ_ASSERT(mImageClient);
-      mExternalImageId = WrBridge()->AllocExternalImageIdForCompositable(mImageClient);
+      mExternalImageId = Some(WrBridge()->AllocExternalImageIdForCompositable(mImageClient));
     }
-    MOZ_ASSERT(mExternalImageId);
+    MOZ_ASSERT(mExternalImageId.isSome());
+    MOZ_ASSERT(mImageClient->AsImageClientSingle());
 
-    if (!mImageClient->UpdateImage(aContainer, /* unused */0)) {
-      return Nothing();
-    }
+    mKey = UpdateImageKey(mImageClient->AsImageClientSingle(),
+                          aContainer,
+                          mKey,
+                          mExternalImageId.ref());
+
     mImageContainer = aContainer;
   }
 
-  WrImageKey key;
-  key.mNamespace = WrBridge()->GetNamespace();
-  key.mHandle = WrBridge()->GetNextResourceId();
+  return mKey;
+}
+
+bool
+WebRenderDisplayItemLayer::PushItemAsImage(wr::DisplayListBuilder& aBuilder,
+                                           nsTArray<layers::WebRenderParentCommand>& aParentCommands)
+{
+  if (!mImageContainer) {
+    mImageContainer = LayerManager::CreateImageContainer();
+  }
+
+  if (!mImageClient) {
+    mImageClient = ImageClient::CreateImageClient(CompositableType::IMAGE,
+                                                  WrBridge(),
+                                                  TextureFlags::DEFAULT);
+    if (!mImageClient) {
+      return false;
+    }
+    mImageClient->Connect();
+  }
+
+  if (mExternalImageId.isNothing()) {
+    MOZ_ASSERT(mImageClient);
+    mExternalImageId = Some(WrBridge()->AllocExternalImageIdForCompositable(mImageClient));
+  }
+
+  const int32_t appUnitsPerDevPixel = mItem->Frame()->PresContext()->AppUnitsPerDevPixel();
+
+  bool snap;
+  LayerRect bounds = ViewAs<LayerPixel>(
+      LayoutDeviceRect::FromAppUnits(mItem->GetBounds(mBuilder, &snap), appUnitsPerDevPixel),
+      PixelCastJustification::WebRenderHasUnitResolution);
+  LayerIntSize imageSize = RoundedToInt(bounds.Size());
+  LayerRect imageRect;
+  imageRect.SizeTo(LayerSize(imageSize));
+
+  UpdateImageHelper helper(mImageContainer, mImageClient, imageSize.ToUnknownSize());
+  LayerPoint offset = ViewAs<LayerPixel>(
+      LayoutDevicePoint::FromAppUnits(mItem->ToReferenceFrame(), appUnitsPerDevPixel),
+      PixelCastJustification::WebRenderHasUnitResolution);
+
+  {
+    RefPtr<gfx::DrawTarget> target = helper.GetDrawTarget();
+    if (!target) {
+      return false;
+    }
+
+    target->ClearRect(imageRect.ToUnknownRect());
+    RefPtr<gfxContext> context = gfxContext::CreateOrNull(target, offset.ToUnknownPoint());
+    MOZ_ASSERT(context);
+
+    nsRenderingContext ctx(context);
+    mItem->Paint(mBuilder, &ctx);
+  }
+
+  if (!helper.UpdateImage()) {
+    return false;
+  }
+
+  LayerRect dest = RelativeToParent(imageRect) + offset;
+  WrClipRegion clipRegion = aBuilder.BuildClipRegion(wr::ToWrRect(dest));
+  WrImageKey key = GetImageKey();
   aParentCommands.AppendElement(layers::OpAddExternalImage(
-                                mExternalImageId,
+                                mExternalImageId.value(),
                                 key));
-  WrManager()->AddImageKeyForDiscard(key);
-  return Some(key);
+  aBuilder.PushImage(wr::ToWrRect(dest),
+                     clipRegion,
+                     WrImageRendering::Auto,
+                     key);
+
+  return true;
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderDisplayItemLayer.h
+++ b/gfx/layers/wr/WebRenderDisplayItemLayer.h
@@ -4,51 +4,50 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GFX_WEBRENDERDISPLAYITEMLAYER_H
 #define GFX_WEBRENDERDISPLAYITEMLAYER_H
 
 #include "Layers.h"
 #include "mozilla/layers/ImageClient.h"
 #include "mozilla/layers/PWebRenderBridgeChild.h"
+#include "mozilla/layers/WebRenderLayer.h"
+#include "mozilla/layers/WebRenderLayerManager.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/webrender/WebRenderTypes.h"
-#include "WebRenderLayerManager.h"
 
 namespace mozilla {
 namespace layers {
 
 class WebRenderDisplayItemLayer : public WebRenderLayer,
                                   public DisplayItemLayer {
 public:
   explicit WebRenderDisplayItemLayer(WebRenderLayerManager* aLayerManager)
     : DisplayItemLayer(aLayerManager, static_cast<WebRenderLayer*>(this))
-    , mExternalImageId(0)
   {
     MOZ_COUNT_CTOR(WebRenderDisplayItemLayer);
   }
 
   Maybe<wr::ImageKey> SendImageContainer(ImageContainer* aContainer,
                                          nsTArray<layers::WebRenderParentCommand>& aParentCommands);
+  bool PushItemAsImage(wr::DisplayListBuilder& aBuilder,
+                       nsTArray<layers::WebRenderParentCommand>& aParentCommands);
 
 protected:
-  virtual ~WebRenderDisplayItemLayer()
-  {
-    MOZ_COUNT_DTOR(WebRenderDisplayItemLayer);
-  }
+  virtual ~WebRenderDisplayItemLayer();
 
 public:
   Layer* GetLayer() override { return this; }
   void RenderLayer(wr::DisplayListBuilder& aBuilder) override;
 
 private:
   wr::BuiltDisplayList mBuiltDisplayList;
   nsTArray<WebRenderParentCommand> mParentCommands;
   RefPtr<ImageClient> mImageClient;
   RefPtr<ImageContainer> mImageContainer;
-  uint64_t mExternalImageId;
-
+  wr::MaybeExternalImageId mExternalImageId;
+  Maybe<wr::ImageKey> mKey;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // GFX_WEBRENDERDisplayItemLayer_H
--- a/gfx/layers/wr/WebRenderImageHost.cpp
+++ b/gfx/layers/wr/WebRenderImageHost.cpp
@@ -4,36 +4,39 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebRenderImageHost.h"
 
 #include "LayersLogging.h"
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/Effects.h"     // for TexturedEffect, Effect, etc
 #include "mozilla/layers/LayerManagerComposite.h"     // for TexturedEffect, Effect, etc
+#include "mozilla/layers/WebRenderCompositableHolder.h"
 #include "nsAString.h"
 #include "nsDebug.h"                    // for NS_WARNING, NS_ASSERTION
 #include "nsPrintfCString.h"            // for nsPrintfCString
 #include "nsString.h"                   // for nsAutoCString
 
 namespace mozilla {
 
 using namespace gfx;
 
 namespace layers {
 
 class ISurfaceAllocator;
 
 WebRenderImageHost::WebRenderImageHost(const TextureInfo& aTextureInfo)
   : CompositableHost(aTextureInfo)
   , ImageComposite()
+  , mWrCompositableHolder(nullptr)
 {}
 
 WebRenderImageHost::~WebRenderImageHost()
 {
+  MOZ_ASSERT(!mWrCompositableHolder);
 }
 
 void
 WebRenderImageHost::UseTextureHost(const nsTArray<TimedTexture>& aTextures)
 {
   CompositableHost::UseTextureHost(aTextures);
   MOZ_ASSERT(aTextures.Length() >= 1);
 
@@ -57,16 +60,32 @@ WebRenderImageHost::UseTextureHost(const
     img.mFrameID = t.mFrameID;
     img.mProducerID = t.mProducerID;
     img.mTextureHost->SetCropRect(img.mPictureRect);
     img.mTextureHost->Updated();
   }
 
   mImages.SwapElements(newImages);
   newImages.Clear();
+
+  // Video producers generally send replacement images with the same frameID but
+  // slightly different timestamps in order to sync with the audio clock. This
+  // means that any CompositeUntil() call we made in Composite() may no longer
+  // guarantee that we'll composite until the next frame is ready. Fix that here.
+  if (mWrCompositableHolder && mLastFrameID >= 0) {
+    for (size_t i = 0; i < mImages.Length(); ++i) {
+      bool frameComesAfter = mImages[i].mFrameID > mLastFrameID ||
+                             mImages[i].mProducerID != mLastProducerID;
+      if (frameComesAfter && !mImages[i].mTimeStamp.IsNull()) {
+        mWrCompositableHolder->CompositeUntil(mImages[i].mTimeStamp +
+                           TimeDuration::FromMilliseconds(BIAS_TIME_MS));
+        break;
+      }
+    }
+  }
 }
 
 void
 WebRenderImageHost::UseComponentAlphaTextures(TextureHost* aTextureOnBlack,
                                               TextureHost* aTextureOnWhite)
 {
   MOZ_ASSERT_UNREACHABLE("unexpected to be called");
 }
@@ -90,30 +109,54 @@ WebRenderImageHost::RemoveTextureHost(Te
       mImages.RemoveElementAt(i);
     }
   }
 }
 
 TimeStamp
 WebRenderImageHost::GetCompositionTime() const
 {
-  // XXX temporary workaround
-  return TimeStamp::Now();
+  TimeStamp time;
+  if (mWrCompositableHolder) {
+    time = mWrCompositableHolder->GetCompositionTime();
+  }
+  return time;
 }
 
 TextureHost*
 WebRenderImageHost::GetAsTextureHost(IntRect* aPictureRect)
 {
   TimedImage* img = ChooseImage();
   if (img) {
     return img->mTextureHost;
   }
   return nullptr;
 }
 
+TextureHost*
+WebRenderImageHost::GetAsTextureHostForComposite()
+{
+  int imageIndex = ChooseImageIndex();
+  if (imageIndex < 0) {
+    return nullptr;
+  }
+
+  if (mWrCompositableHolder && uint32_t(imageIndex) + 1 < mImages.Length()) {
+    mWrCompositableHolder->CompositeUntil(mImages[imageIndex + 1].mTimeStamp + TimeDuration::FromMilliseconds(BIAS_TIME_MS));
+  }
+
+  TimedImage* img = &mImages[imageIndex];
+
+  if (mLastFrameID != img->mFrameID || mLastProducerID != img->mProducerID) {
+    mLastFrameID = img->mFrameID;
+    mLastProducerID = img->mProducerID;
+  }
+  return img->mTextureHost;
+}
+
 void WebRenderImageHost::Attach(Layer* aLayer,
                        TextureSourceProvider* aProvider,
                        AttachFlags aFlags)
 {
   MOZ_ASSERT_UNREACHABLE("unexpected to be called");
 }
 
 void
--- a/gfx/layers/wr/WebRenderImageHost.h
+++ b/gfx/layers/wr/WebRenderImageHost.h
@@ -7,16 +7,18 @@
 #define MOZILLA_GFX_WEBRENDERIMAGEHOST_H
 
 #include "CompositableHost.h"           // for CompositableHost
 #include "mozilla/layers/ImageComposite.h"  // for ImageComposite
 
 namespace mozilla {
 namespace layers {
 
+class WebRenderCompositableHolder;
+
 /**
  * ImageHost. Works with ImageClientSingle and ImageClientBuffered
  */
 class WebRenderImageHost : public CompositableHost,
                            public ImageComposite
 {
 public:
   explicit WebRenderImageHost(const TextureInfo& aTextureInfo);
@@ -60,17 +62,26 @@ public:
   virtual bool Lock() override;
 
   virtual void Unlock() override;
 
   virtual void CleanupResources() override;
 
   virtual WebRenderImageHost* AsWebRenderImageHost() override { return this; }
 
+  TextureHost* GetAsTextureHostForComposite();
+
+  void SetWrCompositableHolder(WebRenderCompositableHolder* aWrCompositableHolder)
+  {
+    mWrCompositableHolder = aWrCompositableHolder;
+  }
+
 protected:
   // ImageComposite
   virtual TimeStamp GetCompositionTime() const override;
+
+  WebRenderCompositableHolder* MOZ_NON_OWNING_REF mWrCompositableHolder;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // MOZILLA_GFX_WEBRENDERIMAGEHOST_H
--- a/gfx/layers/wr/WebRenderImageLayer.cpp
+++ b/gfx/layers/wr/WebRenderImageLayer.cpp
@@ -3,39 +3,43 @@
  * 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 "WebRenderImageLayer.h"
 
 #include "gfxPrefs.h"
 #include "LayersLogging.h"
 #include "mozilla/layers/ImageClient.h"
+#include "mozilla/layers/StackingContextHelper.h"
 #include "mozilla/layers/TextureClientRecycleAllocator.h"
 #include "mozilla/layers/TextureWrapperImage.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 
 namespace mozilla {
 namespace layers {
 
 using namespace gfx;
 
 WebRenderImageLayer::WebRenderImageLayer(WebRenderLayerManager* aLayerManager)
   : ImageLayer(aLayerManager, static_cast<WebRenderLayer*>(this))
-  , mExternalImageId(0)
   , mImageClientTypeContainer(CompositableType::UNKNOWN)
 {
   MOZ_COUNT_CTOR(WebRenderImageLayer);
 }
 
 WebRenderImageLayer::~WebRenderImageLayer()
 {
   MOZ_COUNT_DTOR(WebRenderImageLayer);
-  if (mExternalImageId) {
-    WrBridge()->DeallocExternalImageId(mExternalImageId);
+  mPipelineIdRequest.DisconnectIfExists();
+  if (mKey.isSome()) {
+    WrManager()->AddImageKeyForDiscard(mKey.value());
+  }
+  if (mExternalImageId.isSome()) {
+    WrBridge()->DeallocExternalImageId(mExternalImageId.ref());
   }
 }
 
 CompositableType
 WebRenderImageLayer::GetImageClientType()
 {
   if (mImageClientTypeContainer != CompositableType::UNKNOWN) {
     return mImageClientTypeContainer;
@@ -88,88 +92,108 @@ WebRenderImageLayer::RenderLayer(wr::Dis
 
   CompositableType type = GetImageClientType();
   if (type == CompositableType::UNKNOWN) {
     return;
   }
 
   MOZ_ASSERT(GetImageClientType() != CompositableType::UNKNOWN);
 
+  // Allocate PipelineId if necessary
+  if (GetImageClientType() == CompositableType::IMAGE_BRIDGE &&
+      mPipelineId.isNothing() && !mPipelineIdRequest.Exists()) {
+    // Use Holder to pass this pointer to lambda.
+    // Static anaysis tool does not permit to pass refcounted variable to lambda.
+    // And we do not want to use RefPtr<WebRenderImageLayer> here.
+    Holder holder(this);
+    Manager()->AllocPipelineId()
+      ->Then(AbstractThread::GetCurrent(), __func__,
+      [holder] (const wr::PipelineId& aPipelineId) {
+        holder->mPipelineIdRequest.Complete();
+        holder->mPipelineId = Some(aPipelineId);
+      },
+      [holder] (const ipc::PromiseRejectReason &aReason) {
+        holder->mPipelineIdRequest.Complete();
+      })->Track(mPipelineIdRequest);
+  }
+
   if (GetImageClientType() == CompositableType::IMAGE && !mImageClient) {
     mImageClient = ImageClient::CreateImageClient(CompositableType::IMAGE,
                                                   WrBridge(),
                                                   TextureFlags::DEFAULT);
     if (!mImageClient) {
       return;
     }
     mImageClient->Connect();
   }
 
-  if (!mExternalImageId) {
+  if (mExternalImageId.isNothing()) {
     if (GetImageClientType() == CompositableType::IMAGE_BRIDGE) {
       MOZ_ASSERT(!mImageClient);
-      mExternalImageId = WrBridge()->AllocExternalImageId(mContainer->GetAsyncContainerHandle());
+      mExternalImageId = Some(WrBridge()->AllocExternalImageId(mContainer->GetAsyncContainerHandle()));
     } else {
       // Handle CompositableType::IMAGE case
       MOZ_ASSERT(mImageClient);
-      mExternalImageId = WrBridge()->AllocExternalImageIdForCompositable(mImageClient);
+      mExternalImageId = Some(WrBridge()->AllocExternalImageIdForCompositable(mImageClient));
     }
   }
-  MOZ_ASSERT(mExternalImageId);
+  MOZ_ASSERT(mExternalImageId.isSome());
 
   // XXX Not good for async ImageContainer case.
   AutoLockImage autoLock(mContainer);
   Image* image = autoLock.GetImage();
   if (!image) {
     return;
   }
   gfx::IntSize size = image->GetSize();
 
-  if (mImageClient && !mImageClient->UpdateImage(mContainer, /* unused */0)) {
+  if (GetImageClientType() == CompositableType::IMAGE_BRIDGE) {
+    // Always allocate key
+    WrImageKey key = GetImageKey();
+    WrBridge()->AddWebRenderParentCommand(OpAddExternalImage(mExternalImageId.value(), key));
+    Manager()->AddImageKeyForDiscard(key);
+    mKey = Some(key);
+  } else {
+    // Handle CompositableType::IMAGE case
+    MOZ_ASSERT(mImageClient->AsImageClientSingle());
+    mKey = UpdateImageKey(mImageClient->AsImageClientSingle(),
+                          mContainer,
+                          mKey,
+                          mExternalImageId.ref());
+  }
+
+  if (mKey.isNothing()) {
     return;
   }
 
-  gfx::Matrix4x4 transform = GetTransform();
-  gfx::Rect relBounds = GetWrRelBounds();
+  StackingContextHelper sc(aBuilder, this);
 
-  gfx::Rect rect = gfx::Rect(0, 0, size.width, size.height);
+  LayerRect rect(0, 0, size.width, size.height);
   if (mScaleMode != ScaleMode::SCALE_NONE) {
     NS_ASSERTION(mScaleMode == ScaleMode::STRETCH,
                  "No other scalemodes than stretch and none supported yet.");
-    rect = gfx::Rect(0, 0, mScaleToSize.width, mScaleToSize.height);
+    rect = LayerRect(0, 0, mScaleToSize.width, mScaleToSize.height);
   }
-  rect = RelativeToVisible(rect);
 
-  gfx::Rect clipRect = GetWrClipRect(rect);
+  LayerRect clipRect = ClipRect().valueOr(rect);
   Maybe<WrImageMask> mask = BuildWrMaskLayer(true);
-  WrClipRegion clip = aBuilder.BuildClipRegion(wr::ToWrRect(clipRect), mask.ptrOr(nullptr));
+  WrClipRegion clip = aBuilder.BuildClipRegion(
+      sc.ToRelativeWrRect(clipRect),
+      mask.ptrOr(nullptr));
 
   wr::ImageRendering filter = wr::ToImageRendering(mSamplingFilter);
-  wr::MixBlendMode mixBlendMode = wr::ToWrMixBlendMode(GetMixBlendMode());
 
   DumpLayerInfo("Image Layer", rect);
   if (gfxPrefs::LayersDump()) {
     printf_stderr("ImageLayer %p texture-filter=%s \n",
                   GetLayer(),
                   Stringify(filter).c_str());
   }
 
-  WrImageKey key;
-  key.mNamespace = WrBridge()->GetNamespace();
-  key.mHandle = WrBridge()->GetNextResourceId();
-  WrBridge()->AddWebRenderParentCommand(OpAddExternalImage(mExternalImageId, key));
-  Manager()->AddImageKeyForDiscard(key);
-
-  aBuilder.PushStackingContext(wr::ToWrRect(relBounds),
-                            1.0f,
-                            //GetAnimations(),
-                            transform,
-                            mixBlendMode);
-  aBuilder.PushImage(wr::ToWrRect(rect), clip, filter, key);
-  aBuilder.PopStackingContext();
+  aBuilder.PushImage(sc.ToRelativeWrRect(rect), clip, filter, mKey.value());
 }
 
 Maybe<WrImageMask>
 WebRenderImageLayer::RenderMaskLayer(const gfx::Matrix4x4& aTransform)
 {
   if (!mContainer) {
      return Nothing();
   }
@@ -189,39 +213,38 @@ WebRenderImageLayer::RenderMaskLayer(con
                                                   WrBridge(),
                                                   TextureFlags::DEFAULT);
     if (!mImageClient) {
       return Nothing();
     }
     mImageClient->Connect();
   }
 
-  if (!mExternalImageId) {
-    mExternalImageId = WrBridge()->AllocExternalImageIdForCompositable(mImageClient);
+  if (mExternalImageId.isNothing()) {
+    mExternalImageId = Some(WrBridge()->AllocExternalImageIdForCompositable(mImageClient));
   }
-  MOZ_ASSERT(mExternalImageId);
 
   AutoLockImage autoLock(mContainer);
   Image* image = autoLock.GetImage();
   if (!image) {
     return Nothing();
   }
-  if (!mImageClient->UpdateImage(mContainer, /* unused */0)) {
+
+  MOZ_ASSERT(mImageClient->AsImageClientSingle());
+  mKey = UpdateImageKey(mImageClient->AsImageClientSingle(),
+                        mContainer,
+                        mKey,
+                        mExternalImageId.ref());
+  if (mKey.isNothing()) {
     return Nothing();
   }
 
-  WrImageKey key;
-  key.mNamespace = WrBridge()->GetNamespace();
-  key.mHandle = WrBridge()->GetNextResourceId();
-  WrBridge()->AddWebRenderParentCommand(OpAddExternalImage(mExternalImageId, key));
-  Manager()->AddImageKeyForDiscard(key);
-
   gfx::IntSize size = image->GetSize();
   WrImageMask imageMask;
-  imageMask.image = key;
+  imageMask.image = mKey.value();
   Rect maskRect = aTransform.TransformBounds(Rect(0, 0, size.width, size.height));
   imageMask.rect = wr::ToWrRect(maskRect);
   imageMask.repeat = false;
   return Some(imageMask);
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderImageLayer.h
+++ b/gfx/layers/wr/WebRenderImageLayer.h
@@ -2,48 +2,63 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GFX_WEBRENDERIMAGELAYER_H
 #define GFX_WEBRENDERIMAGELAYER_H
 
 #include "ImageLayers.h"
-#include "WebRenderLayerManager.h"
+#include "mozilla/layers/WebRenderLayer.h"
+#include "mozilla/layers/WebRenderLayerManager.h"
 
 namespace mozilla {
 namespace layers {
 
 class ImageClient;
 
 class WebRenderImageLayer : public WebRenderLayer,
                             public ImageLayer {
 public:
   explicit WebRenderImageLayer(WebRenderLayerManager* aLayerManager);
 
   virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
 
   virtual void ClearCachedResources() override;
+
 protected:
   virtual ~WebRenderImageLayer();
 
   WebRenderLayerManager* Manager()
   {
     return static_cast<WebRenderLayerManager*>(mManager);
   }
 
 public:
   Layer* GetLayer() override { return this; }
   void RenderLayer(wr::DisplayListBuilder& aBuilder) override;
   Maybe<WrImageMask> RenderMaskLayer(const gfx::Matrix4x4& aTransform) override;
 
 protected:
   CompositableType GetImageClientType();
 
-  uint64_t mExternalImageId;
+  class Holder {
+  public:
+    explicit Holder(WebRenderImageLayer* aLayer)
+      : mLayer(aLayer)
+    {}
+    WebRenderImageLayer* operator ->() const { return mLayer; }
+  private:
+    WebRenderImageLayer* mLayer;
+  };
+
+  wr::MaybeExternalImageId mExternalImageId;
+  Maybe<wr::ImageKey> mKey;
   RefPtr<ImageClient> mImageClient;
   CompositableType mImageClientTypeContainer;
+  Maybe<wr::PipelineId> mPipelineId;
+  MozPromiseRequestHolder<PipelineIdPromise> mPipelineIdRequest;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // GFX_WEBRENDERIMAGELAYER_H
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderLayer.cpp
@@ -0,0 +1,201 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WebRenderLayer.h"
+
+#include "gfxPrefs.h"
+#include "LayersLogging.h"
+#include "mozilla/layers/ImageClient.h"
+#include "mozilla/layers/WebRenderBridgeChild.h"
+#include "mozilla/layers/WebRenderLayerManager.h"
+#include "nsThreadUtils.h"
+#include "UnitTransforms.h"
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+WebRenderLayerManager*
+WebRenderLayer::WrManager()
+{
+  return static_cast<WebRenderLayerManager*>(GetLayer()->Manager());
+}
+
+WebRenderBridgeChild*
+WebRenderLayer::WrBridge()
+{
+  return WrManager()->WrBridge();
+}
+
+WrImageKey
+WebRenderLayer::GetImageKey()
+{
+  WrImageKey key;
+  key.mNamespace = WrBridge()->GetNamespace();
+  key.mHandle = WrBridge()->GetNextResourceId();
+  return key;
+}
+
+LayerRect
+WebRenderLayer::ParentBounds()
+{
+  // Walk up to find the parent stacking context. This will be created by the
+  // parent layer which must be a ContainerLayer if it exists.
+  if (Layer* parent = GetLayer()->GetParent()) {
+    return ToWebRenderLayer(parent)->Bounds();
+  }
+  return LayerRect();
+}
+
+LayerRect
+WebRenderLayer::RelativeToParent(const LayerRect& aRect)
+{
+  return aRect - ParentBounds().TopLeft();
+}
+
+LayerRect
+WebRenderLayer::RelativeToParent(const LayoutDeviceRect& aRect)
+{
+  return RelativeToParent(ViewAs<LayerPixel>(
+      aRect, PixelCastJustification::WebRenderHasUnitResolution));
+}
+
+LayerPoint
+WebRenderLayer::GetOffsetToParent()
+{
+  return ParentBounds().TopLeft();
+}
+
+gfx::Rect
+WebRenderLayer::TransformedVisibleBoundsRelativeToParent()
+{
+  IntRect bounds = GetLayer()->GetVisibleRegion().GetBounds().ToUnknownRect();
+  Rect transformed = GetLayer()->GetTransform().TransformBounds(IntRectToRect(bounds));
+  return transformed - ParentBounds().ToUnknownRect().TopLeft();
+}
+
+Maybe<WrImageMask>
+WebRenderLayer::BuildWrMaskLayer(bool aUnapplyLayerTransform)
+{
+  if (GetLayer()->GetMaskLayer()) {
+    WebRenderLayer* maskLayer = ToWebRenderLayer(GetLayer()->GetMaskLayer());
+
+    // The size of mask layer is transformed, and we may set the layer transform
+    // to wr stacking context. So we should apply inverse transform for mask layer
+    // and reverse the offset of the stacking context.
+    gfx::Matrix4x4 transform = maskLayer->GetLayer()->GetTransform();
+    if (aUnapplyLayerTransform) {
+      gfx::Rect bounds = IntRectToRect(GetLayer()->GetVisibleRegion().GetBounds().ToUnknownRect());
+      transform = transform.PreTranslate(-bounds.x, -bounds.y, 0);
+      transform = transform * GetLayer()->GetTransform().Inverse();
+    }
+
+    return maskLayer->RenderMaskLayer(transform);
+  }
+
+  return Nothing();
+}
+
+LayerRect
+WebRenderLayer::Bounds()
+{
+  return LayerRect(GetLayer()->GetVisibleRegion().GetBounds());
+}
+
+BoundsTransformMatrix
+WebRenderLayer::BoundsTransform()
+{
+  gfx::Matrix4x4 transform = GetLayer()->GetTransform();
+  transform._41 = 0.0f;
+  transform._42 = 0.0f;
+  transform._43 = 0.0f;
+  return ViewAs<BoundsTransformMatrix>(transform);
+}
+
+LayerRect
+WebRenderLayer::BoundsForStackingContext()
+{
+  LayerRect bounds = Bounds();
+  BoundsTransformMatrix transform = BoundsTransform();
+  if (!transform.IsIdentity()) {
+    // WR will only apply the 'translate' of the transform, so we need to do the scale/rotation manually.
+    bounds.MoveTo(transform.TransformPoint(bounds.TopLeft()));
+  }
+
+  return bounds;
+}
+
+Maybe<LayerRect>
+WebRenderLayer::ClipRect()
+{
+  Layer* layer = GetLayer();
+  if (!layer->GetClipRect()) {
+    return Nothing();
+  }
+  ParentLayerRect clip(layer->GetClipRect().ref());
+  LayerToParentLayerMatrix4x4 transform = layer->GetLocalTransformTyped();
+  return Some(transform.Inverse().TransformBounds(clip));
+}
+
+Maybe<wr::ImageKey>
+WebRenderLayer::UpdateImageKey(ImageClientSingle* aImageClient,
+                               ImageContainer* aContainer,
+                               Maybe<wr::ImageKey>& aOldKey,
+                               wr::ExternalImageId& aExternalImageId)
+{
+  MOZ_ASSERT(aImageClient);
+  MOZ_ASSERT(aContainer);
+
+  uint32_t oldCounter = aImageClient->GetLastUpdateGenerationCounter();
+
+  bool ret = aImageClient->UpdateImage(aContainer, /* unused */0);
+  if (!ret || aImageClient->IsEmpty()) {
+    // Delete old key
+    if (aOldKey.isSome()) {
+      WrManager()->AddImageKeyForDiscard(aOldKey.value());
+    }
+    return Nothing();
+  }
+
+  // Reuse old key if generation is not updated.
+  if (oldCounter == aImageClient->GetLastUpdateGenerationCounter() && aOldKey.isSome()) {
+    return aOldKey;
+  }
+
+  // Delete old key, we are generating a new key.
+  if (aOldKey.isSome()) {
+    WrManager()->AddImageKeyForDiscard(aOldKey.value());
+  }
+
+  WrImageKey key = GetImageKey();
+  WrBridge()->AddWebRenderParentCommand(OpAddExternalImage(aExternalImageId, key));
+  return Some(key);
+}
+
+void
+WebRenderLayer::DumpLayerInfo(const char* aLayerType, const LayerRect& aRect)
+{
+  if (!gfxPrefs::LayersDump()) {
+    return;
+  }
+
+  Matrix4x4 transform = GetLayer()->GetTransform();
+  LayerRect bounds = Bounds();
+  WrMixBlendMode mixBlendMode = wr::ToWrMixBlendMode(GetLayer()->GetMixBlendMode());
+
+  printf_stderr("%s %p using bounds=%s, transform=%s, rect=%s, clip=%s, mix-blend-mode=%s\n",
+                aLayerType,
+                GetLayer(),
+                Stringify(bounds).c_str(),
+                Stringify(transform).c_str(),
+                Stringify(aRect).c_str(),
+                Stringify(ClipRect().valueOr(aRect)).c_str(),
+                Stringify(mixBlendMode).c_str());
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderLayer.h
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_WEBRENDERLAYER_H
+#define GFX_WEBRENDERLAYER_H
+
+#include "Layers.h"
+#include "mozilla/webrender/WebRenderTypes.h"
+#include "mozilla/webrender/WebRenderAPI.h"
+
+namespace mozilla {
+namespace layers {
+
+class ImageClientSingle;
+class WebRenderBridgeChild;
+class WebRenderLayerManager;
+
+typedef gfx::Matrix4x4Typed<LayerPixel, LayerPixel> BoundsTransformMatrix;
+
+class WebRenderLayer
+{
+public:
+  virtual Layer* GetLayer() = 0;
+  virtual void RenderLayer(wr::DisplayListBuilder& aBuilder) = 0;
+  virtual Maybe<WrImageMask> RenderMaskLayer(const gfx::Matrix4x4& aTransform)
+  {
+    MOZ_ASSERT(false);
+    return Nothing();
+  }
+
+  virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() { return nullptr; }
+  static inline WebRenderLayer*
+  ToWebRenderLayer(Layer* aLayer)
+  {
+    return static_cast<WebRenderLayer*>(aLayer->ImplData());
+  }
+
+  Maybe<wr::ImageKey> UpdateImageKey(ImageClientSingle* aImageClient,
+                                     ImageContainer* aContainer,
+                                     Maybe<wr::ImageKey>& aOldKey,
+                                     wr::ExternalImageId& aExternalImageId);
+
+  WebRenderLayerManager* WrManager();
+  WebRenderBridgeChild* WrBridge();
+  WrImageKey GetImageKey();
+
+  LayerRect RelativeToParent(const LayerRect& aRect);
+  LayerRect RelativeToParent(const LayoutDeviceRect& aRect);
+  LayerPoint GetOffsetToParent();
+
+  LayerRect Bounds();
+  LayerRect BoundsForStackingContext();
+protected:
+  BoundsTransformMatrix BoundsTransform();
+  LayerRect ParentBounds();
+  Maybe<LayerRect> ClipRect();
+
+  gfx::Rect TransformedVisibleBoundsRelativeToParent();
+
+  void DumpLayerInfo(const char* aLayerType, const LayerRect& aRect);
+  Maybe<WrImageMask> BuildWrMaskLayer(bool aUnapplyLayerTransform);
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_WEBRENDERLAYER_H */
--- a/gfx/layers/wr/WebRenderLayerManager.cpp
+++ b/gfx/layers/wr/WebRenderLayerManager.cpp
@@ -1,204 +1,41 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebRenderLayerManager.h"
 
-#include "apz/src/AsyncPanZoomController.h"
 #include "gfxPrefs.h"
 #include "LayersLogging.h"
-#include "mozilla/dom/TabChild.h"
-#include "mozilla/layers/APZCTreeManager.h"
-#include "mozilla/layers/AsyncCompositionManager.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/gfx/GPUProcessManager.h"
 #include "mozilla/layers/CompositorBridgeChild.h"
 #include "mozilla/layers/TextureClient.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
-#include "mozilla/widget/PlatformWidgetTypes.h"
-#include "nsThreadUtils.h"
-#include "TreeTraversal.h"
 #include "WebRenderCanvasLayer.h"
 #include "WebRenderColorLayer.h"
 #include "WebRenderContainerLayer.h"
 #include "WebRenderImageLayer.h"
 #include "WebRenderPaintedLayer.h"
 #include "WebRenderTextLayer.h"
 #include "WebRenderDisplayItemLayer.h"
 
 namespace mozilla {
 
 using namespace gfx;
 
 namespace layers {
 
-WebRenderLayerManager*
-WebRenderLayer::WrManager()
-{
-  return static_cast<WebRenderLayerManager*>(GetLayer()->Manager());
-}
-
-WebRenderBridgeChild*
-WebRenderLayer::WrBridge()
-{
-  return WrManager()->WrBridge();
-}
-
-Rect
-WebRenderLayer::RelativeToVisible(Rect aRect)
-{
-  IntRect bounds = GetLayer()->GetVisibleRegion().GetBounds().ToUnknownRect();
-  aRect.MoveBy(-bounds.x, -bounds.y);
-  return aRect;
-}
-
-Rect
-WebRenderLayer::RelativeToTransformedVisible(Rect aRect)
-{
-  IntRect bounds = GetLayer()->GetVisibleRegion().GetBounds().ToUnknownRect();
-  Rect transformed = GetLayer()->GetTransform().TransformBounds(IntRectToRect(bounds));
-  aRect.MoveBy(-transformed.x, -transformed.y);
-  return aRect;
-}
-
-Rect
-WebRenderLayer::ParentStackingContextBounds()
-{
-  // Walk up to find the parent stacking context. This will be created either
-  // by the nearest scrollable metrics, or by the parent layer which must be a
-  // ContainerLayer.
-  Layer* layer = GetLayer();
-  if (layer->GetParent()) {
-    return IntRectToRect(layer->GetParent()->GetVisibleRegion().GetBounds().ToUnknownRect());
-  }
-  return Rect();
-}
-
-Rect
-WebRenderLayer::RelativeToParent(Rect aRect)
-{
-  Rect parentBounds = ParentStackingContextBounds();
-  aRect.MoveBy(-parentBounds.x, -parentBounds.y);
-  return aRect;
-}
-
-Point
-WebRenderLayer::GetOffsetToParent()
-{
-  Rect parentBounds = ParentStackingContextBounds();
-  return parentBounds.TopLeft();
-}
-
-Rect
-WebRenderLayer::VisibleBoundsRelativeToParent()
-{
-  return RelativeToParent(IntRectToRect(GetLayer()->GetVisibleRegion().GetBounds().ToUnknownRect()));
-}
-
-Rect
-WebRenderLayer::TransformedVisibleBoundsRelativeToParent()
-{
-  IntRect bounds = GetLayer()->GetVisibleRegion().GetBounds().ToUnknownRect();
-  Rect transformed = GetLayer()->GetTransform().TransformBounds(IntRectToRect(bounds));
-  return RelativeToParent(transformed);
-}
-
-Maybe<WrImageMask>
-WebRenderLayer::BuildWrMaskLayer(bool aUnapplyLayerTransform)
-{
-  if (GetLayer()->GetMaskLayer()) {
-    WebRenderLayer* maskLayer = ToWebRenderLayer(GetLayer()->GetMaskLayer());
-    // The size of mask layer is transformed, and we may set the layer transform to wr stacking context.
-    // So we should apply inverse transform for mask layer.
-    gfx::Matrix4x4 transform;
-    if (aUnapplyLayerTransform) {
-      transform = GetWrBoundTransform().Inverse();
-    }
-    return maskLayer->RenderMaskLayer(transform);
-  }
-
-  return Nothing();
-}
-
-gfx::Rect
-WebRenderLayer::GetWrBoundsRect()
-{
-  LayerIntRect bounds = GetLayer()->GetVisibleRegion().GetBounds();
-  return Rect(0, 0, bounds.width, bounds.height);
-}
-
-gfx::Rect
-WebRenderLayer::GetWrClipRect(gfx::Rect& aRect)
-{
-  gfx::Rect clip;
-  Layer* layer = GetLayer();
-  Matrix4x4 transform = layer->GetTransform();
-  if (layer->GetClipRect().isSome()) {
-    clip = RelativeToVisible(transform.Inverse().TransformBounds(
-             IntRectToRect(layer->GetClipRect().ref().ToUnknownRect()))
-           );
-  } else {
-    clip = aRect;
-  }
-
-  return clip;
-}
-
-gfx::Matrix4x4
-WebRenderLayer::GetWrBoundTransform()
-{
-  gfx::Matrix4x4 transform = GetLayer()->GetTransform();
-  transform._41 = 0.0f;
-  transform._42 = 0.0f;
-  transform._43 = 0.0f;
-  return transform;
-}
-
-gfx::Rect
-WebRenderLayer::GetWrRelBounds()
-{
-  gfx::Rect bounds = IntRectToRect(GetLayer()->GetVisibleRegion().GetBounds().ToUnknownRect());
-  gfx::Matrix4x4 transform = GetWrBoundTransform();
-  if (!transform.IsIdentity()) {
-    // WR will only apply the 'translate' of the transform, so we need to do the scale/rotation manually.
-    bounds.MoveTo(transform.TransformPoint(bounds.TopLeft()));
-  }
-
-  return RelativeToParent(bounds);
-}
-
-void
-WebRenderLayer::DumpLayerInfo(const char* aLayerType, gfx::Rect& aRect)
-{
-  if (!gfxPrefs::LayersDump()) {
-    return;
-  }
-
-  Matrix4x4 transform = GetLayer()->GetTransform();
-  Rect clip = GetWrClipRect(aRect);
-  Rect relBounds = GetWrRelBounds();
-  Rect overflow(0, 0, relBounds.width, relBounds.height);
-  WrMixBlendMode mixBlendMode = wr::ToWrMixBlendMode(GetLayer()->GetMixBlendMode());
-
-  printf_stderr("%s %p using bounds=%s, overflow=%s, transform=%s, rect=%s, clip=%s, mix-blend-mode=%s\n",
-                aLayerType,
-                GetLayer(),
-                Stringify(relBounds).c_str(),
-                Stringify(overflow).c_str(),
-                Stringify(transform).c_str(),
-                Stringify(aRect).c_str(),
-                Stringify(clip).c_str(),
-                Stringify(mixBlendMode).c_str());
-}
-
 WebRenderLayerManager::WebRenderLayerManager(nsIWidget* aWidget)
   : mWidget(aWidget)
   , mLatestTransactionId(0)
   , mNeedsComposite(false)
+  , mIsFirstPaint(false)
   , mTarget(nullptr)
 {
   MOZ_COUNT_CTOR(WebRenderLayerManager);
 }
 
 KnowsCompositor*
 WebRenderLayerManager::AsKnowsCompositor()
 {
@@ -241,21 +78,26 @@ WebRenderLayerManager::Destroy()
 
   if (mTransactionIdAllocator) {
     // Make sure to notify the refresh driver just in case it's waiting on a
     // pending transaction. Do this at the top of the event loop so we don't
     // cause a paint to occur during compositor shutdown.
     RefPtr<TransactionIdAllocator> allocator = mTransactionIdAllocator;
     uint64_t id = mLatestTransactionId;
 
-    RefPtr<Runnable> task = NS_NewRunnableFunction([allocator, id] () -> void {
+    RefPtr<Runnable> task = NS_NewRunnableFunction(
+      "TransactionIdAllocator::NotifyTransactionCompleted",
+      [allocator, id] () -> void {
       allocator->NotifyTransactionCompleted(id);
     });
     NS_DispatchToMainThread(task.forget());
   }
+
+  // Forget the widget pointer in case we outlive our owning widget.
+  mWidget = nullptr;
 }
 
 WebRenderLayerManager::~WebRenderLayerManager()
 {
   Destroy();
   MOZ_COUNT_DTOR(WebRenderLayerManager);
 }
 
@@ -282,58 +124,114 @@ bool
 WebRenderLayerManager::BeginTransaction()
 {
   return true;
 }
 
 bool
 WebRenderLayerManager::EndEmptyTransaction(EndTransactionFlags aFlags)
 {
-  return false;
+  if (!mRoot) {
+    return false;
+  }
+
+  // We might used painted layer images so don't delete them yet.
+  return EndTransactionInternal(nullptr, nullptr, aFlags);
+}
+
+/*static*/ int32_t
+PopulateScrollData(WebRenderScrollData& aTarget, Layer* aLayer)
+{
+  MOZ_ASSERT(aLayer);
+
+  // We want to allocate a WebRenderLayerScrollData object for this layer,
+  // but don't keep a pointer to it since it might get memmove'd during the
+  // recursion below. Instead keep the index and get the pointer later.
+  size_t index = aTarget.AddNewLayerData();
+
+  int32_t descendants = 0;
+  for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) {
+    descendants += PopulateScrollData(aTarget, child);
+  }
+  aTarget.GetLayerDataMutable(index)->Initialize(aTarget, aLayer, descendants);
+  return descendants + 1;
 }
 
 void
 WebRenderLayerManager::EndTransaction(DrawPaintedLayerCallback aCallback,
                                       void* aCallbackData,
                                       EndTransactionFlags aFlags)
 {
   DiscardImages();
   WrBridge()->RemoveExpiredFontKeys();
+  EndTransactionInternal(aCallback, aCallbackData, aFlags);
+}
 
+bool
+WebRenderLayerManager::EndTransactionInternal(DrawPaintedLayerCallback aCallback,
+                                              void* aCallbackData,
+                                              EndTransactionFlags aFlags)
+{
   mPaintedLayerCallback = aCallback;
   mPaintedLayerCallbackData = aCallbackData;
+  mTransactionIncomplete = false;
 
   if (gfxPrefs::LayersDump()) {
     this->Dump();
   }
 
   // Since we don't do repeat transactions right now, just set the time
   mAnimationReadyTime = TimeStamp::Now();
 
   LayoutDeviceIntSize size = mWidget->GetClientSize();
   if (!WrBridge()->DPBegin(size.ToUnknownSize())) {
-    return;
+    return false;
   }
+  DiscardCompositorAnimations();
+  mRoot->StartPendingAnimations(mAnimationReadyTime);
 
   wr::DisplayListBuilder builder(WrBridge()->GetPipeline());
   WebRenderLayer::ToWebRenderLayer(mRoot)->RenderLayer(builder);
+  WrBridge()->ClearReadLocks();
+
+  // We can't finish this transaction so return. This usually
+  // happens in an empty transaction where we can't repaint a painted layer.
+  // In this case, leave the transaction open and let a full transaction happen.
+  if (mTransactionIncomplete) {
+    DiscardLocalImages();
+    return false;
+  }
+
+  WebRenderScrollData scrollData;
+  if (mWidget->AsyncPanZoomEnabled()) {
+    if (mIsFirstPaint) {
+      scrollData.SetIsFirstPaint();
+      mIsFirstPaint = false;
+    }
+    if (mRoot) {
+      PopulateScrollData(scrollData, mRoot.get());
+    }
+  }
 
   bool sync = mTarget != nullptr;
   mLatestTransactionId = mTransactionIdAllocator->GetTransactionId();
 
-  WrBridge()->DPEnd(builder, size.ToUnknownSize(), sync, mLatestTransactionId);
+  WrBridge()->DPEnd(builder, size.ToUnknownSize(), sync, mLatestTransactionId, scrollData);
 
   MakeSnapshotIfRequired(size);
   mNeedsComposite = false;
 
   ClearDisplayItemLayers();
 
   // this may result in Layers being deleted, which results in
   // PLayer::Send__delete__() and DeallocShmem()
   mKeepAlive.Clear();
+  ClearMutatedLayers();
+
+  return true;
 }
 
 void
 WebRenderLayerManager::MakeSnapshotIfRequired(LayoutDeviceIntSize aSize)
 {
   if (!mTarget || aSize.IsEmpty()) {
     return;
   }
@@ -404,16 +302,72 @@ WebRenderLayerManager::DiscardImages()
 {
   for (auto key : mImageKeys) {
       WrBridge()->SendDeleteImage(key);
   }
   mImageKeys.clear();
 }
 
 void
+WebRenderLayerManager::AddCompositorAnimationsIdForDiscard(uint64_t aId)
+{
+  mDiscardedCompositorAnimationsIds.push_back(aId);
+}
+
+void
+WebRenderLayerManager::DiscardCompositorAnimations()
+{
+  for (auto id : mDiscardedCompositorAnimationsIds) {
+    WrBridge()->AddWebRenderParentCommand(OpRemoveCompositorAnimations(id));
+  }
+  mDiscardedCompositorAnimationsIds.clear();
+}
+
+void
+WebRenderLayerManager::DiscardLocalImages()
+{
+  // Removes images but doesn't tell the parent side about them
+  // This is useful in empty / failed transactions where we created
+  // image keys but didn't tell the parent about them yet.
+  mImageKeys.clear();
+}
+
+void
+WebRenderLayerManager::Mutated(Layer* aLayer)
+{
+  LayerManager::Mutated(aLayer);
+  AddMutatedLayer(aLayer);
+}
+
+void
+WebRenderLayerManager::MutatedSimple(Layer* aLayer)
+{
+  LayerManager::Mutated(aLayer);
+  AddMutatedLayer(aLayer);
+}
+
+void
+WebRenderLayerManager::AddMutatedLayer(Layer* aLayer)
+{
+  mMutatedLayers.AppendElement(aLayer);
+}
+
+void
+WebRenderLayerManager::ClearMutatedLayers()
+{
+  mMutatedLayers.Clear();
+}
+
+bool
+WebRenderLayerManager::IsMutatedLayer(Layer* aLayer)
+{
+  return mMutatedLayers.Contains(aLayer);
+}
+
+void
 WebRenderLayerManager::Hold(Layer* aLayer)
 {
   mKeepAlive.AppendElement(aLayer);
 }
 
 void
 WebRenderLayerManager::SetLayerObserverEpoch(uint64_t aLayerObserverEpoch)
 {
@@ -511,16 +465,35 @@ WebRenderLayerManager::SendInvalidRegion
 }
 
 void
 WebRenderLayerManager::Composite()
 {
   WrBridge()->SendForceComposite();
 }
 
+RefPtr<PipelineIdPromise>
+WebRenderLayerManager::AllocPipelineId()
+{
+  if (XRE_IsParentProcess()) {
+    GPUProcessManager* pm = GPUProcessManager::Get();
+    if (!pm) {
+      return PipelineIdPromise::CreateAndReject(ipc::PromiseRejectReason::HandlerRejected, __func__);
+    }
+    return PipelineIdPromise::CreateAndResolve(wr::AsPipelineId(pm->AllocateLayerTreeId()), __func__);;
+  }
+
+  MOZ_ASSERT(XRE_IsContentProcess());
+  RefPtr<dom::ContentChild> contentChild = dom::ContentChild::GetSingleton();
+  if (!contentChild) {
+    return PipelineIdPromise::CreateAndReject(ipc::PromiseRejectReason::HandlerRejected, __func__);
+  }
+  return contentChild->SendAllocPipelineId();
+}
+
 void
 WebRenderLayerManager::SetRoot(Layer* aLayer)
 {
   mRoot = aLayer;
 }
 
 already_AddRefed<PaintedLayer>
 WebRenderLayerManager::CreatePaintedLayer()
--- a/gfx/layers/wr/WebRenderLayerManager.h
+++ b/gfx/layers/wr/WebRenderLayerManager.h
@@ -2,70 +2,32 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GFX_WEBRENDERLAYERMANAGER_H
 #define GFX_WEBRENDERLAYERMANAGER_H
 
 #include "Layers.h"
-#include "mozilla/layers/CompositorController.h"
+#include "mozilla/ipc/MessageChannel.h"
+#include "mozilla/MozPromise.h"
 #include "mozilla/layers/TransactionIdAllocator.h"
-#include "mozilla/webrender/webrender_ffi.h"
 #include "mozilla/webrender/WebRenderTypes.h"
-#include "mozilla/webrender/WebRenderAPI.h"
 
 class nsIWidget;
 
 namespace mozilla {
 namespace layers {
 
 class CompositorBridgeChild;
 class KnowsCompositor;
 class PCompositorBridgeChild;
 class WebRenderBridgeChild;
-class WebRenderLayerManager;
-class APZCTreeManager;
 
-class WebRenderLayer
-{
-public:
-  virtual Layer* GetLayer() = 0;
-  virtual void RenderLayer(wr::DisplayListBuilder& aBuilder) = 0;
-  virtual Maybe<WrImageMask> RenderMaskLayer(const gfx::Matrix4x4& aTransform)
-  {
-    MOZ_ASSERT(false);
-    return Nothing();
-  }
-
-  virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() { return nullptr; }
-  static inline WebRenderLayer*
-  ToWebRenderLayer(Layer* aLayer)
-  {
-    return static_cast<WebRenderLayer*>(aLayer->ImplData());
-  }
-
-  WebRenderLayerManager* WrManager();
-  WebRenderBridgeChild* WrBridge();
-
-  gfx::Rect RelativeToVisible(gfx::Rect aRect);
-  gfx::Rect RelativeToTransformedVisible(gfx::Rect aRect);
-  gfx::Rect ParentStackingContextBounds();
-  gfx::Rect RelativeToParent(gfx::Rect aRect);
-  gfx::Rect VisibleBoundsRelativeToParent();
-  gfx::Point GetOffsetToParent();
-  gfx::Rect TransformedVisibleBoundsRelativeToParent();
-protected:
-  gfx::Rect GetWrBoundsRect();
-  gfx::Rect GetWrRelBounds();
-  gfx::Rect GetWrClipRect(gfx::Rect& aRect);
-  gfx::Matrix4x4 GetWrBoundTransform();
-  void DumpLayerInfo(const char* aLayerType, gfx::Rect& aRect);
-  Maybe<WrImageMask> BuildWrMaskLayer(bool aUnapplyLayerTransform);
-};
+typedef MozPromise<mozilla::wr::PipelineId, mozilla::ipc::PromiseRejectReason, false> PipelineIdPromise;
 
 class WebRenderLayerManager final : public LayerManager
 {
   typedef nsTArray<RefPtr<Layer> > LayerRefArray;
 
 public:
   explicit WebRenderLayerManager(nsIWidget* aWidget);
   void Initialize(PCompositorBridgeChild* aCBChild, wr::PipelineId aLayersId, TextureFactoryIdentifier* aTextureFactoryIdentifier);
@@ -131,60 +93,89 @@ public:
 
   virtual void Composite() override;
 
   virtual void SetNeedsComposite(bool aNeedsComposite) override
   {
     mNeedsComposite = aNeedsComposite;
   }
   virtual bool NeedsComposite() const override { return mNeedsComposite; }
+  virtual void SetIsFirstPaint() override { mIsFirstPaint = true; }
 
   DrawPaintedLayerCallback GetPaintedLayerCallback() const
   { return mPaintedLayerCallback; }
 
   void* GetPaintedLayerCallbackData() const
   { return mPaintedLayerCallbackData; }
 
   // adds an imagekey to a list of keys that will be discarded on the next
   // transaction or destruction
   void AddImageKeyForDiscard(wr::ImageKey);
   void DiscardImages();
+  void DiscardLocalImages();
+
+  // Before destroying a layer with animations, add its compositorAnimationsId
+  // to a list of ids that will be discarded on the next transaction
+  void AddCompositorAnimationsIdForDiscard(uint64_t aId);
+  void DiscardCompositorAnimations();
 
   WebRenderBridgeChild* WrBridge() const { return mWrChild; }
 
+  virtual void Mutated(Layer* aLayer) override;
+  virtual void MutatedSimple(Layer* aLayer) override;
+
   void Hold(Layer* aLayer);
+  void SetTransactionIncomplete() { mTransactionIncomplete = true; }
+  bool IsMutatedLayer(Layer* aLayer);
+
+  RefPtr<PipelineIdPromise> AllocPipelineId();
 
 private:
   /**
    * Take a snapshot of the parent context, and copy
    * it into mTarget.
    */
   void MakeSnapshotIfRequired(LayoutDeviceIntSize aSize);
 
   void ClearLayer(Layer* aLayer);
 
+  bool EndTransactionInternal(DrawPaintedLayerCallback aCallback,
+                              void* aCallbackData,
+                              EndTransactionFlags aFlags);
+
+
 private:
   nsIWidget* MOZ_NON_OWNING_REF mWidget;
   std::vector<wr::ImageKey> mImageKeys;
+  std::vector<uint64_t> mDiscardedCompositorAnimationsIds;
 
   /* PaintedLayer callbacks; valid at the end of a transaciton,
    * while rendering */
   DrawPaintedLayerCallback mPaintedLayerCallback;
   void *mPaintedLayerCallbackData;
 
   RefPtr<WebRenderBridgeChild> mWrChild;
 
   RefPtr<TransactionIdAllocator> mTransactionIdAllocator;
   uint64_t mLatestTransactionId;
 
   nsTArray<DidCompositeObserver*> mDidCompositeObservers;
 
   LayerRefArray mKeepAlive;
 
+  // Layers that have been mutated. If we have an empty transaction
+  // then a display item layer will no longer be valid
+  // if it was a mutated layers.
+  void AddMutatedLayer(Layer* aLayer);
+  void ClearMutatedLayers();
+  LayerRefArray mMutatedLayers;
+  bool mTransactionIncomplete;
+
   bool mNeedsComposite;
+  bool mIsFirstPaint;
 
  // When we're doing a transaction in order to draw to a non-default
  // target, the layers transaction is only performed in order to send
  // a PLayers:Update.  We save the original non-default target to
  // mTarget, and then perform the transaction. After the transaction ends,
  // we send a message to our remote side to capture the actual pixels
  // being drawn to the default target, and then copy those pixels
  // back to mTarget.
--- a/gfx/layers/wr/WebRenderMessageUtils.h
+++ b/gfx/layers/wr/WebRenderMessageUtils.h
@@ -67,16 +67,32 @@ struct ParamTraits<mozilla::wr::FontKey>
   Read(const Message* aMsg, PickleIterator* aIter, mozilla::wr::FontKey* aResult)
   {
     return ReadParam(aMsg, aIter, &aResult->mNamespace)
         && ReadParam(aMsg, aIter, &aResult->mHandle);
   }
 };
 
 template<>
+struct ParamTraits<mozilla::wr::ExternalImageId>
+{
+  static void
+  Write(Message* aMsg, const mozilla::wr::ExternalImageId& aParam)
+  {
+    WriteParam(aMsg, aParam.mHandle);
+  }
+
+  static bool
+  Read(const Message* aMsg, PickleIterator* aIter, mozilla::wr::ExternalImageId* aResult)
+  {
+    return ReadParam(aMsg, aIter, &aResult->mHandle);
+  }
+};
+
+template<>
 struct ParamTraits<mozilla::wr::PipelineId>
 {
   static void
   Write(Message* aMsg, const mozilla::wr::PipelineId& aParam)
   {
     WriteParam(aMsg, aParam.mNamespace);
     WriteParam(aMsg, aParam.mHandle);
   }
@@ -158,22 +174,26 @@ struct ParamTraits<WrPoint>
 
 template<>
 struct ParamTraits<WrBuiltDisplayListDescriptor>
 {
   static void
   Write(Message* aMsg, const WrBuiltDisplayListDescriptor& aParam)
   {
     WriteParam(aMsg, aParam.display_list_items_size);
+    WriteParam(aMsg, aParam.builder_start_time);
+    WriteParam(aMsg, aParam.builder_finish_time);
   }
 
   static bool
   Read(const Message* aMsg, PickleIterator* aIter, WrBuiltDisplayListDescriptor* aResult)
   {
-    return ReadParam(aMsg, aIter, &aResult->display_list_items_size);
+    return ReadParam(aMsg, aIter, &aResult->display_list_items_size)
+        && ReadParam(aMsg, aIter, &aResult->builder_start_time)
+        && ReadParam(aMsg, aIter, &aResult->builder_finish_time);
   }
 };
 
 template<>
 struct ParamTraits<WrAuxiliaryListsDescriptor>
 {
   static void
   Write(Message* aMsg, const WrAuxiliaryListsDescriptor& aParam)
--- a/gfx/layers/wr/WebRenderPaintedLayer.cpp
+++ b/gfx/layers/wr/WebRenderPaintedLayer.cpp
@@ -2,219 +2,150 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebRenderPaintedLayer.h"
 
 #include "LayersLogging.h"
 #include "mozilla/ArrayUtils.h"
+#include "mozilla/layers/StackingContextHelper.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
-#include "mozilla/layers/TextureClientRecycleAllocator.h"
-#include "mozilla/layers/TextureWrapperImage.h"
+#include "mozilla/layers/UpdateImageHelper.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 #include "gfxPrefs.h"
 #include "gfxUtils.h"
 
 namespace mozilla {
 namespace layers {
 
 using namespace mozilla::gfx;
 
-void
-WebRenderPaintedLayer::PaintThebes(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates)
-{
-  PROFILER_LABEL("WebRenderPaintedLayer", "PaintThebes",
-    js::ProfileEntry::Category::GRAPHICS);
-
-  mContentClient->BeginPaint();
-
-  uint32_t flags = RotatedContentBuffer::PAINT_CAN_DRAW_ROTATED;
-
-  PaintState state =
-    mContentClient->BeginPaintBuffer(this, flags);
-  mValidRegion.Sub(mValidRegion, state.mRegionToInvalidate);
-
-  if (!state.mRegionToDraw.IsEmpty() && !Manager()->GetPaintedLayerCallback()) {
-    return;
-  }
-
-  // The area that became invalid and is visible needs to be repainted
-  // (this could be the whole visible area if our buffer switched
-  // from RGB to RGBA, because we might need to repaint with
-  // subpixel AA)
-  state.mRegionToInvalidate.And(state.mRegionToInvalidate,
-                                GetLocalVisibleRegion().ToUnknownRegion());
-
-  bool didUpdate = false;
-  RotatedContentBuffer::DrawIterator iter;
-  while (DrawTarget* target = mContentClient->BorrowDrawTargetForPainting(state, &iter)) {
-    if (!target || !target->IsValid()) {
-      if (target) {
-        mContentClient->ReturnDrawTargetToBuffer(target);
-      }
-      continue;
-    }
-
-    SetAntialiasingFlags(this, target);
-
-    RefPtr<gfxContext> ctx = gfxContext::CreatePreservingTransformOrNull(target);
-    MOZ_ASSERT(ctx); // already checked the target above
-    Manager()->GetPaintedLayerCallback()(this,
-                                              ctx,
-                                              iter.mDrawRegion,
-                                              iter.mDrawRegion,
-                                              state.mClip,
-                                              state.mRegionToInvalidate,
-                                              Manager()->GetPaintedLayerCallbackData());
-
-    ctx = nullptr;
-    mContentClient->ReturnDrawTargetToBuffer(target);
-    didUpdate = true;
-  }
-
-  mContentClient->EndPaint(aReadbackUpdates);
-
-  if (didUpdate) {
-    Mutated();
-
-    // XXX It will cause reftests failures. See Bug 1340798.
-    //mValidRegion.Or(mValidRegion, state.mRegionToDraw);
-
-    ContentClientRemote* contentClientRemote = static_cast<ContentClientRemote*>(mContentClient.get());
-
-    // Hold(this) ensures this layer is kept alive through the current transaction
-    // The ContentClient assumes this layer is kept alive (e.g., in CreateBuffer),
-    // so deleting this Hold for whatever reason will break things.
-    Manager()->Hold(this);
-
-    contentClientRemote->Updated(state.mRegionToDraw,
-                                 mVisibleRegion.ToUnknownRegion(),
-                                 state.mDidSelfCopy);
-  }
-}
-
-void
-WebRenderPaintedLayer::RenderLayerWithReadback(ReadbackProcessor *aReadback)
-{
-  if (!mContentClient) {
-    mContentClient = ContentClient::CreateContentClient(Manager()->WrBridge());
-    if (!mContentClient) {
-      return;
-    }
-    mContentClient->Connect();
-    MOZ_ASSERT(mContentClient->GetForwarder());
-  }
-
-  nsTArray<ReadbackProcessor::Update> readbackUpdates;
-  nsIntRegion readbackRegion;
-  if (aReadback && UsedForReadback()) {
-    aReadback->GetPaintedLayerUpdates(this, &readbackUpdates);
-  }
-
-  PaintThebes(&readbackUpdates);
-}
-
-void
-WebRenderPaintedLayer::RenderLayer(wr::DisplayListBuilder& aBuilder)
+bool
+WebRenderPaintedLayer::SetupExternalImages()
 {
   // XXX We won't keep using ContentClient for WebRenderPaintedLayer in the future and
   // there is a crash problem for ContentClient on MacOS. So replace ContentClient with
   // ImageClient. See bug 1341001.
-  //RenderLayerWithReadback(nullptr);
 
   if (!mImageContainer) {
     mImageContainer = LayerManager::CreateImageContainer();
   }
 
   if (!mImageClient) {
     mImageClient = ImageClient::CreateImageClient(CompositableType::IMAGE,
                                                   WrBridge(),
                                                   TextureFlags::DEFAULT);
     if (!mImageClient) {
-      return;
+      return false;
     }
     mImageClient->Connect();
   }
 
-  if (!mExternalImageId) {
-    mExternalImageId = WrBridge()->AllocExternalImageIdForCompositable(mImageClient);
-    MOZ_ASSERT(mExternalImageId);
+  if (mExternalImageId.isNothing()) {
+    mExternalImageId = Some(WrBridge()->AllocExternalImageIdForCompositable(mImageClient));
   }
 
+  return true;
+}
+
+bool
+WebRenderPaintedLayer::UpdateImageClient()
+{
+  MOZ_ASSERT(Manager()->GetPaintedLayerCallback());
   LayerIntRegion visibleRegion = GetVisibleRegion();
   LayerIntRect bounds = visibleRegion.GetBounds();
   LayerIntSize size = bounds.Size();
-  if (size.IsEmpty()) {
-      if (gfxPrefs::LayersDump()) {
-        printf_stderr("PaintedLayer %p skipping\n", this->GetLayer());
-      }
-      return;
-  }
+  IntSize imageSize(size.width, size.height);
 
-  IntSize imageSize(size.width, size.height);
-  RefPtr<TextureClient> texture = mImageClient->GetTextureClientRecycler()->CreateOrRecycle(SurfaceFormat::B8G8R8A8,
-                                                                                            imageSize,
-                                                                                            BackendSelector::Content,
-                                                                                            TextureFlags::DEFAULT);
-  if (!texture) {
-    return;
-  }
+  UpdateImageHelper helper(mImageContainer, mImageClient, imageSize);
 
   {
-    TextureClientAutoLock autoLock(texture, OpenMode::OPEN_WRITE_ONLY);
-    if (!autoLock.Succeeded()) {
-      return;
+    RefPtr<DrawTarget> target = helper.GetDrawTarget();
+    if (!target) {
+      return false;
     }
-    RefPtr<DrawTarget> target = texture->BorrowDrawTarget();
-    if (!target) {
-      return;
-    }
+
     target->ClearRect(Rect(0, 0, imageSize.width, imageSize.height));
     target->SetTransform(Matrix().PreTranslate(-bounds.x, -bounds.y));
-    RefPtr<gfxContext> ctx = gfxContext::CreatePreservingTransformOrNull(target);
+    RefPtr<gfxContext> ctx =
+        gfxContext::CreatePreservingTransformOrNull(target);
     MOZ_ASSERT(ctx); // already checked the target above
 
     Manager()->GetPaintedLayerCallback()(this,
                                          ctx,
                                          visibleRegion.ToUnknownRegion(), visibleRegion.ToUnknownRegion(),
                                          DrawRegionClip::DRAW, nsIntRegion(), Manager()->GetPaintedLayerCallbackData());
 
     if (gfxPrefs::WebRenderHighlightPaintedLayers()) {
       target->SetTransform(Matrix());
       target->FillRect(Rect(0, 0, imageSize.width, imageSize.height), ColorPattern(Color(1.0, 0.0, 0.0, 0.5)));
     }
   }
-  RefPtr<TextureWrapperImage> image = new TextureWrapperImage(texture, IntRect(IntPoint(0, 0), imageSize));
-  mImageContainer->SetCurrentImageInTransaction(image);
-  if (!mImageClient->UpdateImage(mImageContainer, /* unused */0)) {
+
+  if (!helper.UpdateImage()) {
+    return false;
+  }
+
+  return true;
+}
+
+void
+WebRenderPaintedLayer::CreateWebRenderDisplayList(wr::DisplayListBuilder& aBuilder)
+{
+  StackingContextHelper sc(aBuilder, this);
+
+  LayerRect rect = Bounds();
+  DumpLayerInfo("PaintedLayer", rect);
+
+  LayerRect clipRect = ClipRect().valueOr(rect);
+  Maybe<WrImageMask> mask = BuildWrMaskLayer(true);
+  WrClipRegion clip = aBuilder.BuildClipRegion(
+      sc.ToRelativeWrRect(clipRect),
+      mask.ptrOr(nullptr));
+
+  WrImageKey key = GetImageKey();
+  WrBridge()->AddWebRenderParentCommand(OpAddExternalImage(mExternalImageId.value(), key));
+  Manager()->AddImageKeyForDiscard(key);
+
+  aBuilder.PushImage(sc.ToRelativeWrRect(rect), clip, wr::ImageRendering::Auto, key);
+}
+
+void
+WebRenderPaintedLayer::RenderLayer(wr::DisplayListBuilder& aBuilder)
+{
+  if (!SetupExternalImages()) {
     return;
   }
 
-  gfx::Matrix4x4 transform = GetTransform();
-  gfx::Rect relBounds = GetWrRelBounds();
-  gfx::Rect rect(0, 0, size.width, size.height);
+  if (GetVisibleRegion().IsEmpty()) {
+    if (gfxPrefs::LayersDump()) {
+      printf_stderr("PaintedLayer %p skipping\n", this->GetLayer());
+    }
+    return;
+  }
 
-  gfx::Rect clipRect = GetWrClipRect(rect);
-  Maybe<WrImageMask> mask = BuildWrMaskLayer(true);
-  WrClipRegion clip = aBuilder.BuildClipRegion(wr::ToWrRect(clipRect), mask.ptrOr(nullptr));
-
-  wr::MixBlendMode mixBlendMode = wr::ToWrMixBlendMode(GetMixBlendMode());
-
-  DumpLayerInfo("PaintedLayer", rect);
+  nsIntRegion regionToPaint;
+  regionToPaint.Sub(mVisibleRegion.ToUnknownRegion(), mValidRegion);
 
-  WrImageKey key;
-  key.mNamespace = WrBridge()->GetNamespace();
-  key.mHandle = WrBridge()->GetNextResourceId();
-  WrBridge()->AddWebRenderParentCommand(OpAddExternalImage(mExternalImageId, key));
-  Manager()->AddImageKeyForDiscard(key);
+  // We have something to paint but can't. This usually happens only in
+  // empty transactions
+  if (!regionToPaint.IsEmpty() && !Manager()->GetPaintedLayerCallback()) {
+    Manager()->SetTransactionIncomplete();
+    return;
+  }
 
-  aBuilder.PushStackingContext(wr::ToWrRect(relBounds),
-                              1.0f,
-                              //GetAnimations(),
-                              transform,
-                              mixBlendMode);
-  aBuilder.PushImage(wr::ToWrRect(rect), clip, wr::ImageRendering::Auto, key);
-  aBuilder.PopStackingContext();
+  if (!regionToPaint.IsEmpty() && Manager()->GetPaintedLayerCallback()) {
+    if (!UpdateImageClient()) {
+      return;
+    }
+  } else {
+    // We have an empty transaction, just reuse the old image we had before.
+    MOZ_ASSERT(mExternalImageId);
+    MOZ_ASSERT(mImageContainer->HasCurrentImage());
+    MOZ_ASSERT(GetInvalidRegion().IsEmpty());
+  }
+
+  CreateWebRenderDisplayList(aBuilder);
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderPaintedLayer.h
+++ b/gfx/layers/wr/WebRenderPaintedLayer.h
@@ -4,62 +4,64 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GFX_WEBRENDERPAINTEDLAYER_H
 #define GFX_WEBRENDERPAINTEDLAYER_H
 
 #include "Layers.h"
 #include "mozilla/layers/ContentClient.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
-#include "WebRenderLayerManager.h"
+#include "mozilla/layers/WebRenderLayer.h"
+#include "mozilla/layers/WebRenderLayerManager.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 
 namespace mozilla {
 namespace layers {
 
 class WebRenderPaintedLayer : public WebRenderLayer,
                               public PaintedLayer {
 public:
   typedef RotatedContentBuffer::PaintState PaintState;
   typedef RotatedContentBuffer::ContentType ContentType;
 
   explicit WebRenderPaintedLayer(WebRenderLayerManager* aLayerManager)
-    : PaintedLayer(aLayerManager, static_cast<WebRenderLayer*>(this), LayerManager::NONE),
-      mExternalImageId(0)
+    : PaintedLayer(aLayerManager, static_cast<WebRenderLayer*>(this), LayerManager::NONE)
   {
     MOZ_COUNT_CTOR(WebRenderPaintedLayer);
   }
 
 protected:
   virtual ~WebRenderPaintedLayer()
   {
     MOZ_COUNT_DTOR(WebRenderPaintedLayer);
-    if (mExternalImageId) {
-      WrBridge()->DeallocExternalImageId(mExternalImageId);
+    if (mExternalImageId.isSome()) {
+      WrBridge()->DeallocExternalImageId(mExternalImageId.ref());
     }
   }
   WebRenderLayerManager* Manager()
   {
     return static_cast<WebRenderLayerManager*>(mManager);
   }
 
-  uint64_t mExternalImageId;
+  wr::MaybeExternalImageId mExternalImageId;
 
 public:
   virtual void InvalidateRegion(const nsIntRegion& aRegion) override
   {
     mInvalidRegion.Add(aRegion);
     mValidRegion.Sub(mValidRegion, mInvalidRegion.GetRegion());
   }
 
   Layer* GetLayer() override { return this; }
   void RenderLayer(wr::DisplayListBuilder& aBuilder) override;
-  void PaintThebes(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates);
-  void RenderLayerWithReadback(ReadbackProcessor *aReadback);
-  RefPtr<ContentClient> mContentClient;
   RefPtr<ImageContainer> mImageContainer;
   RefPtr<ImageClient> mImageClient;
+
+private:
+  bool SetupExternalImages();
+  bool UpdateImageClient();
+  void CreateWebRenderDisplayList(wr::DisplayListBuilder& aBuilder);
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // GFX_WEBRENDERPAINTEDLAYER_H
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderScrollData.cpp
@@ -0,0 +1,150 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/WebRenderScrollData.h"
+#include "mozilla/Unused.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace layers {
+
+WebRenderLayerScrollData::WebRenderLayerScrollData()
+  : mDescendantCount(-1)
+{
+}
+
+WebRenderLayerScrollData::~WebRenderLayerScrollData()
+{
+}
+
+void
+WebRenderLayerScrollData::Initialize(WebRenderScrollData& aOwner,
+                                     Layer* aLayer,
+                                     int32_t aDescendantCount)
+{
+  MOZ_ASSERT(aDescendantCount >= 0); // Ensure value is valid
+  MOZ_ASSERT(mDescendantCount == -1); // Don't allow re-setting an already set value
+  mDescendantCount = aDescendantCount;
+
+  MOZ_ASSERT(aLayer);
+  for (uint32_t i = 0; i < aLayer->GetScrollMetadataCount(); i++) {
+    mScrollIds.AppendElement(aOwner.AddMetadata(aLayer->GetScrollMetadata(i)));
+  }
+
+  mIsScrollInfoLayer = aLayer->AsContainerLayer() && !aLayer->GetFirstChild();
+  mTransform = aLayer->GetTransform();
+  mTransformIsPerspective = aLayer->GetTransformIsPerspective();
+  mEventRegions = aLayer->GetEventRegions();
+  mReferentId = aLayer->AsRefLayer()
+      ? Some(aLayer->AsRefLayer()->GetReferentId())
+      : Nothing();
+  mEventRegionsOverride = aLayer->AsContainerLayer()
+      ? aLayer->AsContainerLayer()->GetEventRegionsOverride()
+      : EventRegionsOverride::NoOverride;
+  mScrollbarDirection = aLayer->GetScrollbarDirection();
+  mScrollbarTargetContainerId = aLayer->GetScrollbarTargetContainerId();
+  mScrollThumbLength = mScrollbarDirection == ScrollDirection::VERTICAL
+      ? aLayer->GetVisibleRegion().GetBounds().height
+      : aLayer->GetVisibleRegion().GetBounds().width;
+  mIsScrollbarContainer = aLayer->IsScrollbarContainer();
+  mFixedPosScrollContainerId = aLayer->GetFixedPositionScrollContainerId();
+}
+
+int32_t
+WebRenderLayerScrollData::GetDescendantCount() const
+{
+  MOZ_ASSERT(mDescendantCount >= 0); // check that it was set
+  return mDescendantCount;
+}
+
+size_t
+WebRenderLayerScrollData::GetScrollMetadataCount() const
+{
+  return mScrollIds.Length();
+}
+
+const ScrollMetadata&
+WebRenderLayerScrollData::GetScrollMetadata(const WebRenderScrollData& aOwner,
+                                            size_t aIndex) const
+{
+  MOZ_ASSERT(aIndex < mScrollIds.Length());
+  return aOwner.GetScrollMetadata(mScrollIds[aIndex]);
+}
+
+WebRenderScrollData::WebRenderScrollData()
+  : mIsFirstPaint(false)
+{
+}
+
+WebRenderScrollData::~WebRenderScrollData()
+{
+}
+
+size_t
+WebRenderScrollData::AddMetadata(const ScrollMetadata& aMetadata)
+{
+  FrameMetrics::ViewID scrollId = aMetadata.GetMetrics().GetScrollId();
+  auto insertResult = mScrollIdMap.insert(std::make_pair(scrollId, 0));
+  if (insertResult.second) {
+    // Insertion took place, therefore it's a scrollId we hadn't seen before
+    insertResult.first->second = mScrollMetadatas.Length();
+    mScrollMetadatas.AppendElement(aMetadata);
+  } // else we didn't insert, because it already existed
+  return insertResult.first->second;
+}
+
+size_t
+WebRenderScrollData::AddNewLayerData()
+{
+  size_t len = mLayerScrollData.Length();
+  Unused << mLayerScrollData.AppendElement();
+  return len;
+}
+
+size_t
+WebRenderScrollData::GetLayerCount() const
+{
+  return mLayerScrollData.Length();
+}
+
+WebRenderLayerScrollData*
+WebRenderScrollData::GetLayerDataMutable(size_t aIndex)
+{
+  if (aIndex >= mLayerScrollData.Length()) {
+    return nullptr;
+  }
+  return &(mLayerScrollData.ElementAt(aIndex));
+}
+
+const WebRenderLayerScrollData*
+WebRenderScrollData::GetLayerData(size_t aIndex) const
+{
+  if (aIndex >= mLayerScrollData.Length()) {
+    return nullptr;
+  }
+  return &(mLayerScrollData.ElementAt(aIndex));
+}
+
+const ScrollMetadata&
+WebRenderScrollData::GetScrollMetadata(size_t aIndex) const
+{
+  MOZ_ASSERT(aIndex < mScrollMetadatas.Length());
+  return mScrollMetadatas[aIndex];
+}
+
+void
+WebRenderScrollData::SetIsFirstPaint()
+{
+  mIsFirstPaint = true;
+}
+
+bool
+WebRenderScrollData::IsFirstPaint() const
+{
+  return mIsFirstPaint;
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderScrollData.h
@@ -0,0 +1,219 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_WEBRENDERSCROLLDATA_H
+#define GFX_WEBRENDERSCROLLDATA_H
+
+#include <map>
+
+#include "chrome/common/ipc_message_utils.h"
+#include "FrameMetrics.h"
+#include "LayersTypes.h"
+#include "mozilla/Maybe.h"
+#include "nsTArrayForwardDeclare.h"
+
+namespace mozilla {
+namespace layers {
+
+class Layer;
+class WebRenderScrollData;
+
+// Data needed by APZ, per layer. One instance of this class is created for
+// each layer in the layer tree and sent over PWebRenderBridge to the APZ code.
+// Each WebRenderLayerScrollData is conceptually associated with an "owning"
+// WebRenderScrollData.
+class WebRenderLayerScrollData
+{
+public:
+  WebRenderLayerScrollData(); // needed for IPC purposes
+  ~WebRenderLayerScrollData();
+
+  // Actually initialize the object. This is not done during the constructor
+  // for optimization purposes (the call site is hard to write efficiently
+  // if we do this in the constructor).
+  void Initialize(WebRenderScrollData& aOwner,
+                  Layer* aLayer,
+                  int32_t aDescendantCount);
+
+  int32_t GetDescendantCount() const;
+  size_t GetScrollMetadataCount() const;
+
+  // Return the ScrollMetadata object that used to be on the original Layer
+  // at the given index. Since we deduplicate the ScrollMetadata objects into
+  // the array in the owning WebRenderScrollData object, we need to be passed
+  // in a reference to that owner as well.
+  const ScrollMetadata& GetScrollMetadata(const WebRenderScrollData& aOwner,
+                                          size_t aIndex) const;
+
+  bool IsScrollInfoLayer() const { return mIsScrollInfoLayer; }
+  gfx::Matrix4x4 GetTransform() const { return mTransform; }
+  bool GetTransformIsPerspective() const { return mTransformIsPerspective; }
+  EventRegions GetEventRegions() const { return mEventRegions; }
+  Maybe<uint64_t> GetReferentId() const { return mReferentId; }
+  EventRegionsOverride GetEventRegionsOverride() const { return mEventRegionsOverride; }
+  ScrollDirection GetScrollbarDirection() const { return mScrollbarDirection; }
+  FrameMetrics::ViewID GetScrollbarTargetContainerId() const { return mScrollbarTargetContainerId; }
+  int32_t GetScrollThumbLength() const { return mScrollThumbLength; }
+  bool IsScrollbarContainer() const { return mIsScrollbarContainer; }
+  FrameMetrics::ViewID GetFixedPositionScrollContainerId() const { return mFixedPosScrollContainerId; }
+
+  friend struct IPC::ParamTraits<WebRenderLayerScrollData>;
+
+private:
+  // The number of descendants this layer has (not including the layer itself).
+  // This is needed to reconstruct the depth-first layer tree traversal
+  // efficiently. Leaf layers should always have 0 descendants.
+  int32_t mDescendantCount;
+
+  // Handles to the ScrollMetadata objects that were on this layer. The values
+  // stored in this array are indices into the owning WebRenderScrollData's
+  // mScrollMetadatas array. This indirection is used to deduplicate the
+  // ScrollMetadata objects, since there is usually heavy duplication of them
+  // within a layer tree.
+  nsTArray<size_t> mScrollIds;
+
+  // Various data that we collect from the Layer in Initialize(), serialize
+  // over IPC, and use on the parent side in APZ.
+
+  bool mIsScrollInfoLayer;
+  gfx::Matrix4x4 mTransform;
+  bool mTransformIsPerspective;
+  EventRegions mEventRegions;
+  Maybe<uint64_t> mReferentId;
+  EventRegionsOverride mEventRegionsOverride;
+  ScrollDirection mScrollbarDirection;
+  FrameMetrics::ViewID mScrollbarTargetContainerId;
+  int32_t mScrollThumbLength;
+  bool mIsScrollbarContainer;
+  FrameMetrics::ViewID mFixedPosScrollContainerId;
+};
+
+// Data needed by APZ, for the whole layer tree. One instance of this class
+// is created for each transaction sent over PWebRenderBridge. It is populated
+// with information from the WebRender layer tree on the client side and the
+// information is used by APZ on the parent side.
+class WebRenderScrollData
+{
+public:
+  WebRenderScrollData();
+  ~WebRenderScrollData();
+
+  // Add the given ScrollMetadata if it doesn't already exist. Return an index
+  // that can be used to look up the metadata later.
+  size_t AddMetadata(const ScrollMetadata& aMetadata);
+  // Add a new empty WebRenderLayerScrollData and return the index that can be
+  // used to look it up via GetLayerData.
+  size_t AddNewLayerData();
+
+  size_t GetLayerCount() const;
+
+  // Return a pointer to the scroll data at the given index. Use with caution,
+  // as the pointer may be invalidated if this WebRenderScrollData is mutated.
+  WebRenderLayerScrollData* GetLayerDataMutable(size_t aIndex);
+  const WebRenderLayerScrollData* GetLayerData(size_t aIndex) const;
+
+  const ScrollMetadata& GetScrollMetadata(size_t aIndex) const;
+
+  void SetIsFirstPaint();
+  bool IsFirstPaint() const;
+
+  friend struct IPC::ParamTraits<WebRenderScrollData>;
+
+private:
+  // Internal data structure used to maintain uniqueness of mScrollMetadatas.
+  // This is not serialized/deserialized over IPC because there's no need for it,
+  // as the parent side doesn't need this at all. Also because we don't have any
+  // IPC-friendly hashtable implementation lying around.
+  // The key into this map is the scrollId of a ScrollMetadata, and the value is
+  // an index into the mScrollMetadatas array.
+  std::map<FrameMetrics::ViewID, size_t> mScrollIdMap;
+
+  // A list of all the unique ScrollMetadata objects from the layer tree. Each
+  // ScrollMetadata in this list must have a unique scroll id.
+  nsTArray<ScrollMetadata> mScrollMetadatas;
+
+  // A list of per-layer scroll data objects, generated via a depth-first,
+  // pre-order, last-to-first traversal of the layer tree (i.e. a recursive
+  // traversal where a node N first pushes itself, followed by its children in
+  // last-to-first order). Each layer's scroll data object knows how many
+  // descendants that layer had, which allows reconstructing the traversal on the
+  // other side.
+  nsTArray<WebRenderLayerScrollData> mLayerScrollData;
+
+  bool mIsFirstPaint;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+namespace IPC {
+
+template<>
+struct ParamTraits<mozilla::layers::WebRenderLayerScrollData>
+{
+  typedef mozilla::layers::WebRenderLayerScrollData paramType;
+
+  static void
+  Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mDescendantCount);
+    WriteParam(aMsg, aParam.mScrollIds);
+    WriteParam(aMsg, aParam.mIsScrollInfoLayer);
+    WriteParam(aMsg, aParam.mTransform);
+    WriteParam(aMsg, aParam.mTransformIsPerspective);
+    WriteParam(aMsg, aParam.mEventRegions);
+    WriteParam(aMsg, aParam.mReferentId);
+    WriteParam(aMsg, aParam.mEventRegionsOverride);
+    WriteParam(aMsg, aParam.mScrollbarDirection);
+    WriteParam(aMsg, aParam.mScrollbarTargetContainerId);
+    WriteParam(aMsg, aParam.mScrollThumbLength);
+    WriteParam(aMsg, aParam.mIsScrollbarContainer);
+    WriteParam(aMsg, aParam.mFixedPosScrollContainerId);
+  }
+
+  static bool
+  Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  {
+    return ReadParam(aMsg, aIter, &aResult->mDescendantCount)
+        && ReadParam(aMsg, aIter, &aResult->mScrollIds)
+        && ReadParam(aMsg, aIter, &aResult->mIsScrollInfoLayer)
+        && ReadParam(aMsg, aIter, &aResult->mTransform)
+        && ReadParam(aMsg, aIter, &aResult->mTransformIsPerspective)
+        && ReadParam(aMsg, aIter, &aResult->mEventRegions)
+        && ReadParam(aMsg, aIter, &aResult->mReferentId)
+        && ReadParam(aMsg, aIter, &aResult->mEventRegionsOverride)
+        && ReadParam(aMsg, aIter, &aResult->mScrollbarDirection)
+        && ReadParam(aMsg, aIter, &aResult->mScrollbarTargetContainerId)
+        && ReadParam(aMsg, aIter, &aResult->mScrollThumbLength)
+        && ReadParam(aMsg, aIter, &aResult->mIsScrollbarContainer)
+        && ReadParam(aMsg, aIter, &aResult->mFixedPosScrollContainerId);
+  }
+};
+
+template<>
+struct ParamTraits<mozilla::layers::WebRenderScrollData>
+{
+  typedef mozilla::layers::WebRenderScrollData paramType;
+
+  static void
+  Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mScrollMetadatas);
+    WriteParam(aMsg, aParam.mLayerScrollData);
+    WriteParam(aMsg, aParam.mIsFirstPaint);
+  }
+
+  static bool
+  Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  {
+    return ReadParam(aMsg, aIter, &aResult->mScrollMetadatas)
+        && ReadParam(aMsg, aIter, &aResult->mLayerScrollData)
+        && ReadParam(aMsg, aIter, &aResult->mIsFirstPaint);
+  }
+};
+
+} // namespace IPC
+
+#endif /* GFX_WEBRENDERSCROLLDATA_H */
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderScrollDataWrapper.h
@@ -0,0 +1,351 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_WEBRENDERSCROLLDATAWRAPPER_H
+#define GFX_WEBRENDERSCROLLDATAWRAPPER_H
+
+#include "FrameMetrics.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/WebRenderBridgeParent.h"
+#include "mozilla/layers/WebRenderScrollData.h"
+
+namespace mozilla {
+namespace layers {
+
+/*
+ * This class is a wrapper to walk through a WebRenderScrollData object, with
+ * an exposed API that is template-compatible to LayerMetricsWrapper. This allows
+ * APZ to walk through both layer trees and WebRender scroll metadata structures
+ * without a lot of code duplication.
+ * (Note that not all functions from LayerMetricsWrapper are implemented here,
+ * only the ones we've needed in APZ code so far.)
+ *
+ * A WebRenderScrollData object is basically a flattened layer tree, with a
+ * number of WebRenderLayerScrollData objects that have a 1:1 correspondence
+ * to layers in a layer tree. Therefore the mLayer pointer in this class can
+ * be considered equivalent to the mLayer pointer in the LayerMetricsWrapper.
+ * There are some extra fields (mData, mLayerIndex, mContainingSubtreeLastIndex)
+ * to move around between these "layers" given the flattened representation.
+ * The mMetadataIndex field in this class corresponds to the mIndex field in
+ * LayerMetricsWrapper, as both classes also need to manage walking through
+ * "virtual" container layers implied by the list of ScrollMetadata objects.
+ *
+ * One important note here is that this class holds a pointer to the "owning"
+ * WebRenderScrollData. The caller must ensure that this class does not outlive
+ * the owning WebRenderScrollData, or this may result in use-after-free errors.
+ * This class being declared a MOZ_STACK_CLASS should help with that.
+ *
+ * Refer to LayerMetricsWrapper.h for actual documentation on the exposed API.
+ */
+class MOZ_STACK_CLASS WebRenderScrollDataWrapper {
+public:
+  // Basic constructor for external callers. Starts the walker at the root of
+  // the tree.
+  explicit WebRenderScrollDataWrapper(const WebRenderScrollData* aData = nullptr)
+    : mData(aData)
+    , mLayerIndex(0)
+    , mContainingSubtreeLastIndex(0)
+    , mLayer(nullptr)
+    , mMetadataIndex(0)
+  {
+    if (!mData) {
+      return;
+    }
+    mLayer = mData->GetLayerData(mLayerIndex);
+    if (!mLayer) {
+      return;
+    }
+
+    // sanity check on the data
+    MOZ_ASSERT(mData->GetLayerCount() == (size_t)(1 + mLayer->GetDescendantCount()));
+    mContainingSubtreeLastIndex = mData->GetLayerCount();
+
+    // See documentation in LayerMetricsWrapper.h about this. mMetadataIndex
+    // in this class is equivalent to mIndex in that class.
+    mMetadataIndex = mLayer->GetScrollMetadataCount();
+    if (mMetadataIndex > 0) {
+      mMetadataIndex--;
+    }
+  }
+
+private:
+  // Internal constructor for walking from one WebRenderLayerScrollData to
+  // another. In this case we need to recompute the mMetadataIndex to be the
+  // "topmost" scroll metadata on the new layer.
+  WebRenderScrollDataWrapper(const WebRenderScrollData* aData,
+                             size_t aLayerIndex,
+                             size_t aContainingSubtreeLastIndex)
+    : mData(aData)
+    , mLayerIndex(aLayerIndex)
+    , mContainingSubtreeLastIndex(aContainingSubtreeLastIndex)
+    , mLayer(nullptr)
+    , mMetadataIndex(0)
+  {
+    MOZ_ASSERT(mData);
+    mLayer = mData->GetLayerData(mLayerIndex);
+    MOZ_ASSERT(mLayer);
+
+    // See documentation in LayerMetricsWrapper.h about this. mMetadataIndex
+    // in this class is equivalent to mIndex in that class.
+    mMetadataIndex = mLayer->GetScrollMetadataCount();
+    if (mMetadataIndex > 0) {
+      mMetadataIndex--;
+    }
+  }
+
+  // Internal constructor for walking from one metadata to another metadata on
+  // the same WebRenderLayerScrollData.
+  WebRenderScrollDataWrapper(const WebRenderScrollData* aData,
+                             size_t aLayerIndex,
+                             size_t aContainingSubtreeLastIndex,
+                             const WebRenderLayerScrollData* aLayer,
+                             uint32_t aMetadataIndex)
+    : mData(aData)
+    , mLayerIndex(aLayerIndex)
+    , mContainingSubtreeLastIndex(aContainingSubtreeLastIndex)
+    , mLayer(aLayer)
+    , mMetadataIndex(aMetadataIndex)
+  {
+    MOZ_ASSERT(mData);
+    MOZ_ASSERT(mLayer);
+    MOZ_ASSERT(mLayer == mData->GetLayerData(mLayerIndex));
+    MOZ_ASSERT(mMetadataIndex == 0 || mMetadataIndex < mLayer->GetScrollMetadataCount());
+  }
+
+public:
+  bool IsValid() const
+  {
+    return mLayer != nullptr;
+  }
+
+  explicit operator bool() const
+  {
+    return IsValid();
+  }
+
+  bool IsScrollInfoLayer() const
+  {
+    MOZ_ASSERT(IsValid());
+
+    return Metrics().IsScrollable()
+        && mLayer->IsScrollInfoLayer();
+  }
+
+  WebRenderScrollDataWrapper GetLastChild() const
+  {
+    MOZ_ASSERT(IsValid());
+
+    if (!AtBottomLayer()) {
+      // If we're still walking around in the virtual container layers created
+      // by the ScrollMetadata array, we just need to update the metadata index
+      // and that's it.
+      return WebRenderScrollDataWrapper(mData, mLayerIndex,
+          mContainingSubtreeLastIndex, mLayer, mMetadataIndex - 1);
+    }
+
+    // Otherwise, we need to walk to a different WebRenderLayerScrollData in
+    // mData.
+
+    // Since mData contains the layer in depth-first, last-to-first order,
+    // the index after mLayerIndex must be mLayerIndex's last child, if it
+    // has any children (indicated by GetDescendantCount() > 0). Furthermore
+    // we compute the first index outside the subtree rooted at this node
+    // (in |subtreeLastIndex|) and pass that in to the child wrapper to use as
+    // its mContainingSubtreeLastIndex.
+    if (mLayer->GetDescendantCount() > 0) {
+      size_t prevSiblingIndex = mLayerIndex + 1 + mLayer->GetDescendantCount();
+      size_t subtreeLastIndex = std::min(mContainingSubtreeLastIndex, prevSiblingIndex);
+      return WebRenderScrollDataWrapper(mData, mLayerIndex + 1, subtreeLastIndex);
+    }
+
+    // We've run out of descendants. But! If the original layer was a RefLayer,
+    // then it connects to another layer tree and we need to traverse that too.
+    // So return a WebRenderScrollDataWrapper for the root of the child layer
+    // tree.
+    if (mLayer->GetReferentId()) {
+      CompositorBridgeParent::LayerTreeState* lts =
+          CompositorBridgeParent::GetIndirectShadowTree(mLayer->GetReferentId().value());
+      if (lts && lts->mWrBridge) {
+        return WebRenderScrollDataWrapper(&(lts->mWrBridge->GetScrollData()));
+      }
+    }
+
+    return WebRenderScrollDataWrapper();
+  }
+
+  WebRenderScrollDataWrapper GetPrevSibling() const
+  {
+    MOZ_ASSERT(IsValid());
+
+    if (!AtTopLayer()) {
+      // The virtual container layers don't have siblings
+      return WebRenderScrollDataWrapper();
+    }
+
+    // Skip past the descendants to get to the previous sibling. However, we
+    // might be at the last sibling already.
+    size_t prevSiblingIndex = mLayerIndex + 1 + mLayer->GetDescendantCount();
+    if (prevSiblingIndex < mContainingSubtreeLastIndex) {
+      return WebRenderScrollDataWrapper(mData, prevSiblingIndex, mContainingSubtreeLastIndex);
+    }
+    return WebRenderScrollDataWrapper();
+  }
+
+  const ScrollMetadata& Metadata() const
+  {
+    MOZ_ASSERT(IsValid());
+
+    if (mMetadataIndex >= mLayer->GetScrollMetadataCount()) {
+      return *ScrollMetadata::sNullMetadata;
+    }
+    return mLayer->GetScrollMetadata(*mData, mMetadataIndex);
+  }
+
+  const FrameMetrics& Metrics() const
+  {
+    return Metadata().GetMetrics();
+  }
+
+  AsyncPanZoomController* GetApzc() const
+  {
+    return nullptr;
+  }
+
+  void SetApzc(AsyncPanZoomController* aApzc) const
+  {
+  }
+
+  const char* Name() const
+  {
+    return "WebRenderScrollDataWrapper";
+  }
+
+  gfx::Matrix4x4 GetTransform() const
+  {
+    MOZ_ASSERT(IsValid());
+
+    if (AtBottomLayer()) {
+      return mLayer->GetTransform();
+    }
+    return gfx::Matrix4x4();
+  }
+
+  CSSTransformMatrix GetTransformTyped() const
+  {
+    return ViewAs<CSSTransformMatrix>(GetTransform());
+  }
+
+  bool TransformIsPerspective() const
+  {
+    MOZ_ASSERT(IsValid());
+
+    if (AtBottomLayer()) {
+      return mLayer->GetTransformIsPerspective();
+    }
+    return false;
+  }
+
+  EventRegions GetEventRegions() const
+  {
+    MOZ_ASSERT(IsValid());
+
+    if (AtBottomLayer()) {
+      return mLayer->GetEventRegions();
+    }
+    return EventRegions();
+  }
+
+  Maybe<uint64_t> GetReferentId() const
+  {
+    MOZ_ASSERT(IsValid());
+
+    if (AtBottomLayer()) {
+      return mLayer->GetReferentId();
+    }
+    return Nothing();
+  }
+
+  Maybe<ParentLayerIntRect> GetClipRect() const
+  {
+    // TODO
+    return Nothing();
+  }
+
+  EventRegionsOverride GetEventRegionsOverride() const
+  {
+    MOZ_ASSERT(IsValid());
+    return mLayer->GetEventRegionsOverride();
+  }
+
+  ScrollDirection GetScrollbarDirection() const
+  {
+    MOZ_ASSERT(IsValid());
+    return mLayer->GetScrollbarDirection();
+  }
+
+  FrameMetrics::ViewID GetScrollbarTargetContainerId() const
+  {
+    MOZ_ASSERT(IsValid());
+    return mLayer->GetScrollbarTargetContainerId();
+  }
+
+  int32_t GetScrollThumbLength() const
+  {
+    MOZ_ASSERT(IsValid());
+    return mLayer->GetScrollThumbLength();
+  }
+
+  bool IsScrollbarContainer() const
+  {
+    MOZ_ASSERT(IsValid());
+    return mLayer->IsScrollbarContainer();
+  }
+
+  FrameMetrics::ViewID GetFixedPositionScrollContainerId() const
+  {
+    MOZ_ASSERT(IsValid());
+    return mLayer->GetFixedPositionScrollContainerId();
+  }
+
+  const void* GetLayer() const
+  {
+    MOZ_ASSERT(IsValid());
+    return mLayer;
+  }
+
+private:
+  bool AtBottomLayer() const
+  {
+    return mMetadataIndex == 0;
+  }
+
+  bool AtTopLayer() const
+  {
+    return mLayer->GetScrollMetadataCount() == 0 || mMetadataIndex == mLayer->GetScrollMetadataCount() - 1;
+  }
+
+private:
+  const WebRenderScrollData* mData;
+  // The index (in mData->mLayerScrollData) of the WebRenderLayerScrollData this
+  // wrapper is pointing to.
+  size_t mLayerIndex;
+  // The upper bound on the set of valid indices inside the subtree rooted at
+  // the parent of this "layer". That is, any layer index |i| in the range
+  // mLayerIndex <= i < mContainingSubtreeLastIndex is guaranteed to point to
+  // a layer that is a descendant of "parent", where "parent" is the parent
+  // layer of the layer at mLayerIndex. This is needed in order to implement
+  // GetPrevSibling() correctly.
+  size_t mContainingSubtreeLastIndex;
+  // The WebRenderLayerScrollData this wrapper is pointing to.
+  const WebRenderLayerScrollData* mLayer;
+  // The index of the scroll metadata within mLayer that this wrapper is
+  // pointing to.
+  uint32_t mMetadataIndex;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_WEBRENDERSCROLLDATAWRAPPER_H */
--- a/gfx/layers/wr/WebRenderTextLayer.cpp
+++ b/gfx/layers/wr/WebRenderTextLayer.cpp
@@ -19,20 +19,22 @@ using namespace mozilla::gfx;
 
 void
 WebRenderTextLayer::RenderLayer(wr::DisplayListBuilder& aBuilder)
 {
     if (mBounds.IsEmpty()) {
         return;
     }
 
-    gfx::Rect rect = RelativeToParent(GetTransform().TransformBounds(IntRectToRect(mBounds)));
+    gfx::Rect rect = GetTransform().TransformBounds(IntRectToRect(mBounds))
+        - ParentBounds().ToUnknownRect().TopLeft();
     gfx::Rect clip;
     if (GetClipRect().isSome()) {
-      clip = RelativeToParent(IntRectToRect(GetClipRect().ref().ToUnknownRect()));
+      clip = IntRectToRect(GetClipRect().ref().ToUnknownRect())
+          - ParentBounds().ToUnknownRect().TopLeft();
     } else {
       clip = rect;
     }
 
     if (gfxPrefs::LayersDump()) {
         printf_stderr("TextLayer %p using rect=%s, clip=%s\n",
                       this->GetLayer(),
                       Stringify(rect).c_str(),
--- a/gfx/layers/wr/WebRenderTextLayer.h
+++ b/gfx/layers/wr/WebRenderTextLayer.h
@@ -4,17 +4,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 #ifndef GFX_WEBRENDERTEXTLAYER_H
 #define GFX_WEBRENDERTEXTLAYER_H
 
 #include "gfxUtils.h"
 #include "Layers.h"
-#include "WebRenderLayerManager.h"
+#include "mozilla/layers/WebRenderLayer.h"
+#include "mozilla/layers/WebRenderLayerManager.h"
 
 namespace mozilla {
 namespace layers {
 
 class WebRenderTextLayer : public WebRenderLayer,
                            public TextLayer {
 public:
     explicit WebRenderTextLayer(WebRenderLayerManager* aLayerManager)
--- a/gfx/layers/wr/WebRenderTextureHost.cpp
+++ b/gfx/layers/wr/WebRenderTextureHost.cpp
@@ -14,35 +14,34 @@
 #ifdef XP_MACOSX
 #include "mozilla/layers/MacIOSurfaceTextureHostOGL.h"
 #include "mozilla/webrender/RenderMacIOSurfaceTextureHostOGL.h"
 #endif
 
 namespace mozilla {
 namespace layers {
 
-uint64_t WebRenderTextureHost::sSerialCounter(0);
-
 WebRenderTextureHost::WebRenderTextureHost(const SurfaceDescriptor& aDesc,
                                            TextureFlags aFlags,
-                                           TextureHost* aTexture)
+                                           TextureHost* aTexture,
+                                           wr::ExternalImageId& aExternalImageId)
   : TextureHost(aFlags)
-  , mExternalImageId(++sSerialCounter)
+  , mExternalImageId(aExternalImageId)
   , mIsWrappingNativeHandle(false)
 {
   MOZ_COUNT_CTOR(WebRenderTextureHost);
   mWrappedTextureHost = aTexture;
 
   CreateRenderTextureHost(aDesc, aTexture);
 }
 
 WebRenderTextureHost::~WebRenderTextureHost()
 {
   MOZ_COUNT_DTOR(WebRenderTextureHost);
-  wr::RenderThread::Get()->UnregisterExternalImage(mExternalImageId);
+  wr::RenderThread::Get()->UnregisterExternalImage(wr::AsUint64(mExternalImageId));
 }
 
 void
 WebRenderTextureHost::CreateRenderTextureHost(const layers::SurfaceDescriptor& aDesc,
                                               TextureHost* aTexture)
 {
   RefPtr<wr::RenderTextureHost> texture;
 
@@ -63,17 +62,17 @@ WebRenderTextureHost::CreateRenderTextur
       mIsWrappingNativeHandle = true;
       break;
     }
 #endif
     default:
       gfxCriticalError() << "No WR implement for texture type:" << aDesc.type();
   }
 
-  wr::RenderThread::Get()->RegisterExternalImage(mExternalImageId, texture);
+  wr::RenderThread::Get()->RegisterExternalImage(wr::AsUint64(mExternalImageId), texture);
 }
 
 bool
 WebRenderTextureHost::Lock()
 {
   MOZ_ASSERT_UNREACHABLE("unexpected to be called");
   return false;
 }
--- a/gfx/layers/wr/WebRenderTextureHost.h
+++ b/gfx/layers/wr/WebRenderTextureHost.h
@@ -2,16 +2,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_GFX_WEBRENDERTEXTUREHOST_H
 #define MOZILLA_GFX_WEBRENDERTEXTUREHOST_H
 
 #include "mozilla/layers/TextureHost.h"
+#include "mozilla/webrender/WebRenderTypes.h"
 
 namespace mozilla {
 namespace layers {
 
 class SurfaceDescriptor;
 
 // This textureHost is specialized for WebRender usage. With WebRender, there is
 // no Compositor during composition. Instead, we use RendererOGL for composition.
@@ -19,17 +20,18 @@ class SurfaceDescriptor;
 // code path in this class. Furthermore, the RendererOGL runs at RenderThead
 // instead of Compositor thread. This class is also creating the corresponding
 // RenderXXXTextureHost used by RendererOGL at RenderThread.
 class WebRenderTextureHost : public TextureHost
 {
 public:
   WebRenderTextureHost(const SurfaceDescriptor& aDesc,
                        TextureFlags aFlags,
-                       TextureHost* aTexture);
+                       TextureHost* aTexture,
+                       wr::ExternalImageId& aExternalImageId);
   virtual ~WebRenderTextureHost();
 
   virtual void DeallocateDeviceData() override {}
 
   virtual void SetTextureSourceProvider(TextureSourceProvider* aProvider) override;
 
   virtual bool Lock() override;
 
@@ -52,29 +54,27 @@ public:
   virtual gfx::IntSize GetSize() const override;
 
 #ifdef MOZ_LAYERS_HAVE_LOG
   virtual const char* Name() override { return "WebRenderTextureHost"; }
 #endif
 
   virtual WebRenderTextureHost* AsWebRenderTextureHost() override { return this; }
 
-  uint64_t GetExternalImageKey() { return mExternalImageId; }
+  wr::ExternalImageId GetExternalImageKey() { return mExternalImageId; }
 
   int32_t GetRGBStride();
 
   bool IsWrappingNativeHandle() { return mIsWrappingNativeHandle; }
 
 protected:
   void CreateRenderTextureHost(const SurfaceDescriptor& aDesc, TextureHost* aTexture);
 
   RefPtr<TextureHost> mWrappedTextureHost;
-  uint64_t mExternalImageId;
+  wr::ExternalImageId mExternalImageId;
 
   bool mIsWrappingNativeHandle;
-
-  static uint64_t sSerialCounter;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // MOZILLA_GFX_WEBRENDERTEXTUREHOST_H
--- a/gfx/tests/gtest/TextureHelper.h
+++ b/gfx/tests/gtest/TextureHelper.h
@@ -142,14 +142,15 @@ CreateTextureHostWithBackend(TextureClie
   }
 
   // client serialization
   SurfaceDescriptor descriptor;
   RefPtr<TextureHost> textureHost;
 
   aClient->ToSurfaceDescriptor(descriptor);
 
+  wr::MaybeExternalImageId id = Nothing();
   return TextureHost::Create(descriptor, nullptr, aLayersBackend,
-                             aClient->GetFlags());
+                             aClient->GetFlags(), id);
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/tests/reftest/reftest.list
+++ b/gfx/tests/reftest/reftest.list
@@ -1,9 +1,9 @@
 # 468496-1 will also detect bugs in video drivers.
 == 468496-1.html 468496-1-ref.html
 fuzzy(175,443) == 611498-1.html 611498-ref.html
 fuzzy-if(Android,8,1000) == 709477-1.html 709477-1-ref.html
 skip-if(!asyncPan) == 1086723.html 1086723-ref.html
 == 853889-1.html 853889-1-ref.html
 skip-if(Android) fuzzy-if(skiaContent,1,587) == 1143303-1.svg pass.svg
-fuzzy(100,30) fails-if(webrender) == 1149923.html 1149923-ref.html # use fuzzy due to few distorted pixels caused by border-radius
+fuzzy(100,30) == 1149923.html 1149923-ref.html # use fuzzy due to few distorted pixels caused by border-radius
 == 1131264-1.svg pass.svg
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -452,16 +452,17 @@ private:
   DECL_GFX_PREF(Once, "gfx.touch.resample.delay-threshold",    TouchResampleVsyncDelayThreshold, int32_t, 20);
   DECL_GFX_PREF(Once, "gfx.touch.resample.max-predict",        TouchResampleMaxPredict, int32_t, 8);
   DECL_GFX_PREF(Once, "gfx.touch.resample.min-delta",          TouchResampleMinDelta, int32_t, 2);
   DECL_GFX_PREF(Once, "gfx.touch.resample.old-touch-threshold",TouchResampleOldTouchThreshold, int32_t, 17);
   DECL_GFX_PREF(Once, "gfx.touch.resample.vsync-adjust",       TouchVsyncSampleAdjust, int32_t, 5);
 
   DECL_GFX_PREF(Live, "gfx.vsync.collect-scroll-transforms",   CollectScrollTransforms, bool, false);
   DECL_GFX_PREF(Once, "gfx.vsync.compositor.unobserve-count",  CompositorUnobserveCount, int32_t, 10);
+  DECL_GFX_PREF(Live, "gfx.webrender.omta.enabled",            WebRenderOMTAEnabled, bool, false);
   DECL_GFX_PREF(Live, "gfx.webrender.profiler.enable",         WebRenderProfilerEnabled, bool, false);
   // Use vsync events generated by hardware
   DECL_GFX_PREF(Once, "gfx.work-around-driver-bugs",           WorkAroundDriverBugs, bool, true);
   DECL_GFX_PREF(Once, "gfx.screen-mirroring.enabled",          ScreenMirroringEnabled, bool, false);
 
   DECL_GFX_PREF(Live, "gl.ignore-dx-interop2-blacklist",       IgnoreDXInterop2Blacklist, bool, false);
   DECL_GFX_PREF(Live, "gl.msaa-level",                         MSAALevel, uint32_t, 2);
 #if defined(XP_MACOSX)
@@ -486,28 +487,29 @@ private:
 
   DECL_GFX_PREF(Once, "layers.acceleration.disabled",          LayersAccelerationDisabledDoNotUseDirectly, bool, false);
   DECL_GFX_PREF(Live, "layers.acceleration.draw-fps",          LayersDrawFPS, bool, false);
   DECL_GFX_PREF(Live, "layers.acceleration.draw-fps.print-histogram",  FPSPrintHistogram, bool, false);
   DECL_GFX_PREF(Live, "layers.acceleration.draw-fps.write-to-file", WriteFPSToFile, bool, false);
   DECL_GFX_PREF(Once, "layers.acceleration.force-enabled",     LayersAccelerationForceEnabledDoNotUseDirectly, bool, false);
   DECL_GFX_PREF(Live, "layers.advanced.background-color",      LayersAllowBackgroundColorLayers, bool, false);
   DECL_OVERRIDE_PREF(Live, "layers.advanced.background-image", LayersAllowBackgroundImage, false);
+  DECL_GFX_PREF(Live, "layers.advanced.basic-layer.enabled",   LayersAdvancedBasicLayerEnabled, bool, false);
   DECL_OVERRIDE_PREF(Live, "layers.advanced.border-layers",    LayersAllowBorderLayers, false);
   DECL_OVERRIDE_PREF(Live, "layers.advanced.boxshadow-inset-layers", LayersAllowInsetBoxShadow, gfxPrefs::OverrideBase_WebRender());
   DECL_OVERRIDE_PREF(Live, "layers.advanced.boxshadow-outer-layers", LayersAllowOuterBoxShadow, gfxPrefs::OverrideBase_WebRender());
   DECL_GFX_PREF(Live, "layers.advanced.bullet-layers",         LayersAllowBulletLayers, bool, false);
   DECL_OVERRIDE_PREF(Live, "layers.advanced.button-foreground-layers", LayersAllowButtonForegroundLayers, gfxPrefs::OverrideBase_WebRender());
   DECL_GFX_PREF(Live, "layers.advanced.canvas-background-color", LayersAllowCanvasBackgroundColorLayers, bool, false);
   DECL_OVERRIDE_PREF(Live, "layers.advanced.caret-layers",     LayersAllowCaretLayers, gfxPrefs::OverrideBase_WebRender());
   DECL_GFX_PREF(Live, "layers.advanced.columnRule-layers",     LayersAllowColumnRuleLayers, bool, false);
   DECL_OVERRIDE_PREF(Live, "layers.advanced.displaybuttonborder-layers", LayersAllowDisplayButtonBorder, gfxPrefs::OverrideBase_WebRender());
   DECL_GFX_PREF(Live, "layers.advanced.image-layers",          LayersAllowImageLayers, bool, false);
   DECL_OVERRIDE_PREF(Live, "layers.advanced.outline-layers",   LayersAllowOutlineLayers, gfxPrefs::OverrideBase_WebRender());
-  DECL_GFX_PREF(Live, "layers.advanced.solid-color",           LayersAllowSolidColorLayers, bool, false);
+  DECL_OVERRIDE_PREF(Live, "layers.advanced.solid-color",      LayersAllowSolidColorLayers, gfxPrefs::OverrideBase_WebRender());
   DECL_GFX_PREF(Live, "layers.advanced.text-layers",           LayersAllowTextLayers, bool, false);
   DECL_GFX_PREF(Once, "layers.amd-switchable-gfx.enabled",     LayersAMDSwitchableGfxEnabled, bool, false);
   DECL_GFX_PREF(Once, "layers.async-pan-zoom.enabled",         AsyncPanZoomEnabledDoNotUseDirectly, bool, true);
   DECL_GFX_PREF(Once, "layers.async-pan-zoom.separate-event-thread", AsyncPanZoomSeparateEventThread, bool, false);
   DECL_GFX_PREF(Live, "layers.bench.enabled",                  LayersBenchEnabled, bool, false);
   DECL_GFX_PREF(Once, "layers.bufferrotation.enabled",         BufferRotationEnabled, bool, true);
   DECL_GFX_PREF(Live, "layers.child-process-shutdown",         ChildProcessShutdown, bool, true);
 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
--- a/gfx/vr/ipc/VRManagerChild.cpp
+++ b/gfx/vr/ipc/VRManagerChild.cpp
@@ -347,17 +347,18 @@ VRManagerChild::RecvParentAsyncMessages(
   }
   return IPC_OK();
 }
 
 PTextureChild*
 VRManagerChild::CreateTexture(const SurfaceDescriptor& aSharedData,
                               LayersBackend aLayersBackend,
                               TextureFlags aFlags,
-                              uint64_t aSerial)
+                              uint64_t aSerial,
+                              wr::MaybeExternalImageId& aExternalImageId)
 {
   return SendPTextureConstructor(aSharedData, aLayersBackend, aFlags, aSerial);
 }
 
 void
 VRManagerChild::CancelWaitForRecycle(uint64_t aTextureId)
 {
   RefPtr<TextureClient> client = mTexturesWaitingRecycled.Get(aTextureId);
--- a/gfx/vr/ipc/VRManagerChild.h
+++ b/gfx/vr/ipc/VRManagerChild.h
@@ -62,17 +62,18 @@ public:
   static bool ReinitForContent(Endpoint<PVRManagerChild>&& aEndpoint);
   static void ShutDown();
 
   static bool IsCreated();
 
   virtual PTextureChild* CreateTexture(const SurfaceDescriptor& aSharedData,
                                        layers::LayersBackend aLayersBackend,
                                        TextureFlags aFlags,
-                                       uint64_t aSerial) override;
+                                       uint64_t aSerial,
+                                       wr::MaybeExternalImageId& aExternalImageId) override;
   virtual void CancelWaitForRecycle(uint64_t aTextureId) override;
 
   PVRLayerChild* CreateVRLayer(uint32_t aDisplayID,
                                const Rect& aLeftEyeRect,
                                const Rect& aRightEyeRect,
                                nsIEventTarget* aTarget);
 
   static void IdentifyTextureHost(const layers::TextureFactoryIdentifier& aIdentifier);
--- a/gfx/vr/ipc/VRManagerParent.cpp
+++ b/gfx/vr/ipc/VRManagerParent.cpp
@@ -42,17 +42,17 @@ VRManagerParent::~VRManagerParent()
 }
 
 PTextureParent*
 VRManagerParent::AllocPTextureParent(const SurfaceDescriptor& aSharedData,
                                      const LayersBackend& aLayersBackend,
                                      const TextureFlags& aFlags,
                                      const uint64_t& aSerial)
 {
-  return layers::TextureHost::CreateIPDLActor(this, aSharedData, aLayersBackend, aFlags, aSerial);
+  return layers::TextureHost::CreateIPDLActor(this, aSharedData, aLayersBackend, aFlags, aSerial, Nothing());
 }
 
 bool
 VRManagerParent::DeallocPTextureParent(PTextureParent* actor)
 {
   return layers::TextureHost::DestroyIPDLActor(actor);
 }
 
--- a/gfx/webrender/Cargo.toml
+++ b/gfx/webrender/Cargo.toml
@@ -1,11 +1,11 @@
 [package]
 name = "webrender"
-version = "0.31.0"
+version = "0.36.0"
 authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
 license = "MPL-2.0"
 repository = "https://github.com/servo/webrender"
 build = "build.rs"
 
 [features]
 default = ["freetype-lib", "webgl"]
 freetype-lib = ["freetype/servo-freetype-sys"]
@@ -14,32 +14,39 @@ webgl = ["offscreen_gl_context", "webren
 
 [dependencies]
 app_units = "0.4"
 bincode = "1.0.0-alpha6"
 bit-set = "0.4"
 byteorder = "1.0"
 euclid = "0.11"
 fnv = "1.0"
-gleam = "0.4.2"
+gleam = "0.4.3"
 lazy_static = "0.2"
 log = "0.3"
 num-traits = "0.1.32"
 offscreen_gl_context = {version = "0.8.0", features = ["serde", "osmesa"], optional = true}
 time = "0.1"
 threadpool = "1.3.2"
 webrender_traits = {path = "../webrender_traits"}
 bitflags = "0.7"
 gamma-lut = "0.1"
 thread_profiler = "0.1.1"
 
 [dev-dependencies]
 angle = {git = "https://github.com/servo/angle", branch = "servo"}
+servo-glutin = "0.10.1"     # for the example apps
 
 [target.'cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))'.dependencies]
 freetype = { version = "0.2", default-features = false }
 
 [target.'cfg(target_os = "windows")'.dependencies]
 dwrote = "0.3"
 
 [target.'cfg(target_os = "macos")'.dependencies]
 core-graphics = "0.7.0"
 core-text = "4.0"
+
+[[example]]
+name = "basic"
+
+[[example]]
+name = "scrolling"
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/doc/CLIPPING.md
@@ -0,0 +1,97 @@
+# Clipping in WebRender
+
+The WebRender display list allows defining clips in two different ways. The
+first is specified directly on each display item and cannot be reused between
+items. The second is specified using the `SpecificDisplayItem::Clip` display item
+and can be reused between items as well as used to define scrollable regions.
+
+## Clips
+
+Clips are defined using the ClipRegion in both cases.
+
+```rust
+pub struct ClipRegion {
+    pub main: LayoutRect,
+    pub complex: ItemRange,
+    pub image_mask: Option<ImageMask>,
+}
+```
+
+`main` defines a rectangular clip, while the other members make that rectangle
+smaller. `complex`, if it is not empty, defines the boundaries of a rounded
+rectangle. While `image_mask` defines the positioning, repetition, and data of
+a masking image.
+
+## Item Clips
+
+Item clips are simply a `ClipRegion` structure defined directly on the
+`DisplayItem`. The important thing to note about these clips is that all the
+coordinate in `ClipRegion` **are in the same coordinate space as the item
+itself**. This different than for clips defined by `SpecificDisplayItem::Clip`.
+
+## Clip Display Items
+
+Clip display items allow items to share clips in order to increase performance
+(shared clips are only rasterized once) and to allow for scrolling regions.
+Display items can be assigned a clip display item using the `clip_id`
+field. An item can be assigned any clip that is defined by its parent stacking
+context or any of the ancestors. The behavior of assigning an id outside of
+this hierarchy is undefined, because that situation does not occur in CSS
+
+The clip display item has a `ClipRegion` as well as several other fields:
+
+```rust
+pub struct ClipDisplayItem {
+    pub id: ClipId,
+    pub parent_id: ClipId,
+}
+```
+
+A `ClipDisplayItem` also gets a clip and bounds from the `BaseDisplayItem`. The
+clip is shared among all items that use this `ClipDisplayItem`. Critically,
+**coordinates in this ClipRegion are defined relative to the bounds of the
+ClipDisplayItem itself**. Additionally, WebRender only supports clip rectangles
+that start at the origin of the `BaseDisplayItem` bounds.
+
+The `BaseDisplayItem` bounds are known as the *content rectangle* of the clip. If
+the content rectangle is larger than *main* clipping rectangle, the clip will
+be a scrolling clip and participate in scrolling event capture and
+transformation.
+
+`ClipDisplayItems` are positioned, like all other items, relatively to their
+containing stacking context, yet they also live in a parallel tree defined by
+their `parent_id`. Child clips also include any clipping and scrolling that
+their ancestors do. In this way, a clip is positioned by a stacking context,
+but that position may be adjusted by any scroll offset of its parent clips.
+
+## Clip ids
+
+All clips defined by a `ClipDisplayItem` have an id. It is useful to associate
+an external id with WebRender id in order to allow for tracking and updating
+scroll positions using the WebRender API. In order to make this as cheap as
+possible and to avoid having to create a `HashMap` to map between the two types
+of ids, the WebRender API provides an optional id argument in
+`DisplayListBuilder::define_clip`. The only types of ids that are supported
+here are those created with `ClipId::new(...)`. If this argument is not
+provided `define_clip` will return a uniquely generated id. Thus, the following
+should always be true:
+
+```rust
+let id = ClipId::new(my_internal_id, pipeline_id);
+let generated_id = define_clip(content_rect, clip, id);
+assert!(id == generated_id);
+```
+
+Note that calling `define_clip` multiple times with the same `clip_id` value
+results in undefined behaviour, and should be avoided. There is a debug mode
+assertion to catch this.
+
+## Pending changes
+1. Normalize the way that clipping coordinates are defined. Having them
+   specified in two different ways makes for a confusing API. This should be
+   fixed.  ([github issue](https://github.com/servo/webrender/issues/1090))
+
+1. It should be possible to specify more than one predefined clip for an item.
+   This is necessary for items that live in a scrolling frame, but are also
+   clipped by a clip that lives outside that frame.
+   ([github issue](https://github.com/servo/webrender/issues/840))
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/examples/basic.rs
@@ -0,0 +1,524 @@
+/* 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/. */
+
+extern crate app_units;
+extern crate euclid;
+extern crate gleam;
+extern crate glutin;
+extern crate webrender;
+extern crate webrender_traits;
+
+use app_units::Au;
+use gleam::gl;
+use glutin::TouchPhase;
+use std::collections::HashMap;
+use std::env;
+use std::fs::File;
+use std::io::Read;
+use std::path::PathBuf;
+use std::sync::Arc;
+use webrender_traits::{BlobImageData, BlobImageDescriptor, BlobImageError, BlobImageRenderer};
+use webrender_traits::{BlobImageResult, ClipRegion, ColorF, Epoch, GlyphInstance};
+use webrender_traits::{DeviceIntPoint, DeviceUintSize, DeviceUintRect, LayoutPoint, LayoutRect, LayoutSize};
+use webrender_traits::{ImageData, ImageDescriptor, ImageFormat, ImageKey, ImageRendering};
+use webrender_traits::{PipelineId, RasterizedBlobImage, TransformStyle, BoxShadowClipMode};
+
+#[derive(Debug)]
+enum Gesture {
+    None,
+    Pan,
+    Zoom,
+}
+
+#[derive(Debug)]
+struct Touch {
+    id: u64,
+    start_x: f32,
+    start_y: f32,
+    current_x: f32,
+    current_y: f32,
+}
+
+fn dist(x0: f32, y0: f32, x1: f32, y1: f32) -> f32 {
+    let dx = x0 - x1;
+    let dy = y0 - y1;
+    ((dx * dx) + (dy * dy)).sqrt()
+}
+
+impl Touch {
+    fn distance_from_start(&self) -> f32 {
+        dist(self.start_x, self.start_y, self.current_x, self.current_y)
+    }
+
+    fn initial_distance_from_other(&self, other: &Touch) -> f32 {
+        dist(self.start_x, self.start_y, other.start_x, other.start_y)
+    }
+
+    fn current_distance_from_other(&self, other: &Touch) -> f32 {
+        dist(self.current_x, self.current_y, other.current_x, other.current_y)
+    }
+}
+
+struct TouchState {
+    active_touches: HashMap<u64, Touch>,
+    current_gesture: Gesture,
+    start_zoom: f32,
+    current_zoom: f32,
+    start_pan: DeviceIntPoint,
+    current_pan: DeviceIntPoint,
+}
+
+enum TouchResult {
+    None,
+    Pan(DeviceIntPoint),
+    Zoom(f32),
+}
+
+impl TouchState {
+    fn new() -> TouchState {
+        TouchState {
+            active_touches: HashMap::new(),
+            current_gesture: Gesture::None,
+            start_zoom: 1.0,
+            current_zoom: 1.0,
+            start_pan: DeviceIntPoint::zero(),
+            current_pan: DeviceIntPoint::zero(),
+        }
+    }
+
+    fn handle_event(&mut self, touch: glutin::Touch) -> TouchResult {
+        match touch.phase {
+            TouchPhase::Started => {
+                debug_assert!(!self.active_touches.contains_key(&touch.id));
+                self.active_touches.insert(touch.id, Touch {
+                    id: touch.id,
+                    start_x: touch.location.0 as f32,
+                    start_y: touch.location.1 as f32,
+                    current_x: touch.location.0 as f32,
+                    current_y: touch.location.1 as f32,
+                });
+                self.current_gesture = Gesture::None;
+            }
+            TouchPhase::Moved => {
+                match self.active_touches.get_mut(&touch.id) {
+                    Some(active_touch) => {
+                        active_touch.current_x = touch.location.0 as f32;
+                        active_touch.current_y = touch.location.1 as f32;
+                    }
+                    None => panic!("move touch event with unknown touch id!")
+                }
+
+                match self.current_gesture {
+                    Gesture::None => {
+                        let mut over_threshold_count = 0;
+                        let active_touch_count = self.active_touches.len();
+
+                        for (_, touch) in &self.active_touches {
+                            if touch.distance_from_start() > 8.0 {
+                                over_threshold_count += 1;
+                            }
+                        }
+
+                        if active_touch_count == over_threshold_count {
+                            if active_touch_count == 1 {
+                                self.start_pan = self.current_pan;
+                                self.current_gesture = Gesture::Pan;
+                            } else if active_touch_count == 2 {
+                                self.start_zoom = self.current_zoom;
+                                self.current_gesture = Gesture::Zoom;
+                            }
+                        }
+                    }
+                    Gesture::Pan => {
+                        let keys: Vec<u64> = self.active_touches.keys().cloned().collect();
+                        debug_assert!(keys.len() == 1);
+                        let active_touch = &self.active_touches[&keys[0]];
+                        let x = active_touch.current_x - active_touch.start_x;
+                        let y = active_touch.current_y - active_touch.start_y;
+                        self.current_pan.x = self.start_pan.x + x.round() as i32;
+                        self.current_pan.y = self.start_pan.y + y.round() as i32;
+                        return TouchResult::Pan(self.current_pan);
+                    }
+                    Gesture::Zoom => {
+                        let keys: Vec<u64> = self.active_touches.keys().cloned().collect();
+                        debug_assert!(keys.len() == 2);
+                        let touch0 = &self.active_touches[&keys[0]];
+                        let touch1 = &self.active_touches[&keys[1]];
+                        let initial_distance = touch0.initial_distance_from_other(touch1);
+                        let current_distance = touch0.current_distance_from_other(touch1);
+                        self.current_zoom = self.start_zoom * current_distance / initial_distance;
+                        return TouchResult::Zoom(self.current_zoom);
+                    }
+                }
+            }
+            TouchPhase::Ended | TouchPhase::Cancelled => {
+                self.active_touches.remove(&touch.id).unwrap();
+                self.current_gesture = Gesture::None;
+            }
+        }
+
+        TouchResult::None
+    }
+}
+
+fn load_file(name: &str) -> Vec<u8> {
+    let mut file = File::open(name).unwrap();
+    let mut buffer = vec![];
+    file.read_to_end(&mut buffer).unwrap();
+    buffer
+}
+
+struct Notifier {
+    window_proxy: glutin::WindowProxy,
+}
+
+impl Notifier {
+    fn new(window_proxy: glutin::WindowProxy) -> Notifier {
+        Notifier {
+            window_proxy: window_proxy,
+        }
+    }
+}
+
+impl webrender_traits::RenderNotifier for Notifier {
+    fn new_frame_ready(&mut self) {
+        #[cfg(not(target_os = "android"))]
+        self.window_proxy.wakeup_event_loop();
+    }
+
+    fn new_scroll_frame_ready(&mut self, _composite_needed: bool) {
+        #[cfg(not(target_os = "android"))]
+        self.window_proxy.wakeup_event_loop();
+    }
+}
+
+fn main() {
+    let args: Vec<String> = env::args().collect();
+    let res_path = if args.len() > 1 {
+        Some(PathBuf::from(&args[1]))
+    } else {
+        None
+    };
+
+    let window = glutin::WindowBuilder::new()
+                .with_title("WebRender Sample")
+                .with_multitouch()
+                .with_gl(glutin::GlRequest::GlThenGles {
+                    opengl_version: (3, 2),
+                    opengles_version: (3, 0)
+                })
+                .build()
+                .unwrap();
+
+    unsafe {
+        window.make_current().ok();
+    }
+
+    let gl = match gl::GlType::default() {
+        gl::GlType::Gl => unsafe { gl::GlFns::load_with(|symbol| window.get_proc_address(symbol) as *const _) },
+        gl::GlType::Gles => unsafe { gl::GlesFns::load_with(|symbol| window.get_proc_address(symbol) as *const _) },
+    };
+
+    println!("OpenGL version {}", gl.get_string(gl::VERSION));
+    println!("Shader resource path: {:?}", res_path);
+
+    let (width, height) = window.get_inner_size_pixels().unwrap();
+
+    let opts = webrender::RendererOptions {
+        resource_override_path: res_path,
+        debug: true,
+        precache_shaders: true,
+        blob_image_renderer: Some(Box::new(FakeBlobImageRenderer::new())),
+        device_pixel_ratio: window.hidpi_factor(),
+        .. Default::default()
+    };
+
+    let size = DeviceUintSize::new(width, height);
+    let (mut renderer, sender) = webrender::renderer::Renderer::new(gl, opts, size).unwrap();
+    let api = sender.create_api();
+
+    let notifier = Box::new(Notifier::new(window.create_window_proxy()));
+    renderer.set_render_notifier(notifier);
+
+    let epoch = Epoch(0);
+    let root_background_color = ColorF::new(0.3, 0.0, 0.0, 1.0);
+
+    let vector_img = api.generate_image_key();
+    api.add_image(
+        vector_img,
+        ImageDescriptor::new(100, 100, ImageFormat::RGBA8, true),
+        ImageData::new_blob_image(Vec::new()),
+        None,
+    );
+
+    let pipeline_id = PipelineId(0, 0);
+    let mut builder = webrender_traits::DisplayListBuilder::new(pipeline_id);
+
+    let bounds = LayoutRect::new(LayoutPoint::zero(), LayoutSize::new(width as f32, height as f32));
+    builder.push_stacking_context(webrender_traits::ScrollPolicy::Scrollable,
+                                  bounds,
+                                  None,
+                                  TransformStyle::Flat,
+                                  None,
+                                  webrender_traits::MixBlendMode::Normal,
+                                  Vec::new());
+    builder.push_image(
+        LayoutRect::new(LayoutPoint::new(0.0, 0.0), LayoutSize::new(100.0, 100.0)),
+        ClipRegion::simple(&bounds),
+        LayoutSize::new(100.0, 100.0),
+        LayoutSize::new(0.0, 0.0),
+        ImageRendering::Auto,
+        vector_img,
+    );
+
+    let sub_clip = {
+        let mask_image = api.generate_image_key();
+        api.add_image(
+            mask_image,
+            ImageDescriptor::new(2, 2, ImageFormat::A8, true),
+            ImageData::new(vec![0, 80, 180, 255]),
+            None,
+        );
+        let mask = webrender_traits::ImageMask {
+            image: mask_image,
+            rect: LayoutRect::new(LayoutPoint::new(75.0, 75.0), LayoutSize::new(100.0, 100.0)),
+            repeat: false,
+        };
+        let complex = webrender_traits::ComplexClipRegion::new(
+            LayoutRect::new(LayoutPoint::new(50.0, 50.0), LayoutSize::new(100.0, 100.0)),
+            webrender_traits::BorderRadius::uniform(20.0));
+
+        builder.new_clip_region(&bounds, vec![complex], Some(mask))
+    };
+
+    builder.push_rect(LayoutRect::new(LayoutPoint::new(100.0, 100.0), LayoutSize::new(100.0, 100.0)),
+                      sub_clip,
+                      ColorF::new(0.0, 1.0, 0.0, 1.0));
+    builder.push_rect(LayoutRect::new(LayoutPoint::new(250.0, 100.0), LayoutSize::new(100.0, 100.0)),
+                      sub_clip,
+                      ColorF::new(0.0, 1.0, 0.0, 1.0));
+    let border_side = webrender_traits::BorderSide {
+        color: ColorF::new(0.0, 0.0, 1.0, 1.0),
+        style: webrender_traits::BorderStyle::Groove,
+    };
+    let border_widths = webrender_traits::BorderWidths {
+        top: 10.0,
+        left: 10.0,
+        bottom: 10.0,
+        right: 10.0,
+    };
+    let border_details = webrender_traits::BorderDetails::Normal(webrender_traits::NormalBorder {
+        top: border_side,
+        right: border_side,
+        bottom: border_side,
+        left: border_side,
+        radius: webrender_traits::BorderRadius::uniform(20.0),
+    });
+    builder.push_border(LayoutRect::new(LayoutPoint::new(100.0, 100.0), LayoutSize::new(100.0, 100.0)),
+                        sub_clip,
+                        border_widths,
+                        border_details);
+
+
+    if false { // draw text?
+        let font_key = api.generate_font_key();
+        let font_bytes = load_file("res/FreeSans.ttf");
+        api.add_raw_font(font_key, font_bytes, 0);
+
+        let text_bounds = LayoutRect::new(LayoutPoint::new(100.0, 200.0), LayoutSize::new(700.0, 300.0));
+
+        let glyphs = vec![
+            GlyphInstance {
+                index: 48,
+                point: LayoutPoint::new(100.0, 100.0),
+            },
+            GlyphInstance {
+                index: 68,
+                point: LayoutPoint::new(150.0, 100.0),
+            },
+            GlyphInstance {
+                index: 80,
+                point: LayoutPoint::new(200.0, 100.0),
+            },
+            GlyphInstance {
+                index: 82,
+                point: LayoutPoint::new(250.0, 100.0),
+            },
+            GlyphInstance {
+                index: 81,
+                point: LayoutPoint::new(300.0, 100.0),
+            },
+            GlyphInstance {
+                index: 3,
+                point: LayoutPoint::new(350.0, 100.0),
+            },
+            GlyphInstance {
+                index: 86,
+                point: LayoutPoint::new(400.0, 100.0),
+            },
+            GlyphInstance {
+                index: 79,
+                point: LayoutPoint::new(450.0, 100.0),
+            },
+            GlyphInstance {
+                index: 72,
+                point: LayoutPoint::new(500.0, 100.0),
+            },
+            GlyphInstance {
+                index: 83,
+                point: LayoutPoint::new(550.0, 100.0),
+            },
+            GlyphInstance {
+                index: 87,
+                point: LayoutPoint::new(600.0, 100.0),
+            },
+            GlyphInstance {
+                index: 17,
+                point: LayoutPoint::new(650.0, 100.0),
+            },
+        ];
+
+        builder.push_text(text_bounds,
+                          webrender_traits::ClipRegion::simple(&bounds),
+                          &glyphs,
+                          font_key,
+                          ColorF::new(1.0, 1.0, 0.0, 1.0),
+                          Au::from_px(32),
+                          Au::from_px(0),
+                          None);
+    }
+
+    if false { // draw box shadow?
+        let rect = LayoutRect::new(LayoutPoint::new(0.0, 0.0), LayoutSize::new(0.0, 0.0));
+        let simple_box_bounds = LayoutRect::new(LayoutPoint::new(20.0, 200.0),
+                                                LayoutSize::new(50.0, 50.0));
+        let offset = LayoutPoint::new(10.0, 10.0);
+        let color = ColorF::new(1.0, 1.0, 1.0, 1.0);
+        let blur_radius = 0.0;
+        let spread_radius = 0.0;
+        let simple_border_radius = 8.0;
+        let box_shadow_type = BoxShadowClipMode::Inset;
+        let full_screen_clip = builder.new_clip_region(&bounds, Vec::new(), None);
+
+        builder.push_box_shadow(rect,
+                                full_screen_clip,
+                                simple_box_bounds,
+                                offset,
+                                color,
+                                blur_radius,
+                                spread_radius,
+                                simple_border_radius,
+                                box_shadow_type);
+    }
+
+    builder.pop_stacking_context();
+
+    api.set_display_list(
+        Some(root_background_color),
+        epoch,
+        LayoutSize::new(width as f32, height as f32),
+        builder.finalize(),
+        true);
+    api.set_root_pipeline(pipeline_id);
+    api.generate_frame(None);
+
+    let mut touch_state = TouchState::new();
+
+    'outer: for event in window.wait_events() {
+        let mut events = Vec::new();
+        events.push(event);
+
+        for event in window.poll_events() {
+            events.push(event);
+        }
+
+        for event in events {
+            match event {
+                glutin::Event::Closed |
+                glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Escape)) |
+                glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Q)) => break 'outer,
+                glutin::Event::KeyboardInput(glutin::ElementState::Pressed,
+                                             _, Some(glutin::VirtualKeyCode::P)) => {
+                    let enable_profiler = !renderer.get_profiler_enabled();
+                    renderer.set_profiler_enabled(enable_profiler);
+                    api.generate_frame(None);
+                }
+                glutin::Event::Touch(touch) => {
+                    match touch_state.handle_event(touch) {
+                        TouchResult::Pan(pan) => {
+                            api.set_pan(pan);
+                            api.generate_frame(None);
+                        }
+                        TouchResult::Zoom(zoom) => {
+                            api.set_pinch_zoom(webrender_traits::ZoomFactor::new(zoom));
+                            api.generate_frame(None);
+                        }
+                        TouchResult::None => {}
+                    }
+                }
+                _ => ()
+            }
+        }
+
+        renderer.update();
+        renderer.render(DeviceUintSize::new(width, height));
+        window.swap_buffers().ok();
+    }
+}
+
+struct FakeBlobImageRenderer {
+    images: HashMap<ImageKey, BlobImageResult>,
+}
+
+impl FakeBlobImageRenderer {
+    fn new() -> Self {
+        FakeBlobImageRenderer { images: HashMap::new() }
+    }
+}
+
+impl BlobImageRenderer for FakeBlobImageRenderer {
+    fn request_blob_image(&mut self,
+                          key: ImageKey,
+                          _: Arc<BlobImageData>,
+                          descriptor: &BlobImageDescriptor,
+                          _dirty_rect: Option<DeviceUintRect>) {
+        let mut texels = Vec::with_capacity((descriptor.width * descriptor.height * 4) as usize);
+        for y in 0..descriptor.height {
+            for x in 0..descriptor.width {
+                // render a simple checkerboard pattern
+                let a = if (x % 20 >= 10) != (y % 20 >= 10) { 255 } else { 0 };
+                match descriptor.format {
+                    ImageFormat::RGBA8 => {
+                        texels.push(a);
+                        texels.push(a);
+                        texels.push(a);
+                        texels.push(255);
+                    }
+                    ImageFormat::A8 => {
+                        texels.push(a);
+                    }
+                    _ => {
+                        self.images.insert(key,
+                            Err(BlobImageError::Other(format!(
+                                "Usupported image format {:?}",
+                                descriptor.format
+                            )))
+                        );
+                        return;
+                    }
+                }
+            }
+        }
+
+        self.images.insert(key, Ok(RasterizedBlobImage {
+            data: texels,
+            width: descriptor.width,
+            height: descriptor.height,
+        }));
+    }
+
+    fn resolve_blob_image(&mut self, key: ImageKey) -> BlobImageResult {
+        self.images.remove(&key).unwrap_or(Err(BlobImageError::InvalidKey))
+    }
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/examples/scrolling.rs
@@ -0,0 +1,239 @@
+/* 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/. */
+
+extern crate gleam;
+extern crate glutin;
+extern crate webrender;
+extern crate webrender_traits;
+
+use gleam::gl;
+use std::env;
+use std::path::PathBuf;
+use webrender_traits::{ClipId, ClipRegion, ColorF, DeviceUintSize, Epoch, LayoutPoint, LayoutRect};
+use webrender_traits::{LayoutSize, PipelineId, ScrollEventPhase, ScrollLocation, TransformStyle};
+use webrender_traits::WorldPoint;
+
+struct Notifier {
+    window_proxy: glutin::WindowProxy,
+}
+
+impl Notifier {
+    fn new(window_proxy: glutin::WindowProxy) -> Notifier {
+        Notifier {
+            window_proxy: window_proxy,
+        }
+    }
+}
+
+impl webrender_traits::RenderNotifier for Notifier {
+    fn new_frame_ready(&mut self) {
+        #[cfg(not(target_os = "android"))]
+        self.window_proxy.wakeup_event_loop();
+    }
+
+    fn new_scroll_frame_ready(&mut self, _composite_needed: bool) {
+        #[cfg(not(target_os = "android"))]
+        self.window_proxy.wakeup_event_loop();
+    }
+}
+
+trait HandyDandyRectBuilder {
+    fn to(&self, x2: i32, y2: i32) -> LayoutRect;
+}
+// Allows doing `(x, y).to(x2, y2)` to build a LayoutRect
+impl HandyDandyRectBuilder for (i32, i32) {
+    fn to(&self, x2: i32, y2: i32) -> LayoutRect {
+        LayoutRect::new(LayoutPoint::new(self.0 as f32, self.1 as f32),
+                        LayoutSize::new((x2 - self.0) as f32, (y2 - self.1) as f32))
+    }
+}
+
+
+fn main() {
+    let args: Vec<String> = env::args().collect();
+    let res_path = if args.len() > 1 {
+        Some(PathBuf::from(&args[1]))
+    } else {
+        None
+    };
+
+    let window = glutin::WindowBuilder::new()
+                .with_title("WebRender Scrolling Sample")
+                .with_gl(glutin::GlRequest::GlThenGles {
+                    opengl_version: (3, 2),
+                    opengles_version: (3, 0)
+                })
+                .build()
+                .unwrap();
+
+    unsafe {
+        window.make_current().ok();
+    }
+
+    let gl = match gl::GlType::default() {
+        gl::GlType::Gl => unsafe { gl::GlFns::load_with(|symbol| window.get_proc_address(symbol) as *const _) },
+        gl::GlType::Gles => unsafe { gl::GlesFns::load_with(|symbol| window.get_proc_address(symbol) as *const _) },
+    };
+
+    println!("OpenGL version {}", gl.get_string(gl::VERSION));
+    println!("Shader resource path: {:?}", res_path);
+
+    let (width, height) = window.get_inner_size_pixels().unwrap();
+
+    let opts = webrender::RendererOptions {
+        resource_override_path: res_path,
+        debug: true,
+        precache_shaders: true,
+        device_pixel_ratio: window.hidpi_factor(),
+        .. Default::default()
+    };
+
+    let size = DeviceUintSize::new(width, height);
+    let (mut renderer, sender) = webrender::renderer::Renderer::new(gl, opts, size).unwrap();
+    let api = sender.create_api();
+
+    let notifier = Box::new(Notifier::new(window.create_window_proxy()));
+    renderer.set_render_notifier(notifier);
+
+    let epoch = Epoch(0);
+    let root_background_color = ColorF::new(0.3, 0.0, 0.0, 1.0);
+
+    let pipeline_id = PipelineId(0, 0);
+    let mut builder = webrender_traits::DisplayListBuilder::new(pipeline_id);
+
+    let bounds = LayoutRect::new(LayoutPoint::zero(), LayoutSize::new(width as f32, height as f32));
+    builder.push_stacking_context(webrender_traits::ScrollPolicy::Scrollable,
+                                  bounds,
+                                  None,
+                                  TransformStyle::Flat,
+                                  None,
+                                  webrender_traits::MixBlendMode::Normal,
+                                  Vec::new());
+
+    if true {   // scrolling and clips stuff
+        // let's make a scrollbox
+        let scrollbox = (0, 0).to(300, 400);
+        builder.push_stacking_context(webrender_traits::ScrollPolicy::Scrollable,
+                                      LayoutRect::new(LayoutPoint::new(10.0, 10.0),
+                                                      LayoutSize::zero()),
+                                      None,
+                                      TransformStyle::Flat,
+                                      None,
+                                      webrender_traits::MixBlendMode::Normal,
+                                      Vec::new());
+        // set the scrolling clip
+        let clip_id = builder.define_clip((0, 0).to(1000, 1000),
+                                          ClipRegion::simple(&scrollbox),
+                                          Some(ClipId::new(42, pipeline_id)));
+        builder.push_clip_id(clip_id);
+        // now put some content into it.
+        // start with a white background
+        builder.push_rect((0, 0).to(500, 500),
+                          ClipRegion::simple(&(0, 0).to(1000, 1000)),
+                          ColorF::new(1.0, 1.0, 1.0, 1.0));
+        // let's make a 50x50 blue square as a visual reference
+        builder.push_rect((0, 0).to(50, 50),
+                          ClipRegion::simple(&(0, 0).to(50, 50)),
+                          ColorF::new(0.0, 0.0, 1.0, 1.0));
+        // and a 50x50 green square next to it with an offset clip
+        // to see what that looks like
+        builder.push_rect((50, 0).to(100, 50),
+                          ClipRegion::simple(&(60, 10).to(110, 60)),
+                          ColorF::new(0.0, 1.0, 0.0, 1.0));
+
+        // Below the above rectangles, set up a nested scrollbox. It's still in
+        // the same stacking context, so note that the rects passed in need to
+        // be relative to the stacking context.
+        let nested_clip_id = builder.define_clip((0, 100).to(300, 400),
+                                                 ClipRegion::simple(&(0, 100).to(200, 300)),
+                                                 Some(ClipId::new(43, pipeline_id)));
+        builder.push_clip_id(nested_clip_id);
+        // give it a giant gray background just to distinguish it and to easily
+        // visually identify the nested scrollbox
+        builder.push_rect((-1000, -1000).to(5000, 5000),
+                          ClipRegion::simple(&(-1000, -1000).to(5000, 5000)),
+                          ColorF::new(0.5, 0.5, 0.5, 1.0));
+        // add a teal square to visualize the scrolling/clipping behaviour
+        // as you scroll the nested scrollbox with WASD keys
+        builder.push_rect((0, 100).to(50, 150),
+                          ClipRegion::simple(&(0, 100).to(50, 150)),
+                          ColorF::new(0.0, 1.0, 1.0, 1.0));
+        // just for good measure add another teal square in the bottom-right
+        // corner of the nested scrollframe content, which can be scrolled into
+        // view by the user
+        builder.push_rect((250, 350).to(300, 400),
+                          ClipRegion::simple(&(250, 350).to(300, 400)),
+                          ColorF::new(0.0, 1.0, 1.0, 1.0));
+        builder.pop_clip_id(); // nested_clip_id
+
+        builder.pop_clip_id(); // clip_id
+        builder.pop_stacking_context();
+    }
+
+    builder.pop_stacking_context();
+
+    api.set_display_list(
+        Some(root_background_color),
+        epoch,
+        LayoutSize::new(width as f32, height as f32),
+        builder.finalize(),
+        true);
+    api.set_root_pipeline(pipeline_id);
+    api.generate_frame(None);
+
+    let mut cursor_position = WorldPoint::zero();
+
+    'outer: for event in window.wait_events() {
+        let mut events = Vec::new();
+        events.push(event);
+
+        for event in window.poll_events() {
+            events.push(event);
+        }
+
+        for event in events {
+            match event {
+                glutin::Event::Closed |
+                glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Escape)) |
+                glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Q)) => break 'outer,
+                glutin::Event::KeyboardInput(glutin::ElementState::Pressed, _, Some(key)) => {
+                    let offset = match key {
+                         glutin::VirtualKeyCode::Down => (0.0, -10.0),
+                         glutin::VirtualKeyCode::Up => (0.0, 10.0),
+                         glutin::VirtualKeyCode::Right => (-10.0, 0.0),
+                         glutin::VirtualKeyCode::Left => (10.0, 0.0),
+                         _ => continue,
+                    };
+
+                    api.scroll(ScrollLocation::Delta(LayoutPoint::new(offset.0, offset.1)),
+                               cursor_position,
+                               ScrollEventPhase::Start);
+                }
+                glutin::Event::MouseMoved(x, y) => {
+                    cursor_position = WorldPoint::new(x as f32, y as f32);
+                }
+                glutin::Event::MouseWheel(delta, _, event_cursor_position) => {
+                    if let Some((x, y)) = event_cursor_position {
+                        cursor_position = WorldPoint::new(x as f32, y as f32);
+                    }
+
+                    const LINE_HEIGHT: f32 = 38.0;
+                    let (dx, dy) = match delta {
+                        glutin::MouseScrollDelta::LineDelta(dx, dy) => (dx, dy * LINE_HEIGHT),
+                        glutin::MouseScrollDelta::PixelDelta(dx, dy) => (dx, dy),
+                    };
+
+                    api.scroll(ScrollLocation::Delta(LayoutPoint::new(dx, dy)),
+                               cursor_position,
+                               ScrollEventPhase::Start);
+                }
+                _ => ()
+            }
+        }
+
+        renderer.update();
+        renderer.render(DeviceUintSize::new(width, height));
+        window.swap_buffers().ok();
+    }
+}
--- a/gfx/webrender/res/clip_shared.glsl
+++ b/gfx/webrender/res/clip_shared.glsl
@@ -29,28 +29,31 @@ CacheClipInstance fetch_clip_item(int in
     cci.render_task_index = aClipRenderTaskIndex;
     cci.layer_index = aClipLayerIndex;
     cci.data_index = aClipDataIndex;
     cci.segment_index = aClipSegmentIndex;
 
     return cci;
 }
 
+struct ClipVertexInfo {
+    vec3 local_pos;
+    vec2 screen_pos;
+    RectWithSize clipped_local_rect;
+};
+
 // The transformed vertex function that always covers the whole clip area,
 // which is the intersection of all clip instances of a given primitive
-TransformVertexInfo write_clip_tile_vertex(vec4 local_clip_rect,
-                                           Layer layer,
-                                           ClipArea area,
-                                           int segment_index) {
-    vec2 lp0_base = local_clip_rect.xy;
-    vec2 lp1_base = local_clip_rect.xy + local_clip_rect.zw;
+ClipVertexInfo write_clip_tile_vertex(RectWithSize local_clip_rect,
+                                      Layer layer,
+                                      ClipArea area,
+                                      int segment_index) {
 
-    vec2 lp0 = clamp_rect(lp0_base, layer.local_clip_rect);
-    vec2 lp1 = clamp_rect(lp1_base, layer.local_clip_rect);
-    vec4 clipped_local_rect = vec4(lp0, lp1 - lp0);
+    RectWithSize clipped_local_rect = intersect_rect(local_clip_rect,
+                                                     layer.local_clip_rect);
 
     vec2 outer_p0 = area.screen_origin_target_index.xy;
     vec2 outer_p1 = outer_p0 + area.task_bounds.zw - area.task_bounds.xy;
     vec2 inner_p0 = area.inner_rect.xy;
     vec2 inner_p1 = area.inner_rect.zw;
 
     vec2 p0, p1;
     switch (segment_index) {
@@ -80,12 +83,12 @@ TransformVertexInfo write_clip_tile_vert
 
     vec4 layer_pos = get_layer_pos(actual_pos / uDevicePixelRatio, layer);
 
     // compute the point position in side the layer, in CSS space
     vec2 vertex_pos = actual_pos + area.task_bounds.xy - area.screen_origin_target_index.xy;
 
     gl_Position = uTransform * vec4(vertex_pos, 0.0, 1);
 
-    return TransformVertexInfo(layer_pos.xyw, actual_pos, clipped_local_rect);
+    return ClipVertexInfo(layer_pos.xyw, actual_pos, clipped_local_rect);
 }
 
 #endif //WR_VERTEX_SHADER
--- a/gfx/webrender/res/cs_clip_image.fs.glsl
+++ b/gfx/webrender/res/cs_clip_image.fs.glsl
@@ -6,12 +6,12 @@ void main(void) {
     float alpha = 1.f;
     vec2 local_pos = init_transform_fs(vPos, vLocalRect, alpha);
 
     bool repeat_mask = false; //TODO
     vec2 clamped_mask_uv = repeat_mask ? fract(vClipMaskUv.xy) :
         clamp(vClipMaskUv.xy, vec2(0.0, 0.0), vec2(1.0, 1.0));
     vec2 source_uv = clamp(clamped_mask_uv * vClipMaskUvRect.zw + vClipMaskUvRect.xy,
         vClipMaskUvInnerRect.xy, vClipMaskUvInnerRect.zw);
-    float clip_alpha = texture(sMask, source_uv).r; //careful: texture has type A8
+    float clip_alpha = texture(sColor0, source_uv).r; //careful: texture has type A8
 
     oFragColor = vec4(min(alpha, clip_alpha), 1.0, 1.0, 1.0);
 }
--- a/gfx/webrender/res/cs_clip_image.glsl
+++ b/gfx/webrender/res/cs_clip_image.glsl
@@ -1,10 +1,10 @@
 #line 1
 
 /* 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/. */
 
 varying vec3 vPos;
-flat varying vec4 vLocalRect;
+flat varying RectWithSize vLocalRect;
 flat varying vec4 vClipMaskUvRect;
 flat varying vec4 vClipMaskUvInnerRect;
--- a/gfx/webrender/res/cs_clip_image.vs.glsl
+++ b/gfx/webrender/res/cs_clip_image.vs.glsl
@@ -1,42 +1,38 @@
 #line 1
 /* 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/. */
 
 struct ImageMaskData {
-    vec4 uv_rect;
-    vec4 local_rect;
+    RectWithSize uv_rect;
+    RectWithSize local_rect;
 };
 
 ImageMaskData fetch_mask_data(int index) {
-    ImageMaskData info;
-
-    ivec2 uv = get_fetch_uv_2(index);
-
-    info.uv_rect = texelFetchOffset(sData32, uv, 0, ivec2(0, 0));
-    info.local_rect = texelFetchOffset(sData32, uv, 0, ivec2(1, 0));
-
-    return info;
+    vec4 data[2] = fetch_data_2(index);
+    return ImageMaskData(RectWithSize(data[0].xy, data[0].zw),
+                         RectWithSize(data[1].xy, data[1].zw));
 }
 
 void main(void) {
     CacheClipInstance cci = fetch_clip_item(gl_InstanceID);
     ClipArea area = fetch_clip_area(cci.render_task_index);
     Layer layer = fetch_layer(cci.layer_index);
     ImageMaskData mask = fetch_mask_data(cci.data_index);
-    vec4 local_rect = mask.local_rect;
+    RectWithSize local_rect = mask.local_rect;
 
-    TransformVertexInfo vi = write_clip_tile_vertex(local_rect,
-                                                    layer,
-                                                    area,
-                                                    cci.segment_index);
+    ClipVertexInfo vi = write_clip_tile_vertex(local_rect,
+                                               layer,
+                                               area,
+                                               cci.segment_index);
+
     vLocalRect = vi.clipped_local_rect;
     vPos = vi.local_pos;
 
-    vClipMaskUv = vec3((vPos.xy / vPos.z - local_rect.xy) / local_rect.zw, 0.0);
-    vec2 texture_size = vec2(textureSize(sMask, 0));
-    vClipMaskUvRect = mask.uv_rect / texture_size.xyxy;
+    vClipMaskUv = vec3((vPos.xy / vPos.z - local_rect.p0) / local_rect.size, 0.0);
+    vec2 texture_size = vec2(textureSize(sColor0, 0));
+    vClipMaskUvRect = vec4(mask.uv_rect.p0, mask.uv_rect.size) / texture_size.xyxy;
     // applying a half-texel offset to the UV boundaries to prevent linear samples from the outside
-    vec4 inner_rect = vec4(mask.uv_rect.xy, mask.uv_rect.xy + mask.uv_rect.zw);
+    vec4 inner_rect = vec4(mask.uv_rect.p0, mask.uv_rect.p0 + mask.uv_rect.size);
     vClipMaskUvInnerRect = (inner_rect + vec4(0.5, 0.5, -0.5, -0.5)) / texture_size.xyxy;
 }
--- a/gfx/webrender/res/cs_clip_rectangle.glsl
+++ b/gfx/webrender/res/cs_clip_rectangle.glsl
@@ -1,11 +1,11 @@
 #line 1
 
 /* 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/. */
 
 varying vec3 vPos;
-flat varying vec4 vLocalRect;
+flat varying RectWithSize vLocalRect;
 flat varying vec4 vClipRect;
 flat varying vec4 vClipRadius;
 flat varying float vClipMode;
--- a/gfx/webrender/res/cs_clip_rectangle.vs.glsl
+++ b/gfx/webrender/res/cs_clip_rectangle.vs.glsl
@@ -1,43 +1,31 @@
 #line 1
 /* 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/. */
 
 struct ClipRect {
-    vec4 rect;
+    RectWithSize rect;
     vec4 mode;
 };
 
 ClipRect fetch_clip_rect(int index) {
-    ClipRect rect;
-
-    ivec2 uv = get_fetch_uv_2(index);
-
-    rect.rect = texelFetchOffset(sData32, uv, 0, ivec2(0, 0));
-    rect.mode = texelFetchOffset(sData32, uv, 0, ivec2(1, 0));
-
-    return rect;
+    vec4 data[2] = fetch_data_2(index);
+    return ClipRect(RectWithSize(data[0].xy, data[0].zw), data[1]);
 }
 
 struct ClipCorner {
-    vec4 rect;
+    RectWithSize rect;
     vec4 outer_inner_radius;
 };
 
 ClipCorner fetch_clip_corner(int index) {
-    ClipCorner corner;
-
-    ivec2 uv = get_fetch_uv_2(index);
-
-    corner.rect = texelFetchOffset(sData32, uv, 0, ivec2(0, 0));
-    corner.outer_inner_radius = texelFetchOffset(sData32, uv, 0, ivec2(1, 0));
-
-    return corner;
+    vec4 data[2] = fetch_data_2(index);
+    return ClipCorner(RectWithSize(data[0].xy, data[0].zw), data[1]);
 }
 
 struct ClipData {
     ClipRect rect;
     ClipCorner top_left;
     ClipCorner top_right;
     ClipCorner bottom_left;
     ClipCorner bottom_right;
@@ -55,24 +43,24 @@ ClipData fetch_clip(int index) {
     return clip;
 }
 
 void main(void) {
     CacheClipInstance cci = fetch_clip_item(gl_InstanceID);
     ClipArea area = fetch_clip_area(cci.render_task_index);
     Layer layer = fetch_layer(cci.layer_index);
     ClipData clip = fetch_clip(cci.data_index);
-    vec4 local_rect = clip.rect.rect;
+    RectWithSize local_rect = clip.rect.rect;
 
-    TransformVertexInfo vi = write_clip_tile_vertex(local_rect,
-                                                    layer,
-                                                    area,
-                                                    cci.segment_index);
+    ClipVertexInfo vi = write_clip_tile_vertex(local_rect,
+                                               layer,
+                                               area,
+                                               cci.segment_index);
     vLocalRect = vi.clipped_local_rect;
     vPos = vi.local_pos;
 
     vClipMode = clip.rect.mode.x;
-    vClipRect = vec4(local_rect.xy, local_rect.xy + local_rect.zw);
+    vClipRect = vec4(local_rect.p0, local_rect.p0 + local_rect.size);
     vClipRadius = vec4(clip.top_left.outer_inner_radius.x,
                        clip.top_right.outer_inner_radius.x,
                        clip.bottom_right.outer_inner_radius.x,
                        clip.bottom_left.outer_inner_radius.x);
 }
--- a/gfx/webrender/res/prim_shared.glsl
+++ b/gfx/webrender/res/prim_shared.glsl
@@ -22,76 +22,37 @@
 #define PST_BOTTOM_LEFT  6
 #define PST_LEFT         7
 
 #define BORDER_LEFT      0
 #define BORDER_TOP       1
 #define BORDER_RIGHT     2
 #define BORDER_BOTTOM    3
 
+// Border styles as defined in webrender_traits/types.rs
+#define BORDER_STYLE_NONE         0
+#define BORDER_STYLE_SOLID        1
+#define BORDER_STYLE_DOUBLE       2
+#define BORDER_STYLE_DOTTED       3
+#define BORDER_STYLE_DASHED       4
+#define BORDER_STYLE_HIDDEN       5
+#define BORDER_STYLE_GROOVE       6
+#define BORDER_STYLE_RIDGE        7
+#define BORDER_STYLE_INSET        8
+#define BORDER_STYLE_OUTSET       9
+
 #define UV_NORMALIZED    uint(0)
 #define UV_PIXEL         uint(1)
 
 #define EXTEND_MODE_CLAMP  0
 #define EXTEND_MODE_REPEAT 1
 
 uniform sampler2DArray sCacheA8;
 uniform sampler2DArray sCacheRGBA8;
 
-flat varying vec4 vClipMaskUvBounds;
-varying vec3 vClipMaskUv;
-
-#ifdef WR_VERTEX_SHADER
-
-#define VECS_PER_LAYER             13
-#define VECS_PER_RENDER_TASK        3
-#define VECS_PER_PRIM_GEOM          2
-
-uniform sampler2D sLayers;
-uniform sampler2D sRenderTasks;
-uniform sampler2D sPrimGeometry;
-
-uniform sampler2D sData16;
-uniform sampler2D sData32;
-uniform sampler2D sData64;
-uniform sampler2D sData128;
-uniform sampler2D sResourceRects;
-
-// Instanced attributes
-in int aGlobalPrimId;
-in int aPrimitiveAddress;
-in int aTaskIndex;
-in int aClipTaskIndex;
-in int aLayerIndex;
-in int aElementIndex;
-in ivec2 aUserData;
-in int aZIndex;
-
-// 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
-#define get_fetch_uv(i, vpi)  ivec2(vpi * (i % (WR_MAX_VERTEX_TEXTURE_WIDTH/vpi)), i / (WR_MAX_VERTEX_TEXTURE_WIDTH/vpi))
-
-ivec2 get_fetch_uv_1(int index) {
-    return get_fetch_uv(index, 1);
-}
-
-ivec2 get_fetch_uv_2(int index) {
-    return get_fetch_uv(index, 2);
-}
-
-ivec2 get_fetch_uv_4(int index) {
-    return get_fetch_uv(index, 4);
-}
-
-ivec2 get_fetch_uv_8(int index) {
-    return get_fetch_uv(index, 8);
-}
-
 struct RectWithSize {
     vec2 p0;
     vec2 size;
 };
 
 struct RectWithEndpoint {
     vec2 p0;
     vec2 p1;
@@ -125,16 +86,101 @@ vec2 clamp_rect(vec2 point, RectWithEndp
 vec4 clamp_rect(vec4 points, RectWithSize rect) {
     return clamp(points, rect.p0.xyxy, rect.p0.xyxy + rect.size.xyxy);
 }
 
 vec4 clamp_rect(vec4 points, RectWithEndpoint rect) {
     return clamp(points, rect.p0.xyxy, rect.p1.xyxy);
 }
 
+RectWithSize intersect_rect(RectWithSize a, RectWithSize b) {
+    vec4 p = clamp_rect(vec4(a.p0, a.p0 + a.size), b);
+    return RectWithSize(p.xy, max(vec2(0.0), p.zw - p.xy));
+}
+
+RectWithEndpoint intersect_rect(RectWithEndpoint a, RectWithEndpoint b) {
+    vec4 p = clamp_rect(vec4(a.p0, a.p1), b);
+    return RectWithEndpoint(p.xy, max(p.xy, p.zw));
+}
+
+
+// TODO: convert back to RectWithEndPoint if driver issues are resolved, if ever.
+flat varying vec4 vClipMaskUvBounds;
+varying vec3 vClipMaskUv;
+
+#ifdef WR_VERTEX_SHADER
+
+#define VECS_PER_LAYER             13
+#define VECS_PER_RENDER_TASK        3
+#define VECS_PER_PRIM_GEOM          2
+
+uniform sampler2D sLayers;
+uniform sampler2D sRenderTasks;
+uniform sampler2D sPrimGeometry;
+
+uniform sampler2D sData16;
+uniform sampler2D sData32;
+uniform sampler2D sData64;
+uniform sampler2D sData128;
+uniform sampler2D sResourceRects;
+
+// Instanced attributes
+in int aGlobalPrimId;
+in int aPrimitiveAddress;
+in int aTaskIndex;
+in int aClipTaskIndex;
+in int aLayerIndex;
+in int aElementIndex;
+in ivec2 aUserData;
+in int aZIndex;
+
+// 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
+#define get_fetch_uv(i, vpi)  ivec2(vpi * (i % (WR_MAX_VERTEX_TEXTURE_WIDTH/vpi)), i / (WR_MAX_VERTEX_TEXTURE_WIDTH/vpi))
+
+vec4 fetch_data_1(int index) {
+    ivec2 uv = get_fetch_uv(index, 1);
+    return texelFetch(sData16, uv, 0);
+}
+
+vec4[2] fetch_data_2(int index) {
+    ivec2 uv = get_fetch_uv(index, 2);
+    return vec4[2](
+        texelFetchOffset(sData32, uv, 0, ivec2(0, 0)),
+        texelFetchOffset(sData32, uv, 0, ivec2(1, 0))
+    );
+}
+
+vec4[4] fetch_data_4(int index) {
+    ivec2 uv = get_fetch_uv(index, 4);
+    return vec4[4](
+        texelFetchOffset(sData64, uv, 0, ivec2(0, 0)),
+        texelFetchOffset(sData64, uv, 0, ivec2(1, 0)),
+        texelFetchOffset(sData64, uv, 0, ivec2(2, 0)),
+        texelFetchOffset(sData64, uv, 0, ivec2(3, 0))
+    );
+}
+
+vec4[8] fetch_data_8(int index) {
+    ivec2 uv = get_fetch_uv(index, 8);
+    return vec4[8](
+        texelFetchOffset(sData128, uv, 0, ivec2(0, 0)),
+        texelFetchOffset(sData128, uv, 0, ivec2(1, 0)),
+        texelFetchOffset(sData128, uv, 0, ivec2(2, 0)),
+        texelFetchOffset(sData128, uv, 0, ivec2(3, 0)),
+        texelFetchOffset(sData128, uv, 0, ivec2(4, 0)),
+        texelFetchOffset(sData128, uv, 0, ivec2(5, 0)),
+        texelFetchOffset(sData128, uv, 0, ivec2(6, 0)),
+        texelFetchOffset(sData128, uv, 0, ivec2(7, 0))
+    );
+}
+
+
 struct Layer {
     mat4 transform;
     mat4 inv_transform;
     RectWithSize local_clip_rect;
     vec4 screen_vertices[4];
 };
 
 Layer fetch_layer(int index) {
@@ -231,81 +277,132 @@ ClipArea fetch_clip_area(int index) {
 
 struct Gradient {
     vec4 start_end_point;
     vec4 tile_size_repeat;
     vec4 extend_mode;
 };
 
 Gradient fetch_gradient(int index) {
-    Gradient gradient;
-
-    ivec2 uv = get_fetch_uv_4(index);
-
-    gradient.start_end_point = texelFetchOffset(sData64, uv, 0, ivec2(0, 0));
-    gradient.tile_size_repeat = texelFetchOffset(sData64, uv, 0, ivec2(1, 0));
-    gradient.extend_mode = texelFetchOffset(sData64, uv, 0, ivec2(2, 0));
-
-    return gradient;
+    vec4 data[4] = fetch_data_4(index);
+    return Gradient(data[0], data[1], data[2]);
 }
 
 struct GradientStop {
     vec4 color;
     vec4 offset;
 };
 
 GradientStop fetch_gradient_stop(int index) {
-    GradientStop stop;
-
-    ivec2 uv = get_fetch_uv_2(index);
-
-    stop.color = texelFetchOffset(sData32, uv, 0, ivec2(0, 0));
-    stop.offset = texelFetchOffset(sData32, uv, 0, ivec2(1, 0));
-
-    return stop;
+    vec4 data[2] = fetch_data_2(index);
+    return GradientStop(data[0], data[1]);
 }
 
 struct RadialGradient {
     vec4 start_end_center;
     vec4 start_end_radius_ratio_xy_extend_mode;
     vec4 tile_size_repeat;
 };
 
 RadialGradient fetch_radial_gradient(int index) {
-    RadialGradient gradient;
+    vec4 data[4] = fetch_data_4(index);
+    return RadialGradient(data[0], data[1], data[2]);
+}
+
+struct Border {
+    vec4 style;
+    vec4 widths;
+    vec4 colors[4];
+    vec4 radii[2];
+};
+
+vec4 get_effective_border_widths(Border border) {
+    switch (int(border.style.x)) {
+        case BORDER_STYLE_DOUBLE:
+            // Calculate the width of a border segment in a style: double
+            // border. Round to the nearest CSS pixel.
 
-    ivec2 uv = get_fetch_uv_4(index);
+            // The CSS spec doesn't define what width each of the segments
+            // in a style: double border should be. It only says that the
+            // sum of the segments should be equal to the total border
+            // width. We pick to make the segments (almost) equal thirds
+            // for now - we can adjust this if we find other browsers pick
+            // different values in some cases.
+            // SEE: https://drafts.csswg.org/css-backgrounds-3/#double
+            return floor(0.5 + border.widths / 3.0);
+        case BORDER_STYLE_GROOVE:
+        case BORDER_STYLE_RIDGE:
+            return floor(0.5 + border.widths * 0.5);
+        default:
+            return border.widths;
+    }
+}
+
+Border fetch_border(int index) {
+    vec4 data[8] = fetch_data_8(index);
+    return Border(data[0], data[1],
+                  vec4[4](data[2], data[3], data[4], data[5]),
+                  vec4[2](data[6], data[7]));
+}
 
-    gradient.start_end_center = texelFetchOffset(sData64, uv, 0, ivec2(0, 0));
-    gradient.start_end_radius_ratio_xy_extend_mode = texelFetchOffset(sData64, uv, 0, ivec2(1, 0));
-    gradient.tile_size_repeat = texelFetchOffset(sData64, uv, 0, ivec2(2, 0));
+struct BorderCorners {
+    vec2 tl_outer;
+    vec2 tl_inner;
+    vec2 tr_outer;
+    vec2 tr_inner;
+    vec2 br_outer;
+    vec2 br_inner;
+    vec2 bl_outer;
+    vec2 bl_inner;
+};
+
+BorderCorners get_border_corners(Border border, RectWithSize local_rect) {
+    vec2 tl_outer = local_rect.p0;
+    vec2 tl_inner = tl_outer + vec2(max(border.radii[0].x, border.widths.x),
+                                    max(border.radii[0].y, border.widths.y));
+
+    vec2 tr_outer = vec2(local_rect.p0.x + local_rect.size.x,
+                         local_rect.p0.y);
+    vec2 tr_inner = tr_outer + vec2(-max(border.radii[0].z, border.widths.z),
+                                    max(border.radii[0].w, border.widths.y));
 
-    return gradient;
+    vec2 br_outer = vec2(local_rect.p0.x + local_rect.size.x,
+                         local_rect.p0.y + local_rect.size.y);
+    vec2 br_inner = br_outer - vec2(max(border.radii[1].x, border.widths.z),
+                                    max(border.radii[1].y, border.widths.w));
+
+    vec2 bl_outer = vec2(local_rect.p0.x,
+                         local_rect.p0.y + local_rect.size.y);
+    vec2 bl_inner = bl_outer + vec2(max(border.radii[1].z, border.widths.x),
+                                    -max(border.radii[1].w, border.widths.w));
+
+    return BorderCorners(
+        tl_outer,
+        tl_inner,
+        tr_outer,
+        tr_inner,
+        br_outer,
+        br_inner,
+        bl_outer,
+        bl_inner
+    );
 }
 
 struct Glyph {
     vec4 offset;
 };
 
 Glyph fetch_glyph(int index) {
-    Glyph glyph;
-
-    ivec2 uv = get_fetch_uv_1(index);
-
-    glyph.offset = texelFetchOffset(sData16, uv, 0, ivec2(0, 0));
-
-    return glyph;
+    vec4 data = fetch_data_1(index);
+    return Glyph(data);
 }
 
 RectWithSize fetch_instance_geometry(int index) {
-    ivec2 uv = get_fetch_uv_1(index);
-
-    vec4 rect = texelFetchOffset(sData16, uv, 0, ivec2(0, 0));
-
-    return RectWithSize(rect.xy, rect.zw);
+    vec4 data = fetch_data_1(index);
+    return RectWithSize(data.xy, data.zw);
 }
 
 struct PrimitiveGeometry {
     RectWithSize local_rect;
     RectWithSize local_clip_rect;
 };
 
 PrimitiveGeometry fetch_prim_geometry(int index) {
@@ -449,33 +546,31 @@ vec4 get_layer_pos(vec2 pos, Layer layer
     vec3 b = layer.screen_vertices[3].xyz / layer.screen_vertices[3].w;
     vec3 c = layer.screen_vertices[2].xyz / layer.screen_vertices[2].w;
     // get the normal to the layer plane
     vec3 n = normalize(cross(b-a, c-a));
     return untransform(pos, n, a, layer.inv_transform);
 }
 
 struct VertexInfo {
-    RectWithEndpoint local_rect;
     vec2 local_pos;
     vec2 screen_pos;
 };
 
 VertexInfo write_vertex(RectWithSize instance_rect,
                         RectWithSize local_clip_rect,
                         float z,
                         Layer layer,
-                        AlphaBatchTask task) {
-    RectWithEndpoint local_rect = to_rect_with_endpoint(instance_rect);
-
+                        AlphaBatchTask task,
+                        vec2 snap_ref) {
     // Select the corner of the local rect that we are processing.
-    vec2 local_pos = mix(local_rect.p0, local_rect.p1, aPosition.xy);
+    vec2 local_pos = instance_rect.p0 + instance_rect.size * aPosition.xy;
 
     // xy = top left corner of the local rect, zw = position of current vertex.
-    vec4 local_p0_pos = vec4(local_rect.p0, local_pos);
+    vec4 local_p0_pos = vec4(snap_ref, local_pos);
 
     // Clamp to the two local clip rects.
     local_p0_pos = clamp_rect(local_p0_pos, local_clip_rect);
     local_p0_pos = clamp_rect(local_p0_pos, layer.local_clip_rect);
 
     // Transform the top corner and current vertex to world space.
     vec4 world_p0 = layer.transform * vec4(local_p0_pos.xy, 0.0, 1.0);
     world_p0.xyz /= world_p0.w;
@@ -491,26 +586,25 @@ VertexInfo write_vertex(RectWithSize ins
     // Apply offsets for the render task to get correct screen location.
     vec2 final_pos = device_p0_pos.zw -
                      snap_delta -
                      task.screen_space_origin +
                      task.render_target_origin;
 
     gl_Position = uTransform * vec4(final_pos, z, 1.0);
 
-    VertexInfo vi = VertexInfo(local_rect, local_p0_pos.zw, device_p0_pos.zw);
+    VertexInfo vi = VertexInfo(local_p0_pos.zw, device_p0_pos.zw);
     return vi;
 }
 
 #ifdef WR_FEATURE_TRANSFORM
 
 struct TransformVertexInfo {
     vec3 local_pos;
     vec2 screen_pos;
-    vec4 clipped_local_rect;
 };
 
 float cross2(vec2 v0, vec2 v1) {
     return v0.x * v1.y - v0.y * v1.x;
 }
 
 // Return intersection of line (p0,p1) and line (p2,p3)
 vec2 intersect_lines(vec2 p0, vec2 p1, vec2 p2, vec2 p3) {
@@ -526,17 +620,18 @@ vec2 intersect_lines(vec2 p0, vec2 p1, v
 
     return vec2(nx / d, ny / d);
 }
 
 TransformVertexInfo write_transform_vertex(RectWithSize instance_rect,
                                            RectWithSize local_clip_rect,
                                            float z,
                                            Layer layer,
-                                           AlphaBatchTask task) {
+                                           AlphaBatchTask task,
+                                           vec2 snap_ref) {
     RectWithEndpoint local_rect = to_rect_with_endpoint(instance_rect);
 
     vec2 current_local_pos, prev_local_pos, next_local_pos;
 
     // Select the current vertex and the previous/next vertices,
     // based on the vertex ID that is known based on the instance rect.
     switch (gl_VertexID) {
         case 0:
@@ -586,168 +681,138 @@ TransformVertexInfo write_transform_vert
 
     // Intersect those adjusted lines to find the actual vertex position.
     vec2 device_pos = intersect_lines(adjusted_prev_p0,
                                       adjusted_prev_p1,
                                       adjusted_next_p0,
                                       adjusted_next_p1);
 
     // Calculate the snap amount based on the first vertex as a reference point.
-    vec4 world_p0 = layer.transform * vec4(local_rect.p0, 0.0, 1.0);
+    vec4 world_p0 = layer.transform * vec4(snap_ref, 0.0, 1.0);
     vec2 device_p0 = uDevicePixelRatio * world_p0.xy / world_p0.w;
     vec2 snap_delta = device_p0 - floor(device_p0 + 0.5);
 
     // Apply offsets for the render task to get correct screen location.
     vec2 final_pos = device_pos -
                      snap_delta -
                      task.screen_space_origin +
                      task.render_target_origin;
 
     gl_Position = uTransform * vec4(final_pos, z, 1.0);
 
     vec4 layer_pos = get_layer_pos(device_pos / uDevicePixelRatio, layer);
 
-    return TransformVertexInfo(layer_pos.xyw, device_pos, vec4(instance_rect.p0, instance_rect.size));
+    return TransformVertexInfo(layer_pos.xyw, device_pos);
 }
 
 #endif //WR_FEATURE_TRANSFORM
 
 struct ResourceRect {
     vec4 uv_rect;
 };
 
 ResourceRect fetch_resource_rect(int index) {
     ResourceRect rect;
 
-    ivec2 uv = get_fetch_uv_1(index);
+    ivec2 uv = get_fetch_uv(index, 1);
 
     rect.uv_rect = texelFetchOffset(sResourceRects, uv, 0, ivec2(0, 0));
 
     return rect;
 }
 
 struct Rectangle {
     vec4 color;
 };
 
 Rectangle fetch_rectangle(int index) {
-    Rectangle rect;
-
-    ivec2 uv = get_fetch_uv_1(index);
-
-    rect.color = texelFetchOffset(sData16, uv, 0, ivec2(0, 0));
-
-    return rect;
+    vec4 data = fetch_data_1(index);
+    return Rectangle(data);
 }
 
 struct TextRun {
     vec4 color;
 };
 
 TextRun fetch_text_run(int index) {
-    TextRun text;
-
-    ivec2 uv = get_fetch_uv_1(index);
-
-    text.color = texelFetchOffset(sData16, uv, 0, ivec2(0, 0));
-
-    return text;
+    vec4 data = fetch_data_1(index);
+    return TextRun(data);
 }
 
 struct Image {
     vec4 stretch_size_and_tile_spacing;  // Size of the actual image and amount of space between
                                          //     tiled instances of this image.
 };
 
 Image fetch_image(int index) {
-    Image image;
-
-    ivec2 uv = get_fetch_uv_1(index);
-
-    image.stretch_size_and_tile_spacing = texelFetchOffset(sData16, uv, 0, ivec2(0, 0));
-
-    return image;
+    vec4 data = fetch_data_1(index);
+    return Image(data);
 }
 
 // YUV color spaces
 #define YUV_REC601 1
 #define YUV_REC709 2
 
 struct YuvImage {
     vec4 y_st_rect;
     vec4 u_st_rect;
     vec4 v_st_rect;
     vec2 size;
     int color_space;
 };
 
 YuvImage fetch_yuv_image(int index) {
-    YuvImage image;
-
-    ivec2 uv = get_fetch_uv_1(index);
-
-    vec4 size_color_space = texelFetchOffset(sData16, uv, 0, ivec2(0, 0));
-    image.size = size_color_space.xy;
-    image.color_space = int(size_color_space.z);
-
-    return image;
+    vec4 data = fetch_data_1(index);
+    return YuvImage(vec4(0.0), vec4(0.0), vec4(0.0), data.xy, int(data.z));
 }
 
 struct BoxShadow {
     vec4 src_rect;
     vec4 bs_rect;
     vec4 color;
     vec4 border_radius_edge_size_blur_radius_inverted;
 };
 
 BoxShadow fetch_boxshadow(int index) {
-    BoxShadow bs;
-
-    ivec2 uv = get_fetch_uv_4(index);
-
-    bs.src_rect = texelFetchOffset(sData64, uv, 0, ivec2(0, 0));
-    bs.bs_rect = texelFetchOffset(sData64, uv, 0, ivec2(1, 0));
-    bs.color = texelFetchOffset(sData64, uv, 0, ivec2(2, 0));
-    bs.border_radius_edge_size_blur_radius_inverted = texelFetchOffset(sData64, uv, 0, ivec2(3, 0));
-
-    return bs;
+    vec4 data[4] = fetch_data_4(index);
+    return BoxShadow(data[0], data[1], data[2], data[3]);
 }
 
 void write_clip(vec2 global_pos, ClipArea area) {
     vec2 texture_size = vec2(textureSize(sCacheA8, 0).xy);
     vec2 uv = global_pos + area.task_bounds.xy - area.screen_origin_target_index.xy;
     vClipMaskUvBounds = area.task_bounds / texture_size.xyxy;
     vClipMaskUv = vec3(uv / texture_size, area.screen_origin_target_index.z);
 }
 #endif //WR_VERTEX_SHADER
 
 #ifdef WR_FRAGMENT_SHADER
 float signed_distance_rect(vec2 pos, vec2 p0, vec2 p1) {
     vec2 d = max(p0 - pos, pos - p1);
     return length(max(vec2(0.0), d)) + min(0.0, max(d.x, d.y));
 }
 
-vec2 init_transform_fs(vec3 local_pos, vec4 local_rect, out float fragment_alpha) {
+vec2 init_transform_fs(vec3 local_pos, RectWithSize local_rect, out float fragment_alpha) {
     fragment_alpha = 1.0;
     vec2 pos = local_pos.xy / local_pos.z;
 
     // Because the local rect is placed on whole coordinates, but the interpolation
     // occurs at pixel centers, we need to offset the signed distance by that amount.
     // In the simple case of no zoom, and no transform, this is 0.5. However, we
     // need to scale this by the amount that the local rect is changing by per
     // fragment, based on the current zoom and transform.
     vec2 fw = fwidth(pos.xy);
     vec2 dxdy = 0.5 * fw;
 
     // Now get the actual signed distance. Inset the local rect by the offset amount
     // above to get correct distance values. This ensures that we only apply
     // anti-aliasing when the fragment has partial coverage.
     float d = signed_distance_rect(pos,
-                                   local_rect.xy + dxdy,
-                                   local_rect.xy + local_rect.zw - dxdy);
+                                   local_rect.p0 + dxdy,
+                                   local_rect.p0 + local_rect.size - dxdy);
 
     // Find the appropriate distance to apply the AA smoothstep over.
     float afwidth = 0.5 / length(fw);
 
     // Only apply AA to fragments outside the signed distance field.
     fragment_alpha = 1.0 - smoothstep(0.0, afwidth, d);
 
     return pos;
--- a/gfx/webrender/res/ps_angle_gradient.vs.glsl
+++ b/gfx/webrender/res/ps_angle_gradient.vs.glsl
@@ -6,17 +6,18 @@
 void main(void) {
     Primitive prim = load_primitive();
     Gradient gradient = fetch_gradient(prim.prim_index);
 
     VertexInfo vi = write_vertex(prim.local_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
-                                 prim.task);
+                                 prim.task,
+                                 prim.local_rect.p0);
 
     vPos = vi.local_pos - prim.local_rect.p0;
 
     vec2 start_point = gradient.start_end_point.xy;
     vec2 end_point = gradient.start_end_point.zw;
     vec2 dir = end_point - start_point;
 
     vStartPoint = start_point;
--- a/gfx/webrender/res/ps_border.fs.glsl
+++ b/gfx/webrender/res/ps_border.fs.glsl
@@ -1,26 +1,14 @@
 #line 1
 
 /* 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/. */
 
-// Border styles as defined in webrender_traits/types.rs
-#define BORDER_STYLE_NONE         0
-#define BORDER_STYLE_SOLID        1
-#define BORDER_STYLE_DOUBLE       2
-#define BORDER_STYLE_DOTTED       3
-#define BORDER_STYLE_DASHED       4
-#define BORDER_STYLE_HIDDEN       5
-#define BORDER_STYLE_GROOVE       6
-#define BORDER_STYLE_RIDGE        7
-#define BORDER_STYLE_INSET        8
-#define BORDER_STYLE_OUTSET       9
-
 void discard_pixels_in_rounded_borders(vec2 local_pos) {
   float distanceFromRef = distance(vRefPoint, local_pos);
   if (vRadii.x > 0.0 && (distanceFromRef > vRadii.x || distanceFromRef < vRadii.z)) {
       discard;
   }
 }
 
 vec4 get_fragment_color(float distanceFromMixLine, float pixelsPerFragment) {
@@ -170,61 +158,52 @@ vec4 draw_dashed_edge(float position, fl
 
   if (mod(segment + 2.0, 2.0) == 0.0) {
     return vHorizontalColor * vec4(1.0, 1.0, 1.0, 1.0 - alpha);
   } else {
     return vHorizontalColor * vec4(1.0, 1.0, 1.0, alpha);
   }
 }
 
-void draw_dashed_or_dotted_border(vec2 local_pos, float distance_from_mix_line) {
+vec4 draw_dashed_or_dotted_border(vec2 local_pos, float distance_from_mix_line) {
   // This is the conversion factor for transformations and device pixel scaling.
   float pixels_per_fragment = length(fwidth(local_pos.xy));
 
   switch (vBorderPart) {
     // These are the layer tile part PrimitivePart as uploaded by the tiling.rs
     case PST_TOP_LEFT:
     case PST_TOP_RIGHT:
     case PST_BOTTOM_LEFT:
     case PST_BOTTOM_RIGHT:
     {
-      oFragColor = get_fragment_color(distance_from_mix_line, pixels_per_fragment);
+      vec4 color = get_fragment_color(distance_from_mix_line, pixels_per_fragment);
       if (vRadii.x > 0.0) {
-        oFragColor *= vec4(1.0, 1.0, 1.0, alpha_for_solid_border_corner(local_pos,
-                                                                  vRadii.zw,
-                                                                  vRadii.xy,
-                                                                  pixels_per_fragment));
+        color.a *= alpha_for_solid_border_corner(local_pos,
+                                                 vRadii.zw,
+                                                 vRadii.xy,
+                                                 pixels_per_fragment);
       }
-
-      break;
+      return color;
     }
     case PST_BOTTOM:
     case PST_TOP: {
-      if (vBorderStyle == BORDER_STYLE_DASHED) {
-        oFragColor = draw_dashed_edge(vLocalPos.x - vPieceRect.x,
-                                      vPieceRect.w,
-                                      pixels_per_fragment);
-      } else {
-        oFragColor = draw_dotted_edge(local_pos.yx, vPieceRect.yxwz, pixels_per_fragment);
-      }
-      break;
+      return vBorderStyle == BORDER_STYLE_DASHED ?
+        draw_dashed_edge(vLocalPos.x - vPieceRect.x, vPieceRect.w, pixels_per_fragment) :
+        draw_dotted_edge(local_pos.yx, vPieceRect.yxwz, pixels_per_fragment);
     }
     case PST_LEFT:
     case PST_RIGHT:
     {
-      if (vBorderStyle == BORDER_STYLE_DASHED) {
-        oFragColor = draw_dashed_edge(vLocalPos.y - vPieceRect.y,
-                                      vPieceRect.z,
-                                      pixels_per_fragment);
-      } else {
-        oFragColor = draw_dotted_edge(local_pos.xy, vPieceRect.xyzw, pixels_per_fragment);
-      }
-      break;
+      return vBorderStyle == BORDER_STYLE_DASHED ?
+        draw_dashed_edge(vLocalPos.y - vPieceRect.y, vPieceRect.z, pixels_per_fragment) :
+        draw_dotted_edge(local_pos.xy, vPieceRect.xyzw, pixels_per_fragment);
     }
   }
+
+  return vec4(0.0);
 }
 
 vec4 draw_double_edge(float pos,
                       float len,
                       float distance_from_mix_line,
                       float pixels_per_fragment) {
   float total_border_width = len;
   float one_third_width = total_border_width / 3.0;
@@ -292,187 +271,164 @@ vec4 draw_double_edge_corner(vec2 local_
                                                      distance_from_mix_line >= 0.0;
   if (is_vertical) {
     return draw_double_edge_vertical(local_pos, distance_from_mix_line, pixels_per_fragment);
   } else {
     return draw_double_edge_horizontal(local_pos, distance_from_mix_line, pixels_per_fragment);
   }
 }
 
-void draw_double_border(float distance_from_mix_line, vec2 local_pos) {
+vec4 draw_double_border(float distance_from_mix_line, vec2 local_pos) {
   float pixels_per_fragment = length(fwidth(local_pos.xy));
   switch (vBorderPart) {
     // These are the layer tile part PrimitivePart as uploaded by the tiling.rs
     case PST_TOP_LEFT:
     case PST_TOP_RIGHT:
     case PST_BOTTOM_LEFT:
     case PST_BOTTOM_RIGHT:
-    {
-      oFragColor = draw_double_edge_corner(local_pos, distance_from_mix_line, pixels_per_fragment);
-      break;
-    }
+      return draw_double_edge_corner(local_pos, distance_from_mix_line, pixels_per_fragment);
     case PST_BOTTOM:
     case PST_TOP:
-    {
-      oFragColor = draw_double_edge_horizontal(local_pos,
-                                               distance_from_mix_line,
-                                               pixels_per_fragment);
-      break;
-    }
+      return draw_double_edge_horizontal(local_pos,
+                                         distance_from_mix_line,
+                                         pixels_per_fragment);
     case PST_LEFT:
     case PST_RIGHT:
-    {
-      oFragColor = draw_double_edge_vertical(local_pos,
-                                             distance_from_mix_line,
-                                             pixels_per_fragment);
-      break;
-    }
+      return draw_double_edge_vertical(local_pos,
+                                       distance_from_mix_line,
+                                       pixels_per_fragment);
   }
+  return vec4(0.0);
 }
 
-void draw_solid_border(float distanceFromMixLine, vec2 localPos) {
+vec4 draw_solid_border(float distanceFromMixLine, vec2 localPos) {
   switch (vBorderPart) {
     case PST_TOP_LEFT:
     case PST_TOP_RIGHT:
     case PST_BOTTOM_LEFT:
     case PST_BOTTOM_RIGHT: {
       // This is the conversion factor for transformations and device pixel scaling.
       float pixelsPerFragment = length(fwidth(localPos.xy));
-      oFragColor = get_fragment_color(distanceFromMixLine, pixelsPerFragment);
+      vec4 color = get_fragment_color(distanceFromMixLine, pixelsPerFragment);
 
       if (vRadii.x > 0.0) {
-        float alpha = alpha_for_solid_border_corner(localPos, vRadii.zw, vRadii.xy, pixelsPerFragment);
-        oFragColor *= vec4(1.0, 1.0, 1.0, alpha);
+        color.a *= alpha_for_solid_border_corner(localPos, vRadii.zw, vRadii.xy, pixelsPerFragment);
       }
 
-      break;
+      return color;
     }
     default:
-      oFragColor = vHorizontalColor;
       discard_pixels_in_rounded_borders(localPos);
-      break;
+      return vHorizontalColor;
   }
 }
 
 vec4 draw_mixed_edge(float distance, float border_len, vec4 color, vec2 brightness_mod) {
   float modulator = distance / border_len > 0.5 ? brightness_mod.x : brightness_mod.y;
   return vec4(color.xyz * modulator, color.a);
 }
 
-void draw_mixed_border(float distanceFromMixLine, float distanceFromMiddle, vec2 localPos, vec2 brightness_mod) {
+vec4 draw_mixed_border(float distanceFromMixLine, float distanceFromMiddle, vec2 localPos, vec2 brightness_mod) {
   switch (vBorderPart) {
     case PST_TOP_LEFT:
     case PST_TOP_RIGHT:
     case PST_BOTTOM_LEFT:
     case PST_BOTTOM_RIGHT: {
       // This is the conversion factor for transformations and device pixel scaling.
       float pixelsPerFragment = length(fwidth(localPos.xy));
       vec4 color = get_fragment_color(distanceFromMixLine, pixelsPerFragment);
 
       if (vRadii.x > 0.0) {
         float distance = distance(vRefPoint, localPos) - vRadii.z;
         float length = vRadii.x - vRadii.z;
         if (distanceFromMiddle < 0.0) {
           distance = length - distance;
         }
 
-        oFragColor = 0.0 <= distance && distance <= length ?
-          draw_mixed_edge(distance, length, color, brightness_mod) : vec4(0.0, 0.0, 0.0, 0.0);
-        break;
+        return 0.0 <= distance && distance <= length ?
+          draw_mixed_edge(distance, length, color, brightness_mod) :
+          vec4(0.0, 0.0, 0.0, 0.0);
       }
 
       bool is_vertical = (vBorderPart == PST_TOP_LEFT) ? distanceFromMixLine < 0.0 :
                                                          distanceFromMixLine >= 0.0;
       float distance = is_vertical ? abs(localPos.x - vRefPoint.x) : abs(localPos.y - vRefPoint.y);
       float length = is_vertical ? abs(vPieceRect.z) : abs(vPieceRect.w);
       if (distanceFromMiddle > 0.0) {
           distance = length - distance;
       }
 
-      oFragColor = 0.0 <= distance && distance <= length ?
-        draw_mixed_edge(distance, length, color, brightness_mod) : vec4(0.0, 0.0, 0.0, 0.0);
-      break;
+      return 0.0 <= distance && distance <= length ?
+        draw_mixed_edge(distance, length, color, brightness_mod) :
+        vec4(0.0, 0.0, 0.0, 0.0);
     }
     case PST_BOTTOM:
-    case PST_TOP: {
-      oFragColor = draw_mixed_edge(localPos.y - vPieceRect.y, vPieceRect.w, vVerticalColor, brightness_mod);
-      break;
-    }
+    case PST_TOP:
+      return draw_mixed_edge(localPos.y - vPieceRect.y, vPieceRect.w, vVerticalColor, brightness_mod);
     case PST_LEFT:
-    case PST_RIGHT: {
-      oFragColor = draw_mixed_edge(localPos.x - vPieceRect.x, vPieceRect.z, vHorizontalColor, brightness_mod);
+    case PST_RIGHT:
+      return draw_mixed_edge(localPos.x - vPieceRect.x, vPieceRect.z, vHorizontalColor, brightness_mod);
+  }
+  return vec4(0.0);
+}
+
+vec4 draw_complete_border(vec2 local_pos, float distance_from_mix_line, float distance_from_middle) {
+  vec2 brightness_mod = vec2(0.7, 1.3);
+
+  // Note: we can't pass-through in the following cases,
+  // because Angle doesn't support it and fails to compile the shaders.
+  switch (vBorderStyle) {
+    case BORDER_STYLE_DASHED:
+      return draw_dashed_or_dotted_border(local_pos, distance_from_mix_line);
+    case BORDER_STYLE_DOTTED:
+      return draw_dashed_or_dotted_border(local_pos, distance_from_mix_line);
+    case BORDER_STYLE_DOUBLE:
+      return draw_double_border(distance_from_mix_line, local_pos);
+    case BORDER_STYLE_OUTSET:
+      return draw_solid_border(distance_from_mix_line, local_pos);
+    case BORDER_STYLE_INSET:
+      return draw_solid_border(distance_from_mix_line, local_pos);
+    case BORDER_STYLE_SOLID:
+      return draw_solid_border(distance_from_mix_line, local_pos);
+    case BORDER_STYLE_NONE:
+      return draw_solid_border(distance_from_mix_line, local_pos);
+    case BORDER_STYLE_GROOVE:
+      return draw_mixed_border(distance_from_mix_line, distance_from_middle, local_pos, brightness_mod.yx);
+    case BORDER_STYLE_RIDGE:
+      return draw_mixed_border(distance_from_mix_line, distance_from_middle, local_pos, brightness_mod.xy);
+    case BORDER_STYLE_HIDDEN:
+    default:
       break;
-    }
   }
+
+  // Note: Workaround for Angle on Windows,
+  // because non-empty case statements must have break or return.
+  discard;
+  return vec4(0.0);
 }
 
 // TODO: Investigate performance of this shader and see
 //       if it's worthwhile splitting it / removing branches etc.
 void main(void) {
 #ifdef WR_FEATURE_TRANSFORM
     float alpha = 0.0;
     vec2 local_pos = init_transform_fs(vLocalPos, vLocalRect, alpha);
 #else
+    float alpha = 1.0;
     vec2 local_pos = vLocalPos;
 #endif
 
 #ifdef WR_FEATURE_TRANSFORM
     // TODO(gw): Support other border styles for transformed elements.
     float distance_from_mix_line = (local_pos.x - vPieceRect.x) * vPieceRect.w -
                                    (local_pos.y - vPieceRect.y) * vPieceRect.z;
     distance_from_mix_line /= vPieceRectHypotenuseLength;
-    float distance_from_middle = (local_pos.x - vLocalRect.x) +
-                                 (local_pos.y - vLocalRect.y) -
-                                 0.5 * (vLocalRect.z + vLocalRect.w);
+    float distance_from_middle = (local_pos.x - vBorderRect.p0.x) +
+                                 (local_pos.y - vBorderRect.p0.y) -
+                                 0.5 * (vBorderRect.size.x + vBorderRect.size.y);
 #else
     float distance_from_mix_line = vDistanceFromMixLine;
     float distance_from_middle = vDistanceFromMiddle;
 #endif
 
-    vec2 brightness_mod = vec2(0.7, 1.3);
-    bool needs_discard = false;
-
-    // Note: we can't pass-through in the following cases,
-    // because Angle doesn't support it and fails to compile the shaders.
-    switch (vBorderStyle) {
-        case BORDER_STYLE_DASHED:
-          draw_dashed_or_dotted_border(local_pos, distance_from_mix_line);
-          break;
-        case BORDER_STYLE_DOTTED:
-          draw_dashed_or_dotted_border(local_pos, distance_from_mix_line);
-          break;
-        case BORDER_STYLE_DOUBLE:
-          draw_double_border(distance_from_mix_line, local_pos);
-          break;
-        case BORDER_STYLE_OUTSET:
-          draw_solid_border(distance_from_mix_line, local_pos);
-          break;
-        case BORDER_STYLE_INSET:
-          draw_solid_border(distance_from_mix_line, local_pos);
-          break;
-        case BORDER_STYLE_SOLID:
-          draw_solid_border(distance_from_mix_line, local_pos);
-          break;
-        case BORDER_STYLE_NONE:
-          draw_solid_border(distance_from_mix_line, local_pos);
-          break;
-        case BORDER_STYLE_GROOVE:
-          draw_mixed_border(distance_from_mix_line, distance_from_middle, local_pos, brightness_mod.yx);
-          break;
-        case BORDER_STYLE_RIDGE:
-          draw_mixed_border(distance_from_mix_line, distance_from_middle, local_pos, brightness_mod.xy);
-          break;
-        case BORDER_STYLE_HIDDEN:
-        default:
-          needs_discard = true;
-          break;
-    }
-
-    // Note: Workaround for Angle on Windows,
-    // because non-empty case statements must have break or return.
-    if (needs_discard) {
-      discard;
-    }
-
-#ifdef WR_FEATURE_TRANSFORM
-    oFragColor *= vec4(1.0, 1.0, 1.0, alpha);
-#endif
+    oFragColor = draw_complete_border(local_pos, distance_from_mix_line, distance_from_middle);
+    oFragColor.a *= min(alpha, do_clip());
 }
--- a/gfx/webrender/res/ps_border.glsl
+++ b/gfx/webrender/res/ps_border.glsl
@@ -1,31 +1,32 @@
 #line 1
 
 /* 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/. */
 
 // These are not changing.
-flat varying vec4 vVerticalColor;     // The vertical color, e.g. top/bottom
-flat varying vec4 vHorizontalColor;   // The horizontal color e.g. left/right
-flat varying vec4 vRadii;             // The border radius from CSS border-radius
-flat varying vec4 vLocalRect; // The rect of the border (x, y, w, h) in local space.
+flat varying vec4 vVerticalColor;      // The vertical color, e.g. top/bottom
+flat varying vec4 vHorizontalColor;    // The horizontal color e.g. left/right
+flat varying vec4 vRadii;              // The border radius from CSS border-radius
+flat varying RectWithSize vBorderRect; // The rect of the border in local space.
 
 // for corners, this is the beginning of the corner.
 // For the lines, this is the top left of the line.
 flat varying vec2 vRefPoint;
 flat varying int vBorderStyle;
 flat varying int vBorderPart; // Which part of the border we're drawing.
 
 flat varying vec4 vPieceRect;
 
 // These are in device space
 #ifdef WR_FEATURE_TRANSFORM
 varying vec3 vLocalPos;     // The clamped position in local space.
+flat varying RectWithSize vLocalRect;
 flat varying float vPieceRectHypotenuseLength;
 #else
 varying vec2 vLocalPos;     // The clamped position in local space.
 
 // These two are interpolated
 varying float vDistanceFromMixLine;  // This is the distance from the line where two colors
                                      // meet in border corners.
 varying float vDistanceFromMiddle;   // This is the distance from the line between the top
--- a/gfx/webrender/res/ps_border.vs.glsl
+++ b/gfx/webrender/res/ps_border.vs.glsl
@@ -1,58 +1,35 @@
 #line 1
 /* 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/. */
 
-struct Border {
-    vec4 style;
-    vec4 widths;
-    vec4 colors[4];
-    vec4 radii[2];
-};
-
-Border fetch_border(int index) {
-    Border border;
-
-    ivec2 uv = get_fetch_uv_8(index);
-
-    border.style = texelFetchOffset(sData128, uv, 0, ivec2(0, 0));
-    border.widths = texelFetchOffset(sData128, uv, 0, ivec2(1, 0));
-    border.colors[0] = texelFetchOffset(sData128, uv, 0, ivec2(2, 0));
-    border.colors[1] = texelFetchOffset(sData128, uv, 0, ivec2(3, 0));
-    border.colors[2] = texelFetchOffset(sData128, uv, 0, ivec2(4, 0));
-    border.colors[3] = texelFetchOffset(sData128, uv, 0, ivec2(5, 0));
-    border.radii[0] = texelFetchOffset(sData128, uv, 0, ivec2(6, 0));
-    border.radii[1] = texelFetchOffset(sData128, uv, 0, ivec2(7, 0));
-
-    return border;
-}
-
 void main(void) {
     Primitive prim = load_primitive();
     Border border = fetch_border(prim.prim_index);
     int sub_part = prim.sub_index;
+    vBorderRect = prim.local_rect;
 
-    vec2 tl_outer = prim.local_rect.p0;
+    vec2 tl_outer = vBorderRect.p0;
     vec2 tl_inner = tl_outer + vec2(max(border.radii[0].x, border.widths.x),
                                     max(border.radii[0].y, border.widths.y));
 
-    vec2 tr_outer = vec2(prim.local_rect.p0.x + prim.local_rect.size.x,
-                         prim.local_rect.p0.y);
+    vec2 tr_outer = vec2(vBorderRect.p0.x + vBorderRect.size.x,
+                         vBorderRect.p0.y);
     vec2 tr_inner = tr_outer + vec2(-max(border.radii[0].z, border.widths.z),
                                     max(border.radii[0].w, border.widths.y));
 
-    vec2 br_outer = vec2(prim.local_rect.p0.x + prim.local_rect.size.x,
-                         prim.local_rect.p0.y + prim.local_rect.size.y);
+    vec2 br_outer = vec2(vBorderRect.p0.x + vBorderRect.size.x,
+                         vBorderRect.p0.y + vBorderRect.size.y);
     vec2 br_inner = br_outer - vec2(max(border.radii[1].x, border.widths.z),
                                     max(border.radii[1].y, border.widths.w));
 
-    vec2 bl_outer = vec2(prim.local_rect.p0.x,
-                         prim.local_rect.p0.y + prim.local_rect.size.y);
+    vec2 bl_outer = vec2(vBorderRect.p0.x,
+                         vBorderRect.p0.y + vBorderRect.size.y);
     vec2 bl_inner = bl_outer + vec2(max(border.radii[1].z, border.widths.x),
                                     -max(border.radii[1].w, border.widths.w));
 
     RectWithSize segment_rect;
     switch (sub_part) {
         case PST_TOP_LEFT:
             segment_rect.p0 = tl_outer;
             segment_rect.size = tl_inner - tl_outer;
@@ -123,32 +100,31 @@ void main(void) {
             break;
     }
 
 #ifdef WR_FEATURE_TRANSFORM
     TransformVertexInfo vi = write_transform_vertex(segment_rect,
                                                     prim.local_clip_rect,
                                                     prim.z,
                                                     prim.layer,
-                                                    prim.task);
+                                                    prim.task,
+                                                    prim.local_rect.p0);
     vLocalPos = vi.local_pos;
-
-    // Local space
-    vLocalRect = vi.clipped_local_rect;
+    vLocalRect = segment_rect;
 #else
     VertexInfo vi = write_vertex(segment_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
-                                 prim.task);
+                                 prim.task,
+                                 prim.local_rect.p0);
     vLocalPos = vi.local_pos.xy;
+#endif
 
-    // Local space
-    vLocalRect = vec4(prim.local_rect.p0, prim.local_rect.size);
-#endif
+    write_clip(vi.screen_pos, prim.clip_area);
 
     float x0, y0, x1, y1;
     switch (sub_part) {
         // These are the layer tile part PrimitivePart as uploaded by the tiling.rs
         case PST_TOP_LEFT:
             x0 = segment_rect.p0.x;
             y0 = segment_rect.p0.y;
             // These are width / heights
@@ -204,13 +180,13 @@ void main(void) {
     // to properly mix border colors. For transformed borders, we calculate this distance
     // in the fragment shader itself. For non-transformed borders, we can use the
     // interpolator.
 #ifdef WR_FEATURE_TRANSFORM
     vPieceRectHypotenuseLength = sqrt(pow(width, 2.0) + pow(height, 2.0));
 #else
     vDistanceFromMixLine = (vi.local_pos.x - x0) * height -
                            (vi.local_pos.y - y0) * width;
-    vDistanceFromMiddle = (vi.local_pos.x - vLocalRect.x) +
-                          (vi.local_pos.y - vLocalRect.y) -
-                          0.5 * (vLocalRect.z + vLocalRect.w);
+    vDistanceFromMiddle = (vi.local_pos.x - vBorderRect.p0.x) +
+                          (vi.local_pos.y - vBorderRect.p0.y) -
+                          0.5 * (vBorderRect.size.x + vBorderRect.size.y);
 #endif
 }
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_border_corner.fs.glsl
@@ -0,0 +1,153 @@
+#line 1
+/* 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/. */
+
+//
+// Signed distance to an ellipse.
+// Taken from http://www.iquilezles.org/www/articles/ellipsedist/ellipsedist.htm
+// Note that this fails for exact circles.
+//
+float sdEllipse( vec2 p, in vec2 ab ) {
+    p = abs( p ); if( p.x > p.y ){ p=p.yx; ab=ab.yx; }
+    float l = ab.y*ab.y - ab.x*ab.x;
+
+    float m = ab.x*p.x/l;
+    float n = ab.y*p.y/l;
+    float m2 = m*m;
+    float n2 = n*n;
+
+    float c = (m2 + n2 - 1.0)/3.0;
+    float c3 = c*c*c;
+
+    float q = c3 + m2*n2*2.0;
+    float d = c3 + m2*n2;
+    float g = m + m*n2;
+
+    float co;
+
+    if( d<0.0 )
+    {
+        float p = acos(q/c3)/3.0;
+        float s = cos(p);
+        float t = sin(p)*sqrt(3.0);
+        float rx = sqrt( -c*(s + t + 2.0) + m2 );
+        float ry = sqrt( -c*(s - t + 2.0) + m2 );
+        co = ( ry + sign(l)*rx + abs(g)/(rx*ry) - m)/2.0;
+    }
+    else
+    {
+        float h = 2.0*m*n*sqrt( d );
+        float s = sign(q+h)*pow( abs(q+h), 1.0/3.0 );
+        float u = sign(q-h)*pow( abs(q-h), 1.0/3.0 );
+        float rx = -s - u - c*4.0 + 2.0*m2;
+        float ry = (s - u)*sqrt(3.0);
+        float rm = sqrt( rx*rx + ry*ry );
+        float p = ry/sqrt(rm-rx);
+        co = (p + 2.0*g/rm - m)/2.0;
+    }
+
+    float si = sqrt( 1.0 - co*co );
+
+    vec2 r = vec2( ab.x*co, ab.y*si );
+
+    return length(r - p ) * sign(p.y-r.y);
+}
+
+float distance_to_line(vec2 p0, vec2 perp_dir, vec2 p) {
+    vec2 dir_to_p0 = p0 - p;
+    return dot(normalize(perp_dir), dir_to_p0);
+}
+
+float distance_to_ellipse(vec2 p, vec2 radii) {
+    // sdEllipse fails on exact circles, so handle equal
+    // radii here. The branch coherency should make this
+    // a performance win for the circle case too.
+    if (radii.x == radii.y) {
+        return length(p) - radii.x;
+    } else {
+        return sdEllipse(p, radii);
+    }
+}
+
+void main(void) {
+    float alpha = 1.0;
+#ifdef WR_FEATURE_TRANSFORM
+    alpha = 0.0;
+    vec2 local_pos = init_transform_fs(vLocalPos, vLocalRect, alpha);
+#else
+    vec2 local_pos = vLocalPos;
+#endif
+
+    alpha = min(alpha, do_clip());
+
+    // Find the appropriate distance to apply the AA smoothstep over.
+    vec2 fw = fwidth(local_pos);
+    float afwidth = length(fw);
+    float distance_for_color;
+    float color_mix_factor;
+
+    // Only apply the clip AA if inside the clip region. This is
+    // necessary for correctness when the border width is greater
+    // than the border radius.
+    if (all(lessThan(local_pos * vClipSign, vClipCenter * vClipSign))) {
+        vec2 p = local_pos - vClipCenter;
+
+        // Get signed distance from the inner/outer clips.
+        float d0 = distance_to_ellipse(p, vRadii0.xy);
+        float d1 = distance_to_ellipse(p, vRadii0.zw);
+        float d2 = distance_to_ellipse(p, vRadii1.xy);
+        float d3 = distance_to_ellipse(p, vRadii1.zw);
+
+        // SDF subtract main radii
+        float d_main = max(d0, 0.5 * afwidth - d1);
+
+        // SDF subtract inner radii (double style borders)
+        float d_inner = max(d2 - 0.5 * afwidth, -d3);
+
+        // Select how to combine the SDF based on border style.
+        float d = mix(max(d_main, -d_inner), d_main, vSDFSelect);
+
+        // Only apply AA to fragments outside the signed distance field.
+        alpha = min(alpha, 1.0 - smoothstep(0.0, 0.5 * afwidth, d));
+
+        // Get the groove/ridge mix factor.
+        color_mix_factor = smoothstep(-0.5 * afwidth,
+                                      0.5 * afwidth,
+                                      -d2);
+    } else {
+        // Handle the case where the fragment is outside the clip
+        // region in a corner. This occurs when border width is
+        // greater than border radius.
+
+        // Get linear distances along horizontal and vertical edges.
+        vec2 d0 = vClipSign.xx * (local_pos.xx - vEdgeDistance.xz);
+        vec2 d1 = vClipSign.yy * (local_pos.yy - vEdgeDistance.yw);
+        // Apply union to get the outer edge signed distance.
+        float da = min(d0.x, d1.x);
+        // Apply intersection to get the inner edge signed distance.
+        float db = max(-d0.y, -d1.y);
+        // Apply union to get both edges.
+        float d = min(da, db);
+        // Select fragment on/off based on signed distance.
+        // No AA here, since we know we're on a straight edge
+        // and the width is rounded to a whole CSS pixel.
+        alpha = min(alpha, mix(vAlphaSelect, 1.0, d < 0.0));
+
+        // Get the groove/ridge mix factor.
+        // TODO(gw): Support AA for groove/ridge border edge with transforms.
+        color_mix_factor = mix(0.0, 1.0, da > 0.0);
+    }
+
+    // Mix inner/outer color.
+    vec4 color0 = mix(vColor00, vColor01, color_mix_factor);
+    vec4 color1 = mix(vColor10, vColor11, color_mix_factor);
+
+    // Select color based on side of line. Get distance from the
+    // reference line, and then apply AA along the edge.
+    float ld = distance_to_line(vColorEdgeLine.xy, vColorEdgeLine.zw, local_pos);
+    float m = smoothstep(-0.5 * afwidth, 0.5 * afwidth, ld);
+    vec4 color = mix(color0, color1, m);
+
+    oFragColor = color * vec4(1.0, 1.0, 1.0, alpha);
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_border_corner.glsl
@@ -0,0 +1,29 @@
+#line 1
+/* 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/. */
+
+// Edge color transition
+flat varying vec4 vColor00;
+flat varying vec4 vColor01;
+flat varying vec4 vColor10;
+flat varying vec4 vColor11;
+flat varying vec4 vColorEdgeLine;
+
+// Border radius
+flat varying vec2 vClipCenter;
+flat varying vec4 vRadii0;
+flat varying vec4 vRadii1;
+flat varying vec2 vClipSign;
+flat varying vec4 vEdgeDistance;
+flat varying float vSDFSelect;
+
+// Border style
+flat varying float vAlphaSelect;
+
+#ifdef WR_FEATURE_TRANSFORM
+flat varying RectWithSize vLocalRect;
+varying vec3 vLocalPos;
+#else
+varying vec2 vLocalPos;
+#endif
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_border_corner.vs.glsl
@@ -0,0 +1,226 @@
+#line 1
+/* 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/. */
+
+vec2 get_radii(vec2 radius, vec2 invalid) {
+    if (all(greaterThan(radius, vec2(0.0)))) {
+        return radius;
+    }
+
+    return invalid;
+}
+
+void set_radii(float style,
+               vec2 radii,
+               vec2 widths,
+               vec2 adjusted_widths) {
+    vRadii0.xy = get_radii(radii, 2.0 * widths);
+    vRadii0.zw = get_radii(radii - widths, -widths);
+
+    switch (int(style)) {
+        case BORDER_STYLE_RIDGE:
+        case BORDER_STYLE_GROOVE:
+            vRadii1.xy = radii - adjusted_widths;
+            vRadii1.zw = -widths;
+            break;
+        case BORDER_STYLE_DOUBLE:
+            vRadii1.xy = get_radii(radii - adjusted_widths, -widths);
+            vRadii1.zw = get_radii(radii - widths + adjusted_widths, -widths);
+            break;
+        default:
+            vRadii1.xy = -widths;
+            vRadii1.zw = -widths;
+            break;
+    }
+}
+
+void set_edge_line(vec2 border_width,
+                   vec2 outer_corner,
+                   vec2 gradient_sign) {
+    vec2 gradient = border_width * gradient_sign;
+    vColorEdgeLine = vec4(outer_corner, vec2(-gradient.y, gradient.x));
+}
+
+void write_color(vec4 color0, vec4 color1, int style, vec2 delta) {
+    vec4 modulate;
+
+    switch (style) {
+        case BORDER_STYLE_GROOVE:
+            modulate = vec4(1.0 - 0.3 * delta.x,
+                            1.0 + 0.3 * delta.x,
+                            1.0 - 0.3 * delta.y,
+                            1.0 + 0.3 * delta.y);
+
+            break;
+        case BORDER_STYLE_RIDGE:
+            modulate = vec4(1.0 + 0.3 * delta.x,
+                            1.0 - 0.3 * delta.x,
+                            1.0 + 0.3 * delta.y,
+                            1.0 - 0.3 * delta.y);
+            break;
+        default:
+            modulate = vec4(1.0);
+            break;
+    }
+
+    vColor00 = vec4(color0.rgb * modulate.x, color0.a);
+    vColor01 = vec4(color0.rgb * modulate.y, color0.a);
+    vColor10 = vec4(color1.rgb * modulate.z, color1.a);
+    vColor11 = vec4(color1.rgb * modulate.w, color1.a);
+}
+
+void main(void) {
+    Primitive prim = load_primitive();
+    Border border = fetch_border(prim.prim_index);
+    int sub_part = prim.sub_index;
+    BorderCorners corners = get_border_corners(border, prim.local_rect);
+
+    vec4 adjusted_widths = get_effective_border_widths(border);
+    vec4 inv_adjusted_widths = border.widths - adjusted_widths;
+    vec2 p0, p1;
+
+    // TODO(gw): We'll need to pass through multiple styles
+    //           once we support style transitions per corner.
+    int style;
+    vec4 edge_distances;
+    vec4 color0, color1;
+    vec2 color_delta;
+
+    switch (sub_part) {
+        case 0: {
+            p0 = corners.tl_outer;
+            p1 = corners.tl_inner;
+            color0 = border.colors[0];
+            color1 = border.colors[1];
+            vClipCenter = corners.tl_outer + border.radii[0].xy;
+            vClipSign = vec2(1.0);
+            set_radii(border.style.x,
+                      border.radii[0].xy,
+                      border.widths.xy,
+                      adjusted_widths.xy);
+            set_edge_line(border.widths.xy,
+                          corners.tl_outer,
+                          vec2(1.0, 1.0));
+            style = int(border.style.x);
+            edge_distances = vec4(p0 + adjusted_widths.xy,
+                                  p0 + inv_adjusted_widths.xy);
+            color_delta = vec2(1.0);
+            break;
+        }
+        case 1: {
+            p0 = vec2(corners.tr_inner.x, corners.tr_outer.y);
+            p1 = vec2(corners.tr_outer.x, corners.tr_inner.y);
+            color0 = border.colors[1];
+            color1 = border.colors[2];
+            vClipCenter = corners.tr_outer + vec2(-border.radii[0].z, border.radii[0].w);
+            vClipSign = vec2(-1.0, 1.0);
+            set_radii(border.style.y,
+                      border.radii[0].zw,
+                      border.widths.zy,
+                      adjusted_widths.zy);
+            set_edge_line(border.widths.zy,
+                          corners.tr_outer,
+                          vec2(-1.0, 1.0));
+            style = int(border.style.y);
+            edge_distances = vec4(p1.x - adjusted_widths.z,
+                                  p0.y + adjusted_widths.y,
+                                  p1.x - border.widths.z + adjusted_widths.z,
+                                  p0.y + inv_adjusted_widths.y);
+            color_delta = vec2(1.0, -1.0);
+            break;
+        }
+        case 2: {
+            p0 = corners.br_inner;
+            p1 = corners.br_outer;
+            color0 = border.colors[2];
+            color1 = border.colors[3];
+            vClipCenter = corners.br_outer - border.radii[1].xy;
+            vClipSign = vec2(-1.0, -1.0);
+            set_radii(border.style.z,
+                      border.radii[1].xy,
+                      border.widths.zw,
+                      adjusted_widths.zw);
+            set_edge_line(border.widths.zw,
+                          corners.br_outer,
+                          vec2(-1.0, -1.0));
+            style = int(border.style.z);
+            edge_distances = vec4(p1.x - adjusted_widths.z,
+                                  p1.y - adjusted_widths.w,
+                                  p1.x - border.widths.z + adjusted_widths.z,
+                                  p1.y - border.widths.w + adjusted_widths.w);
+            color_delta = vec2(-1.0);
+            break;
+        }
+        case 3: {
+            p0 = vec2(corners.bl_outer.x, corners.bl_inner.y);
+            p1 = vec2(corners.bl_inner.x, corners.bl_outer.y);
+            color0 = border.colors[3];
+            color1 = border.colors[0];
+            vClipCenter = corners.bl_outer + vec2(border.radii[1].z, -border.radii[1].w);
+            vClipSign = vec2(1.0, -1.0);
+            set_radii(border.style.w,
+                      border.radii[1].zw,
+                      border.widths.xw,
+                      adjusted_widths.xw);
+            set_edge_line(border.widths.xw,
+                          corners.bl_outer,
+                          vec2(1.0, -1.0));
+            style = int(border.style.w);
+            edge_distances = vec4(p0.x + adjusted_widths.x,
+                                  p1.y - adjusted_widths.w,
+                                  p0.x + inv_adjusted_widths.x,
+                                  p1.y - border.widths.w + adjusted_widths.w);
+            color_delta = vec2(-1.0, 1.0);
+            break;
+        }
+    }
+
+    switch (int(style)) {
+        case BORDER_STYLE_DOUBLE: {
+            vEdgeDistance = edge_distances;
+            vAlphaSelect = 0.0;
+            vSDFSelect = 0.0;
+            break;
+        }
+        case BORDER_STYLE_GROOVE:
+        case BORDER_STYLE_RIDGE:
+            vEdgeDistance = vec4(edge_distances.xy, 0.0, 0.0);
+            vAlphaSelect = 1.0;
+            vSDFSelect = 1.0;
+            break;
+        default: {
+            vEdgeDistance = vec4(0.0);
+            vAlphaSelect = 1.0;
+            vSDFSelect = 0.0;
+            break;
+        }
+    }
+
+    write_color(color0, color1, style, color_delta);
+
+    RectWithSize segment_rect;
+    segment_rect.p0 = p0;
+    segment_rect.size = p1 - p0;
+
+#ifdef WR_FEATURE_TRANSFORM
+    TransformVertexInfo vi = write_transform_vertex(segment_rect,
+                                                    prim.local_clip_rect,
+                                                    prim.z,
+                                                    prim.layer,
+                                                    prim.task,
+                                                    prim.local_rect.p0);
+    vLocalPos = vi.local_pos;
+    vLocalRect = segment_rect;
+#else
+    VertexInfo vi = write_vertex(segment_rect,
+                                 prim.local_clip_rect,
+                                 prim.z,
+                                 prim.layer,
+                                 prim.task,
+                                 prim.local_rect.p0);
+    vLocalPos = vi.local_pos.xy;
+#endif
+
+    write_clip(vi.screen_pos, prim.clip_area);
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_border_edge.fs.glsl
@@ -0,0 +1,47 @@
+#line 1
+
+/* 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/. */
+
+void main(void) {
+    float alpha = 1.0;
+#ifdef WR_FEATURE_TRANSFORM
+    alpha = 0.0;
+    vec2 local_pos = init_transform_fs(vLocalPos, vLocalRect, alpha);
+#else
+    vec2 local_pos = vLocalPos;
+#endif
+
+    alpha = min(alpha, do_clip());
+
+    // Find the appropriate distance to apply the step over.
+    vec2 fw = fwidth(local_pos);
+
+    // Applies the math necessary to draw a style: double
+    // border. In the case of a solid border, the vertex
+    // shader sets interpolator values that make this have
+    // no effect.
+
+    // Select the x/y coord, depending on which axis this edge is.
+    float pos = mix(local_pos.x, local_pos.y, vAxisSelect);
+
+    // Get signed distance from each of the inner edges.
+    float d0 = pos - vEdgeDistance.x;
+    float d1 = vEdgeDistance.y - pos;
+
+    // SDF union to select both outer edges.
+    float d = min(d0, d1);
+
+    // Select fragment on/off based on signed distance.
+    // No AA here, since we know we're on a straight edge
+    // and the width is rounded to a whole CSS pixel.
+    alpha = min(alpha, mix(vAlphaSelect, 1.0, d < 0.0));
+
+    // Mix color based on first distance.
+    // TODO(gw): Support AA for groove/ridge border edge with transforms.
+    vec4 color = mix(vColor0, vColor1, bvec4(d0 * vEdgeDistance.y > 0.0));
+
+    //oFragColor = vec4(d0 * vEdgeDistance.y, -d0 * vEdgeDistance.y, 0, 1.0);
+    oFragColor = color * vec4(1.0, 1.0, 1.0, alpha);
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_border_edge.glsl
@@ -0,0 +1,16 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+flat varying vec4 vColor0;
+flat varying vec4 vColor1;
+flat varying vec2 vEdgeDistance;
+flat varying float vAxisSelect;
+flat varying float vAlphaSelect;
+
+#ifdef WR_FEATURE_TRANSFORM
+varying vec3 vLocalPos;
+flat varying RectWithSize vLocalRect;
+#else
+varying vec2 vLocalPos;
+#endif
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_border_edge.vs.glsl
@@ -0,0 +1,119 @@
+#line 1
+/* 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/. */
+
+void write_edge_distance(float p0,
+                         float original_width,
+                         float adjusted_width,
+                         float style,
+                         float axis_select,
+                         float sign_adjust) {
+    switch (int(style)) {
+        case BORDER_STYLE_DOUBLE:
+            vEdgeDistance = vec2(p0 + adjusted_width,
+                                 p0 + original_width - adjusted_width);
+            break;
+        case BORDER_STYLE_GROOVE:
+        case BORDER_STYLE_RIDGE:
+            vEdgeDistance = vec2(p0 + adjusted_width, sign_adjust);
+            break;
+        default:
+            vEdgeDistance = vec2(0.0);
+            break;
+    }
+
+    vAxisSelect = axis_select;
+}
+
+void write_alpha_select(float style) {
+    switch (int(style)) {
+        case BORDER_STYLE_DOUBLE:
+            vAlphaSelect = 0.0;
+            break;
+        default:
+            vAlphaSelect = 1.0;
+            break;
+    }
+}
+
+void write_color(vec4 color, float style, bool flip) {
+    vec2 modulate;
+
+    switch (int(style)) {
+        case BORDER_STYLE_GROOVE:
+            modulate = flip ? vec2(1.3, 0.7) : vec2(0.7, 1.3);
+            break;
+        case BORDER_STYLE_RIDGE:
+            modulate = flip ? vec2(0.7, 1.3) : vec2(1.3, 0.7);
+            break;
+        default:
+            modulate = vec2(1.0);
+            break;
+    }
+
+    vColor0 = vec4(color.rgb * modulate.x, color.a);
+    vColor1 = vec4(color.rgb * modulate.y, color.a);
+}
+
+void main(void) {
+    Primitive prim = load_primitive();
+    Border border = fetch_border(prim.prim_index);
+    int sub_part = prim.sub_index;
+    BorderCorners corners = get_border_corners(border, prim.local_rect);
+    vec4 adjusted_widths = get_effective_border_widths(border);
+    vec4 color = border.colors[sub_part];
+
+    RectWithSize segment_rect;
+    switch (sub_part) {
+        case 0:
+            segment_rect.p0 = vec2(corners.tl_outer.x, corners.tl_inner.y);
+            segment_rect.size = vec2(border.widths.x, corners.bl_inner.y - corners.tl_inner.y);
+            write_edge_distance(segment_rect.p0.x, border.widths.x, adjusted_widths.x, border.style.x, 0.0, 1.0);
+            write_alpha_select(border.style.x);
+            write_color(color, border.style.x, false);
+            break;
+        case 1:
+            segment_rect.p0 = vec2(corners.tl_inner.x, corners.tl_outer.y);
+            segment_rect.size = vec2(corners.tr_inner.x - corners.tl_inner.x, border.widths.y);
+            write_edge_distance(segment_rect.p0.y, border.widths.y, adjusted_widths.y, border.style.y, 1.0, 1.0);
+            write_alpha_select(border.style.y);
+            write_color(color, border.style.y, false);
+            break;
+        case 2:
+            segment_rect.p0 = vec2(corners.tr_outer.x - border.widths.z, corners.tr_inner.y);
+            segment_rect.size = vec2(border.widths.z, corners.br_inner.y - corners.tr_inner.y);
+            write_edge_distance(segment_rect.p0.x, border.widths.z, adjusted_widths.z, border.style.z, 0.0, -1.0);
+            write_alpha_select(border.style.z);
+            write_color(color, border.style.z, true);
+            break;
+        case 3:
+            segment_rect.p0 = vec2(corners.bl_inner.x, corners.bl_outer.y - border.widths.w);
+            segment_rect.size = vec2(corners.br_inner.x - corners.bl_inner.x, border.widths.w);
+            write_edge_distance(segment_rect.p0.y, border.widths.w, adjusted_widths.w, border.style.w, 1.0, -1.0);
+            write_alpha_select(border.style.w);
+            write_color(color, border.style.w, true);
+            break;
+    }
+
+#ifdef WR_FEATURE_TRANSFORM
+    TransformVertexInfo vi = write_transform_vertex(segment_rect,
+                                                    prim.local_clip_rect,
+                                                    prim.z,
+                                                    prim.layer,
+                                                    prim.task,
+                                                    prim.local_rect.p0);
+    vLocalPos = vi.local_pos;
+    vLocalRect = segment_rect;
+#else
+    VertexInfo vi = write_vertex(segment_rect,
+                                 prim.local_clip_rect,
+                                 prim.z,
+                                 prim.layer,
+                                 prim.task,
+                                 prim.local_rect.p0);
+    vLocalPos = vi.local_pos;
+#endif
+
+    write_clip(vi.screen_pos, prim.clip_area);
+}
--- a/gfx/webrender/res/ps_box_shadow.vs.glsl
+++ b/gfx/webrender/res/ps_box_shadow.vs.glsl
@@ -7,17 +7,18 @@ void main(void) {
     Primitive prim = load_primitive();
     BoxShadow bs = fetch_boxshadow(prim.prim_index);
     RectWithSize segment_rect = fetch_instance_geometry(prim.sub_index);
 
     VertexInfo vi = write_vertex(segment_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
-                                 prim.task);
+                                 prim.task,
+                                 prim.local_rect.p0);
 
     RenderTaskData child_task = fetch_render_task(prim.user_data.x);
     vUv.z = child_task.data1.x;
 
     // Constant offsets to inset from bilinear filtering border.
     vec2 patch_origin = child_task.data0.xy + vec2(1.0);
     vec2 patch_size_device_pixels = child_task.data0.zw - vec2(2.0);
     vec2 patch_size = patch_size_device_pixels / uDevicePixelRatio;
--- a/gfx/webrender/res/ps_cache_image.vs.glsl
+++ b/gfx/webrender/res/ps_cache_image.vs.glsl
@@ -8,17 +8,18 @@
 
 void main(void) {
     Primitive prim = load_primitive();
 
     VertexInfo vi = write_vertex(prim.local_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
-                                 prim.task);
+                                 prim.task,
+                                 prim.local_rect.p0);
 
     RenderTaskData child_task = fetch_render_task(prim.user_data.x);
     vUv.z = child_task.data1.x;
 
     vec2 texture_size = vec2(textureSize(sCacheRGBA8, 0));
     vec2 uv0 = child_task.data0.xy / texture_size;
     vec2 uv1 = (child_task.data0.xy + child_task.data0.zw) / texture_size;
 
--- a/gfx/webrender/res/ps_composite.fs.glsl
+++ b/gfx/webrender/res/ps_composite.fs.glsl
@@ -166,29 +166,29 @@ const int MixBlendMode_Hue         = 12;
 const int MixBlendMode_Saturation  = 13;
 const int MixBlendMode_Color       = 14;
 const int MixBlendMode_Luminosity  = 15;
 
 void main(void) {
     vec4 Cb = texture(sCacheRGBA8, vUv0);
     vec4 Cs = texture(sCacheRGBA8, vUv1);
 
+    // The mix-blend-mode functions assume no premultiplied alpha
+    Cb.rgb /= Cb.a;
+    Cs.rgb /= Cs.a;
+
     if (Cb.a == 0.0) {
         oFragColor = Cs;
         return;
     }
     if (Cs.a == 0.0) {
         oFragColor = vec4(0.0, 0.0, 0.0, 0.0);
         return;
     }
 
-    // The mix-blend-mode functions assume no premultiplied alpha
-    Cb.rgb /= Cb.a;
-    Cs.rgb /= Cs.a;
-
     // Return yellow if none of the branches match (shouldn't happen).
     vec4 result = vec4(1.0, 1.0, 0.0, 1.0);
 
     switch (vOp) {
         case MixBlendMode_Multiply:
             result.rgb = Multiply(Cb.rgb, Cs.rgb);
             break;
         case MixBlendMode_Screen:
--- a/gfx/webrender/res/ps_gradient.glsl
+++ b/gfx/webrender/res/ps_gradient.glsl
@@ -1,12 +1,12 @@
 /* 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/. */
 
 varying vec4 vColor;
 
 #ifdef WR_FEATURE_TRANSFORM
 varying vec3 vLocalPos;
-flat varying vec4 vLocalRect;
+flat varying RectWithSize vLocalRect;
 #else
 varying vec2 vPos;
 #endif
--- a/gfx/webrender/res/ps_gradient.vs.glsl
+++ b/gfx/webrender/res/ps_gradient.vs.glsl
@@ -56,26 +56,28 @@ void main(void) {
         adjusted_color_g1 = mix(g0.color, g1.color, adjusted_offset.y);
     }
 
 #ifdef WR_FEATURE_TRANSFORM
     TransformVertexInfo vi = write_transform_vertex(segment_rect,
                                                     prim.local_clip_rect,
                                                     prim.z,
                                                     prim.layer,
-                                                    prim.task);
-    vLocalRect = vi.clipped_local_rect;
+                                                    prim.task,
+                                                    prim.local_rect.p0);
+    vLocalRect = segment_rect;
     vLocalPos = vi.local_pos;
     vec2 f = (vi.local_pos.xy - prim.local_rect.p0) / prim.local_rect.size;
 #else
     VertexInfo vi = write_vertex(segment_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
-                                 prim.task);
+                                 prim.task,
+                                 prim.local_rect.p0);
 
     vec2 f = (vi.local_pos - segment_rect.p0) / segment_rect.size;
     vPos = vi.local_pos;
 #endif
 
     write_clip(vi.screen_pos, prim.clip_area);
 
     vColor = mix(adjusted_color_g0, adjusted_color_g1, dot(f, axis));
--- a/gfx/webrender/res/ps_image.fs.glsl
+++ b/gfx/webrender/res/ps_image.fs.glsl
@@ -6,18 +6,17 @@
 
 void main(void) {
 #ifdef WR_FEATURE_TRANSFORM
     float alpha = 0.0;
     vec2 pos = init_transform_fs(vLocalPos, vLocalRect, alpha);
 
     // We clamp the texture coordinate calculation here to the local rectangle boundaries,
     // which makes the edge of the texture stretch instead of repeat.
-    vec2 relative_pos_in_rect =
-         clamp(pos, vLocalRect.xy, vLocalRect.xy + vLocalRect.zw) - vLocalRect.xy;
+    vec2 relative_pos_in_rect = clamp_rect(pos, vLocalRect) - vLocalRect.p0;
 #else
     float alpha = 1.0;
     vec2 relative_pos_in_rect = vLocalPos;
 #endif
 
     alpha = min(alpha, do_clip());
 
     // We calculate the particular tile this fragment belongs to, taking into
--- a/gfx/webrender/res/ps_image.glsl
+++ b/gfx/webrender/res/ps_image.glsl
@@ -7,14 +7,14 @@
 // check GL_TEXTURE_RECTANGLE.
 flat varying vec2 vTextureOffset; // Offset of this image into the texture atlas.
 flat varying vec2 vTextureSize;   // Size of the image in the texture atlas.
 flat varying vec2 vTileSpacing;   // Amount of space between tiled instances of this image.
 flat varying vec4 vStRect;        // Rectangle of valid texture rect.
 
 #ifdef WR_FEATURE_TRANSFORM
 varying vec3 vLocalPos;
-flat varying vec4 vLocalRect;
+flat varying RectWithSize vLocalRect;
 flat varying vec2 vStretchSize;
 #else
 varying vec2 vLocalPos;
 flat varying vec2 vStretchSize;
 #endif
--- a/gfx/webrender/res/ps_image.vs.glsl
+++ b/gfx/webrender/res/ps_image.vs.glsl
@@ -8,26 +8,28 @@ void main(void) {
     Image image = fetch_image(prim.prim_index);
     ResourceRect res = fetch_resource_rect(prim.user_data.x);
 
 #ifdef WR_FEATURE_TRANSFORM
     TransformVertexInfo vi = write_transform_vertex(prim.local_rect,
                                                     prim.local_clip_rect,
                                                     prim.z,
                                                     prim.layer,
-                                                    prim.task);
-    vLocalRect = vi.clipped_local_rect;
+                                                    prim.task,
+                                                    prim.local_rect.p0);
+    vLocalRect = prim.local_rect;
     vLocalPos = vi.local_pos;
 #else
     VertexInfo vi = write_vertex(prim.local_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
-                                 prim.task);
-    vLocalPos = vi.local_pos - vi.local_rect.p0;
+                                 prim.task,
+                                 prim.local_rect.p0);
+    vLocalPos = vi.local_pos - prim.local_rect.p0;
 #endif
 
     write_clip(vi.screen_pos, prim.clip_area);
 
     vTileSpacing = image.stretch_size_and_tile_spacing.zw;
     vStretchSize = image.stretch_size_and_tile_spacing.xy;
 
     // If this is in WR_FEATURE_TEXTURE_RECT mode, the rect and size use
--- a/gfx/webrender/res/ps_radial_gradient.vs.glsl
+++ b/gfx/webrender/res/ps_radial_gradient.vs.glsl
@@ -6,17 +6,18 @@
 void main(void) {
     Primitive prim = load_primitive();
     RadialGradient gradient = fetch_radial_gradient(prim.prim_index);
 
     VertexInfo vi = write_vertex(prim.local_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
-                                 prim.task);
+                                 prim.task,
+                                 prim.local_rect.p0);
 
     vPos = vi.local_pos - prim.local_rect.p0;
 
     vStartCenter = gradient.start_end_center.xy;
     vEndCenter = gradient.start_end_center.zw;
 
     vStartRadius = gradient.start_end_radius_ratio_xy_extend_mode.x;
     vEndRadius = gradient.start_end_radius_ratio_xy_extend_mode.y;
--- a/gfx/webrender/res/ps_rectangle.glsl
+++ b/gfx/webrender/res/ps_rectangle.glsl
@@ -1,10 +1,10 @@
 /* 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/. */
 
 varying vec4 vColor;
 
 #ifdef WR_FEATURE_TRANSFORM
 varying vec3 vLocalPos;
-flat varying vec4 vLocalRect;
+flat varying RectWithSize vLocalRect;
 #endif
--- a/gfx/webrender/res/ps_rectangle.vs.glsl
+++ b/gfx/webrender/res/ps_rectangle.vs.glsl
@@ -7,23 +7,25 @@ void main(void) {
     Primitive prim = load_primitive();
     Rectangle rect = fetch_rectangle(prim.prim_index);
     vColor = rect.color;
 #ifdef WR_FEATURE_TRANSFORM
     TransformVertexInfo vi = write_transform_vertex(prim.local_rect,
                                                     prim.local_clip_rect,
                                                     prim.z,
                                                     prim.layer,
-                                                    prim.task);
-    vLocalRect = vi.clipped_local_rect;
+                                                    prim.task,
+                                                    prim.local_rect.p0);
+    vLocalRect = prim.local_rect;
     vLocalPos = vi.local_pos;
 #else
     VertexInfo vi = write_vertex(prim.local_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
-                                 prim.task);
+                                 prim.task,
+                                 prim.local_rect.p0);
 #endif
 
 #ifdef WR_FEATURE_CLIP
     write_clip(vi.screen_pos, prim.clip_area);
 #endif
 }
--- a/gfx/webrender/res/ps_text_run.glsl
+++ b/gfx/webrender/res/ps_text_run.glsl
@@ -3,10 +3,10 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 flat varying vec4 vColor;
 varying vec2 vUv;
 flat varying vec4 vUvBorder;
 
 #ifdef WR_FEATURE_TRANSFORM
 varying vec3 vLocalPos;
-flat varying vec4 vLocalRect;
+flat varying RectWithSize vLocalRect;
 #endif
--- a/gfx/webrender/res/ps_text_run.vs.glsl
+++ b/gfx/webrender/res/ps_text_run.vs.glsl
@@ -12,27 +12,29 @@ void main(void) {
     RectWithSize local_rect = RectWithSize(glyph.offset.xy,
                                            (res.uv_rect.zw - res.uv_rect.xy) / uDevicePixelRatio);
 
 #ifdef WR_FEATURE_TRANSFORM
     TransformVertexInfo vi = write_transform_vertex(local_rect,
                                                     prim.local_clip_rect,
                                                     prim.z,
                                                     prim.layer,
-                                                    prim.task);
-    vLocalRect = vi.clipped_local_rect;
+                                                    prim.task,
+                                                    local_rect.p0);
+    vLocalRect = local_rect;
     vLocalPos = vi.local_pos;
     vec2 f = (vi.local_pos.xy / vi.local_pos.z - local_rect.p0) / local_rect.size;
 #else
     VertexInfo vi = write_vertex(local_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
-                                 prim.task);
-    vec2 f = (vi.local_pos - vi.local_rect.p0) / (vi.local_rect.p1 - vi.local_rect.p0);
+                                 prim.task,
+                                 local_rect.p0);
+    vec2 f = (vi.local_pos - local_rect.p0) / local_rect.size;
 #endif
 
     write_clip(vi.screen_pos, prim.clip_area);
 
     vec2 texture_size = vec2(textureSize(sColor0, 0));
     vec2 st0 = res.uv_rect.xy / texture_size;
     vec2 st1 = res.uv_rect.zw / texture_size;
 
--- a/gfx/webrender/res/ps_yuv_image.fs.glsl
+++ b/gfx/webrender/res/ps_yuv_image.fs.glsl
@@ -5,18 +5,17 @@
 
 void main(void) {
 #ifdef WR_FEATURE_TRANSFORM
     float alpha = 0.0;
     vec2 pos = init_transform_fs(vLocalPos, vLocalRect, alpha);
 
     // We clamp the texture coordinate calculation here to the local rectangle boundaries,
     // which makes the edge of the texture stretch instead of repeat.
-    vec2 relative_pos_in_rect =
-         clamp(pos, vLocalRect.xy, vLocalRect.xy + vLocalRect.zw) - vLocalRect.xy;
+    vec2 relative_pos_in_rect = clamp_rect(pos, vLocalRect) - vLocalRect.p0;
 #else
     float alpha = 1.0;;
     vec2 relative_pos_in_rect = vLocalPos;
 #endif
 
     alpha = min(alpha, do_clip());
 
     // We clamp the texture coordinates to the half-pixel offset from the borders
--- a/gfx/webrender/res/ps_yuv_image.glsl
+++ b/gfx/webrender/res/ps_yuv_image.glsl
@@ -10,12 +10,12 @@ flat varying vec2 vTextureSizeUv;  // Si
 flat varying vec2 vStretchSize;
 flat varying vec2 vHalfTexelY;     // Normalized length of the half of a Y texel.
 flat varying vec2 vHalfTexelUv;    // Normalized length of the half of u and v texels.
 
 flat varying mat3 vYuvColorMatrix;
 
 #ifdef WR_FEATURE_TRANSFORM
 varying vec3 vLocalPos;
-flat varying vec4 vLocalRect;
+flat varying RectWithSize vLocalRect;
 #else
 varying vec2 vLocalPos;
 #endif
--- a/gfx/webrender/res/ps_yuv_image.vs.glsl
+++ b/gfx/webrender/res/ps_yuv_image.vs.glsl
@@ -5,26 +5,28 @@
 
 void main(void) {
     Primitive prim = load_primitive();
 #ifdef WR_FEATURE_TRANSFORM
     TransformVertexInfo vi = write_transform_vertex(prim.local_rect,
                                                     prim.local_clip_rect,
                                                     prim.z,
                                                     prim.layer,
-                                                    prim.task);
-    vLocalRect = vi.clipped_local_rect;
+                                                    prim.task,
+                                                    prim.local_rect.p0);
+    vLocalRect = prim.local_rect;
     vLocalPos = vi.local_pos;
 #else
     VertexInfo vi = write_vertex(prim.local_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
-                                 prim.task);
-    vLocalPos = vi.local_pos - vi.local_rect.p0;
+                                 prim.task,
+                                 prim.local_rect.p0);
+    vLocalPos = vi.local_pos - prim.local_rect.p0;
 #endif
 
     YuvImage image = fetch_yuv_image(prim.prim_index);
     ResourceRect y_rect = fetch_resource_rect(prim.user_data.x);
     ResourceRect u_rect = fetch_resource_rect(prim.user_data.x + 1);
     ResourceRect v_rect = fetch_resource_rect(prim.user_data.x + 2);
 
     vec2 y_texture_size = vec2(textureSize(sColor0, 0));
--- a/gfx/webrender/res/shared.glsl
+++ b/gfx/webrender/res/shared.glsl
@@ -38,17 +38,16 @@ uniform sampler2D sColor0;
 uniform sampler2D sColor1;
 uniform sampler2D sColor2;
 #else
 uniform sampler2DRect sColor0;
 uniform sampler2DRect sColor1;
 uniform sampler2DRect sColor2;
 #endif
 uniform sampler2D sDither;
-uniform sampler2D sMask;
 
 //======================================================================================
 // Interpolator definitions
 //======================================================================================
 
 //======================================================================================
 // VS only types and UBOs
 //======================================================================================
deleted file mode 100644
--- a/gfx/webrender/src/CLIPPING.md
+++ /dev/null
@@ -1,98 +0,0 @@
-# Clipping in WebRender
-
-The WebRender display list allows defining clips in two different ways. The
-first is specified directly on each display item and cannot be reused between
-items. The second is specified using the `SpecificDisplayItem::Clip` display item
-and can be reused between items as well as used to define scrollable regions.
-
-## Clips
-
-Clips are defined using the ClipRegion in both cases.
-
-```rust
-pub struct ClipRegion {
-    pub main: LayoutRect,
-    pub complex: ItemRange,
-    pub image_mask: Option<ImageMask>,
-}
-```
-
-`main` defines a rectangular clip, while the other members make that rectangle
-smaller. `complex`, if it is not empty, defines the boundaries of a rounded
-rectangle. While `image_mask` defines the positioning, repetition, and data of
-a masking image.
-
-## Item Clips
-
-Item clips are simply a `ClipRegion` structure defined directly on the
-`DisplayItem`. The important thing to note about these clips is that all the
-coordinate in `ClipRegion` **are in the same coordinate space as the item
-itself**. This different than for clips defined by `SpecificDisplayItem::Clip`.
-
-## Clip Display Items
-
-Clip display items allow items to share clips in order to increase performance
-(shared clips are only rasterized once) and to allow for scrolling regions.
-Display items can be assigned a clip display item using the `scroll_layer_id`
-field. An item can be assigned any clip that is defined by its parent stacking
-context or any of the ancestors. The behavior of assigning an id outside of
-this hierarchy is undefined, because that situation does not occur in CSS
-
-The clip display item has a `ClipRegion` as well as several other fields:
-
-```rust
-pub struct ClipDisplayItem {
-    pub id: ScrollLayerId,
-    pub parent_id: ScrollLayerId,
-}
-```
-
-A `ClipDisplayItem` also gets a clip and bounds from the `BaseDisplayItem`. The
-clip is shared among all items that use this `ClipDisplayItem`. Critically,
-**coordinates in this ClipRegion are defined relative to the bounds of the
-ClipDisplayItem itself**. Additionally, WebRender only supports clip rectangles
-that start at the origin of the `BaseDisplayItem` bounds.
-
-The `BaseDisplayItem` bounds are known as the *content rectangle* of the clip. If
-the content rectangle is larger than *main* clipping rectangle, the clip will
-be a scrolling clip and participate in scrolling event capture and
-transformation.
-
-`ClipDisplayItems` are positioned, like all other items, relatively to their
-containing stacking context, yet they also live in a parallel tree defined by
-their `parent_id`. Child clips also include any clipping and scrolling that
-their ancestors do. In this way, a clip is positioned by a stacking context,
-but that position may be adjusted by any scroll offset of its parent clips.
-
-## Clip ids
-
-All clips defined by a `ClipDisplayItem` have an id. It is useful to associate
-an external id with WebRender id in order to allow for tracking and updating
-scroll positions using the WebRender API. In order to make this as cheap as
-possible and to avoid having to create a `HashMap` to map between the two types
-of ids, the WebRender API provides an optional id argument in
-`DisplayListBuilder::define_clip`. The only types of ids that are supported
-here are those created with `ScrollLayerId::new(...)`. If this argument is not
-provided `define_clip` will return a uniquely generated id. Thus, the following
-should always be true:
-
-```rust
-let id = ScrollLayerId::new(my_internal_id, pipeline_id);
-let generated_id = define_clip(content_rect, clip, id);
-assert!(id == generated_id);
-```
-
-## Pending changes
-
-1. Rename `ScrollLayerId` to `ClipId`. The current name is a holdover from the
-   previous design.  ([github issue](https://github.com/servo/webrender/issues/1089))
-
-2. Normalize the way that clipping coordinates are defined. Having them
-   specified in two different ways makes for a confusing API. This should be
-   fixed.  ([github issue](https://github.com/servo/webrender/issues/1090))
-
-3. It should be possible to specify more than one predefined clip for an item.
-   This is necessary for items that live in a scrolling frame, but are also
-   clipped by a clip that lives outside that frame.
-   ([github issue](https://github.com/servo/webrender/issues/840))
-
deleted file mode 100644
--- a/gfx/webrender/src/batch_builder.rs
+++ /dev/null
@@ -1,43 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-use std::f32;
-use webrender_traits::{ColorF, BorderStyle};
-use webrender_traits::{BorderSide};
-
-//const BORDER_DASH_SIZE: f32 = 3.0;
-
-pub trait BorderSideHelpers {
-    fn border_color(&self,
-                    scale_factor_0: f32,
-                    scale_factor_1: f32,
-                    black_color_0: f32,
-                    black_color_1: f32) -> ColorF;
-}
-
-impl BorderSideHelpers for BorderSide {
-    fn border_color(&self,
-                    scale_factor_0: f32,
-                    scale_factor_1: f32,
-                    black_color_0: f32,
-                    black_color_1: f32) -> ColorF {
-        match self.style {
-            BorderStyle::Inset => {
-                if self.color.r != 0.0 || self.color.g != 0.0 || self.color.b != 0.0 {
-                    self.color.scale_rgb(scale_factor_1)
-                } else {
-                    ColorF::new(black_color_0, black_color_0, black_color_0, self.color.a)
-                }
-            }
-            BorderStyle::Outset => {
-                if self.color.r != 0.0 || self.color.g != 0.0 || self.color.b != 0.0 {
-                    self.color.scale_rgb(scale_factor_0)
-                } else {
-                    ColorF::new(black_color_1, black_color_1, black_color_1, self.color.a)
-                }
-            }
-            _ => self.color,
-        }
-    }
-}
--- a/gfx/webrender/src/border.rs
+++ b/gfx/webrender/src/border.rs
@@ -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/. */
 
 use frame_builder::FrameBuilder;
+use prim_store::{BorderPrimitiveCpu, BorderPrimitiveGpu, PrimitiveContainer};
 use tiling::PrimitiveFlags;
-use webrender_traits::{BorderSide, BorderStyle, BorderWidths, NormalBorder};
-use webrender_traits::{ClipRegion, LayerPoint, LayerRect, LayerSize, ScrollLayerId};
+use util::pack_as_float;
+use webrender_traits::{BorderSide, BorderStyle, BorderWidths, ColorF, NormalBorder};
+use webrender_traits::{ClipId, ClipRegion, LayerPoint, LayerRect, LayerSize};
 
 #[derive(Copy, Clone, Debug, PartialEq)]
 pub enum BorderCornerKind {
     None,
     Solid,
-    Complex,
+    Clip,
+    Unhandled,
 }
 
 #[derive(Copy, Clone, Debug, PartialEq)]
 pub enum BorderEdgeKind {
     None,
     Solid,
-    Complex,
+    Clip,