Merge m-c to autoland, a=merge
authorWes Kocher <wkocher@mozilla.com>
Wed, 05 Apr 2017 14:20:01 -0700
changeset 399525 4faa91fb814cc4d641d89b55ca7cd90f4e79765d
parent 399524 44d2f2d786a8719a628895f128757f62efa99d0b (current diff)
parent 399488 867df9483d5af4c8c12e19fab9b0de18bee30db7 (diff)
child 399526 f6144ffbe6368251d2a4fb57c2f112b03b58092f
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to autoland, a=merge
modules/libpref/init/all.js
--- a/browser/components/sessionstore/SessionCookies.jsm
+++ b/browser/components/sessionstore/SessionCookies.jsm
@@ -127,16 +127,18 @@ var SessionCookiesInternal = {
 
   /**
    * Handles observers notifications that are sent whenever cookies are added,
    * changed, or removed. Ensures that the storage is updated accordingly.
    */
   observe(subject, topic, data) {
     switch (data) {
       case "added":
+        this._addCookie(subject);
+        break;
       case "changed":
         this._updateCookie(subject);
         break;
       case "deleted":
         this._removeCookie(subject);
         break;
       case "cleared":
         CookieStore.clear();
@@ -186,17 +188,28 @@ var SessionCookiesInternal = {
     if (entry.children) {
       for (let child of entry.children) {
         this._extractHostsFromEntry(child, hosts);
       }
     }
   },
 
   /**
-   * Updates or adds a given cookie to the store.
+   * Adds a given cookie to the store.
+   */
+  _addCookie(cookie) {
+    cookie.QueryInterface(Ci.nsICookie2);
+
+    if (cookie.isSession) {
+      CookieStore.set(cookie);
+    }
+  },
+
+  /**
+   * Updates a given cookie.
    */
   _updateCookie(cookie) {
     cookie.QueryInterface(Ci.nsICookie2);
 
     if (cookie.isSession) {
       CookieStore.set(cookie);
     } else {
       CookieStore.delete(cookie);
@@ -225,17 +238,17 @@ var SessionCookiesInternal = {
 
   /**
    * Iterates all cookies in the cookies service and puts them into the store
    * if they're session cookies.
    */
   _reloadCookies() {
     let iter = Services.cookies.enumerator;
     while (iter.hasMoreElements()) {
-      this._updateCookie(iter.getNext());
+      this._addCookie(iter.getNext());
     }
   }
 };
 
 /**
  * Generates all possible subdomains for a given host and prepends a leading
  * dot to all variants.
  *
@@ -378,17 +391,34 @@ var CookieStore = {
 
   /**
    * Removes a given cookie.
    *
    * @param cookie
    *        The nsICookie2 object to be removed from storage.
    */
   delete(cookie) {
-    this._ensureMap(cookie).delete(cookie.name);
+    // Deletion shouldn't create new maps.
+    // Bail out whenever we find that an entry or a map doesn't exist.
+    let map = this._hosts.get(cookie.host);
+    if (!map) {
+      return;
+    }
+
+    map = map.get(ChromeUtils.originAttributesToSuffix(cookie.originAttributes));
+    if (!map) {
+      return;
+    }
+
+    map = map.get(cookie.path);
+    if (!map) {
+      return;
+    }
+
+    map.delete(cookie.name);
   },
 
   /**
    * Removes all cookies.
    */
   clear() {
     this._hosts.clear();
   },
--- a/browser/locales/searchplugins/yahoo-jp.xml
+++ b/browser/locales/searchplugins/yahoo-jp.xml
@@ -2,15 +2,15 @@
    - 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/. -->
 
 <SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
 <ShortName>Yahoo! JAPAN</ShortName>
 <Description>Yahoo Search</Description>
 <InputEncoding>UTF-8</InputEncoding>
 <Image width="16" height="16">data:image/x-icon;base64,AAABAAIAEBAAAAEAIABoBAAAJgAAACAgAAABACAAqBAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMA/24zAP//MwD//zMA//8zAP//MwD//wAAAAAAAAAAMwD/7TMA/+MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADkI//8zAP//MwD/bwAAAAAAAAAAAAAAADEA/zQyAP8pNwD/FwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5CP//MwD//zIA/3AAAAAAAAAAAAAAAAAAAAAAMgD/wDIA/6cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0AP8sMwD/9zMA//8zAP//NwD/FwAAAAAAAAAAAAAAADQA/3EzAP//MQD/PgAAAAAAAAAAAAAAAAAAAAA3AP8OMwD/2DMA//80AP9PMwD/WjMA//8xAP8qAAAAAAAAAAAyAP8kMwD//zMA/9EAAAAIAAAAAAAAAAAAAAADMwD/tDMA//8yAP+JAAAAAAAAAAAxAP85MwD//zQA/1gAAAAAAAAAADMA/9UzAP//MwD/qQAAAAAAAAAAMgD/kzMA//8zAP/EAAAABAAAAAAAAAAAMwD//zMA//8zAP//MwD//zMA/30xAP8+MwD/+TEA/z4yAP+sMwD//zMA//8zAP//MwD//zMA/5oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AAD//wAA//8AAP//AADgZwAA+OMAAPjzAADwcQAA4DAAAMMYAADDAAAAA/8AAP//AAD//wAA//8AAP//AAAoAAAAIAAAAEAAAAABACAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMwD//zMA//8zAP//MwD//zMA//8zAP//MwD//zMA//8zAP//MwD//zMA//8zAP//AAAAAAAAAAAAAAAAMQD/HzQA/2MyAP+iAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAyAP+dMwD/9jMA//8zAP//MwD//zMA//8zAP//MwD//zMA//8zAP//MwD//zMA/3gAAAAAAAAAAAAAAAAzAP+/MwD//zMA//80AP9UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANAD/JzQA/4ozAP//MwD//zMA//8zAP/nMwD/NzQA/ycxAP8VAAAABAAAAAAAAAAAAAAAADMA/1AzAP//MwD/zjMA/18AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMA//8zAP//MwD//zMA/98AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjAA/yUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMwD//zMA//8zAP//MwD/3wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADUA/yIzAP9zMwD/UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzAP//MwD//zMA//8zAP/fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANAD/njMA//8zAP/pMwD/DwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANAD/TzMA//8zAP//MwD//zMA/+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzAP9QMwD//zMA//80AP+PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg0AP+UMwD//zMA//8zAP//MwD//jIA/1IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC4A/wszAP/2MwD//zMA//sxAP8qAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMwD/lzMA//8zAP//MwD//zMA/84zAP/5MwD/+TIA/1EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMA/7MzAP//MwD//zQA/7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADIA/1szAP//MwD//zMA//8zAP/fOQD/EjMA/0EzAP/tMwD//DQA/2wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMgD/ZTMA//8zAP//MwD//zIA/1IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAxAP8vMwD/9TMA//8zAP//MwD/+DEA/zQAAAAAAAAAADUA/yIzAP/UMwD//zMA/5YAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAzAP8ZMwD//TMA//8zAP//MwD/3wAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALgD/FjMA/98zAP//MwD//zMA//8yAP9mAAAAAAAAAAAAAAAAAAAAADMA/w8zAP+4MwD//zMA/8MzAP8ZAAAAAAAAAAAAAAAAAAAAAAAAAAAzAP/IMwD//zMA//8zAP//MgD/fwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYzAP/DMwD//zMA//8zAP//MgD/ogAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYzAP+zMwD//zMA/+k0AP9JAAAAAAAAAAAAAAAAAAAAADMA/3kzAP//MwD//zMA//8zAP/2MAD/IAAAAAAAAAAAAAAAAAAAAAAAAAABMwD/pDMA//8zAP//MwD//zMA/9YuAP8LAAAAAAAAAAAAAAAAAAAAAAAAAAA1AP8/NAD/YjMA/5ozAP//MwD//zMA//8zAP+/MwD/gzIA/00wAP8QNQD/KzMA//8zAP//MwD//zMA//8zAP+zAAAAAAAAAAAAAAAAAAAAADMA/40zAP//MwD//zMA//8zAP/3MwD/LQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMA/98zAP/fMwD/3zMA/98zAP/fMwD/3zMA/98zAP/fMwD/3zMA/58AAAAAMwD/3TMA//8zAP/fMwD/kTEA/0MzAP88MgD/YDMA/4IzAP+6MwD//zMA//8zAP//MwD//zMA/7oyAP9HNAD/NjAA/yUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0AP9FMgD/LgAAAAAAAAAAAAAAADMA//YzAP//MwD//zMA//8zAP//MwD//zMA//8zAP//MwD//zMA//8zAP//MwD//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/////////////////////////////////////+ABw//gAcP/+AHD//4fz//+H/H//h/w//wf8P/4D/B/+Af4f/AD+D/gYPgfwPB8H4H4PA8B8AAPA/ACAAD//nAA//////////////////////////////////////////////</Image>
-<Url type="text/html" method="GET" template="http://search.yahoo.co.jp/search" resultdomain="yahoo.co.jp">
+<Url type="text/html" method="GET" template="https://search.yahoo.co.jp/search" resultdomain="yahoo.co.jp">
   <Param name="p" value="{searchTerms}"/>
   <Param name="ei" value="UTF-8"/>
   <Param name="fr" value="mozff" />
 </Url>
-<SearchForm>http://search.yahoo.co.jp/</SearchForm>
+<SearchForm>https://search.yahoo.co.jp/</SearchForm>
 </SearchPlugin>
--- a/devtools/client/projecteditor/test/browser.ini
+++ b/devtools/client/projecteditor/test/browser.ini
@@ -5,16 +5,17 @@ support-files =
   head.js
   helper_homepage.html
   helper_edits.js
   projecteditor-test.xul
 
 [browser_projecteditor_app_options.js]
 [browser_projecteditor_confirm_unsaved.js]
 [browser_projecteditor_contextmenu_01.js]
+skip-if = asan # Bug 1083140
 [browser_projecteditor_contextmenu_02.js]
 skip-if = true # Bug 1173950
 [browser_projecteditor_delete_file.js]
 skip-if = e10s # Frequent failures in e10s - Bug 1020027
 [browser_projecteditor_rename_file_01.js]
 [browser_projecteditor_rename_file_02.js]
 [browser_projecteditor_editing_01.js]
 [browser_projecteditor_editors_image.js]
--- a/dom/filesystem/FileSystemTaskBase.cpp
+++ b/dom/filesystem/FileSystemTaskBase.cpp
@@ -79,17 +79,18 @@ DispatchToIOThread(nsIRunnable* aRunnabl
 
 // This runnable is used when an error value is set before doing any real
 // operation on the I/O thread. In this case we skip all and we directly
 // communicate the error.
 class ErrorRunnable final : public CancelableRunnable
 {
 public:
   explicit ErrorRunnable(FileSystemTaskChildBase* aTask)
-    : mTask(aTask)
+    : CancelableRunnable("ErrorRunnable")
+    , mTask(aTask)
   {
     MOZ_ASSERT(aTask);
   }
 
   NS_IMETHOD
   Run() override
   {
     MOZ_ASSERT(NS_IsMainThread());
@@ -106,22 +107,25 @@ private:
 } // anonymous namespace
 
 NS_IMPL_ISUPPORTS(FileSystemTaskChildBase, nsIIPCBackgroundChildCreateCallback)
 
 /**
  * FileSystemTaskBase class
  */
 
-FileSystemTaskChildBase::FileSystemTaskChildBase(FileSystemBase* aFileSystem)
+FileSystemTaskChildBase::FileSystemTaskChildBase(nsIGlobalObject* aGlobalObject,
+                                                 FileSystemBase* aFileSystem)
   : mErrorValue(NS_OK)
   , mFileSystem(aFileSystem)
+  , mGlobalObject(aGlobalObject)
 {
   MOZ_ASSERT(aFileSystem, "aFileSystem should not be null.");
   aFileSystem->AssertIsOnOwningThread();
+  MOZ_ASSERT(aGlobalObject);
 }
 
 FileSystemTaskChildBase::~FileSystemTaskChildBase()
 {
   mFileSystem->AssertIsOnOwningThread();
 }
 
 FileSystemBase*
@@ -155,18 +159,17 @@ FileSystemTaskChildBase::ActorFailed()
 }
 
 void
 FileSystemTaskChildBase::ActorCreated(mozilla::ipc::PBackgroundChild* aActor)
 {
   if (HasError()) {
     // In this case we don't want to use IPC at all.
     RefPtr<ErrorRunnable> runnable = new ErrorRunnable(this);
-    DebugOnly<nsresult> rv = NS_DispatchToCurrentThread(runnable);
-    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToCurrentThread failed");
+    FileSystemUtils::DispatchRunnable(mGlobalObject, runnable.forget());
     return;
   }
 
   if (mFileSystem->IsShutdown()) {
     return;
   }
 
   nsAutoString serialization;
@@ -179,21 +182,24 @@ FileSystemTaskChildBase::ActorCreated(mo
     return;
   }
 
   // Retain a reference so the task object isn't deleted without IPDL's
   // knowledge. The reference will be released by
   // mozilla::ipc::BackgroundChildImpl::DeallocPFileSystemRequestChild.
   NS_ADDREF_THIS();
 
-  mozilla::ipc::PBackgroundChild* actor =
-    mozilla::ipc::BackgroundChild::GetForCurrentThread();
-  MOZ_ASSERT(actor);
+  if (NS_IsMainThread()) {
+    nsIEventTarget* target = mGlobalObject->EventTargetFor(TaskCategory::Other);
+    MOZ_ASSERT(target);
 
-  actor->SendPFileSystemRequestConstructor(this, params);
+    aActor->SetEventTargetForActor(this, target);
+  }
+
+  aActor->SendPFileSystemRequestConstructor(this, params);
 }
 
 void
 FileSystemTaskChildBase::SetRequestResult(const FileSystemResponseValue& aValue)
 {
   mFileSystem->AssertIsOnOwningThread();
 
   if (aValue.type() == FileSystemResponseValue::TFileSystemErrorResponse) {
--- a/dom/filesystem/FileSystemTaskBase.h
+++ b/dom/filesystem/FileSystemTaskBase.h
@@ -133,17 +133,18 @@ public:
 
   bool
   HasError() const { return NS_FAILED(mErrorValue); }
 
 protected:
   /*
    * To create a task to handle the page content request.
    */
-  explicit FileSystemTaskChildBase(FileSystemBase* aFileSystem);
+  FileSystemTaskChildBase(nsIGlobalObject* aGlobalObject,
+                          FileSystemBase* aFileSystem);
 
   virtual
   ~FileSystemTaskChildBase();
 
   /*
    * Wrap the task parameter to FileSystemParams for sending it through IPC.
    * It will be called when we need to forward a task from the child process to
    * the parent process. This method runs in the owning thread.
@@ -164,16 +165,17 @@ protected:
                           ErrorResult& aRv) = 0;
 
   // Overrides PFileSystemRequestChild
   virtual mozilla::ipc::IPCResult
   Recv__delete__(const FileSystemResponseValue& value) override;
 
   nsresult mErrorValue;
   RefPtr<FileSystemBase> mFileSystem;
+  nsCOMPtr<nsIGlobalObject> mGlobalObject;
 
 private:
 
   /*
    * Unwrap the IPC message to get the task result.
    * It will be called when the task is completed and an IPC message is received
    * in the content process and we want to get the task result. This runs on the
    * owning thread.
@@ -182,16 +184,20 @@ private:
   SetRequestResult(const FileSystemResponseValue& aValue);
 };
 
 // This class is the 'alter ego' of FileSystemTaskChildBase in the PBackground
 // world.
 class FileSystemTaskParentBase : public Runnable
 {
 public:
+  FileSystemTaskParentBase()
+    : Runnable("FileSystemTaskParentBase")
+  {}
+
   /*
    * Start the task. This must be called from the PBackground thread only.
    */
   void
   Start();
 
   /*
    * The error codes are defined in xpcom/base/ErrorList.h and their
--- a/dom/filesystem/FileSystemUtils.cpp
+++ b/dom/filesystem/FileSystemUtils.cpp
@@ -66,10 +66,33 @@ FileSystemUtils::IsValidRelativeDOMPath(
     }
 
     aParts.AppendElement(pathComponent);
   }
 
   return true;
 }
 
+/* static */ nsresult
+FileSystemUtils::DispatchRunnable(nsIGlobalObject* aGlobal,
+                                  already_AddRefed<nsIRunnable>&& aRunnable)
+{
+  nsCOMPtr<nsIRunnable> runnable = aRunnable;
+
+  nsCOMPtr<nsIEventTarget> target;
+  if (!aGlobal) {
+    target = SystemGroup::EventTargetFor(TaskCategory::Other);
+  } else {
+    target = aGlobal->EventTargetFor(TaskCategory::Other);
+  }
+
+  MOZ_ASSERT(target);
+
+  nsresult rv = target->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/FileSystemUtils.h
+++ b/dom/filesystem/FileSystemUtils.h
@@ -3,16 +3,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_dom_FileSystemUtils_h
 #define mozilla_dom_FileSystemUtils_h
 
 class nsIFile;
+class nsIRunnable;
 
 namespace mozilla {
 namespace dom {
 
 #define FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL "/"
 #define FILESYSTEM_DOM_PATH_SEPARATOR_CHAR '/'
 
 /*
@@ -31,14 +32,22 @@ public:
 
   /**
    * Return true if this is valid DOMPath. It also splits the path in
    * subdirectories and stores them in aParts.
    */
   static bool
   IsValidRelativeDOMPath(const nsAString& aPath,
                          nsTArray<nsString>& aParts);
+
+  /**
+   * Helper method. If aGlobal is null, the SystemGroup EventTarget will be
+   * used.
+   */
+  static nsresult
+  DispatchRunnable(nsIGlobalObject* aGlobal,
+                   already_AddRefed<nsIRunnable>&& aRunnable);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_FileSystemUtils_h
--- a/dom/filesystem/GetDirectoryListingTask.cpp
+++ b/dom/filesystem/GetDirectoryListingTask.cpp
@@ -33,42 +33,43 @@ GetDirectoryListingTaskChild::Create(Fil
                                      nsIFile* aTargetPath,
                                      const nsAString& aFilters,
                                      ErrorResult& aRv)
 {
   MOZ_ASSERT(aFileSystem);
   MOZ_ASSERT(aDirectory);
   aFileSystem->AssertIsOnOwningThread();
 
-  RefPtr<GetDirectoryListingTaskChild> task =
-    new GetDirectoryListingTaskChild(aFileSystem, aDirectory, aTargetPath,
-                                     aFilters);
-
-  // aTargetPath can be null. In this case SetError will be called.
-
   nsCOMPtr<nsIGlobalObject> globalObject =
     do_QueryInterface(aFileSystem->GetParentObject());
   if (NS_WARN_IF(!globalObject)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
+  RefPtr<GetDirectoryListingTaskChild> task =
+    new GetDirectoryListingTaskChild(globalObject, aFileSystem, aDirectory,
+                                     aTargetPath, aFilters);
+
+  // aTargetPath can be null. In this case SetError will be called.
+
   task->mPromise = Promise::Create(globalObject, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return task.forget();
 }
 
-GetDirectoryListingTaskChild::GetDirectoryListingTaskChild(FileSystemBase* aFileSystem,
+GetDirectoryListingTaskChild::GetDirectoryListingTaskChild(nsIGlobalObject* aGlobalObject,
+                                                           FileSystemBase* aFileSystem,
                                                            Directory* aDirectory,
                                                            nsIFile* aTargetPath,
                                                            const nsAString& aFilters)
-  : FileSystemTaskChildBase(aFileSystem)
+  : FileSystemTaskChildBase(aGlobalObject, aFileSystem)
   , mDirectory(aDirectory)
   , mTargetPath(aTargetPath)
   , mFilters(aFilters)
 {
   MOZ_ASSERT(aFileSystem);
   aFileSystem->AssertIsOnOwningThread();
 }
 
--- a/dom/filesystem/GetDirectoryListingTask.h
+++ b/dom/filesystem/GetDirectoryListingTask.h
@@ -31,17 +31,18 @@ public:
   virtual
   ~GetDirectoryListingTaskChild();
 
   already_AddRefed<Promise>
   GetPromise();
 
 private:
   // If aDirectoryOnly is set, we should ensure that the target is a directory.
-  GetDirectoryListingTaskChild(FileSystemBase* aFileSystem,
+  GetDirectoryListingTaskChild(nsIGlobalObject* aGlobalObject,
+                               FileSystemBase* aFileSystem,
                                Directory* aDirectory,
                                nsIFile* aTargetPath,
                                const nsAString& aFilters);
 
   virtual FileSystemParams
   GetRequestParams(const nsString& aSerializedDOMPath,
                    ErrorResult& aRv) const override;
 
--- a/dom/filesystem/GetFileOrDirectoryTask.cpp
+++ b/dom/filesystem/GetFileOrDirectoryTask.cpp
@@ -27,39 +27,40 @@ namespace dom {
 /* static */ already_AddRefed<GetFileOrDirectoryTaskChild>
 GetFileOrDirectoryTaskChild::Create(FileSystemBase* aFileSystem,
                                     nsIFile* aTargetPath,
                                     ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem);
 
-  RefPtr<GetFileOrDirectoryTaskChild> task =
-    new GetFileOrDirectoryTaskChild(aFileSystem, aTargetPath);
-
-  // aTargetPath can be null. In this case SetError will be called.
-
   nsCOMPtr<nsIGlobalObject> globalObject =
     do_QueryInterface(aFileSystem->GetParentObject());
   if (NS_WARN_IF(!globalObject)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
+  RefPtr<GetFileOrDirectoryTaskChild> task =
+    new GetFileOrDirectoryTaskChild(globalObject, aFileSystem, aTargetPath);
+
+  // aTargetPath can be null. In this case SetError will be called.
+
   task->mPromise = Promise::Create(globalObject, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return task.forget();
 }
 
-GetFileOrDirectoryTaskChild::GetFileOrDirectoryTaskChild(FileSystemBase* aFileSystem,
+GetFileOrDirectoryTaskChild::GetFileOrDirectoryTaskChild(nsIGlobalObject* aGlobalObject,
+                                                         FileSystemBase* aFileSystem,
                                                          nsIFile* aTargetPath)
-  : FileSystemTaskChildBase(aFileSystem)
+  : FileSystemTaskChildBase(aGlobalObject, aFileSystem)
   , mTargetPath(aTargetPath)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem);
 }
 
 GetFileOrDirectoryTaskChild::~GetFileOrDirectoryTaskChild()
 {
--- a/dom/filesystem/GetFileOrDirectoryTask.h
+++ b/dom/filesystem/GetFileOrDirectoryTask.h
@@ -38,17 +38,18 @@ protected:
 
   virtual void
   SetSuccessRequestResult(const FileSystemResponseValue& aValue,
                           ErrorResult& aRv) override;
   virtual void
   HandlerCallback() override;
 
 private:
-  GetFileOrDirectoryTaskChild(FileSystemBase* aFileSystem,
+  GetFileOrDirectoryTaskChild(nsIGlobalObject* aGlobalObject,
+                              FileSystemBase* aFileSystem,
                               nsIFile* aTargetPath);
 
   RefPtr<Promise> mPromise;
   nsCOMPtr<nsIFile> mTargetPath;
 
   RefPtr<File> mResultFile;
   RefPtr<Directory> mResultDirectory;
 };
--- a/dom/filesystem/GetFilesHelper.cpp
+++ b/dom/filesystem/GetFilesHelper.cpp
@@ -3,42 +3,47 @@
 /* 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 "GetFilesHelper.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/FileBlobImpl.h"
+#include "FileSystemUtils.h"
 #include "nsProxyRelease.h"
 
 namespace mozilla {
 namespace dom {
 
 namespace {
 
 // This class is used in the DTOR of GetFilesHelper to release resources in the
 // correct thread.
 class ReleaseRunnable final : public Runnable
 {
 public:
+  ReleaseRunnable()
+    : Runnable("ReleaseRunnable")
+  {}
+
   static void
   MaybeReleaseOnMainThread(nsTArray<RefPtr<Promise>>& aPromises,
                            nsTArray<RefPtr<GetFilesCallback>>& aCallbacks,
                            Sequence<RefPtr<File>>& aFiles,
                            already_AddRefed<nsIGlobalObject> aGlobal)
   {
     nsCOMPtr<nsIGlobalObject> global(aGlobal);
     if (NS_IsMainThread()) {
       return;
     }
 
     RefPtr<ReleaseRunnable> runnable =
       new ReleaseRunnable(aPromises, aCallbacks, aFiles, global.forget());
-    NS_DispatchToMainThread(runnable);
+    FileSystemUtils::DispatchRunnable(nullptr, runnable.forget());
   }
 
   NS_IMETHOD
   Run() override
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     mPromises.Clear();
@@ -125,17 +130,18 @@ GetFilesHelper::Create(nsIGlobalObject* 
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return helper.forget();
 }
 
 GetFilesHelper::GetFilesHelper(nsIGlobalObject* aGlobal, bool aRecursiveFlag)
-  : GetFilesHelperBase(aRecursiveFlag)
+  : Runnable("GetFilesHelper")
+  , GetFilesHelperBase(aRecursiveFlag)
   , mGlobal(aGlobal)
   , mListingCompleted(false)
   , mErrorResult(NS_OK)
   , mMutex("GetFilesHelper::mMutex")
   , mCanceled(false)
 {
 }
 
@@ -222,17 +228,18 @@ GetFilesHelper::Run()
     RunIO();
 
     // If this operation has been canceled, we don't have to go back to
     // main-thread.
     if (IsCanceled()) {
       return NS_OK;
     }
 
-    return NS_DispatchToMainThread(this);
+    RefPtr<Runnable> runnable = this;
+    return FileSystemUtils::DispatchRunnable(nullptr, runnable.forget());
   }
 
   // We are here, but we should not do anything on this thread because, in the
   // meantime, the operation has been canceled.
   if (IsCanceled()) {
     return NS_OK;
   }
 
--- a/dom/filesystem/GetFilesTask.cpp
+++ b/dom/filesystem/GetFilesTask.cpp
@@ -39,34 +39,35 @@ GetFilesTaskChild::Create(FileSystemBase
   nsCOMPtr<nsIGlobalObject> globalObject =
     do_QueryInterface(aFileSystem->GetParentObject());
   if (NS_WARN_IF(!globalObject)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   RefPtr<GetFilesTaskChild> task =
-    new GetFilesTaskChild(aFileSystem, aDirectory, aTargetPath,
+    new GetFilesTaskChild(globalObject, aFileSystem, aDirectory, aTargetPath,
                           aRecursiveFlag);
 
   // aTargetPath can be null. In this case SetError will be called.
 
   task->mPromise = Promise::Create(globalObject, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return task.forget();
 }
 
-GetFilesTaskChild::GetFilesTaskChild(FileSystemBase* aFileSystem,
+GetFilesTaskChild::GetFilesTaskChild(nsIGlobalObject *aGlobalObject,
+                                     FileSystemBase* aFileSystem,
                                      Directory* aDirectory,
                                      nsIFile* aTargetPath,
                                      bool aRecursiveFlag)
-  : FileSystemTaskChildBase(aFileSystem)
+  : FileSystemTaskChildBase(aGlobalObject, aFileSystem)
   , mDirectory(aDirectory)
   , mTargetPath(aTargetPath)
   , mRecursiveFlag(aRecursiveFlag)
 {
   MOZ_ASSERT(aFileSystem);
   MOZ_ASSERT(aDirectory);
   aFileSystem->AssertIsOnOwningThread();
 }
--- a/dom/filesystem/GetFilesTask.h
+++ b/dom/filesystem/GetFilesTask.h
@@ -31,17 +31,18 @@ public:
   virtual
   ~GetFilesTaskChild();
 
   already_AddRefed<Promise>
   GetPromise();
 
 private:
   // If aDirectoryOnly is set, we should ensure that the target is a directory.
-  GetFilesTaskChild(FileSystemBase* aFileSystem,
+  GetFilesTaskChild(nsIGlobalObject* aGlobalObject,
+                    FileSystemBase* aFileSystem,
                     Directory* aDirectory,
                     nsIFile* aTargetPath,
                     bool aRecursiveFlag);
 
   virtual FileSystemParams
   GetRequestParams(const nsString& aSerializedDOMPath,
                    ErrorResult& aRv) const override;
 
--- a/dom/filesystem/compat/CallbackRunnables.cpp
+++ b/dom/filesystem/compat/CallbackRunnables.cpp
@@ -21,34 +21,36 @@
 
 #include "../GetFileOrDirectoryTask.h"
 
 namespace mozilla {
 namespace dom {
 
 EntryCallbackRunnable::EntryCallbackRunnable(FileSystemEntryCallback* aCallback,
                                              FileSystemEntry* aEntry)
-  : mCallback(aCallback)
+  : Runnable("EntryCallbackRunnable")
+  , mCallback(aCallback)
   , mEntry(aEntry)
 {
   MOZ_ASSERT(aCallback);
   MOZ_ASSERT(aEntry);
 }
 
 NS_IMETHODIMP
 EntryCallbackRunnable::Run()
 {
   mCallback->HandleEvent(*mEntry);
   return NS_OK;
 }
 
 ErrorCallbackRunnable::ErrorCallbackRunnable(nsIGlobalObject* aGlobalObject,
                                              ErrorCallback* aCallback,
                                              nsresult aError)
-  : mGlobal(aGlobalObject)
+  : Runnable("ErrorCallbackRunnable")
+  , mGlobal(aGlobalObject)
   , mCallback(aCallback)
   , mError(aError)
 {
   MOZ_ASSERT(aGlobalObject);
   MOZ_ASSERT(aCallback);
   MOZ_ASSERT(NS_FAILED(aError));
 }
 
@@ -56,17 +58,18 @@ NS_IMETHODIMP
 ErrorCallbackRunnable::Run()
 {
   RefPtr<DOMException> exception = DOMException::Create(mError);
   mCallback->HandleEvent(*exception);
   return NS_OK;
 }
 
 EmptyEntriesCallbackRunnable::EmptyEntriesCallbackRunnable(FileSystemEntriesCallback* aCallback)
-  : mCallback(aCallback)
+  : Runnable("EmptyEntriesCallbackRunnable")
+  , mCallback(aCallback)
 {
   MOZ_ASSERT(aCallback);
 }
 
 NS_IMETHODIMP
 EmptyEntriesCallbackRunnable::Run()
 {
   Sequence<OwningNonNull<FileSystemEntry>> sequence;
@@ -253,49 +256,51 @@ void
 GetEntryHelper::Error(nsresult aError)
 {
   MOZ_ASSERT(NS_FAILED(aError));
 
   if (mErrorCallback) {
     RefPtr<ErrorCallbackRunnable> runnable =
       new ErrorCallbackRunnable(mParentEntry->GetParentObject(),
                                 mErrorCallback, aError);
-    DebugOnly<nsresult> rv = NS_DispatchToMainThread(runnable);
-    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed");
+
+    FileSystemUtils::DispatchRunnable(mParentEntry->GetParentObject(),
+                                      runnable.forget());
   }
 }
 
 NS_IMPL_ISUPPORTS0(GetEntryHelper);
 
 /* static */ void
-FileSystemEntryCallbackHelper::Call(const Optional<OwningNonNull<FileSystemEntryCallback>>& aEntryCallback,
+FileSystemEntryCallbackHelper::Call(nsIGlobalObject* aGlobalObject,
+                                    const Optional<OwningNonNull<FileSystemEntryCallback>>& aEntryCallback,
                                     FileSystemEntry* aEntry)
 {
+  MOZ_ASSERT(aGlobalObject);
   MOZ_ASSERT(aEntry);
 
   if (aEntryCallback.WasPassed()) {
     RefPtr<EntryCallbackRunnable> runnable =
       new EntryCallbackRunnable(&aEntryCallback.Value(), aEntry);
 
-    DebugOnly<nsresult> rv = NS_DispatchToMainThread(runnable);
-    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed");
+    FileSystemUtils::DispatchRunnable(aGlobalObject, runnable.forget());
   }
 }
 
 /* static */ void
 ErrorCallbackHelper::Call(nsIGlobalObject* aGlobal,
                           const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback,
                           nsresult aError)
 {
   MOZ_ASSERT(aGlobal);
   MOZ_ASSERT(NS_FAILED(aError));
 
   if (aErrorCallback.WasPassed()) {
     RefPtr<ErrorCallbackRunnable> runnable =
       new ErrorCallbackRunnable(aGlobal, &aErrorCallback.Value(), aError);
-    DebugOnly<nsresult> rv = NS_DispatchToMainThread(runnable);
-    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed");
+
+    FileSystemUtils::DispatchRunnable(aGlobal, runnable.forget());
   }
 }
 
 } // dom namespace
 } // mozilla namespace
 
--- a/dom/filesystem/compat/CallbackRunnables.h
+++ b/dom/filesystem/compat/CallbackRunnables.h
@@ -104,17 +104,18 @@ private:
 
   FileSystemDirectoryEntry::GetInternalType mType;
 };
 
 class FileSystemEntryCallbackHelper
 {
 public:
   static void
-  Call(const Optional<OwningNonNull<FileSystemEntryCallback>>& aEntryCallback,
+  Call(nsIGlobalObject* aGlobalObject,
+       const Optional<OwningNonNull<FileSystemEntryCallback>>& aEntryCallback,
        FileSystemEntry* aEntry);
 };
 
 class ErrorCallbackHelper
 {
 public:
   static void
   Call(nsIGlobalObject* aGlobal,
--- a/dom/filesystem/compat/FileSystemDirectoryReader.cpp
+++ b/dom/filesystem/compat/FileSystemDirectoryReader.cpp
@@ -3,16 +3,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/. */
 
 #include "FileSystemDirectoryReader.h"
 #include "CallbackRunnables.h"
 #include "FileSystemFileEntry.h"
 #include "mozilla/dom/FileBinding.h"
+#include "mozilla/dom/FileSystemUtils.h"
 #include "mozilla/dom/Directory.h"
 #include "mozilla/dom/DirectoryBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
 
 namespace mozilla {
 namespace dom {
 
@@ -95,18 +96,19 @@ public:
   virtual void
   RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
   {
     if (mErrorCallback) {
       RefPtr<ErrorCallbackRunnable> runnable =
         new ErrorCallbackRunnable(mParentEntry->GetParentObject(),
                                   mErrorCallback,
                                   NS_ERROR_DOM_INVALID_STATE_ERR);
-      DebugOnly<nsresult> rv = NS_DispatchToMainThread(runnable);
-      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed");
+
+      FileSystemUtils::DispatchRunnable(mParentEntry->GetParentObject(),
+                                        runnable.forget());
     }
   }
 
 private:
   ~PromiseHandler() {}
 
   RefPtr<FileSystemDirectoryEntry> mParentEntry;
   RefPtr<FileSystem> mFileSystem;
@@ -156,18 +158,18 @@ FileSystemDirectoryReader::ReadEntries(F
                                        const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback,
                                        ErrorResult& aRv)
 {
   MOZ_ASSERT(mDirectory);
 
   if (mAlreadyRead) {
     RefPtr<EmptyEntriesCallbackRunnable> runnable =
       new EmptyEntriesCallbackRunnable(&aSuccessCallback);
-    aRv = NS_DispatchToMainThread(runnable);
-    NS_WARNING_ASSERTION(!aRv.Failed(), "NS_DispatchToMainThread failed");
+
+    FileSystemUtils::DispatchRunnable(GetParentObject(), runnable.forget());
     return;
   }
 
   // This object can be used only once.
   mAlreadyRead = true;
 
   ErrorResult rv;
   RefPtr<Promise> promise = mDirectory->GetFilesAndDirectories(rv);
--- a/dom/filesystem/compat/FileSystemEntry.cpp
+++ b/dom/filesystem/compat/FileSystemEntry.cpp
@@ -73,17 +73,18 @@ void
 FileSystemEntry::GetParent(const Optional<OwningNonNull<FileSystemEntryCallback>>& aSuccessCallback,
                            const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback)
 {
   if (!aSuccessCallback.WasPassed() && !aErrorCallback.WasPassed()) {
     return;
   }
 
   if (mParentEntry) {
-    FileSystemEntryCallbackHelper::Call(aSuccessCallback, mParentEntry);
+    FileSystemEntryCallbackHelper::Call(GetParentObject(), aSuccessCallback,
+                                        mParentEntry);
     return;
   }
 
-  FileSystemEntryCallbackHelper::Call(aSuccessCallback, this);
+  FileSystemEntryCallbackHelper::Call(GetParentObject(), aSuccessCallback, this);
 }
 
 } // dom namespace
 } // mozilla namespace
--- a/dom/filesystem/compat/FileSystemFileEntry.cpp
+++ b/dom/filesystem/compat/FileSystemFileEntry.cpp
@@ -2,30 +2,32 @@
 /* vim: set ts=8 sts=2 et sw=2 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 "FileSystemFileEntry.h"
 #include "CallbackRunnables.h"
 #include "mozilla/dom/File.h"
+#include "mozilla/dom/FileSystemUtils.h"
 #include "mozilla/dom/MultipartBlobImpl.h"
 #include "mozilla/dom/FileSystemFileEntryBinding.h"
 
 namespace mozilla {
 namespace dom {
 
 namespace {
 
 class FileCallbackRunnable final : public Runnable
 {
 public:
   FileCallbackRunnable(FileCallback* aCallback, ErrorCallback* aErrorCallback,
                        File* aFile)
-    : mCallback(aCallback)
+    : Runnable("FileCallbackRunnable")
+    , mCallback(aCallback)
     , mErrorCallback(aErrorCallback)
     , mFile(aFile)
   {
     MOZ_ASSERT(aCallback);
     MOZ_ASSERT(aFile);
   }
 
   NS_IMETHOD
@@ -125,14 +127,14 @@ void
 FileSystemFileEntry::GetFile(FileCallback& aSuccessCallback,
                              const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback) const
 {
   RefPtr<FileCallbackRunnable> runnable =
     new FileCallbackRunnable(&aSuccessCallback,
                              aErrorCallback.WasPassed()
                                ? &aErrorCallback.Value() : nullptr,
                              mFile);
-  DebugOnly<nsresult> rv = NS_DispatchToMainThread(runnable);
-  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed");
+
+  FileSystemUtils::DispatchRunnable(GetParentObject(), runnable.forget());
 }
 
 } // dom namespace
 } // mozilla namespace
--- a/dom/filesystem/compat/FileSystemRootDirectoryEntry.cpp
+++ b/dom/filesystem/compat/FileSystemRootDirectoryEntry.cpp
@@ -110,18 +110,19 @@ FileSystemRootDirectoryEntry::GetInterna
       ErrorCallbackHelper::Call(GetParentObject(), aErrorCallback,
                                 NS_ERROR_DOM_TYPE_MISMATCH_ERR);
       return;
     }
 
     if (aSuccessCallback.WasPassed()) {
       RefPtr<EntryCallbackRunnable> runnable =
         new EntryCallbackRunnable(&aSuccessCallback.Value(), entry);
-      DebugOnly<nsresult> rv = NS_DispatchToMainThread(runnable);
-      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed");
+
+      FileSystemUtils::DispatchRunnable(GetParentObject(), runnable.forget());
+
     }
     return;
   }
 
   // Subdirectories, but this is a file.
   if (entry->IsFile()) {
     ErrorCallbackHelper::Call(GetParentObject(), aErrorCallback,
                               NS_ERROR_DOM_NOT_FOUND_ERR);
--- a/dom/filesystem/compat/FileSystemRootDirectoryReader.cpp
+++ b/dom/filesystem/compat/FileSystemRootDirectoryReader.cpp
@@ -2,28 +2,30 @@
 /* vim: set ts=8 sts=2 et sw=2 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 "FileSystemRootDirectoryReader.h"
 #include "CallbackRunnables.h"
 #include "nsIGlobalObject.h"
+#include "mozilla/dom/FileSystemUtils.h"
 
 namespace mozilla {
 namespace dom {
 
 namespace {
 
 class EntriesCallbackRunnable final : public Runnable
 {
 public:
   EntriesCallbackRunnable(FileSystemEntriesCallback* aCallback,
                           const Sequence<RefPtr<FileSystemEntry>>& aEntries)
-    : mCallback(aCallback)
+    : Runnable("EntriesCallbackRunnable")
+    , mCallback(aCallback)
     , mEntries(aEntries)
   {
     MOZ_ASSERT(aCallback);
   }
 
   NS_IMETHOD
   Run() override
   {
@@ -73,24 +75,24 @@ FileSystemRootDirectoryReader::~FileSyst
 void
 FileSystemRootDirectoryReader::ReadEntries(FileSystemEntriesCallback& aSuccessCallback,
                                            const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback,
                                            ErrorResult& aRv)
 {
   if (mAlreadyRead) {
     RefPtr<EmptyEntriesCallbackRunnable> runnable =
       new EmptyEntriesCallbackRunnable(&aSuccessCallback);
-    aRv = NS_DispatchToMainThread(runnable);
-    NS_WARNING_ASSERTION(!aRv.Failed(), "NS_DispatchToMainThread failed");
+
+    aRv = FileSystemUtils::DispatchRunnable(GetParentObject(), runnable.forget());
     return;
   }
 
   // This object can be used only once.
   mAlreadyRead = true;
 
   RefPtr<EntriesCallbackRunnable> runnable =
     new EntriesCallbackRunnable(&aSuccessCallback, mEntries);
-  aRv = NS_DispatchToMainThread(runnable);
-  NS_WARNING_ASSERTION(!aRv.Failed(), "NS_DispatchToMainThread failed");
+
+  aRv = FileSystemUtils::DispatchRunnable(GetParentObject(), runnable.forget());
 }
 
 } // dom namespace
 } // mozilla namespace
--- 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: dafe3579e8dc886e6584116dc52a9362b543c169
+Latest Commit: 7463ae5908ca1d4065763a8753af5d72a6f78b85
--- a/gfx/layers/composite/TextureHost.cpp
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -84,16 +84,28 @@ public:
   uint64_t GetSerial() const { return mSerial; }
 
   HostIPCAllocator* mSurfaceAllocator;
   RefPtr<TextureHost> mTextureHost;
   // mSerial is unique in TextureClient's process.
   const uint64_t mSerial;
 };
 
+static bool
+WrapWithWebRenderTextureHost(LayersBackend aBackend,
+                             TextureFlags aFlags)
+{
+  if (!gfxVars::UseWebRender() ||
+      (aFlags & TextureFlags::SNAPSHOT) ||
+      (aBackend != LayersBackend::LAYERS_WR)) {
+    return false;
+  }
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 PTextureParent*
 TextureHost::CreateIPDLActor(HostIPCAllocator* aAllocator,
                              const SurfaceDescriptor& aSharedData,
                              LayersBackend aLayersBackend,
                              TextureFlags aFlags,
                              uint64_t aSerial)
 {
@@ -179,62 +191,65 @@ already_AddRefed<TextureHost> CreateText
                                                      TextureFlags aFlags);
 
 already_AddRefed<TextureHost>
 TextureHost::Create(const SurfaceDescriptor& aDesc,
                     ISurfaceAllocator* aDeallocator,
                     LayersBackend aBackend,
                     TextureFlags aFlags)
 {
+  RefPtr<TextureHost> result;
+
   switch (aDesc.type()) {
     case SurfaceDescriptor::TSurfaceDescriptorBuffer:
     case SurfaceDescriptor::TSurfaceDescriptorDIB:
     case SurfaceDescriptor::TSurfaceDescriptorFileMapping:
     case SurfaceDescriptor::TSurfaceDescriptorGPUVideo:
-      return CreateBackendIndependentTextureHost(aDesc, aDeallocator, aBackend, aFlags);
+      result = CreateBackendIndependentTextureHost(aDesc, aDeallocator, aBackend, aFlags);
+      break;
 
     case SurfaceDescriptor::TEGLImageDescriptor:
     case SurfaceDescriptor::TSurfaceTextureDescriptor:
     case SurfaceDescriptor::TSurfaceDescriptorSharedGLTexture:
-      return CreateTextureHostOGL(aDesc, aDeallocator, aBackend, aFlags);
+      result = CreateTextureHostOGL(aDesc, aDeallocator, aBackend, aFlags);
+      break;
 
     case SurfaceDescriptor::TSurfaceDescriptorMacIOSurface:
       if (aBackend == LayersBackend::LAYERS_OPENGL ||
           aBackend == LayersBackend::LAYERS_WR) {
-        return CreateTextureHostOGL(aDesc, aDeallocator, aBackend, aFlags);
+        result = CreateTextureHostOGL(aDesc, aDeallocator, aBackend, aFlags);
+        break;
       } else {
-        return CreateTextureHostBasic(aDesc, aDeallocator, aBackend, aFlags);
+        result = CreateTextureHostBasic(aDesc, aDeallocator, aBackend, aFlags);
+        break;
       }
 
 #ifdef MOZ_X11
     case SurfaceDescriptor::TSurfaceDescriptorX11: {
       const SurfaceDescriptorX11& desc = aDesc.get_SurfaceDescriptorX11();
-      return MakeAndAddRef<X11TextureHost>(aFlags, desc);
+      result = MakeAndAddRef<X11TextureHost>(aFlags, desc);
+      break;
     }
 #endif
 
 #ifdef XP_WIN
     case SurfaceDescriptor::TSurfaceDescriptorD3D10:
     case SurfaceDescriptor::TSurfaceDescriptorDXGIYCbCr:
-      return CreateTextureHostD3D11(aDesc, aDeallocator, aBackend, aFlags);
+      result = CreateTextureHostD3D11(aDesc, aDeallocator, aBackend, aFlags);
+      break;
 #endif
     default:
       MOZ_CRASH("GFX: Unsupported Surface type host");
   }
-}
 
-bool WrapWithWebRenderTextureHost(LayersBackend aBackend,
-                                  TextureFlags aFlags)
-{
-  if (!gfxVars::UseWebRender() ||
-      (aFlags & TextureFlags::SNAPSHOT) ||
-      (aBackend != LayersBackend::LAYERS_WR)) {
-    return false;
+  if (WrapWithWebRenderTextureHost(aBackend, aFlags)) {
+    result = new WebRenderTextureHost(aDesc, aFlags, result);
   }
-  return true;
+
+  return result.forget();
 }
 
 already_AddRefed<TextureHost>
 CreateBackendIndependentTextureHost(const SurfaceDescriptor& aDesc,
                                     ISurfaceAllocator* aDeallocator,
                                     LayersBackend aBackend,
                                     TextureFlags aFlags)
 {
@@ -244,28 +259,22 @@ CreateBackendIndependentTextureHost(cons
       const SurfaceDescriptorBuffer& bufferDesc = aDesc.get_SurfaceDescriptorBuffer();
       const MemoryOrShmem& data = bufferDesc.data();
       switch (data.type()) {
         case MemoryOrShmem::TShmem: {
           result = new ShmemTextureHost(data.get_Shmem(),
                                         bufferDesc.desc(),
                                         aDeallocator,
                                         aFlags);
-          if (WrapWithWebRenderTextureHost(aBackend, aFlags)) {
-            result = new WebRenderTextureHost(aFlags, result);
-          }
           break;
         }
         case MemoryOrShmem::Tuintptr_t: {
           result = new MemoryTextureHost(reinterpret_cast<uint8_t*>(data.get_uintptr_t()),
                                          bufferDesc.desc(),
                                          aFlags);
-          if (WrapWithWebRenderTextureHost(aBackend, aFlags)) {
-            result = new WebRenderTextureHost(aFlags, result);
-          }
           break;
         }
         default:
           gfxCriticalError() << "Failed texture host for backend " << (int)data.type();
           MOZ_CRASH("GFX: No texture host for backend");
       }
       break;
     }
--- a/gfx/layers/composite/TextureHost.h
+++ b/gfx/layers/composite/TextureHost.h
@@ -41,16 +41,17 @@ class BufferDescriptor;
 class BufferTextureHost;
 class Compositor;
 class CompositableParentManager;
 class ReadLockDescriptor;
 class CompositorBridgeParent;
 class SurfaceDescriptor;
 class HostIPCAllocator;
 class ISurfaceAllocator;
+class MacIOSurfaceTextureHostOGL;
 class TextureHostOGL;
 class TextureReadLock;
 class TextureSourceOGL;
 class TextureSourceD3D11;
 class TextureSourceBasic;
 class DataTextureSource;
 class PTextureParent;
 class TextureParent;
@@ -579,17 +580,17 @@ public:
 
   void DeserializeReadLock(const ReadLockDescriptor& aDesc,
                            ISurfaceAllocator* aAllocator);
   void SetReadLock(TextureReadLock* aReadLock);
 
   TextureReadLock* GetReadLock() { return mReadLock; }
 
   virtual BufferTextureHost* AsBufferTextureHost() { return nullptr; }
-
+  virtual MacIOSurfaceTextureHostOGL* AsMacIOSurfaceTextureHost() { return nullptr; }
   virtual WebRenderTextureHost* AsWebRenderTextureHost() { return nullptr; }
 
 protected:
   void ReadUnlock();
 
   void RecycleTexture(TextureFlags aFlags);
 
   virtual void UpdatedInternal(const nsIntRegion *Region) {}
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -681,35 +681,39 @@ CompositorBridgeParent::PauseComposition
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread(),
              "PauseComposition() can only be called on the compositor thread");
 
   MonitorAutoLock lock(mPauseCompositionMonitor);
 
   if (!mPaused) {
     mPaused = true;
 
-    mCompositor->Pause();
-
+    if (!gfxVars::UseWebRender()) {
+      mCompositor->Pause();
+    } else {
+      mWrBridge->Pause();
+    }
     TimeStamp now = TimeStamp::Now();
     DidComposite(now, now);
   }
 
   // if anyone's waiting to make sure that composition really got paused, tell them
   lock.NotifyAll();
 }
 
 void
 CompositorBridgeParent::ResumeComposition()
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread(),
              "ResumeComposition() can only be called on the compositor thread");
 
   MonitorAutoLock lock(mResumeCompositionMonitor);
 
-  if (!mCompositor->Resume()) {
+  bool resumed = gfxVars::UseWebRender() ? mWrBridge->Resume() : mCompositor->Resume();
+  if (!resumed) {
 #ifdef MOZ_WIDGET_ANDROID
     // We can't get a surface. This could be because the activity changed between
     // the time resume was scheduled and now.
     __android_log_print(ANDROID_LOG_INFO, "CompositorBridgeParent", "Unable to renew compositor surface; remaining in paused state");
 #endif
     lock.NotifyAll();
     return;
   }
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -386,25 +386,27 @@ UNIFIED_SOURCES += [
     'wr/WebRenderContainerLayer.cpp',
     'wr/WebRenderDisplayItemLayer.cpp',
     'wr/WebRenderImageHost.cpp',
     'wr/WebRenderImageLayer.cpp',
     'wr/WebRenderLayerManager.cpp',
     'wr/WebRenderLayersLogging.cpp',
     'wr/WebRenderPaintedLayer.cpp',
     'wr/WebRenderTextLayer.cpp',
-    'wr/WebRenderTextureHost.cpp',
+    # XXX here are some unified build error.
+    #'wr/WebRenderTextureHost.cpp'
 ]
 
 SOURCES += [
     'basic/BasicImageLayer.cpp',
     'ImageContainer.cpp',
     'Layers.cpp',
     'LayerTreeInvalidation.cpp',
     'PersistentBufferProvider.cpp',
+    'wr/WebRenderTextureHost.cpp',
 ]
 
 # Disable RTTI in google protocol buffer
 DEFINES['GOOGLE_PROTOBUF_NO_RTTI'] = True
 
 # Workaround compiler bug (Bug 795594)
 if CONFIG['_MSC_VER'] and CONFIG['CPU_ARCH'] == 'x86_64':
     for src in [
--- a/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h
+++ b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h
@@ -93,16 +93,23 @@ public:
   gl::GLContext* gl() const;
 
   virtual gfx::IntSize GetSize() const override;
 
 #ifdef MOZ_LAYERS_HAVE_LOG
   virtual const char* Name() override { return "MacIOSurfaceTextureHostOGL"; }
 #endif
 
+  virtual MacIOSurfaceTextureHostOGL* AsMacIOSurfaceTextureHost() override { return this; }
+
+  MacIOSurface* GetMacIOSurface()
+  {
+    return mSurface;
+  }
+
 protected:
   GLTextureSource* CreateTextureSourceForPlane(size_t aPlane);
 
   RefPtr<GLTextureSource> mTextureSource;
   RefPtr<MacIOSurface> mSurface;
 };
 
 } // namespace layers
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -17,16 +17,20 @@
 #include "mozilla/layers/ImageBridgeParent.h"
 #include "mozilla/layers/ImageDataSerializer.h"
 #include "mozilla/layers/TextureHost.h"
 #include "mozilla/layers/WebRenderCompositableHolder.h"
 #include "mozilla/layers/WebRenderTextureHost.h"
 #include "mozilla/webrender/RenderThread.h"
 #include "mozilla/widget/CompositorWidget.h"
 
+#if defined(MOZ_WIDGET_ANDROID)
+# include "mozilla/widget/AndroidCompositorWidget.h"
+#endif
+
 bool is_in_main_thread()
 {
   return NS_IsMainThread();
 }
 
 bool is_in_compositor_thread()
 {
   return mozilla::layers::CompositorThreadHolder::IsInCompositorThread();
@@ -110,16 +114,17 @@ WebRenderBridgeParent::WebRenderBridgePa
   , mWidget(aWidget)
   , mApi(aApi)
   , mCompositableHolder(aHolder)
   , mCompositorScheduler(aScheduler)
   , mChildLayerObserverEpoch(0)
   , mParentLayerObserverEpoch(0)
   , mWrEpoch(0)
   , mIdNameSpace(++sIdNameSpace)
+  , mPaused(false)
   , mDestroyed(false)
 {
   MOZ_ASSERT(mCompositableHolder);
   mCompositableHolder->AddPipeline(mPipelineId);
   if (mWidget) {
     MOZ_ASSERT(!mCompositorScheduler);
     mCompositorScheduler = new CompositorVsyncScheduler(this, mWidget);
   }
@@ -130,16 +135,25 @@ mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvCreate(const gfx::IntSize& aSize)
 {
   if (mDestroyed) {
     return IPC_OK();
   }
 
   MOZ_ASSERT(mApi);
 
+#ifdef MOZ_WIDGET_ANDROID
+  // XXX temporary hack.
+  // XXX Remove it when APZ is supported.
+  widget::AndroidCompositorWidget* widget = mWidget->AsAndroid();
+  if (widget) {
+    widget->SetFirstPaintViewport(LayerIntPoint(0, 0), CSSToLayerScale(), CSSRect(0, 0, aSize.width, aSize.height));
+  }
+#endif
+
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvShutdown()
 {
   if (mDestroyed) {
     return IPC_OK();
@@ -339,25 +353,37 @@ WebRenderBridgeParent::ProcessWebRenderC
         // XXX select Texture for video in CompositeToTarget().
         TextureHost* texture = host->GetAsTextureHost();
         if (!texture) {
           NS_ERROR("TextureHost does not exist");
           break;
         }
         WebRenderTextureHost* wrTexture = texture->AsWebRenderTextureHost();
         if (wrTexture) {
-          // XXX handling YUV
-          gfx::SurfaceFormat format =
-            wrTexture->GetFormat() == SurfaceFormat::YUV ? SurfaceFormat::B8G8R8A8 : wrTexture->GetFormat();
-          wr::ImageDescriptor descriptor(wrTexture->GetSize(), wrTexture->GetRGBStride(), format);
-          mApi->AddExternalImageBuffer(key,
-                                       descriptor,
-                                       wrTexture->GetExternalImageKey());
-          mCompositableHolder->HoldExternalImage(mPipelineId, aEpoch, texture->AsWebRenderTextureHost());
-          keysToDelete.push_back(key);
+          if (wrTexture->IsWrappingNativeHandle()) {
+            // XXX only for MacIOSurface right now.
+            // XXX remove the redundant codes for both native handle and yuv case.
+            wr::ImageDescriptor descriptor(wrTexture->GetSize(), wrTexture->GetReadFormat());
+            mApi->AddExternalImageHandle(key,
+                                         descriptor,
+                                         wrTexture->GetExternalImageKey());
+            mCompositableHolder->HoldExternalImage(mPipelineId, aEpoch, texture->AsWebRenderTextureHost());
+            keysToDelete.push_back(key);
+          } else {
+            // XXX handling YUV
+            gfx::SurfaceFormat format =
+              wrTexture->GetFormat() == SurfaceFormat::YUV ? SurfaceFormat::B8G8R8A8 : wrTexture->GetFormat();
+            wr::ImageDescriptor descriptor(wrTexture->GetSize(), wrTexture->GetRGBStride(), format);
+            mApi->AddExternalImageBuffer(key,
+                                         descriptor,
+                                         wrTexture->GetExternalImageKey());
+            mCompositableHolder->HoldExternalImage(mPipelineId, aEpoch, texture->AsWebRenderTextureHost());
+            keysToDelete.push_back(key);
+          }
+
           break;
         }
         RefPtr<DataSourceSurface> dSurf = host->GetAsSurface();
         if (!dSurf) {
           break;
         }
 
         DataSourceSurface::MappedSurface map;
@@ -409,16 +435,17 @@ WebRenderBridgeParent::ProcessWebRenderC
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvDPGetSnapshot(PTextureParent* aTexture)
 {
   if (mDestroyed) {
     return IPC_OK();
   }
+  MOZ_ASSERT(!mPaused);
 
   RefPtr<TextureHost> texture = TextureHost::AsTextureHost(aTexture);
   if (!texture) {
     // We kill the content process rather than have it continue with an invalid
     // snapshot, that may be too harsh and we could decide to return some sort
     // of error to the child process and let it deal with it...
     return IPC_FAIL_NO_REASON(this);
   }
@@ -539,16 +566,19 @@ void
 WebRenderBridgeParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   Destroy();
 }
 
 void
 WebRenderBridgeParent::CompositeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect)
 {
+  if (mPaused) {
+    return;
+  }
   mApi->GenerateFrame();
 }
 
 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
@@ -614,16 +644,46 @@ void
 WebRenderBridgeParent::ScheduleComposition()
 {
   if (mCompositorScheduler) {
     mCompositorScheduler->ScheduleComposition();
   }
 }
 
 void
+WebRenderBridgeParent::Pause()
+{
+  MOZ_ASSERT(mWidget);
+#ifdef MOZ_WIDGET_ANDROID
+  if (!mWidget || mDestroyed) {
+    return;
+  }
+  mApi->Pause();
+#endif
+  mPaused = true;
+}
+
+bool
+WebRenderBridgeParent::Resume()
+{
+  MOZ_ASSERT(mWidget);
+#ifdef MOZ_WIDGET_ANDROID
+  if (!mWidget || mDestroyed) {
+    return false;
+  }
+
+  if (!mApi->Resume()) {
+    return false;
+  }
+#endif
+  mPaused = false;
+  return true;
+}
+
+void
 WebRenderBridgeParent::ClearResources()
 {
   if (mApi) {
     ++mWrEpoch; // Update webrender epoch
     mApi->ClearRootDisplayList(wr::NewEpoch(mWrEpoch), mPipelineId);
     if (!mKeysToDelete.empty()) {
       // XXX Sync wait.
       mApi->WaitFlushed();
--- a/gfx/layers/wr/WebRenderBridgeParent.h
+++ b/gfx/layers/wr/WebRenderBridgeParent.h
@@ -109,16 +109,19 @@ public:
   mozilla::ipc::IPCResult RecvRemoveExternalImageId(const uint64_t& aImageId) override;
   mozilla::ipc::IPCResult RecvSetLayerObserverEpoch(const uint64_t& aLayerObserverEpoch) override;
 
   mozilla::ipc::IPCResult RecvClearCachedResources() override;
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
   void SetWebRenderProfilerEnabled(bool aEnabled);
 
+  void Pause();
+  bool Resume();
+
   void Destroy();
 
   // CompositorVsyncSchedulerOwner
   bool IsPendingComposite() override { return false; }
   void FinishPendingComposite() override { }
   void CompositeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect = nullptr) override;
 
   // CompositableParentManager
@@ -202,16 +205,17 @@ private:
   // (via ObserveLayerUpdate).
   uint64_t mChildLayerObserverEpoch;
   uint64_t mParentLayerObserverEpoch;
 
   std::queue<PendingTransactionId> mPendingTransactionIds;
   uint32_t mWrEpoch;
   uint32_t mIdNameSpace;
 
+  bool mPaused;
   bool mDestroyed;
 
   static uint32_t sIdNameSpace;
 };
 
 } // namespace layers
 } // namespace mozilla
 
--- a/gfx/layers/wr/WebRenderTextureHost.cpp
+++ b/gfx/layers/wr/WebRenderTextureHost.cpp
@@ -1,47 +1,81 @@
 /* -*- 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 "WebRenderTextureHost.h"
 
 #include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/layers/LayersSurfaces.h"
+#include "mozilla/webrender/RenderBufferTextureHost.h"
 #include "mozilla/webrender/RenderTextureHost.h"
 #include "mozilla/webrender/RenderThread.h"
 
+#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(TextureFlags aFlags,
+WebRenderTextureHost::WebRenderTextureHost(const SurfaceDescriptor& aDesc,
+                                           TextureFlags aFlags,
                                            TextureHost* aTexture)
   : TextureHost(aFlags)
   , mExternalImageId(++sSerialCounter)
+  , mIsWrappingNativeHandle(false)
 {
   MOZ_COUNT_CTOR(WebRenderTextureHost);
   mWrappedTextureHost = aTexture;
 
-  // XXX support only BufferTextureHost for now.
-  BufferTextureHost* bufferTexture = aTexture->AsBufferTextureHost();
-  MOZ_ASSERT(bufferTexture);
-  RefPtr<wr::RenderTextureHost> texture =
-    new wr::RenderTextureHost(bufferTexture->GetBuffer(),
-                              bufferTexture->GetBufferDescriptor());
-  wr::RenderThread::Get()->RegisterExternalImage(mExternalImageId, texture);
+  CreateRenderTextureHost(aDesc, aTexture);
 }
 
 WebRenderTextureHost::~WebRenderTextureHost()
 {
   MOZ_COUNT_DTOR(WebRenderTextureHost);
   wr::RenderThread::Get()->UnregisterExternalImage(mExternalImageId);
 }
 
+void
+WebRenderTextureHost::CreateRenderTextureHost(const layers::SurfaceDescriptor& aDesc,
+                                              TextureHost* aTexture)
+{
+  RefPtr<wr::RenderTextureHost> texture;
+
+  switch (aDesc.type()) {
+    case SurfaceDescriptor::TSurfaceDescriptorBuffer: {
+      BufferTextureHost* bufferTexture = aTexture->AsBufferTextureHost();
+      MOZ_ASSERT(bufferTexture);
+      texture = new wr::RenderBufferTextureHost(bufferTexture->GetBuffer(),
+                                                bufferTexture->GetBufferDescriptor());
+      mIsWrappingNativeHandle = false;
+      break;
+    }
+#ifdef XP_MACOSX
+    case SurfaceDescriptor::TSurfaceDescriptorMacIOSurface: {
+      MacIOSurfaceTextureHostOGL* macTexture = aTexture->AsMacIOSurfaceTextureHost();
+      MOZ_ASSERT(macTexture);
+      texture = new wr::RenderMacIOSurfaceTextureHostOGL(macTexture->GetMacIOSurface());
+      mIsWrappingNativeHandle = true;
+      break;
+    }
+#endif
+    default:
+      gfxCriticalError() << "No WR implement for texture type:" << aDesc.type();
+  }
+
+  wr::RenderThread::Get()->RegisterExternalImage(mExternalImageId, texture);
+}
+
 bool
 WebRenderTextureHost::Lock()
 {
   MOZ_ASSERT_UNREACHABLE("unexpected to be called");
   return false;
 }
 
 void
@@ -94,25 +128,34 @@ gfx::SurfaceFormat
 WebRenderTextureHost::GetFormat() const
 {
   if (!mWrappedTextureHost) {
     return gfx::SurfaceFormat::UNKNOWN;
   }
   return mWrappedTextureHost->GetFormat();
 }
 
+gfx::SurfaceFormat
+WebRenderTextureHost::GetReadFormat() const
+{
+  if (!mWrappedTextureHost) {
+    return gfx::SurfaceFormat::UNKNOWN;
+  }
+  return mWrappedTextureHost->GetReadFormat();
+}
+
 int32_t
 WebRenderTextureHost::GetRGBStride()
 {
   if (!mWrappedTextureHost) {
     return 0;
   }
   gfx::SurfaceFormat format = GetFormat();
-  if (GetFormat() == SurfaceFormat::YUV) {
+  if (GetFormat() == gfx::SurfaceFormat::YUV) {
     // XXX this stride is used until yuv image rendering by webrender is used.
     // Software converted RGB buffers strides are aliened to 16
-    return GetAlignedStride<16>(GetSize().width, BytesPerPixel(SurfaceFormat::B8G8R8A8));
+    return gfx::GetAlignedStride<16>(GetSize().width, BytesPerPixel(gfx::SurfaceFormat::B8G8R8A8));
   }
   return ImageDataSerializer::ComputeRGBStride(format, GetSize().width);
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderTextureHost.h
+++ b/gfx/layers/wr/WebRenderTextureHost.h
@@ -6,33 +6,48 @@
 #ifndef MOZILLA_GFX_WEBRENDERTEXTUREHOST_H
 #define MOZILLA_GFX_WEBRENDERTEXTUREHOST_H
 
 #include "mozilla/layers/TextureHost.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.
+// So, there are some UNREACHABLE asserts for the original Compositor related
+// 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(TextureFlags aFlags,
+  WebRenderTextureHost(const SurfaceDescriptor& aDesc,
+                       TextureFlags aFlags,
                        TextureHost* aTexture);
   virtual ~WebRenderTextureHost();
 
   virtual void DeallocateDeviceData() override {}
 
   virtual void SetTextureSourceProvider(TextureSourceProvider* aProvider) override;
 
   virtual bool Lock() override;
 
   virtual void Unlock() override;
 
   virtual gfx::SurfaceFormat GetFormat() const override;
 
+  // Return the format used for reading the texture. Some hardware specific
+  // textureHosts use their special data representation internally, but we could
+  // treat these textureHost as the read-format when we read them.
+  // Please check TextureHost::GetReadFormat().
+  virtual gfx::SurfaceFormat GetReadFormat() const override;
+
   virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override;
 
   virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
 
   virtual YUVColorSpace GetYUVColorSpace() const override;
 
   virtual gfx::IntSize GetSize() const override;
 
@@ -40,19 +55,26 @@ public:
   virtual const char* Name() override { return "WebRenderTextureHost"; }
 #endif
 
   virtual WebRenderTextureHost* AsWebRenderTextureHost() override { return this; }
 
   uint64_t GetExternalImageKey() { return mExternalImageId; }
 
   int32_t GetRGBStride();
+
+  bool IsWrappingNativeHandle() { return mIsWrappingNativeHandle; }
+
 protected:
+  void CreateRenderTextureHost(const SurfaceDescriptor& aDesc, TextureHost* aTexture);
+
   RefPtr<TextureHost> mWrappedTextureHost;
   uint64_t mExternalImageId;
 
+  bool mIsWrappingNativeHandle;
+
   static uint64_t sSerialCounter;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // MOZILLA_GFX_WEBRENDERTEXTUREHOST_H
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -485,24 +485,25 @@ private:
   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_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, false);
+  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_GFX_PREF(Live, "layers.advanced.button-foreground-layers", LayersAllowButtonForegroundLayers, bool, false);
   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_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_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/webrender/Cargo.toml
+++ b/gfx/webrender/Cargo.toml
@@ -1,30 +1,30 @@
 [package]
 name = "webrender"
-version = "0.26.0"
+version = "0.30.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"]
 profiler = ["thread_profiler/thread_profiler"]
 webgl = ["offscreen_gl_context", "webrender_traits/webgl"]
 
 [dependencies]
 app_units = "0.4"
-bincode = "1.0.0-alpha2"
+bincode = "1.0.0-alpha6"
 bit-set = "0.4"
 byteorder = "1.0"
 euclid = "0.11"
 fnv = "1.0"
-gleam = "0.4.1"
+gleam = "0.4.2"
 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"
--- a/gfx/webrender/res/cs_clip_rectangle.fs.glsl
+++ b/gfx/webrender/res/cs_clip_rectangle.fs.glsl
@@ -35,10 +35,15 @@ float rounded_rect(vec2 pos) {
 
 
 void main(void) {
     float alpha = 1.f;
     vec2 local_pos = init_transform_fs(vPos, vLocalRect, alpha);
 
     float clip_alpha = rounded_rect(local_pos);
 
-    oFragColor = vec4(min(alpha, clip_alpha), 0.0, 0.0, 1.0);
+    float combined_alpha = min(alpha, clip_alpha);
+
+    // Select alpha or inverse alpha depending on clip in/out.
+    float final_alpha = mix(combined_alpha, 1.0 - combined_alpha, vClipMode);
+
+    oFragColor = vec4(final_alpha, 0.0, 0.0, 1.0);
 }
--- a/gfx/webrender/res/cs_clip_rectangle.glsl
+++ b/gfx/webrender/res/cs_clip_rectangle.glsl
@@ -3,8 +3,9 @@
 /* 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 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,26 +1,25 @@
 #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;
-    vec4 dummy;
+    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.dummy = texelFetchOffset(sData32, uv, 0, ivec2(1, 0));
-    rect.dummy = vec4(0.0, 0.0, 0.0, 0.0);
+    rect.mode = texelFetchOffset(sData32, uv, 0, ivec2(1, 0));
 
     return rect;
 }
 
 struct ClipCorner {
     vec4 rect;
     vec4 outer_inner_radius;
 };
@@ -65,14 +64,15 @@ void main(void) {
 
     TransformVertexInfo 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);
     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
@@ -258,26 +258,26 @@ GradientStop fetch_gradient_stop(int ind
     stop.color = texelFetchOffset(sData32, uv, 0, ivec2(0, 0));
     stop.offset = texelFetchOffset(sData32, uv, 0, ivec2(1, 0));
 
     return stop;
 }
 
 struct RadialGradient {
     vec4 start_end_center;
-    vec4 start_end_radius_extend_mode;
+    vec4 start_end_radius_ratio_xy_extend_mode;
 };
 
 RadialGradient fetch_radial_gradient(int index) {
     RadialGradient gradient;
 
     ivec2 uv = get_fetch_uv_2(index);
 
     gradient.start_end_center = texelFetchOffset(sData32, uv, 0, ivec2(0, 0));
-    gradient.start_end_radius_extend_mode = texelFetchOffset(sData32, uv, 0, ivec2(1, 0));
+    gradient.start_end_radius_ratio_xy_extend_mode = texelFetchOffset(sData32, uv, 0, ivec2(1, 0));
 
     return gradient;
 }
 
 struct Glyph {
     vec4 offset;
 };
 
--- a/gfx/webrender/res/ps_box_shadow.fs.glsl
+++ b/gfx/webrender/res/ps_box_shadow.fs.glsl
@@ -1,20 +1,22 @@
 /* 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) {
+    vec4 clip_scale = vec4(1.0, 1.0, 1.0, do_clip());
+
     // Mirror and stretch the box shadow corner over the entire
     // primitives.
     vec2 uv = vMirrorPoint - abs(vUv.xy - vMirrorPoint);
 
     // Ensure that we don't fetch texels outside the box
     // shadow corner. This can happen, for example, when
     // drawing the outer parts of an inset box shadow.
     uv = clamp(uv, vec2(0.0), vec2(1.0));
 
     // Map the unit UV to the actual UV rect in the cache.
     uv = mix(vCacheUvRectCoords.xy, vCacheUvRectCoords.zw, uv);
 
     // Modulate the box shadow by the color.
-    oFragColor = dither(vColor * texture(sCacheRGBA8, vec3(uv, vUv.z)));
+    oFragColor = clip_scale * dither(vColor * texture(sCacheRGBA8, vec3(uv, vUv.z)));
 }
--- a/gfx/webrender/res/ps_box_shadow.vs.glsl
+++ b/gfx/webrender/res/ps_box_shadow.vs.glsl
@@ -24,9 +24,11 @@ void main(void) {
 
     vUv.xy = (vi.local_pos - prim.local_rect.p0) / patch_size;
     vMirrorPoint = 0.5 * prim.local_rect.size / patch_size;
 
     vec2 texture_size = vec2(textureSize(sCacheRGBA8, 0));
     vCacheUvRectCoords = vec4(patch_origin, patch_origin + patch_size_device_pixels) / texture_size.xyxy;
 
     vColor = bs.color;
+
+    write_clip(vi.screen_pos, prim.clip_area);
 }
--- a/gfx/webrender/res/ps_composite.fs.glsl
+++ b/gfx/webrender/res/ps_composite.fs.glsl
@@ -166,16 +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);
 
+    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.fs.glsl
+++ b/gfx/webrender/res/ps_gradient.fs.glsl
@@ -7,10 +7,14 @@ void main(void) {
     float alpha = 0.0;
     vec2 local_pos = init_transform_fs(vLocalPos, vLocalRect, alpha);
 #else
     float alpha = 1.0;
     vec2 local_pos = vPos;
 #endif
 
     alpha = min(alpha, do_clip());
-    oFragColor = dither(vColor * vec4(1.0, 1.0, 1.0, alpha));
+
+    // TODO(gw): Re-enable the gradient dither once we get the
+    //           reftests passing.
+    //oFragColor = dither(vColor * vec4(1.0, 1.0, 1.0, alpha));
+    oFragColor = vColor * vec4(1.0, 1.0, 1.0, alpha);
 }
--- a/gfx/webrender/res/ps_gradient.vs.glsl
+++ b/gfx/webrender/res/ps_gradient.vs.glsl
@@ -14,47 +14,47 @@ void main(void) {
     vec2 axis;
     vec4 adjusted_color_g0 = g0.color;
     vec4 adjusted_color_g1 = g1.color;
     if (gradient.start_end_point.y == gradient.start_end_point.w) {
         // Calculate the x coord of the gradient stops
         vec2 g01_x = mix(gradient.start_end_point.xx, gradient.start_end_point.zz,
                          vec2(g0.offset.x, g1.offset.x));
 
-        // The start and end point of gradient might exceed the geometry rect. So clamp
-        // it to the geometry rect.
-        g01_x = clamp(g01_x, prim.local_rect.p0.xx, prim.local_rect.p0.xx + prim.local_rect.size.xx);
+        // The gradient stops might exceed the geometry rect so clamp them
+        vec2 g01_x_clamped = clamp(g01_x,
+                                   prim.local_rect.p0.xx,
+                                   prim.local_rect.p0.xx + prim.local_rect.size.xx);
 
-        // Calculate the rect using the clamped coords
-        segment_rect.p0 = vec2(g01_x.x, prim.local_rect.p0.y);
-        segment_rect.size = vec2(g01_x.y - g01_x.x, prim.local_rect.size.y);
+        // Calculate the segment rect using the clamped coords
+        segment_rect.p0 = vec2(g01_x_clamped.x, prim.local_rect.p0.y);
+        segment_rect.size = vec2(g01_x_clamped.y - g01_x_clamped.x, prim.local_rect.size.y);
         axis = vec2(1.0, 0.0);
 
-        // We need to adjust the colors of the stops because they may have been clamped
-        vec2 adjusted_offset =
-            (g01_x - segment_rect.p0.xx) / segment_rect.size.xx;
+        // Adjust the stop colors by how much they were clamped
+        vec2 adjusted_offset = (g01_x_clamped - g01_x.xx) / (g01_x.y - g01_x.x);
         adjusted_color_g0 = mix(g0.color, g1.color, adjusted_offset.x);
         adjusted_color_g1 = mix(g0.color, g1.color, adjusted_offset.y);
     } else {
         // Calculate the y coord of the gradient stops
         vec2 g01_y = mix(gradient.start_end_point.yy, gradient.start_end_point.ww,
                          vec2(g0.offset.x, g1.offset.x));
 
-        // The start and end point of gradient might exceed the geometry rect. So clamp
-        // it to the geometry rect.
-        g01_y = clamp(g01_y, prim.local_rect.p0.yy, prim.local_rect.p0.yy + prim.local_rect.size.yy);
+        // The gradient stops might exceed the geometry rect so clamp them
+        vec2 g01_y_clamped = clamp(g01_y,
+                                   prim.local_rect.p0.yy,
+                                   prim.local_rect.p0.yy + prim.local_rect.size.yy);
 
-        // Calculate the rect using the clamped coords
-        segment_rect.p0 = vec2(prim.local_rect.p0.x, g01_y.x);
-        segment_rect.size = vec2(prim.local_rect.size.x, g01_y.y - g01_y.x);
+        // Calculate the segment rect using the clamped coords
+        segment_rect.p0 = vec2(prim.local_rect.p0.x, g01_y_clamped.x);
+        segment_rect.size = vec2(prim.local_rect.size.x, g01_y_clamped.y - g01_y_clamped.x);
         axis = vec2(0.0, 1.0);
 
-        // We need to adjust the colors of the stops because they may have been clamped
-        vec2 adjusted_offset =
-            (g01_y - segment_rect.p0.yy) / segment_rect.size.yy;
+        // Adjust the stop colors by how much they were clamped
+        vec2 adjusted_offset = (g01_y_clamped - g01_y.xx) / (g01_y.y - g01_y.x);
         adjusted_color_g0 = mix(g0.color, g1.color, adjusted_offset.x);
         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,
--- a/gfx/webrender/res/ps_image.fs.glsl
+++ b/gfx/webrender/res/ps_image.fs.glsl
@@ -19,17 +19,20 @@ void main(void) {
 #endif
 
     alpha = min(alpha, do_clip());
 
     // We calculate the particular tile this fragment belongs to, taking into
     // account the spacing in between tiles. We only paint if our fragment does
     // not fall into that spacing.
     vec2 position_in_tile = mod(relative_pos_in_rect, vStretchSize + vTileSpacing);
-    // We clamp the texture coordinates to the half-pixel offset from the borders
-    // in order to avoid sampling outside of the texture area.
     vec2 st = vTextureOffset + ((position_in_tile / vStretchSize) * vTextureSize);
     st = clamp(st, vStRect.xy, vStRect.zw);
 
     alpha = alpha * float(all(bvec2(step(position_in_tile, vStretchSize))));
 
+#ifdef WR_FEATURE_TEXTURE_RECT
+    // textureLod doesn't support sampler2DRect. Use texture() instead.
+    oFragColor = vec4(alpha) * texture(sColor0, st);
+#else
     oFragColor = vec4(alpha) * textureLod(sColor0, st, 0.0);
+#endif
 }
--- a/gfx/webrender/res/ps_image.glsl
+++ b/gfx/webrender/res/ps_image.glsl
@@ -1,16 +1,19 @@
 /* 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/. */
 
+// If this is in WR_FEATURE_TEXTURE_RECT mode, the rect and size use non-normalized
+// texture coordinates. Otherwise, it uses normalized texture coordinates. Please
+// 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, in st-space.
+flat varying vec4 vStRect;        // Rectangle of valid texture rect.
 
 #ifdef WR_FEATURE_TRANSFORM
 varying vec3 vLocalPos;
 flat varying vec4 vLocalRect;
 flat varying vec2 vStretchSize;
 #else
 varying vec2 vLocalPos;
 flat varying vec2 vStretchSize;
--- a/gfx/webrender/res/ps_image.vs.glsl
+++ b/gfx/webrender/res/ps_image.vs.glsl
@@ -22,21 +22,33 @@ void main(void) {
                                  prim.z,
                                  prim.layer,
                                  prim.task);
     vLocalPos = vi.local_pos - vi.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
+    // non-normalized texture coordinates.
+#ifdef WR_FEATURE_TEXTURE_RECT
+    vec2 texture_size_normalization_factor = vec2(1, 1);
+#else
+    vec2 texture_size_normalization_factor = vec2(textureSize(sColor0, 0));
+#endif
+
     // vUv will contain how many times this image has wrapped around the image size.
-    vec2 texture_size = vec2(textureSize(sColor0, 0));
-    vec2 st0 = res.uv_rect.xy / texture_size;
-    vec2 st1 = res.uv_rect.zw / texture_size;
+    vec2 st0 = res.uv_rect.xy / texture_size_normalization_factor;
+    vec2 st1 = res.uv_rect.zw / texture_size_normalization_factor;
 
     vTextureSize = st1 - st0;
     vTextureOffset = st0;
     vTileSpacing = image.stretch_size_and_tile_spacing.zw;
     vStretchSize = image.stretch_size_and_tile_spacing.xy;
 
-    vec2 half_texel = vec2(0.5) / texture_size;
+    // We clamp the texture coordinates to the half-pixel offset from the borders
+    // in order to avoid sampling outside of the texture area.
+    vec2 half_texel = vec2(0.5) / texture_size_normalization_factor;
     vStRect = vec4(min(st0, st1) + half_texel, max(st0, st1) - half_texel);
 }
--- a/gfx/webrender/res/ps_radial_gradient.vs.glsl
+++ b/gfx/webrender/res/ps_radial_gradient.vs.glsl
@@ -18,17 +18,24 @@ void main(void) {
     // Snap the start/end points to device pixel units.
     // I'm not sure this is entirely correct, but the
     // old render path does this, and it is needed to
     // make the angle gradient ref tests pass. It might
     // be better to fix this higher up in DL construction
     // and not snap here?
     vStartCenter = floor(0.5 + gradient.start_end_center.xy * uDevicePixelRatio) / uDevicePixelRatio;
     vEndCenter = floor(0.5 + gradient.start_end_center.zw * uDevicePixelRatio) / uDevicePixelRatio;
-    vStartRadius = gradient.start_end_radius_extend_mode.x;
-    vEndRadius = gradient.start_end_radius_extend_mode.y;
+    vStartRadius = gradient.start_end_radius_ratio_xy_extend_mode.x;
+    vEndRadius = gradient.start_end_radius_ratio_xy_extend_mode.y;
+
+    // Transform all coordinates by the y scale so the
+    // fragment shader can work with circles
+    float ratio_xy = gradient.start_end_radius_ratio_xy_extend_mode.z;
+    vPos.y *= ratio_xy;
+    vStartCenter.y *= ratio_xy;
+    vEndCenter.y *= ratio_xy;
 
     // V coordinate of gradient row in lookup texture.
     vGradientIndex = float(prim.sub_index);
 
     // Whether to repeat the gradient instead of clamping.
-    vGradientRepeat = float(int(gradient.start_end_radius_extend_mode.z) == EXTEND_MODE_REPEAT);
+    vGradientRepeat = float(int(gradient.start_end_radius_ratio_xy_extend_mode.w) == EXTEND_MODE_REPEAT);
 }
--- a/gfx/webrender/res/shared.glsl
+++ b/gfx/webrender/res/shared.glsl
@@ -28,19 +28,25 @@
 
     // Fragment shader outputs
     out vec4 oFragColor;
 #endif
 
 //======================================================================================
 // Shared shader uniforms
 //======================================================================================
+#ifndef WR_FEATURE_TEXTURE_RECT
 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
 //======================================================================================
 
 //======================================================================================
--- a/gfx/webrender/src/clip_scroll_node.rs
+++ b/gfx/webrender/src/clip_scroll_node.rs
@@ -1,15 +1,15 @@
 /* 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 euclid::Point3D;
 use geometry::ray_intersects_rect;
-use mask_cache::{ClipSource, MaskCacheInfo};
+use mask_cache::{ClipSource, MaskCacheInfo, RegionMode};
 use prim_store::GpuBlock32;
 use renderer::VertexDataStore;
 use spring::{DAMPING, STIFFNESS, Spring};
 use tiling::PackedLayerIndex;
 use util::TransformedRect;
 use webrender_traits::{ClipRegion, LayerPixel, LayerPoint, LayerRect, LayerSize};
 use webrender_traits::{LayerToScrollTransform, LayerToWorldTransform, PipelineId};
 use webrender_traits::{ScrollEventPhase, ScrollLayerId, ScrollLayerRect, ScrollLocation};
@@ -19,17 +19,17 @@ use webrender_traits::{WorldPoint, World
 const CAN_OVERSCROLL: bool = true;
 
 #[cfg(not(target_os = "macos"))]
 const CAN_OVERSCROLL: bool = false;
 
 #[derive(Clone, Debug)]
 pub struct ClipInfo {
     /// The ClipSource for this node, which is used to generate mask_cache_info.
-    pub clip_source: ClipSource,
+    pub clip_sources: Vec<ClipSource>,
 
     /// The MaskCacheInfo for this node, which is produced by processing the
     /// provided ClipSource.
     pub mask_cache_info: Option<MaskCacheInfo>,
 
     /// The packed layer index for this node, which is used to render a clip mask
     /// for it, if necessary.
     pub packed_layer_index: PackedLayerIndex,
@@ -42,20 +42,20 @@ pub struct ClipInfo {
 
 impl ClipInfo {
     pub fn new(clip_region: &ClipRegion,
                clip_store: &mut VertexDataStore<GpuBlock32>,
                packed_layer_index: PackedLayerIndex,)
                -> ClipInfo {
         // We pass true here for the MaskCacheInfo because this type of
         // mask needs an extra clip for the clip rectangle.
-        let clip_source = ClipSource::Region(clip_region.clone());
+        let clip_sources = vec![ClipSource::Region(clip_region.clone(), RegionMode::IncludeRect)];
         ClipInfo {
-            mask_cache_info: MaskCacheInfo::new(&clip_source, true, clip_store),
-            clip_source: clip_source,
+            mask_cache_info: MaskCacheInfo::new(&clip_sources, clip_store),
+            clip_sources: clip_sources,
             packed_layer_index: packed_layer_index,
             xf_rect: None,
         }
     }
 
     pub fn is_masking(&self) -> bool {
         match self.mask_cache_info {
             Some(ref info) => info.is_masking(),
--- a/gfx/webrender/src/clip_scroll_tree.rs
+++ b/gfx/webrender/src/clip_scroll_tree.rs
@@ -18,17 +18,17 @@ pub struct ClipScrollTree {
 
     /// The ScrollLayerId of the currently scrolling node. Used to allow the same
     /// node to scroll even if a touch operation leaves the boundaries of that node.
     pub current_scroll_layer_id: Option<ScrollLayerId>,
 
     /// The current reference frame id, used for giving a unique id to all new
     /// reference frames. The ClipScrollTree increments this by one every time a
     /// reference frame is created.
-    current_reference_frame_id: usize,
+    current_reference_frame_id: u64,
 
     /// The root reference frame, which is the true root of the ClipScrollTree. Initially
     /// this ID is not valid, which is indicated by ```node``` being empty.
     pub root_reference_frame_id: ScrollLayerId,
 
     /// The root scroll node which is the first child of the root reference frame.
     /// Initially this ID is not valid, which is indicated by ```nodes``` being empty.
     pub topmost_scroll_layer_id: ScrollLayerId,
--- a/gfx/webrender/src/device.rs
+++ b/gfx/webrender/src/device.rs
@@ -46,16 +46,27 @@ static SHADER_PREAMBLE: &'static str = "
 pub enum DepthFunction {
     Less = gl::LESS,
 }
 
 #[derive(Copy, Clone, Debug, PartialEq)]
 pub enum TextureTarget {
     Default,
     Array,
+    Rect,
+}
+
+impl TextureTarget {
+    pub fn to_gl_target(&self) -> gl::GLuint {
+        match *self {
+            TextureTarget::Default => gl::TEXTURE_2D,
+            TextureTarget::Array => gl::TEXTURE_2D_ARRAY,
+            TextureTarget::Rect => gl::TEXTURE_RECTANGLE,
+        }
+    }
 }
 
 #[derive(Copy, Clone, Debug, PartialEq)]
 pub enum TextureFilter {
     Nearest,
     Linear,
 }
 
@@ -299,20 +310,20 @@ impl VertexFormat {
     }
 }
 
 impl TextureId {
     pub fn bind(&self, gl: &gl::Gl) {
         gl.bind_texture(self.target, self.name);
     }
 
-    pub fn new(name: gl::GLuint) -> TextureId {
+    pub fn new(name: gl::GLuint, texture_target: TextureTarget) -> TextureId {
         TextureId {
             name: name,
-            target: gl::TEXTURE_2D,
+            target: texture_target.to_gl_target(),
         }
     }
 
     pub fn invalid() -> TextureId {
         TextureId {
             name: 0,
             target: gl::TEXTURE_2D,
         }
@@ -1085,25 +1096,20 @@ impl Device {
     }
 
     pub fn create_texture_ids(&mut self,
                               count: i32,
                               target: TextureTarget) -> Vec<TextureId> {
         let id_list = self.gl.gen_textures(count);
         let mut texture_ids = Vec::new();
 
-        let target = match target {
-            TextureTarget::Default => gl::TEXTURE_2D,
-            TextureTarget::Array => gl::TEXTURE_2D_ARRAY,
-        };
-
         for id in id_list {
             let texture_id = TextureId {
                 name: id,
-                target: target,
+                target: target.to_gl_target(),
             };
 
             let texture = Texture {
                 gl: self.gl.clone(),
                 id: id,
                 width: 0,
                 height: 0,
                 format: ImageFormat::Invalid,
@@ -2044,19 +2050,18 @@ impl Device {
     }
 
     pub fn set_blend_mode_premultiplied_alpha(&self) {
         self.gl.blend_func(gl::ONE, gl::ONE_MINUS_SRC_ALPHA);
         self.gl.blend_equation(gl::FUNC_ADD);
     }
 
     pub fn set_blend_mode_alpha(&self) {
-        //self.gl.blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
         self.gl.blend_func_separate(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA,
-                                     gl::ONE, gl::ONE);
+                                    gl::ONE, gl::ONE_MINUS_SRC_ALPHA);
         self.gl.blend_equation(gl::FUNC_ADD);
     }
 
     pub fn set_blend_mode_subpixel(&self, color: ColorF) {
         self.gl.blend_color(color.r, color.g, color.b, color.a);
         self.gl.blend_func(gl::CONSTANT_COLOR, gl::ONE_MINUS_SRC_COLOR);
     }
 
--- a/gfx/webrender/src/frame.rs
+++ b/gfx/webrender/src/frame.rs
@@ -436,17 +436,19 @@ impl Frame {
         }
 
         // TODO(gw): Int with overflow etc
         context.builder.push_stacking_context(&reference_frame_relative_offset,
                                               pipeline_id,
                                               level == 0,
                                               composition_operations);
 
-        if level == 0 {
+        // For the root pipeline, there's no need to add a full screen rectangle
+        // here, as it's handled by the framebuffer clear.
+        if level == 0 && context.scene.root_pipeline_id.unwrap() != pipeline_id {
             if let Some(pipeline) = context.scene.pipeline_map.get(&pipeline_id) {
                 if let Some(bg_color) = pipeline.background_color {
                     // Note: we don't use the original clip region here,
                     // it's already processed by the node we just pushed.
                     let background_rect = LayerRect::new(LayerPoint::zero(), bounds.size);
                     context.builder.add_solid_rectangle(scroll_layer_id,
                                                         &bounds,
                                                         &ClipRegion::simple(&background_rect),
@@ -651,16 +653,17 @@ impl Frame {
                 SpecificDisplayItem::RadialGradient(ref info) => {
                     context.builder.add_radial_gradient(scroll_layer_id,
                                                         item.rect,
                                                         &item.clip,
                                                         info.gradient.start_center,
                                                         info.gradient.start_radius,
                                                         info.gradient.end_center,
                                                         info.gradient.end_radius,
+                                                        info.gradient.ratio_xy,
                                                         info.gradient.stops,
                                                         info.gradient.extend_mode);
                 }
                 SpecificDisplayItem::BoxShadow(ref box_shadow_info) => {
                     context.builder.add_box_shadow(scroll_layer_id,
                                                    &box_shadow_info.box_bounds,
                                                    &item.clip,
                                                    &box_shadow_info.offset,
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use app_units::Au;
 use batch_builder::BorderSideHelpers;
 use frame::FrameId;
 use gpu_store::GpuStoreAddress;
 use internal_types::{HardwareCompositeOp, SourceTexture};
-use mask_cache::{ClipSource, MaskCacheInfo};
+use mask_cache::{ClipMode, ClipSource, MaskCacheInfo, RegionMode};
 use prim_store::{BorderPrimitiveCpu, BorderPrimitiveGpu, BoxShadowPrimitiveGpu};
 use prim_store::{GradientPrimitiveCpu, GradientPrimitiveGpu, ImagePrimitiveCpu, ImagePrimitiveGpu};
 use prim_store::{ImagePrimitiveKind, PrimitiveContainer, PrimitiveGeometry, PrimitiveIndex};
 use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu, RadialGradientPrimitiveGpu};
 use prim_store::{RectanglePrimitive, TextRunPrimitiveCpu, TextRunPrimitiveGpu};
 use prim_store::{TexelRect, YuvImagePrimitiveCpu, YuvImagePrimitiveGpu};
 use profiler::{FrameProfileCounters, TextureCacheProfileCounters};
 use render_task::{AlphaRenderItem, MaskCacheKey, MaskResult, RenderTask, RenderTaskIndex};
@@ -143,42 +143,47 @@ impl FrameBuilder {
             stacking_context_stack: Vec::new(),
         }
     }
 
     fn add_primitive(&mut self,
                      scroll_layer_id: ScrollLayerId,
                      rect: &LayerRect,
                      clip_region: &ClipRegion,
+                     extra_clip: Option<ClipSource>,
                      container: PrimitiveContainer)
                      -> PrimitiveIndex {
         let stacking_context_index = *self.stacking_context_stack.last().unwrap();
         if !self.stacking_context_store[stacking_context_index.0]
                 .has_clip_scroll_group(scroll_layer_id) {
             let group_index = self.create_clip_scroll_group(stacking_context_index,
                                                             scroll_layer_id);
             let stacking_context = &mut self.stacking_context_store[stacking_context_index.0];
             stacking_context.clip_scroll_groups.push(group_index);
         }
 
         let geometry = PrimitiveGeometry {
             local_rect: *rect,
             local_clip_rect: clip_region.main,
         };
-        let clip_source = if clip_region.is_complex() {
-            ClipSource::Region(clip_region.clone())
-        } else {
-            ClipSource::NoClip
-        };
-        let clip_info = MaskCacheInfo::new(&clip_source,
-                                           false,
+        let mut clip_sources = Vec::new();
+        if clip_region.is_complex() {
+            clip_sources.push(ClipSource::Region(clip_region.clone(), RegionMode::ExcludeRect));
+        }
+        // TODO(gw): Perhaps in the future it's worth passing in an array
+        //           so that callers can provide an arbitrary number
+        //           of clips?
+        if let Some(extra_clip) = extra_clip {
+            clip_sources.push(extra_clip);
+        }
+        let clip_info = MaskCacheInfo::new(&clip_sources,
                                            &mut self.prim_store.gpu_data32);
 
         let prim_index = self.prim_store.add_primitive(geometry,
-                                                       Box::new(clip_source),
+                                                       clip_sources,
                                                        clip_info,
                                                        container);
 
         match self.cmds.last_mut().unwrap() {
             &mut PrimitiveRunCmd::PrimitiveRun(_run_prim_index, ref mut count, run_layer_id)
                 if run_layer_id == scroll_layer_id => {
                     debug_assert!(_run_prim_index.0 + *count == prim_index.0);
                     *count += 1;
@@ -348,16 +353,17 @@ impl FrameBuilder {
 
         let prim = RectanglePrimitive {
             color: *color,
         };
 
         let prim_index = self.add_primitive(scroll_layer_id,
                                             rect,
                                             clip_region,
+                                            None,
                                             PrimitiveContainer::Rectangle(prim));
 
         match flags {
             PrimitiveFlags::None => {}
             PrimitiveFlags::Scrollbar(scroll_layer_id, border_radius) => {
                 self.scrollbar_prims.push(ScrollbarPrimitive {
                     prim_index: prim_index,
                     scroll_layer_id: scroll_layer_id,
@@ -646,16 +652,17 @@ impl FrameBuilder {
                         radius.bottom_right,
                         radius.bottom_left,
                     ],
                 };
 
                 self.add_primitive(scroll_layer_id,
                                    &rect,
                                    clip_region,
+                                   None,
                                    PrimitiveContainer::Border(prim_cpu, prim_gpu));
             }
             BorderDetails::Gradient(ref border) => {
                 for segment in create_segments(border.outset) {
                     self.add_gradient(scroll_layer_id,
                                       segment,
                                       clip_region,
                                       border.gradient.start_point,
@@ -668,16 +675,17 @@ impl FrameBuilder {
                 for segment in create_segments(border.outset) {
                     self.add_radial_gradient(scroll_layer_id,
                                              segment,
                                              clip_region,
                                              border.gradient.start_center,
                                              border.gradient.start_radius,
                                              border.gradient.end_center,
                                              border.gradient.end_radius,
+                                             border.gradient.ratio_xy,
                                              border.gradient.stops,
                                              border.gradient.extend_mode);
                 }
             }
         }
     }
 
     pub fn add_gradient(&mut self,
@@ -685,18 +693,22 @@ impl FrameBuilder {
                         rect: LayerRect,
                         clip_region: &ClipRegion,
                         start_point: LayerPoint,
                         end_point: LayerPoint,
                         stops: ItemRange,
                         extend_mode: ExtendMode) {
         // Fast path for clamped, axis-aligned gradients:
         let aligned = extend_mode == ExtendMode::Clamp &&
-                      (start_point.x == end_point.x ||
-                       start_point.y == end_point.y);
+                      (start_point.x == end_point.x &&
+                       start_point.x.min(end_point.x) <= rect.min_x() &&
+                       start_point.x.max(end_point.x) >= rect.max_x()) ||
+                       (start_point.y == end_point.y &&
+                       start_point.y.min(end_point.y) <= rect.min_y() &&
+                       start_point.y.max(end_point.y) >= rect.max_y());
         // Try to ensure that if the gradient is specified in reverse, then so long as the stops
         // are also supplied in reverse that the rendered result will be equivalent. To do this,
         // a reference orientation for the gradient line must be chosen, somewhat arbitrarily, so
         // just designate the reference orientation as start < end. Aligned gradient rendering
         // manages to produce the same result regardless of orientation, so don't worry about
         // reversing in that case.
         let reverse_stops = !aligned &&
                             (start_point.x > end_point.x ||
@@ -727,47 +739,53 @@ impl FrameBuilder {
         };
 
         let prim = if aligned {
             PrimitiveContainer::AlignedGradient(gradient_cpu, gradient_gpu)
         } else {
             PrimitiveContainer::AngleGradient(gradient_cpu, gradient_gpu)
         };
 
-        self.add_primitive(scroll_layer_id, &rect, clip_region, prim);
+        self.add_primitive(scroll_layer_id,
+                           &rect,
+                           clip_region,
+                           None,
+                           prim);
     }
 
     pub fn add_radial_gradient(&mut self,
                                scroll_layer_id: ScrollLayerId,
                                rect: LayerRect,
                                clip_region: &ClipRegion,
                                start_center: LayerPoint,
                                start_radius: f32,
                                end_center: LayerPoint,
                                end_radius: f32,
+                               ratio_xy: f32,
                                stops: ItemRange,
                                extend_mode: ExtendMode) {
         let radial_gradient_cpu = RadialGradientPrimitiveCpu {
             stops_range: stops,
             extend_mode: extend_mode,
             cache_dirty: true,
         };
 
         let radial_gradient_gpu = RadialGradientPrimitiveGpu {
             start_center: start_center,
             end_center: end_center,
             start_radius: start_radius,
             end_radius: end_radius,
+            ratio_xy: ratio_xy,
             extend_mode: pack_as_float(extend_mode as u32),
-            padding: [0.0],
         };
 
         self.add_primitive(scroll_layer_id,
                            &rect,
                            clip_region,
+                           None,
                            PrimitiveContainer::RadialGradient(radial_gradient_cpu, radial_gradient_gpu));
     }
 
     pub fn add_text(&mut self,
                     scroll_layer_id: ScrollLayerId,
                     rect: LayerRect,
                     clip_region: &ClipRegion,
                     font_key: FontKey,
@@ -827,16 +845,17 @@ impl FrameBuilder {
 
             let prim_gpu = TextRunPrimitiveGpu {
                 color: *color,
             };
 
             self.add_primitive(scroll_layer_id,
                                &rect,
                                clip_region,
+                               None,
                                PrimitiveContainer::TextRun(prim_cpu, prim_gpu));
         }
     }
 
     pub fn add_box_shadow(&mut self,
                           scroll_layer_id: ScrollLayerId,
                           box_bounds: &LayerRect,
                           clip_region: &ClipRegion,
@@ -883,22 +902,28 @@ impl FrameBuilder {
         // expensive box-shadow shader.
         enum BoxShadowKind {
             Simple(Vec<LayerRect>),     // Can be drawn via simple rectangles only
             Shadow(Vec<LayerRect>),     // Requires the full box-shadow code path
         }
 
         let shadow_kind = match clip_mode {
             BoxShadowClipMode::Outset | BoxShadowClipMode::None => {
+                // If a border radius is set, we need to draw inside
+                // the original box in order to draw where the border
+                // corners are. A clip-out mask applied below will
+                // ensure that we don't draw on the box itself.
+                let inner_box_bounds = box_bounds.inflate(-border_radius,
+                                                          -border_radius);
                 // For outset shadows, subtracting the element rectangle
                 // from the outer rectangle gives the rectangles we need
                 // to draw. In the simple case (no blur radius), we can
                 // just draw these as solid colors.
                 let mut rects = Vec::new();
-                subtract_rect(&outer_rect, box_bounds, &mut rects);
+                subtract_rect(&outer_rect, &inner_box_bounds, &mut rects);
                 if edge_size == 0.0 {
                     BoxShadowKind::Simple(rects)
                 } else {
                     BoxShadowKind::Shadow(rects)
                 }
             }
             BoxShadowClipMode::Inset => {
                 // For inset shadows, in the simple case (no blur) we
@@ -937,30 +962,40 @@ impl FrameBuilder {
                 }
             }
             BoxShadowKind::Shadow(rects) => {
                 let inverted = match clip_mode {
                     BoxShadowClipMode::Outset | BoxShadowClipMode::None => 0.0,
                     BoxShadowClipMode::Inset => 1.0,
                 };
 
+                // If we have a border radius, we'll need to apply
+                // a clip-out mask.
+                let extra_clip = if border_radius > 0.0 {
+                    Some(ClipSource::Complex(*box_bounds,
+                                             border_radius,
+                                             ClipMode::ClipOut))
+                } else {
+                    None
+                };
 
                 let prim_gpu = BoxShadowPrimitiveGpu {
                     src_rect: *box_bounds,
                     bs_rect: bs_rect,
                     color: *color,
                     blur_radius: blur_radius,
                     border_radius: border_radius,
                     edge_size: edge_size,
                     inverted: inverted,
                 };
 
                 self.add_primitive(scroll_layer_id,
                                    &outer_rect,
                                    clip_region,
+                                   extra_clip,
                                    PrimitiveContainer::BoxShadow(prim_gpu, rects));
             }
         }
     }
 
     pub fn add_webgl_rectangle(&mut self,
                                scroll_layer_id: ScrollLayerId,
                                rect: LayerRect,
@@ -976,16 +1011,17 @@ impl FrameBuilder {
         let prim_gpu = ImagePrimitiveGpu {
             stretch_size: rect.size,
             tile_spacing: LayerSize::zero(),
         };
 
         self.add_primitive(scroll_layer_id,
                            &rect,
                            clip_region,
+                           None,
                            PrimitiveContainer::Image(prim_cpu, prim_gpu));
     }
 
     pub fn add_image(&mut self,
                      scroll_layer_id: ScrollLayerId,
                      rect: LayerRect,
                      clip_region: &ClipRegion,
                      stretch_size: &LayerSize,
@@ -1007,16 +1043,17 @@ impl FrameBuilder {
         let prim_gpu = ImagePrimitiveGpu {
             stretch_size: *stretch_size,
             tile_spacing: *tile_spacing,
         };
 
         self.add_primitive(scroll_layer_id,
                            &rect,
                            clip_region,
+                           None,
                            PrimitiveContainer::Image(prim_cpu, prim_gpu));
     }
 
     pub fn add_yuv_image(&mut self,
                          scroll_layer_id: ScrollLayerId,
                          rect: LayerRect,
                          clip_region: &ClipRegion,
                          y_image_key: ImageKey,
@@ -1030,16 +1067,17 @@ impl FrameBuilder {
             yuv_resource_address: GpuStoreAddress(0),
         };
 
         let prim_gpu = YuvImagePrimitiveGpu::new(rect.size, color_space);
 
         self.add_primitive(scroll_layer_id,
                            &rect,
                            clip_region,
+                           None,
                            PrimitiveContainer::YuvImage(prim_cpu, prim_gpu));
     }
 
     /// Compute the contribution (bounding rectangles, and resources) of layers and their
     /// primitives in screen space.
     fn build_layer_screen_rects_and_cull_layers(&mut self,
                                                 screen_rect: &DeviceIntRect,
                                                 clip_scroll_tree: &mut ClipScrollTree,
@@ -1087,20 +1125,20 @@ impl FrameBuilder {
             geom.local_rect.origin.x = clip_scroll_node.local_viewport_rect.origin.x +
                                        clip_scroll_node.local_viewport_rect.size.width -
                                        geom.local_rect.size.width -
                                        distance_from_edge;
 
             geom.local_rect.origin.y = util::lerp(min_y, max_y, f);
             geom.local_clip_rect = geom.local_rect;
 
-            let clip_source = if scrollbar_prim.border_radius == 0.0 {
-                ClipSource::NoClip
+            let clip_source = if scrollbar_prim.border_radius > 0.0 {
+                Some(ClipSource::Complex(geom.local_rect, scrollbar_prim.border_radius, ClipMode::Clip))
             } else {
-                ClipSource::Complex(geom.local_rect, scrollbar_prim.border_radius)
+                None
             };
             self.prim_store.set_clip_source(scrollbar_prim.prim_index, clip_source);
             *self.prim_store.gpu_geometry.get_mut(GpuStoreAddress(scrollbar_prim.prim_index.0 as i32)) = geom;
         }
     }
 
     fn build_render_task(&self) -> (RenderTask, usize) {
         profile_scope!("build_render_task");
@@ -1160,17 +1198,17 @@ impl FrameBuilder {
                     }
 
                     let composite_count = stacking_context.composite_ops.count();
 
                     if composite_count == 0 && stacking_context.should_isolate {
                         let mut prev_task = alpha_task_stack.pop().unwrap();
                         let item = AlphaRenderItem::HardwareComposite(stacking_context_index,
                                                                       current_task.id,
-                                                                      HardwareCompositeOp::Alpha,
+                                                                      HardwareCompositeOp::PremultipliedAlpha,
                                                                       next_z);
                         next_z += 1;
                         prev_task.as_alpha_batch().alpha_items.push(item);
                         prev_task.children.push(current_task);
                         current_task = prev_task;
                     }
 
                     for filter in &stacking_context.composite_ops.filters {
@@ -1208,35 +1246,37 @@ impl FrameBuilder {
 
                     if !stacking_context.is_visible {
                         continue;
                     }
 
                     let stacking_context_index = *sc_stack.last().unwrap();
                     let group_index = self.stacking_context_store[stacking_context_index.0]
                                           .clip_scroll_group(scroll_layer_id);
-                    let clip_scroll_group = &self.clip_scroll_group_store[group_index.0];
+                    let xf_rect = match &self.clip_scroll_group_store[group_index.0].xf_rect {
+                        &Some(ref xf_rect) => xf_rect,
+                        &None => continue,
+                    };
 
                     for i in 0..prim_count {
                         let prim_index = PrimitiveIndex(first_prim_index.0 + i);
 
                         if self.prim_store.cpu_bounding_rects[prim_index.0].is_some() {
                             let prim_metadata = self.prim_store.get_metadata(prim_index);
 
                             // Add any dynamic render tasks needed to render this primitive
                             if let Some(ref render_task) = prim_metadata.render_task {
                                 current_task.children.push(render_task.clone());
                             }
                             if let Some(ref clip_task) = prim_metadata.clip_task {
                                 current_task.children.push(clip_task.clone());
                             }
 
-                            let transform_kind = clip_scroll_group.xf_rect.as_ref().unwrap().kind;
                             let needs_clipping = prim_metadata.clip_task.is_some();
-                            let needs_blending = transform_kind == TransformedRectKind::Complex ||
+                            let needs_blending = xf_rect.kind == TransformedRectKind::Complex ||
                                                  !prim_metadata.is_opaque ||
                                                  needs_clipping;
 
                             let items = if needs_blending {
                                 &mut current_task.as_alpha_batch().alpha_items
                             } else {
                                 &mut current_task.as_alpha_batch().opaque_items
                             };
@@ -1370,17 +1410,17 @@ struct LayerRectCalculationAndCullingPas
 
     /// A cached clip info stack, which should handle the most common situation,
     /// which is that we are using the same clip info stack that we were using
     /// previously.
     current_clip_stack: Vec<(PackedLayerIndex, MaskCacheInfo)>,
 
     /// Information about the cached clip stack, which is used to avoid having
     /// to recalculate it for every primitive.
-    current_clip_info: Option<(ScrollLayerId, DeviceIntRect)>
+    current_clip_info: Option<(ScrollLayerId, Option<DeviceIntRect>)>
 }
 
 impl<'a> LayerRectCalculationAndCullingPass<'a> {
     fn create_and_run(frame_builder: &'a mut FrameBuilder,
                       screen_rect: &'a DeviceIntRect,
                       clip_scroll_tree: &'a mut ClipScrollTree,
                       auxiliary_lists_map: &'a AuxiliaryListsMap,
                       resource_cache: &'a mut ResourceCache,
@@ -1452,26 +1492,28 @@ impl<'a> LayerRectCalculationAndCullingP
             let mask_info = match node_clip_info.mask_cache_info {
                 Some(ref mut mask_info) => mask_info,
                 _ => continue,
             };
 
             let auxiliary_lists = self.auxiliary_lists_map.get(&node.pipeline_id)
                                                           .expect("No auxiliary lists?");
 
-            mask_info.update(&node_clip_info.clip_source,
+            mask_info.update(&node_clip_info.clip_sources,
                              &packed_layer.transform,
                              &mut self.frame_builder.prim_store.gpu_data32,
                              self.device_pixel_ratio,
                              auxiliary_lists);
 
-            if let Some(mask) = node_clip_info.clip_source.image_mask() {
-                // We don't add the image mask for resolution, because
-                // layer masks are resolved later.
-                self.resource_cache.request_image(mask.image, ImageRendering::Auto, None);
+            for clip_source in &node_clip_info.clip_sources {
+                if let Some(mask) = clip_source.image_mask() {
+                    // We don't add the image mask for resolution, because
+                    // layer masks are resolved later.
+                    self.resource_cache.request_image(mask.image, ImageRendering::Auto, None);
+                }
             }
         }
     }
 
     fn recalculate_clip_scroll_groups(&mut self) {
         for ref mut group in &mut self.frame_builder.clip_scroll_group_store {
             let stacking_context_index = group.stacking_context_index;
             let stacking_context = &mut self.frame_builder
@@ -1548,17 +1590,17 @@ impl<'a> LayerRectCalculationAndCullingP
         // calculate the device bounding rect. In the future, we could cache this during
         // the initial adding of items for the common case (where there is only a single
         // scroll layer for items in a stacking context).
         let stacking_context = &mut self.frame_builder
                                         .stacking_context_store[stacking_context_index.0];
         stacking_context.bounding_rect = DeviceIntRect::zero();
     }
 
-    fn rebuild_clip_info_stack_if_necessary(&mut self, id: ScrollLayerId) -> DeviceIntRect {
+    fn rebuild_clip_info_stack_if_necessary(&mut self, id: ScrollLayerId) -> Option<DeviceIntRect> {
         if let Some((current_scroll_id, bounding_rect)) = self.current_clip_info {
             if current_scroll_id == id {
                 return bounding_rect;
             }
         }
 
         // TODO(mrobinson): If we notice that this process is expensive, we can special-case
         // more common situations, such as moving from a child or a parent.
@@ -1571,24 +1613,25 @@ impl<'a> LayerRectCalculationAndCullingP
             current_id = node.parent;
 
             let clip_info = match node.node_type {
                 NodeType::Clip(ref clip) if clip.is_masking() => clip,
                 _ => continue,
             };
 
             if bounding_rect.is_none() {
-                bounding_rect = Some(clip_info.xf_rect.as_ref().unwrap().bounding_rect);
+                bounding_rect =
+                    Some(clip_info.xf_rect.as_ref().map_or_else(DeviceIntRect::zero,
+                                                                |x| x.bounding_rect))
             }
             self.current_clip_stack.push((clip_info.packed_layer_index,
                                           clip_info.mask_cache_info.clone().unwrap()))
         }
         self.current_clip_stack.reverse();
 
-        let bounding_rect = bounding_rect.unwrap_or_else(DeviceIntRect::zero);
         self.current_clip_info = Some((id, bounding_rect));
         bounding_rect
     }
 
     fn handle_primitive_run(&mut self,
                             prim_index: PrimitiveIndex,
                             prim_count: usize,
                             scroll_layer_id: ScrollLayerId) {
@@ -1602,16 +1645,19 @@ impl<'a> LayerRectCalculationAndCullingP
             }
 
             let group_index = stacking_context.clip_scroll_group(scroll_layer_id);
             let clip_scroll_group = &self.frame_builder.clip_scroll_group_store[group_index.0];
             (clip_scroll_group.packed_layer_index, stacking_context.pipeline_id)
         };
 
         let node_clip_bounds = self.rebuild_clip_info_stack_if_necessary(scroll_layer_id);
+        if node_clip_bounds.map_or(false, |bounds| bounds.is_empty()) {
+            return;
+        }
 
         let stacking_context =
             &mut self.frame_builder.stacking_context_store[stacking_context_index.0];
         let packed_layer = &self.frame_builder.packed_layers[packed_layer_index.0];
         let auxiliary_lists = self.auxiliary_lists_map.get(&pipeline_id)
                                                       .expect("No auxiliary lists?");
 
         for i in 0..prim_count {
@@ -1653,16 +1699,17 @@ impl<'a> LayerRectCalculationAndCullingP
                 }
 
                 // Try to create a mask if we may need to.
                 if !self.current_clip_stack.is_empty() {
                     // If the primitive doesn't have a specific clip, key the task ID off the
                     // stacking context. This means that two primitives which are only clipped
                     // by the stacking context stack can share clip masks during render task
                     // assignment to targets.
+                    let node_clip_bounds = node_clip_bounds.unwrap_or_else(DeviceIntRect::zero);
                     let (mask_key, mask_rect) = match prim_clip_info {
                         Some(..) => (MaskCacheKey::Primitive(prim_index), prim_bounding_rect),
                         None => (MaskCacheKey::ScrollLayer(scroll_layer_id), node_clip_bounds)
                     };
                     let mask_opt =
                         RenderTask::new_mask(mask_rect, mask_key, &self.current_clip_stack);
                     match mask_opt {
                         MaskResult::Outside => { // Primitive is completely clipped out.
--- a/gfx/webrender/src/gpu_store.rs
+++ b/gfx/webrender/src/gpu_store.rs
@@ -15,16 +15,24 @@ pub struct GpuStoreAddress(pub i32);
 impl Add<i32> for GpuStoreAddress {
     type Output = GpuStoreAddress;
 
     fn add(self, other: i32) -> GpuStoreAddress {
         GpuStoreAddress(self.0 + other)
     }
 }
 
+impl Add<usize> for GpuStoreAddress {
+    type Output = GpuStoreAddress;
+
+    fn add(self, other: usize) -> GpuStoreAddress {
+        GpuStoreAddress(self.0 + other as i32)
+    }
+}
+
 pub trait GpuStoreLayout {
     fn image_format() -> ImageFormat;
 
     fn texture_width<T>() -> usize;
 
     fn texture_filter() -> TextureFilter;
 
     fn texel_size() -> usize {
--- a/gfx/webrender/src/internal_types.rs
+++ b/gfx/webrender/src/internal_types.rs
@@ -12,17 +12,17 @@ use std::f32;
 use std::hash::BuildHasherDefault;
 use std::{i32, usize};
 use std::path::PathBuf;
 use std::sync::Arc;
 use tiling;
 use renderer::BlendMode;
 use webrender_traits::{Epoch, ColorF, PipelineId};
 use webrender_traits::{ImageFormat, NativeFontHandle};
-use webrender_traits::{ExternalImageId, ScrollLayerId};
+use webrender_traits::{ExternalImageData, ExternalImageId, ScrollLayerId};
 use webrender_traits::{ImageData};
 use webrender_traits::{DeviceUintRect};
 
 // An ID for a texture that is owned by the
 // texture cache module. This can include atlases
 // or standalone textures allocated via the
 // texture cache (e.g. if an image is too large
 // to be added to an atlas). The texture cache
@@ -38,17 +38,17 @@ pub struct CacheTextureId(pub usize);
 // pipeline until they reach the rendering
 // thread, where they are resolved to a
 // native texture ID.
 
 #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
 pub enum SourceTexture {
     Invalid,
     TextureCache(CacheTextureId),
-    External(ExternalImageId),
+    External(ExternalImageData),
     #[cfg_attr(not(feature = "webgl"), allow(dead_code))]
     /// This is actually a gl::GLuint, with the shared texture id between the
     /// main context and the WebGL context.
     WebGL(u32),
 }
 
 const COLOR_FLOAT_TO_FIXED: f32 = 255.0;
 const COLOR_FLOAT_TO_FIXED_WIDE: f32 = 65535.0;
@@ -382,18 +382,18 @@ pub enum LowLevelFilterOp {
     Invert(Au),
     Opacity(Au),
     Saturate(Au),
     Sepia(Au),
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 pub enum HardwareCompositeOp {
-    Alpha,
+    PremultipliedAlpha,
 }
 
 impl HardwareCompositeOp {
     pub fn to_blend_mode(&self) -> BlendMode {
         match self {
-            &HardwareCompositeOp::Alpha => BlendMode::Alpha,
+            &HardwareCompositeOp::PremultipliedAlpha => BlendMode::PremultipliedAlpha,
         }
     }
 }
--- a/gfx/webrender/src/lib.rs
+++ b/gfx/webrender/src/lib.rs
@@ -1,14 +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/. */
 
-//#![feature(mpsc_select)]
-
 //! A GPU based Webrender.
 //!
 //! It serves as an experimental render backend for [Servo](https://servo.org/),
 //! but it can also be used as such in a standalone application.
 //!
 //! # External dependencies
 //! Webrender currently depends on [FreeType](https://www.freetype.org/)
 //!
@@ -19,33 +17,29 @@
 //! Your `Renderer` is responsible to render the previously processed frames onto the screen.
 //!
 //! By calling `yourRenderApiSenderInstance.create_api()`, you'll get a `RenderApi` instance,
 //! which is responsible for the processing of new frames. A worker thread is used internally to
 //! untie the workload from the application thread and therefore be able
 //! to make better use of multicore systems.
 //!
 //! What is referred to as a `frame`, is the current geometry on the screen.
-//! A new Frame is created by calling [set_root_stacking_context()][newframe] on the `RenderApi`.
+//! A new Frame is created by calling [`set_display_list()`][newframe] on the `RenderApi`.
 //! When the geometry is processed, the application will be informed via a `RenderNotifier`,
 //! a callback which you employ with [set_render_notifier][notifier] on the `Renderer`
 //! More information about [stacking contexts][stacking_contexts].
 //!
-//! `set_root_stacking_context()` also needs to be supplied with `BuiltDisplayList`s.
+//! `set_display_list()` also needs to be supplied with `BuiltDisplayList`s.
 //! These are obtained by finalizing a `DisplayListBuilder`. These are used to draw your geometry.
 //! But it doesn't only contain trivial geometry, it can also store another StackingContext, as
 //! they're nestable.
 //!
-//! Remember to insert the DisplayListId into the StackingContext as well, as they'll be referenced
-//! from there. An Id for your DisplayList can be obtained by calling
-//! `yourRenderApiInstance.next_display_list_id();`
-//!
 //! [stacking_contexts]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context
-//! [newframe]: ../webrender_traits/struct.RenderApi.html#method.set_root_stacking_context
-//! [notifier]: struct.Renderer.html#method.set_render_notifier
+//! [newframe]: ../webrender_traits/struct.RenderApi.html#method.set_display_list
+//! [notifier]: renderer/struct.Renderer.html#method.set_render_notifier
 
 #[macro_use]
 extern crate lazy_static;
 #[macro_use]
 extern crate log;
 #[macro_use]
 extern crate bitflags;
 #[macro_use]
--- a/gfx/webrender/src/mask_cache.rs
+++ b/gfx/webrender/src/mask_cache.rs
@@ -3,168 +3,281 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use gpu_store::GpuStoreAddress;
 use prim_store::{ClipData, GpuBlock32, PrimitiveStore};
 use prim_store::{CLIP_DATA_GPU_SIZE, MASK_DATA_GPU_SIZE};
 use renderer::VertexDataStore;
 use util::{MatrixHelpers, TransformedRect};
 use webrender_traits::{AuxiliaryLists, BorderRadius, ClipRegion, ComplexClipRegion, ImageMask};
-use webrender_traits::{DeviceIntRect, DeviceIntSize, LayerRect, LayerToWorldTransform};
+use webrender_traits::{DeviceIntRect, LayerToWorldTransform};
+use webrender_traits::{LayerRect, LayerPoint, LayerSize};
+
+const MAX_CLIP: f32 = 1000000.0;
+
+#[repr(C)]
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum ClipMode {
+    Clip,           // Pixels inside the region are visible.
+    ClipOut,        // Pixels outside the region are visible.
+}
+
+#[repr(C)]
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum RegionMode {
+    IncludeRect,
+    ExcludeRect,
+}
 
 #[derive(Clone, Debug)]
 pub enum ClipSource {
-    NoClip,
-    Complex(LayerRect, f32),
-    Region(ClipRegion),
+    Complex(LayerRect, f32, ClipMode),
+    // The RegionMode here specifies whether to consider the rect
+    // from the clip region as part of the mask. This is true
+    // for clip/scroll nodes, but false for primitives, where
+    // the clip rect is handled in local space.
+    Region(ClipRegion, RegionMode),
 }
 
 impl ClipSource {
     pub fn image_mask(&self) -> Option<ImageMask> {
         match self {
-            &ClipSource::NoClip => None,
             &ClipSource::Complex(..) => None,
-            &ClipSource::Region(ref region) => region.image_mask,
+            &ClipSource::Region(ref region, _) => region.image_mask,
         }
     }
 }
 
 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
 pub struct ClipAddressRange {
     pub start: GpuStoreAddress,
-    item_count: u32,
+    item_count: usize,
+}
+
+/// Represents a local rect and a device space
+/// bounding rect that can be updated when the
+/// transform changes.
+#[derive(Clone, Debug, PartialEq)]
+pub struct Geometry {
+    pub local_rect: LayerRect,
+    pub bounding_rect: DeviceIntRect,
+}
+
+impl Geometry {
+    fn new(local_rect: LayerRect) -> Geometry {
+        Geometry {
+            local_rect: local_rect,
+            bounding_rect: DeviceIntRect::zero(),
+        }
+    }
+
+    fn update(&mut self,
+              transform: &LayerToWorldTransform,
+              device_pixel_ratio: f32) {
+        let transformed = TransformedRect::new(&self.local_rect,
+                                               &transform,
+                                               device_pixel_ratio);
+        self.bounding_rect = transformed.bounding_rect;
+    }
+}
+
+/// Depending on the complexity of the clip, we may either
+/// know the outer and/or inner rect, or neither or these.
+/// In the case of a clip-out, we currently set the mask
+/// bounds to be unknown. This is conservative, but ensures
+/// correctness. In the future we can make this a lot
+/// more clever with some proper region handling.
+#[derive(Clone, Debug, PartialEq)]
+pub enum MaskBounds {
+    /// We know both the outer and inner rect. This is the
+    /// fast path for, e.g. a simple rounded rect.
+    OuterInner(Geometry, Geometry),
+    /// We know the outer rect only.
+    Outer(Geometry),
+    /// We can't determine the bounds - draw mask over entire rect.
+    /// This is currently used for clip-out operations on
+    /// box shadows.
+    None,
 }
 
 #[derive(Clone, Debug)]
 pub struct MaskCacheInfo {
     pub clip_range: ClipAddressRange,
-    pub effective_clip_count: u32,
+    pub effective_clip_count: usize,
     pub image: Option<(ImageMask, GpuStoreAddress)>,
-    pub local_rect: Option<LayerRect>,
-    pub local_inner: Option<LayerRect>,
-    pub inner_rect: DeviceIntRect,
-    pub outer_rect: DeviceIntRect,
+    pub bounds: Option<MaskBounds>,
     pub is_aligned: bool,
 }
 
 impl MaskCacheInfo {
     /// Create a new mask cache info. It allocates the GPU store data but leaves
     /// it unitialized for the following `update()` call to deal with.
-    pub fn new(source: &ClipSource,
-               extra_clip: bool,
+    pub fn new(clips: &[ClipSource],
                clip_store: &mut VertexDataStore<GpuBlock32>)
                -> Option<MaskCacheInfo> {
-        let (image, clip_range) = match source {
-            &ClipSource::NoClip => return None,
-            &ClipSource::Complex(..) => {
-                (None,
-                ClipAddressRange {
-                    start: clip_store.alloc(CLIP_DATA_GPU_SIZE),
-                    item_count: 1,
-                })
+        if clips.is_empty() {
+            return None;
+        }
+
+        let mut image = None;
+        let mut clip_count = 0;
+
+        // Work out how much clip data space we need to allocate
+        // and if we have an image mask.
+        for clip in clips {
+            match clip {
+                &ClipSource::Complex(..) => {
+                    clip_count += 1;
+                },
+                &ClipSource::Region(ref region, region_mode) => {
+                    if let Some(info) = region.image_mask {
+                        debug_assert!(image.is_none());     // TODO(gw): Support >1 image mask!
+                        image = Some((info, clip_store.alloc(MASK_DATA_GPU_SIZE)));
+                    }
+
+                    clip_count += region.complex.length;
+                    if region_mode == RegionMode::IncludeRect {
+                        clip_count += 1;
+                    }
+                },
+            }
+        }
+
+        let clip_range = ClipAddressRange {
+            start: if clip_count > 0 {
+                clip_store.alloc(CLIP_DATA_GPU_SIZE * clip_count)
+            } else {
+                GpuStoreAddress(0)
             },
-            &ClipSource::Region(ref region) => {
-                let count = region.complex.length + if extra_clip {1} else {0};
-                (region.image_mask.map(|info|
-                    (info, clip_store.alloc(MASK_DATA_GPU_SIZE))),
-                ClipAddressRange {
-                    start: if count > 0 {
-                        clip_store.alloc(CLIP_DATA_GPU_SIZE * count)
-                    } else {
-                        GpuStoreAddress(0)
-                    },
-                    item_count: count as u32,
-                })
-            },
+            item_count: clip_count,
         };
 
         Some(MaskCacheInfo {
             clip_range: clip_range,
             effective_clip_count: clip_range.item_count,
             image: image,
-            local_rect: None,
-            local_inner: None,
-            inner_rect: DeviceIntRect::zero(),
-            outer_rect: DeviceIntRect::zero(),
+            bounds: None,
             is_aligned: true,
         })
     }
 
     pub fn update(&mut self,
-                  source: &ClipSource,
+                  sources: &[ClipSource],
                   transform: &LayerToWorldTransform,
                   clip_store: &mut VertexDataStore<GpuBlock32>,
                   device_pixel_ratio: f32,
                   aux_lists: &AuxiliaryLists) {
+        let is_aligned = transform.can_losslessly_transform_and_perspective_project_a_2d_rect();
 
-        self.is_aligned = transform.can_losslessly_transform_and_perspective_project_a_2d_rect();
+        // If we haven't cached this info, or if the transform type has changed
+        // we need to re-calculate the number of clips.
+        if self.bounds.is_none() || self.is_aligned != is_aligned {
+            let mut local_rect = Some(LayerRect::new(LayerPoint::new(-MAX_CLIP, -MAX_CLIP),
+                                                     LayerSize::new(2.0 * MAX_CLIP, 2.0 * MAX_CLIP)));
+            let mut local_inner: Option<LayerRect> = None;
+            let mut has_clip_out = false;
+
+            self.effective_clip_count = 0;
+            self.is_aligned = is_aligned;
+
+            for source in sources {
+                match source {
+                    &ClipSource::Complex(rect, radius, mode) => {
+                        // Once we encounter a clip-out, we just assume the worst
+                        // case clip mask size, for now.
+                        if mode == ClipMode::ClipOut {
+                            has_clip_out = true;
+                        }
+                        debug_assert!(self.effective_clip_count < self.clip_range.item_count);
+                        let address = self.clip_range.start + self.effective_clip_count * CLIP_DATA_GPU_SIZE;
+                        self.effective_clip_count += 1;
 
-        if self.local_rect.is_none() {
-            let mut local_rect;
-            let mut local_inner: Option<LayerRect>;
-            match source {
-                &ClipSource::NoClip => unreachable!(),
-                &ClipSource::Complex(rect, radius) => {
-                    let slice = clip_store.get_slice_mut(self.clip_range.start, CLIP_DATA_GPU_SIZE);
-                    let data = ClipData::uniform(rect, radius);
-                    PrimitiveStore::populate_clip_data(slice, data);
-                    debug_assert_eq!(self.clip_range.item_count, 1);
-                    local_rect = Some(rect);
-                    local_inner = ComplexClipRegion::new(rect, BorderRadius::uniform(radius))
-                                                    .get_inner_rect();
-                }
-                &ClipSource::Region(ref region) => {
-                    local_rect = Some(region.main);
-                    local_inner = match region.image_mask {
-                        Some(ref mask) if !mask.repeat => {
-                            local_rect = local_rect.and_then(|r| r.intersection(&mask.rect));
-                            None
-                        },
-                        Some(_) => None,
-                        None => local_rect,
-                    };
+                        let slice = clip_store.get_slice_mut(address, CLIP_DATA_GPU_SIZE);
+                        let data = ClipData::uniform(rect, radius, mode);
+                        PrimitiveStore::populate_clip_data(slice, data);
+                        local_rect = local_rect.and_then(|r| r.intersection(&rect));
+                        local_inner = ComplexClipRegion::new(rect, BorderRadius::uniform(radius))
+                                                        .get_inner_rect();
+                    }
+                    &ClipSource::Region(ref region, region_mode) => {
+                        local_rect = local_rect.and_then(|r| r.intersection(&region.main));
+                        local_inner = match region.image_mask {
+                            Some(ref mask) if !mask.repeat => {
+                                local_rect = local_rect.and_then(|r| r.intersection(&mask.rect));
+                                None
+                            },
+                            Some(_) => None,
+                            None => local_rect,
+                        };
 
-                    let clips = aux_lists.complex_clip_regions(&region.complex);
-                    self.effective_clip_count = if !self.is_aligned && self.clip_range.item_count > clips.len() as u32 {
-                        // we have an extra clip rect coming from the transformed layer
-                        assert_eq!(self.clip_range.item_count, clips.len() as u32 + 1);
-                        let address = GpuStoreAddress(self.clip_range.start.0 + (CLIP_DATA_GPU_SIZE * clips.len()) as i32);
-                        let slice = clip_store.get_slice_mut(address, CLIP_DATA_GPU_SIZE);
-                        PrimitiveStore::populate_clip_data(slice, ClipData::uniform(region.main, 0.0));
-                        self.clip_range.item_count
-                    } else {
-                        clips.len() as u32
-                    };
+                        let clips = aux_lists.complex_clip_regions(&region.complex);
+                        if !self.is_aligned && region_mode == RegionMode::IncludeRect {
+                            // we have an extra clip rect coming from the transformed layer
+                            debug_assert!(self.effective_clip_count < self.clip_range.item_count);
+                            let address = self.clip_range.start + self.effective_clip_count * CLIP_DATA_GPU_SIZE;
+                            self.effective_clip_count += 1;
+
+                            let slice = clip_store.get_slice_mut(address, CLIP_DATA_GPU_SIZE);
+                            PrimitiveStore::populate_clip_data(slice, ClipData::uniform(region.main, 0.0, ClipMode::Clip));
+                        }
 
-                    let slice = clip_store.get_slice_mut(self.clip_range.start, CLIP_DATA_GPU_SIZE * clips.len());
-                    for (clip, chunk) in clips.iter().zip(slice.chunks_mut(CLIP_DATA_GPU_SIZE)) {
-                        let data = ClipData::from_clip_region(clip);
-                        PrimitiveStore::populate_clip_data(chunk, data);
-                        local_rect = local_rect.and_then(|r| r.intersection(&clip.rect));
-                        local_inner = local_inner.and_then(|r| clip.get_inner_rect()
-                                                                   .and_then(|ref inner| r.intersection(&inner)));
+                        debug_assert!(self.effective_clip_count + clips.len() <= self.clip_range.item_count);
+                        let address = self.clip_range.start + self.effective_clip_count * CLIP_DATA_GPU_SIZE;
+                        self.effective_clip_count += clips.len();
+
+                        let slice = clip_store.get_slice_mut(address, CLIP_DATA_GPU_SIZE * clips.len());
+                        for (clip, chunk) in clips.iter().zip(slice.chunks_mut(CLIP_DATA_GPU_SIZE)) {
+                            let data = ClipData::from_clip_region(clip);
+                            PrimitiveStore::populate_clip_data(chunk, data);
+                            local_rect = local_rect.and_then(|r| r.intersection(&clip.rect));
+                            local_inner = local_inner.and_then(|r| clip.get_inner_rect()
+                                                                       .and_then(|ref inner| r.intersection(&inner)));
+                        }
                     }
                 }
-            };
-            self.local_rect = Some(local_rect.unwrap_or(LayerRect::zero()));
-            self.local_inner = local_inner;
+            }
+
+            // Work out the type of mask geometry we have, based on the
+            // list of clip sources above.
+            if has_clip_out {
+                // For clip-out, the mask rect is not known.
+                self.bounds = Some(MaskBounds::None);
+            } else {
+                // TODO(gw): local inner is only valid if there's a single clip (for now).
+                // This can be improved in the future, with some proper
+                // rectangle region handling.
+                if sources.len() > 1 {
+                    local_inner = None;
+                }
+
+                let local_rect = local_rect.unwrap_or(LayerRect::zero());
+
+                self.bounds = match local_inner {
+                    Some(local_inner) => {
+                        Some(MaskBounds::OuterInner(Geometry::new(local_rect),
+                                                    Geometry::new(local_inner)))
+                    }
+                    None => {
+                        Some(MaskBounds::Outer(Geometry::new(local_rect)))
+                    }
+                };
+            }
         }
 
-        let transformed = TransformedRect::new(self.local_rect.as_ref().unwrap(),
-                                               &transform,
-                                               device_pixel_ratio);
-        self.outer_rect = transformed.bounding_rect;
-
-        self.inner_rect = if let Some(ref inner_rect) = self.local_inner {
-            let transformed = TransformedRect::new(inner_rect,
-                                                   &transform,
-                                                   device_pixel_ratio);
-            transformed.inner_rect
-        } else {
-            DeviceIntRect::new(self.outer_rect.origin, DeviceIntSize::zero())
+        // Update the device space bounding rects of the mask
+        // geometry.
+        match self.bounds.as_mut().unwrap() {
+            &mut MaskBounds::None => {}
+            &mut MaskBounds::Outer(ref mut outer) => {
+                outer.update(transform, device_pixel_ratio);
+            }
+            &mut MaskBounds::OuterInner(ref mut outer, ref mut inner) => {
+                outer.update(transform, device_pixel_ratio);
+                inner.update(transform, device_pixel_ratio);
+            }
         }
     }
 
     /// Check if this `MaskCacheInfo` actually carries any masks. `effective_clip_count`
     /// can change during the `update` call depending on the transformation, so the mask may
     /// appear to be empty.
     pub fn is_masking(&self) -> bool {
         self.image.is_some() || self.effective_clip_count != 0
--- a/gfx/webrender/src/platform/unix/font.rs
+++ b/gfx/webrender/src/platform/unix/font.rs
@@ -41,19 +41,17 @@ fn float_to_fixed_ft(f: f64) -> i32 {
     float_to_fixed(6, f)
 }
 
 impl FontContext {
     pub fn new() -> FontContext {
         let mut lib: FT_Library = ptr::null_mut();
         unsafe {
             let result = FT_Init_FreeType(&mut lib);
-            if !result.succeeded() {
-                panic!("Unable to initialize FreeType library {:?}", result);
-            }
+            assert!(result.succeeded(), "Unable to initialize FreeType library {:?}", result);
 
             // TODO(gw): Check result of this to determine if freetype build supports subpixel.
             let result = FT_Library_SetLcdFilter(lib, FT_LcdFilter::FT_LCD_FILTER_DEFAULT);
             if !result.succeeded() {
                 println!("WARN: Initializing a FreeType library build without subpixel AA enabled!");
             }
         }
 
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -1,17 +1,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/. */
 
 use app_units::Au;
 use euclid::{Point2D, Size2D};
 use gpu_store::GpuStoreAddress;
 use internal_types::{SourceTexture, PackedTexel};
-use mask_cache::{ClipSource, MaskCacheInfo};
+use mask_cache::{ClipMode, ClipSource, MaskCacheInfo};
 use renderer::{VertexDataStore, GradientDataStore};
 use render_task::{RenderTask, RenderTaskLocation};
 use resource_cache::{CacheItem, ImageProperties, ResourceCache};
 use std::mem;
 use std::usize;
 use util::TransformedRect;
 use webrender_traits::{AuxiliaryLists, ColorF, ImageKey, ImageRendering, YuvColorSpace};
 use webrender_traits::{ClipRegion, ComplexClipRegion, ItemRange, GlyphKey};
@@ -104,17 +104,17 @@ pub enum PrimitiveCacheKey {
     BoxShadow(BoxShadowPrimitiveCacheKey),
     TextShadow(PrimitiveIndex),
 }
 
 // TODO(gw): Pack the fields here better!
 #[derive(Debug)]
 pub struct PrimitiveMetadata {
     pub is_opaque: bool,
-    pub clip_source: Box<ClipSource>,
+    pub clips: Vec<ClipSource>,
     pub clip_cache_info: Option<MaskCacheInfo>,
     pub prim_kind: PrimitiveKind,
     pub cpu_prim_index: SpecificPrimitiveIndex,
     pub gpu_prim_index: GpuStoreAddress,
     pub gpu_data_address: GpuStoreAddress,
     pub gpu_data_count: i32,
     // An optional render task that is a dependency of
     // drawing this primitive. For instance, box shadows
@@ -244,18 +244,18 @@ pub struct GradientPrimitiveCpu {
 
 #[derive(Debug, Clone)]
 #[repr(C)]
 pub struct RadialGradientPrimitiveGpu {
     pub start_center: LayerPoint,
     pub end_center: LayerPoint,
     pub start_radius: f32,
     pub end_radius: f32,
+    pub ratio_xy: f32,
     pub extend_mode: f32,
-    pub padding: [f32; 1],
 }
 
 #[derive(Debug)]
 pub struct RadialGradientPrimitiveCpu {
     pub stops_range: ItemRange,
     pub extend_mode: ExtendMode,
     pub cache_dirty: bool,
 }
@@ -410,17 +410,18 @@ struct GlyphPrimitive {
     offset: LayerPoint,
     padding: LayerPoint,
 }
 
 #[derive(Debug, Clone)]
 #[repr(C)]
 struct ClipRect {
     rect: LayerRect,
-    padding: [f32; 4],
+    mode: f32,
+    padding: [f32; 3],
 }
 
 #[derive(Debug, Clone)]
 #[repr(C)]
 struct ClipCorner {
     rect: LayerRect,
     outer_radius_x: f32,
     outer_radius_y: f32,
@@ -456,17 +457,19 @@ pub struct ClipData {
     bottom_right: ClipCorner,
 }
 
 impl ClipData {
     pub fn from_clip_region(clip: &ComplexClipRegion) -> ClipData {
         ClipData {
             rect: ClipRect {
                 rect: clip.rect,
-                padding: [0.0, 0.0, 0.0, 0.0],
+                padding: [0.0; 3],
+                // TODO(gw): Support other clip modes for regions?
+                mode: ClipMode::Clip as u32 as f32,
             },
             top_left: ClipCorner {
                 rect: LayerRect::new(
                     LayerPoint::new(clip.rect.origin.x, clip.rect.origin.y),
                     LayerSize::new(clip.radii.top_left.width, clip.radii.top_left.height)),
                 outer_radius_x: clip.radii.top_left.width,
                 outer_radius_y: clip.radii.top_left.height,
                 inner_radius_x: 0.0,
@@ -498,21 +501,22 @@ impl ClipData {
                 outer_radius_x: clip.radii.bottom_right.width,
                 outer_radius_y: clip.radii.bottom_right.height,
                 inner_radius_x: 0.0,
                 inner_radius_y: 0.0,
             },
         }
     }
 
-    pub fn uniform(rect: LayerRect, radius: f32) -> ClipData {
+    pub fn uniform(rect: LayerRect, radius: f32, mode: ClipMode) -> ClipData {
         ClipData {
             rect: ClipRect {
                 rect: rect,
-                padding: [0.0; 4],
+                padding: [0.0; 3],
+                mode: mode as u32 as f32,
             },
             top_left: ClipCorner::uniform(
                 LayerRect::new(
                     LayerPoint::new(rect.origin.x, rect.origin.y),
                     LayerSize::new(radius, radius)),
                 radius, 0.0),
             top_right: ClipCorner::uniform(
                 LayerRect::new(
@@ -599,31 +603,31 @@ impl PrimitiveStore {
         data[1] = GpuBlock32::from(clip.top_left);
         data[2] = GpuBlock32::from(clip.top_right);
         data[3] = GpuBlock32::from(clip.bottom_left);
         data[4] = GpuBlock32::from(clip.bottom_right);
     }
 
     pub fn add_primitive(&mut self,
                          geometry: PrimitiveGeometry,
-                         clip_source: Box<ClipSource>,
+                         clips: Vec<ClipSource>,
                          clip_info: Option<MaskCacheInfo>,
                          container: PrimitiveContainer) -> PrimitiveIndex {
         let prim_index = self.cpu_metadata.len();
         self.cpu_bounding_rects.push(None);
         self.gpu_geometry.push(geometry);
 
         let metadata = match container {
             PrimitiveContainer::Rectangle(rect) => {
                 let is_opaque = rect.color.a == 1.0;
                 let gpu_address = self.gpu_data16.push(rect);
 
                 let metadata = PrimitiveMetadata {
                     is_opaque: is_opaque,
-                    clip_source: clip_source,
+                    clips: clips,
                     clip_cache_info: clip_info,
                     prim_kind: PrimitiveKind::Rectangle,
                     cpu_prim_index: SpecificPrimitiveIndex::invalid(),
                     gpu_prim_index: gpu_address,
                     gpu_data_address: GpuStoreAddress(0),
                     gpu_data_count: 0,
                     render_task: None,
                     clip_task: None,
@@ -633,17 +637,17 @@ impl PrimitiveStore {
             }
             PrimitiveContainer::TextRun(mut text_cpu, text_gpu) => {
                 let gpu_address = self.gpu_data16.push(text_gpu);
                 let gpu_glyphs_address = self.gpu_data16.alloc(text_cpu.glyph_range.length);
                 text_cpu.resource_address = self.gpu_resource_rects.alloc(text_cpu.glyph_range.length);
 
                 let metadata = PrimitiveMetadata {
                     is_opaque: false,
-                    clip_source: clip_source,
+                    clips: clips,
                     clip_cache_info: clip_info,
                     prim_kind: PrimitiveKind::TextRun,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_text_runs.len()),
                     gpu_prim_index: gpu_address,
                     gpu_data_address: gpu_glyphs_address,
                     gpu_data_count: text_cpu.glyph_range.length as i32,
                     render_task: None,
                     clip_task: None,
@@ -654,17 +658,17 @@ impl PrimitiveStore {
             }
             PrimitiveContainer::Image(mut image_cpu, image_gpu) => {
                 image_cpu.resource_address = self.gpu_resource_rects.alloc(1);
 
                 let gpu_address = self.gpu_data16.push(image_gpu);
 
                 let metadata = PrimitiveMetadata {
                     is_opaque: false,
-                    clip_source: clip_source,
+                    clips: clips,
                     clip_cache_info: clip_info,
                     prim_kind: PrimitiveKind::Image,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_images.len()),
                     gpu_prim_index: gpu_address,
                     gpu_data_address: GpuStoreAddress(0),
                     gpu_data_count: 0,
                     render_task: None,
                     clip_task: None,
@@ -675,17 +679,17 @@ impl PrimitiveStore {
             }
             PrimitiveContainer::YuvImage(mut image_cpu, image_gpu) => {
                 image_cpu.yuv_resource_address = self.gpu_resource_rects.alloc(3);
 
                 let gpu_address = self.gpu_data16.push(image_gpu);
 
                 let metadata = PrimitiveMetadata {
                     is_opaque: true,
-                    clip_source: clip_source,
+                    clips: clips,
                     clip_cache_info: clip_info,
                     prim_kind: PrimitiveKind::YuvImage,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_yuv_images.len()),
                     gpu_prim_index: gpu_address,
                     gpu_data_address: GpuStoreAddress(0),
                     gpu_data_count: 0,
                     render_task: None,
                     clip_task: None,
@@ -694,17 +698,17 @@ impl PrimitiveStore {
                 self.cpu_yuv_images.push(image_cpu);
                 metadata
             }
             PrimitiveContainer::Border(border_cpu, border_gpu) => {
                 let gpu_address = self.gpu_data128.push(border_gpu);
 
                 let metadata = PrimitiveMetadata {
                     is_opaque: false,
-                    clip_source: clip_source,
+                    clips: clips,
                     clip_cache_info: clip_info,
                     prim_kind: PrimitiveKind::Border,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_borders.len()),
                     gpu_prim_index: gpu_address,
                     gpu_data_address: GpuStoreAddress(0),
                     gpu_data_count: 0,
                     render_task: None,
                     clip_task: None,
@@ -715,17 +719,17 @@ impl PrimitiveStore {
             }
             PrimitiveContainer::AlignedGradient(gradient_cpu, gradient_gpu) => {
                 let gpu_address = self.gpu_data32.push(gradient_gpu);
                 let gpu_stops_address = self.gpu_data32.alloc(gradient_cpu.stops_range.length);
 
                 let metadata = PrimitiveMetadata {
                     // TODO: calculate if the gradient is actually opaque
                     is_opaque: false,
-                    clip_source: clip_source,
+                    clips: clips,
                     clip_cache_info: clip_info,
                     prim_kind: PrimitiveKind::AlignedGradient,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_gradients.len()),
                     gpu_prim_index: gpu_address,
                     gpu_data_address: gpu_stops_address,
                     gpu_data_count: gradient_cpu.stops_range.length as i32,
                     render_task: None,
                     clip_task: None,
@@ -736,17 +740,17 @@ impl PrimitiveStore {
             }
             PrimitiveContainer::AngleGradient(gradient_cpu, gradient_gpu) => {
                 let gpu_address = self.gpu_data32.push(gradient_gpu);
                 let gpu_gradient_address = self.gpu_gradient_data.alloc(1);
 
                 let metadata = PrimitiveMetadata {
                     // TODO: calculate if the gradient is actually opaque
                     is_opaque: false,
-                    clip_source: clip_source,
+                    clips: clips,
                     clip_cache_info: clip_info,
                     prim_kind: PrimitiveKind::AngleGradient,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_gradients.len()),
                     gpu_prim_index: gpu_address,
                     gpu_data_address: gpu_gradient_address,
                     gpu_data_count: 1,
                     render_task: None,
                     clip_task: None,
@@ -757,17 +761,17 @@ impl PrimitiveStore {
             }
             PrimitiveContainer::RadialGradient(radial_gradient_cpu, radial_gradient_gpu) => {
                 let gpu_address = self.gpu_data32.push(radial_gradient_gpu);
                 let gpu_gradient_address = self.gpu_gradient_data.alloc(1);
 
                 let metadata = PrimitiveMetadata {
                     // TODO: calculate if the gradient is actually opaque
                     is_opaque: false,
-                    clip_source: clip_source,
+                    clips: clips,
                     clip_cache_info: clip_info,
                     prim_kind: PrimitiveKind::RadialGradient,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_radial_gradients.len()),
                     gpu_prim_index: gpu_address,
                     gpu_data_address: gpu_gradient_address,
                     gpu_data_count: 1,
                     render_task: None,
                     clip_task: None,
@@ -802,18 +806,18 @@ impl PrimitiveStore {
                                                              cache_size,
                                                              PrimitiveIndex(prim_index));
 
                 let gpu_prim_address = self.gpu_data64.push(box_shadow_gpu);
                 let gpu_data_address = self.gpu_data16.get_next_address();
 
                 let metadata = PrimitiveMetadata {
                     is_opaque: false,
-                    clip_source: clip_source,
-                    clip_cache_info: None,
+                    clips: clips,
+                    clip_cache_info: clip_info,
                     prim_kind: PrimitiveKind::BoxShadow,
                     cpu_prim_index: SpecificPrimitiveIndex::invalid(),
                     gpu_prim_index: gpu_prim_address,
                     gpu_data_address: gpu_data_address,
                     gpu_data_count: instance_rects.len() as i32,
                     render_task: Some(render_task),
                     clip_task: None,
                 };
@@ -859,27 +863,27 @@ impl PrimitiveStore {
                      image_key: ImageKey,
                      image_uv_address: GpuStoreAddress,
                      image_rendering: ImageRendering,
                      tile_offset: Option<TileOffset>) -> (SourceTexture, Option<CacheItem>) {
         let image_properties = resource_cache.get_image_properties(image_key);
 
         // Check if an external image that needs to be resolved
         // by the render thread.
-        match image_properties.external_id {
-            Some(external_id) => {
+        match image_properties.external_image {
+            Some(external_image) => {
                 // This is an external texture - we will add it to
                 // the deferred resolves list to be patched by
                 // the render thread...
                 deferred_resolves.push(DeferredResolve {
                     image_properties: image_properties,
                     resource_address: image_uv_address,
                 });
 
-                (SourceTexture::External(external_id), None)
+                (SourceTexture::External(external_image), None)
             }
             None => {
                 let cache_item = resource_cache.get_cached_image(image_key, image_rendering, tile_offset);
                 (cache_item.texture_id, Some(cache_item))
             }
         }
     }
 
@@ -989,31 +993,35 @@ impl PrimitiveStore {
                     }
                 }
             }
         }
 
         deferred_resolves
     }
 
-    pub fn set_clip_source(&mut self, index: PrimitiveIndex, source: ClipSource) {
+    pub fn set_clip_source(&mut self, index: PrimitiveIndex, source: Option<ClipSource>) {
         let metadata = &mut self.cpu_metadata[index.0];
-        let (rect, is_complex) = match source {
-            ClipSource::NoClip => (None, false),
-            ClipSource::Complex(rect, radius) => (Some(rect), radius > 0.0),
-            ClipSource::Region(ref region) => (Some(region.main), region.is_complex()),
-        };
-        if let Some(rect) = rect {
-            self.gpu_geometry.get_mut(GpuStoreAddress(index.0 as i32))
-                .local_clip_rect = rect;
-            if is_complex {
-                metadata.clip_cache_info = None; //CLIP TODO: re-use the existing GPU allocation
+        metadata.clips = match source {
+            Some(source) => {
+                let (rect, is_complex) = match source {
+                    ClipSource::Complex(rect, radius, _) => (rect, radius > 0.0),
+                    ClipSource::Region(ref region, _) => (region.main, region.is_complex()),
+                };
+                self.gpu_geometry.get_mut(GpuStoreAddress(index.0 as i32))
+                    .local_clip_rect = rect;
+                if is_complex {
+                    metadata.clip_cache_info = None; //CLIP TODO: re-use the existing GPU allocation
+                }
+                vec![source]
+            }
+            None => {
+                vec![]
             }
         }
-        *metadata.clip_source.as_mut() = source;
     }
 
     pub fn get_metadata(&self, index: PrimitiveIndex) -> &PrimitiveMetadata {
         &self.cpu_metadata[index.0]
     }
 
     pub fn prim_count(&self) -> usize {
         self.cpu_metadata.len()
@@ -1049,24 +1057,26 @@ impl PrimitiveStore {
                                    device_pixel_ratio: f32,
                                    auxiliary_lists: &AuxiliaryLists) -> bool {
 
         let metadata = &mut self.cpu_metadata[prim_index.0];
         let mut prim_needs_resolve = false;
         let mut rebuild_bounding_rect = false;
 
         if let Some(ref mut clip_info) = metadata.clip_cache_info {
-            clip_info.update(&metadata.clip_source,
+            clip_info.update(&metadata.clips,
                              layer_transform,
                              &mut self.gpu_data32,
                              device_pixel_ratio,
                              auxiliary_lists);
-            if let &ClipSource::Region(ClipRegion{ image_mask: Some(ref mask), .. }) = metadata.clip_source.as_ref() {
-                resource_cache.request_image(mask.image, ImageRendering::Auto, None);
-                prim_needs_resolve = true;
+            for clip in &metadata.clips {
+                if let &ClipSource::Region(ClipRegion{ image_mask: Some(ref mask), .. }, _) = clip {
+                    resource_cache.request_image(mask.image, ImageRendering::Auto, None);
+                    prim_needs_resolve = true;
+                }
             }
         }
 
         match metadata.prim_kind {
             PrimitiveKind::Rectangle |
             PrimitiveKind::Border  => {}
             PrimitiveKind::BoxShadow => {
                 // TODO(gw): Account for zoom factor!
--- a/gfx/webrender/src/record.rs
+++ b/gfx/webrender/src/record.rs
@@ -1,13 +1,13 @@
 /* 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 bincode::{SizeLimit, serialize};
+use bincode::{Infinite, serialize};
 use std::fmt::Debug;
 use std::mem;
 use std::any::TypeId;
 use std::fs::File;
 use std::io::Write;
 use std::path::PathBuf;
 use webrender_traits::ApiMsg;
 use byteorder::{LittleEndian, WriteBytesExt};
@@ -45,17 +45,17 @@ impl BinaryRecorder {
         self.file.write_u32::<LittleEndian>(data.len() as u32).ok();
         self.file.write(data).ok();
     }
 }
 
 impl ApiRecordingReceiver for BinaryRecorder {
     fn write_msg(&mut self, _: u32, msg: &ApiMsg) {
         if should_record_msg(msg) {
-            let buf = serialize(msg, SizeLimit::Infinite).unwrap();
+            let buf = serialize(msg, Infinite).unwrap();
             self.write_length_and_data(&buf);
         }
     }
 
     fn write_payload(&mut self, _: u32, data: &[u8]) {
         // signal payload with a 0 length
         self.file.write_u32::<LittleEndian>(0).ok();
         self.write_length_and_data(data);
@@ -66,17 +66,17 @@ pub fn should_record_msg(msg: &ApiMsg) -
     match msg {
         &ApiMsg::AddRawFont(..) |
         &ApiMsg::AddNativeFont(..) |
         &ApiMsg::DeleteFont(..) |
         &ApiMsg::AddImage(..) |
         &ApiMsg::GenerateFrame(..) |
         &ApiMsg::UpdateImage(..) |
         &ApiMsg::DeleteImage(..) |
-        &ApiMsg::SetRootDisplayList(..) |
+        &ApiMsg::SetDisplayList(..) |
         &ApiMsg::SetRootPipeline(..) |
         &ApiMsg::Scroll(..) |
         &ApiMsg::TickScrollingBounce |
         &ApiMsg::WebGLCommand(..) =>
             true,
         _ => false
     }
 }
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -1,32 +1,30 @@
 /* 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 byteorder::{LittleEndian, ReadBytesExt};
 use frame::Frame;
 use frame_builder::FrameBuilderConfig;
 use internal_types::{FontTemplate, SourceTexture, ResultMsg, RendererFrame};
 use profiler::{BackendProfileCounters, TextureCacheProfileCounters};
 use record::ApiRecordingReceiver;
 use resource_cache::ResourceCache;
 use scene::Scene;
 use std::collections::HashMap;
-use std::io::{Cursor, Read};
 use std::sync::{Arc, Mutex};
 use std::sync::mpsc::Sender;
 use texture_cache::TextureCache;
 use thread_profiler::register_thread_with_profiler;
 use threadpool::ThreadPool;
 use webgl_types::{GLContextHandleWrapper, GLContextWrapper};
 use webrender_traits::{DeviceIntPoint, DeviceUintPoint, DeviceUintRect, DeviceUintSize, LayerPoint};
 use webrender_traits::{ApiMsg, AuxiliaryLists, BuiltDisplayList, IdNamespace, ImageData};
 use webrender_traits::{PipelineId, RenderNotifier, RenderDispatcher, WebGLCommand, WebGLContextId};
-use webrender_traits::channel::{PayloadHelperMethods, PayloadReceiver, PayloadSender, MsgReceiver};
+use webrender_traits::channel::{PayloadSenderHelperMethods, PayloadReceiverHelperMethods, PayloadReceiver, PayloadSender, MsgReceiver};
 use webrender_traits::{BlobImageRenderer, VRCompositorCommand, VRCompositorHandler};
 #[cfg(feature = "webgl")]
 use offscreen_gl_context::GLContextDispatcher;
 #[cfg(not(feature = "webgl"))]
 use webgl_types::GLContextDispatcher;
 
 /// The render backend is responsible for transforming high level display lists into
 /// GPU-friendly work which is then submitted to the renderer in the form of a frame::Frame.
@@ -172,80 +170,74 @@ impl RenderBackend {
                         ApiMsg::CloneApi(sender) => {
                             let result = self.next_namespace_id;
 
                             let IdNamespace(id_namespace) = self.next_namespace_id;
                             self.next_namespace_id = IdNamespace(id_namespace + 1);
 
                             sender.send(result).unwrap();
                         }
-                        ApiMsg::SetRootDisplayList(background_color,
-                                                   epoch,
-                                                   pipeline_id,
-                                                   viewport_size,
-                                                   display_list_descriptor,
-                                                   auxiliary_lists_descriptor,
-                                                   preserve_frame_state) => {
-                            profile_scope!("SetRootDisplayList");
+                        ApiMsg::SetDisplayList(background_color,
+                                               epoch,
+                                               pipeline_id,
+                                               viewport_size,
+                                               display_list_descriptor,
+                                               auxiliary_lists_descriptor,
+                                               preserve_frame_state) => {
+                            profile_scope!("SetDisplayList");
                             let mut leftover_auxiliary_data = vec![];
                             let mut auxiliary_data;
                             loop {
-                                auxiliary_data = self.payload_rx.recv().unwrap();
+                                auxiliary_data = self.payload_rx.recv_payload().unwrap();
                                 {
-                                    let mut payload_reader = Cursor::new(&auxiliary_data[..]);
-                                    let payload_epoch =
-                                        payload_reader.read_u32::<LittleEndian>().unwrap();
-                                    if payload_epoch == epoch.0 {
+                                    if auxiliary_data.epoch == epoch &&
+                                       auxiliary_data.pipeline_id == pipeline_id {
                                         break
                                     }
                                 }
                                 leftover_auxiliary_data.push(auxiliary_data)
                             }
                             for leftover_auxiliary_data in leftover_auxiliary_data {
-                                self.payload_tx.send_vec(leftover_auxiliary_data).unwrap()
+                                self.payload_tx.send_payload(leftover_auxiliary_data).unwrap()
                             }
                             if let Some(ref mut r) = self.recorder {
-                                r.write_payload(frame_counter, &auxiliary_data);
+                                r.write_payload(frame_counter, &auxiliary_data.to_data());
                             }
 
-                            let mut auxiliary_data = Cursor::new(&mut auxiliary_data[4..]);
-                            let mut built_display_list_data =
-                                vec![0; display_list_descriptor.size()];
-                            auxiliary_data.read_exact(&mut built_display_list_data[..]).unwrap();
                             let built_display_list =
-                                BuiltDisplayList::from_data(built_display_list_data,
+                                BuiltDisplayList::from_data(auxiliary_data.display_list_data,
                                                             display_list_descriptor);
-                            let mut auxiliary_lists_data =
-                                vec![0; auxiliary_lists_descriptor.size()];
-                            auxiliary_data.read_exact(&mut auxiliary_lists_data[..]).unwrap();
                             let auxiliary_lists =
-                                AuxiliaryLists::from_data(auxiliary_lists_data,
+                                AuxiliaryLists::from_data(auxiliary_data.auxiliary_lists_data,
                                                           auxiliary_lists_descriptor);
 
                             if !preserve_frame_state {
                                 self.discard_frame_state_for_pipeline(pipeline_id);
                             }
-
-                            self.scene.set_root_display_list(pipeline_id,
-                                                             epoch,
-                                                             built_display_list,
-                                                             background_color,
-                                                             viewport_size,
-                                                             auxiliary_lists);
-                            self.build_scene();
+                            profile_counters.total_time.profile(|| {
+                                self.scene.set_display_list(pipeline_id,
+                                                            epoch,
+                                                            built_display_list,
+                                                            background_color,
+                                                            viewport_size,
+                                                            auxiliary_lists);
+                                self.build_scene();
+                            })
                         }
                         ApiMsg::SetRootPipeline(pipeline_id) => {
                             profile_scope!("SetRootPipeline");
                             self.scene.set_root_pipeline_id(pipeline_id);
 
                             if self.scene.display_lists.get(&pipeline_id).is_none() {
                                 continue;
                             }
 
-                            self.build_scene();
+                            profile_counters.total_time.profile(|| {
+                                self.build_scene();
+                            })
                         }
                         ApiMsg::Scroll(delta, cursor, move_phase) => {
                             profile_scope!("Scroll");
                             let frame = {
                                 let counters = &mut profile_counters.texture_cache;
                                 profile_counters.total_time.profile(|| {
                                     if self.frame.scroll(delta, cursor, move_phase) {
                                         Some(self.render(counters))
@@ -378,17 +370,19 @@ impl RenderBackend {
                             // just rebuilds the frame if there are animated property
                             // bindings present for now.
                             // TODO(gw): Once the scrolling / reference frame changes
                             //           are completed, optimize the internals of
                             //           animated properties to not require a full
                             //           rebuild of the frame!
                             if let Some(property_bindings) = property_bindings {
                                 self.scene.properties.set_properties(property_bindings);
-                                self.build_scene();
+                                profile_counters.total_time.profile(|| {
+                                    self.build_scene();
+                                });
                             }
 
                             let frame = {
                                 let counters = &mut profile_counters.texture_cache;
                                 profile_counters.total_time.profile(|| {
                                     self.render(counters)
                                 })
                             };
--- a/gfx/webrender/src/render_task.rs
+++ b/gfx/webrender/src/render_task.rs
@@ -1,14 +1,14 @@
 /* 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 internal_types::{HardwareCompositeOp, LowLevelFilterOp};
-use mask_cache::MaskCacheInfo;
+use mask_cache::{MaskBounds, MaskCacheInfo};
 use prim_store::{PrimitiveCacheKey, PrimitiveIndex};
 use std::{cmp, f32, i32, mem, usize};
 use tiling::{ClipScrollGroupIndex, PackedLayerIndex, RenderPass, RenderTargetIndex};
 use tiling::{RenderTargetKind, StackingContextIndex};
 use webrender_traits::{DeviceIntLength, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
 use webrender_traits::{MixBlendMode, ScrollLayerId};
 
 const FLOATS_PER_RENDER_TASK_INFO: usize = 12;
@@ -187,29 +187,54 @@ impl RenderTask {
                     -> MaskResult {
         if clips.is_empty() {
             return MaskResult::Outside;
         }
 
         // We scan through the clip stack and detect if our actual rectangle
         // is in the intersection of all of all the outer bounds,
         // and if it's completely inside the intersection of all of the inner bounds.
-        let result = clips.iter()
-                          .fold(Some(actual_rect), |current, clip| {
-            current.and_then(|rect| rect.intersection(&clip.1.outer_rect))
-        });
+
+        // TODO(gw): If we encounter a clip with unknown bounds, we'll just use
+        // the original rect. This is overly conservative, but can
+        // be optimized later.
+        let mut result = Some(actual_rect);
+        for &(_, ref clip) in clips {
+            match clip.bounds.as_ref().unwrap() {
+                &MaskBounds::OuterInner(ref outer, _) |
+                &MaskBounds::Outer(ref outer) => {
+                    result = result.and_then(|rect| {
+                        rect.intersection(&outer.bounding_rect)
+                    });
+                }
+                &MaskBounds::None => {
+                    result = Some(actual_rect);
+                    break;
+                }
+            }
+        }
 
         let task_rect = match result {
             None => return MaskResult::Outside,
             Some(rect) => rect,
         };
 
+        // Accumulate inner rects. As soon as we encounter
+        // a clip mask where we don't have or don't know
+        // the inner rect, this will become None.
         let inner_rect = clips.iter()
                               .fold(Some(task_rect), |current, clip| {
-            current.and_then(|rect| rect.intersection(&clip.1.inner_rect))
+            current.and_then(|rect| {
+                let inner_rect = match clip.1.bounds.as_ref().unwrap() {
+                    &MaskBounds::Outer(..) |
+                    &MaskBounds::None => DeviceIntRect::zero(),
+                    &MaskBounds::OuterInner(_, ref inner) => inner.bounding_rect
+                };
+                rect.intersection(&inner_rect)
+            })
         });
 
         // TODO(gw): This optimization is very conservative for now.
         //           For now, only draw optimized geometry if it is
         //           a single aligned rect mask with rounded corners.
         //           In the future, we'll expand this to handle the
         //           more complex types of clip mask geometry.
         let mut geometry_kind = MaskGeometryKind::Default;
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -37,39 +37,40 @@ use std::marker::PhantomData;
 use std::mem;
 use std::path::PathBuf;
 use std::rc::Rc;
 use std::sync::{Arc, Mutex};
 use std::sync::mpsc::{channel, Receiver, Sender};
 use std::thread;
 use texture_cache::TextureCache;
 use threadpool::ThreadPool;
-use tiling::{AlphaBatchKind, BlurCommand, Frame, PrimitiveBatch, PrimitiveBatchData};
+use tiling::{AlphaBatchKind, BlurCommand, Frame, PrimitiveBatch, PrimitiveBatchData, RenderTarget};
 use tiling::{AlphaRenderTarget, CacheClipInstance, PrimitiveInstance, ColorRenderTarget, RenderTargetKind};
 use time::precise_time_ns;
 use thread_profiler::{register_thread_with_profiler, write_profile};
 use util::TransformedRectKind;
 use webgl_types::GLContextHandleWrapper;
 use webrender_traits::{ColorF, Epoch, PipelineId, RenderNotifier, RenderDispatcher};
-use webrender_traits::{ExternalImageId, ImageData, ImageFormat, RenderApiSender};
+use webrender_traits::{ExternalImageId, ExternalImageType, ImageData, ImageFormat, RenderApiSender};
 use webrender_traits::{DeviceIntRect, DevicePoint, DeviceIntPoint, DeviceIntSize, DeviceUintSize};
 use webrender_traits::{ImageDescriptor, BlobImageRenderer};
 use webrender_traits::channel;
 use webrender_traits::VRCompositorHandler;
 
 pub const GPU_DATA_TEXTURE_POOL: usize = 5;
 pub const MAX_VERTEX_TEXTURE_WIDTH: usize = 1024;
 
 const GPU_TAG_CACHE_BOX_SHADOW: GpuProfileTag = GpuProfileTag { label: "C_BoxShadow", color: debug_colors::BLACK };
 const GPU_TAG_CACHE_CLIP: GpuProfileTag = GpuProfileTag { label: "C_Clip", color: debug_colors::PURPLE };
 const GPU_TAG_CACHE_TEXT_RUN: GpuProfileTag = GpuProfileTag { label: "C_TextRun", color: debug_colors::MISTYROSE };
 const GPU_TAG_INIT: GpuProfileTag = GpuProfileTag { label: "Init", color: debug_colors::WHITE };
 const GPU_TAG_SETUP_TARGET: GpuProfileTag = GpuProfileTag { label: "Target", color: debug_colors::SLATEGREY };
 const GPU_TAG_PRIM_RECT: GpuProfileTag = GpuProfileTag { label: "Rect", color: debug_colors::RED };
 const GPU_TAG_PRIM_IMAGE: GpuProfileTag = GpuProfileTag { label: "Image", color: debug_colors::GREEN };
+const GPU_TAG_PRIM_IMAGE_RECT: GpuProfileTag = GpuProfileTag { label: "ImageRect", color: debug_colors::GREENYELLOW };
 const GPU_TAG_PRIM_YUV_IMAGE: GpuProfileTag = GpuProfileTag { label: "YuvImage", color: debug_colors::DARKGREEN };
 const GPU_TAG_PRIM_BLEND: GpuProfileTag = GpuProfileTag { label: "Blend", color: debug_colors::LIGHTBLUE };
 const GPU_TAG_PRIM_HW_COMPOSITE: GpuProfileTag = GpuProfileTag { label: "HwComposite", color: debug_colors::DODGERBLUE };
 const GPU_TAG_PRIM_COMPOSITE: GpuProfileTag = GpuProfileTag { label: "Composite", color: debug_colors::MAGENTA };
 const GPU_TAG_PRIM_TEXT_RUN: GpuProfileTag = GpuProfileTag { label: "TextRun", color: debug_colors::BLUE };
 const GPU_TAG_PRIM_GRADIENT: GpuProfileTag = GpuProfileTag { label: "Gradient", color: debug_colors::YELLOW };
 const GPU_TAG_PRIM_ANGLE_GRADIENT: GpuProfileTag = GpuProfileTag { label: "AngleGradient", color: debug_colors::POWDERBLUE };
 const GPU_TAG_PRIM_RADIAL_GRADIENT: GpuProfileTag = GpuProfileTag { label: "RadialGradient", color: debug_colors::LIGHTPINK };
@@ -218,16 +219,17 @@ impl GpuStoreLayout for GradientDataText
 }
 
 type GradientDataTexture = GpuDataTexture<GradientDataTextureLayout>;
 pub type GradientDataStore = GpuStore<GradientData, GradientDataTextureLayout>;
 
 const TRANSFORM_FEATURE: &'static str = "TRANSFORM";
 const SUBPIXEL_AA_FEATURE: &'static str = "SUBPIXEL_AA";
 const CLIP_FEATURE: &'static str = "CLIP";
+const TEXTURE_RECT_FEATURE: &'static str = "TEXTURE_RECT";
 
 enum ShaderKind {
     Primitive,
     Cache(VertexFormat),
     ClipCache,
 }
 
 struct LazilyCompiledShader {
@@ -460,16 +462,17 @@ pub struct Renderer {
     // shadow primitive shader stretches the box shadow cache
     // output, and the cache_image shader blits the results of
     // a cache shader (e.g. blur) to the screen.
     ps_rectangle: PrimitiveShader,
     ps_rectangle_clip: PrimitiveShader,
     ps_text_run: PrimitiveShader,
     ps_text_run_subpixel: PrimitiveShader,
     ps_image: PrimitiveShader,
+    ps_image_rect: PrimitiveShader,
     ps_yuv_image: PrimitiveShader,
     ps_border: PrimitiveShader,
     ps_gradient: PrimitiveShader,
     ps_angle_gradient: PrimitiveShader,
     ps_radial_gradient: PrimitiveShader,
     ps_box_shadow: PrimitiveShader,
     ps_cache_image: PrimitiveShader,
 
@@ -550,34 +553,34 @@ impl From<ShaderError> for InitError {
     fn from(err: ShaderError) -> Self { InitError::Shader(err) }
 }
 
 impl From<std::io::Error> for InitError {
     fn from(err: std::io::Error) -> Self { InitError::Thread(err) }
 }
 
 impl Renderer {
-    /// Initializes webrender and creates a Renderer and RenderApiSender.
+    /// Initializes webrender and creates a `Renderer` and `RenderApiSender`.
     ///
     /// # Examples
-    /// Initializes a Renderer with some reasonable values. For more information see
-    /// [RendererOptions][rendereroptions].
-    /// [rendereroptions]: struct.RendererOptions.html
+    /// Initializes a `Renderer` with some reasonable values. For more information see
+    /// [`RendererOptions`][rendereroptions].
     ///
     /// ```rust,ignore
     /// # use webrender::renderer::Renderer;
     /// # use std::path::PathBuf;
     /// let opts = webrender::RendererOptions {
     ///    device_pixel_ratio: 1.0,
     ///    resource_override_path: None,
     ///    enable_aa: false,
     ///    enable_profiler: false,
     /// };
     /// let (renderer, sender) = Renderer::new(opts);
     /// ```
+    /// [rendereroptions]: struct.RendererOptions.html
     pub fn new(gl: Rc<gl::Gl>,
                mut options: RendererOptions,
                initial_window_size: DeviceUintSize) -> Result<(Renderer, RenderApiSender), InitError> {
         let (api_tx, api_rx) = try!{ channel::msg_channel() };
         let (payload_tx, payload_rx) = try!{ channel::payload_channel() };
         let (result_tx, result_rx) = channel();
 
         register_thread_with_profiler("Compositor".to_owned());
@@ -665,16 +668,23 @@ impl Renderer {
 
         let ps_image = try!{
             PrimitiveShader::new("ps_image",
                                  &mut device,
                                  &[],
                                  options.precache_shaders)
         };
 
+        let ps_image_rect = try!{
+            PrimitiveShader::new("ps_image",
+                                 &mut device,
+                                 &[ TEXTURE_RECT_FEATURE ],
+                                 options.precache_shaders)
+        };
+
         let ps_yuv_image = try!{
             PrimitiveShader::new("ps_yuv_image",
                                  &mut device,
                                  &[],
                                  options.precache_shaders)
         };
 
         let ps_border = try!{
@@ -907,16 +917,17 @@ impl Renderer {
             cs_blur: cs_blur,
             cs_clip_rectangle: cs_clip_rectangle,
             cs_clip_image: cs_clip_image,
             ps_rectangle: ps_rectangle,
             ps_rectangle_clip: ps_rectangle_clip,
             ps_text_run: ps_text_run,
             ps_text_run_subpixel: ps_text_run_subpixel,
             ps_image: ps_image,
+            ps_image_rect: ps_image_rect,
             ps_yuv_image: ps_yuv_image,
             ps_border: ps_border,
             ps_box_shadow: ps_box_shadow,
             ps_gradient: ps_gradient,
             ps_angle_gradient: ps_angle_gradient,
             ps_radial_gradient: ps_radial_gradient,
             ps_cache_image: ps_cache_image,
             ps_blend: ps_blend,
@@ -1034,20 +1045,20 @@ impl Renderer {
     // For a texture cache texture, the IDs are stored in a vector
     // map for fast access. For WebGL textures, the native texture ID
     // is stored inline. When we add support for external textures,
     // we will add a callback here that is able to ask the caller
     // for the image data.
     fn resolve_source_texture(&mut self, texture_id: &SourceTexture) -> TextureId {
         match *texture_id {
             SourceTexture::Invalid => TextureId::invalid(),
-            SourceTexture::WebGL(id) => TextureId::new(id),
-            SourceTexture::External(ref key) => {
+            SourceTexture::WebGL(id) => TextureId::new(id, TextureTarget::Default),
+            SourceTexture::External(external_image) => {
                 *self.external_images
-                     .get(key)
+                     .get(&external_image.id)
                      .expect("BUG: External image should be resolved by now!")
             }
             SourceTexture::TextureCache(index) => {
                 self.cache_texture_id_map[index.0]
             }
         }
     }
 
@@ -1060,18 +1071,18 @@ impl Renderer {
     pub fn get_frame_profiles(&mut self) -> (Vec<CpuProfile>, Vec<GpuProfile>) {
         let cpu_profiles = self.cpu_profiles.drain(..).collect();
         let gpu_profiles = self.gpu_profiles.drain(..).collect();
         (cpu_profiles, gpu_profiles)
     }
 
     /// Renders the current frame.
     ///
-    /// A Frame is supplied by calling [set_root_stacking_context()][newframe].
-    /// [newframe]: ../../webrender_traits/struct.RenderApi.html#method.set_root_stacking_context
+    /// A Frame is supplied by calling [`set_display_list()`][newframe].
+    /// [newframe]: ../../webrender_traits/struct.RenderApi.html#method.set_display_list
     pub fn render(&mut self, framebuffer_size: DeviceUintSize) {
         profile_scope!("render");
 
         if let Some(mut frame) = self.current_frame.take() {
             if let Some(ref mut frame) = frame.frame {
                 let mut profile_timers = RendererProfileTimers::new();
 
                 // Block CPU waiting for last frame's GPU profiles to arrive.
@@ -1188,34 +1199,41 @@ impl Renderer {
                                     self.device.init_texture(texture_id,
                                                              width,
                                                              height,
                                                              format,
                                                              filter,
                                                              mode,
                                                              Some(raw.as_slice()));
                                 }
-                                ImageData::ExternalBuffer(id) => {
-                                    let handler = self.external_image_handler
-                                                      .as_mut()
-                                                      .expect("Found external image, but no handler set!");
+                                ImageData::External(ext_image) => {
+                                    match ext_image.image_type {
+                                        ExternalImageType::ExternalBuffer => {
+                                            let handler = self.external_image_handler
+                                                              .as_mut()
+                                                              .expect("Found external image, but no handler set!");
 
-                                    match handler.lock(id).source {
-                                        ExternalImageSource::RawData(raw) => {
-                                            self.device.init_texture(texture_id,
-                                                                     width,
-                                                                     height,
-                                                                     format,
-                                                                     filter,
-                                                                     mode,
-                                                                     Some(raw));
+                                            match handler.lock(ext_image.id).source {
+                                                ExternalImageSource::RawData(raw) => {
+                                                    self.device.init_texture(texture_id,
+                                                                             width,
+                                                                             height,
+                                                                             format,
+                                                                             filter,
+                                                                             mode,
+                                                                             Some(raw));
+                                                }
+                                                _ => panic!("No external buffer found"),
+                                            };
+                                            handler.unlock(ext_image.id);
                                         }
-                                        _ => panic!("No external buffer found"),
-                                    };
-                                    handler.unlock(id);
+                                        _ => {
+                                            panic!("External texture handle should not use TextureUpdateOp::Create.");
+                                        }
+                                    }
                                 }
                                 _ => {
                                     panic!("No suitable image buffer for TextureUpdateOp::Create.");
                                 }
                             }
                         } else {
                             self.device.init_texture(texture_id,
                                                      width,
@@ -1334,16 +1352,20 @@ impl Renderer {
                             BlendMode::Alpha | BlendMode::PremultipliedAlpha | BlendMode::None => self.ps_text_run.get(&mut self.device, transform_kind),
                         };
                         (GPU_TAG_PRIM_TEXT_RUN, shader)
                     }
                     AlphaBatchKind::Image => {
                         let shader = self.ps_image.get(&mut self.device, transform_kind);
                         (GPU_TAG_PRIM_IMAGE, shader)
                     }
+                    AlphaBatchKind::ImageRect => {
+                        let shader = self.ps_image_rect.get(&mut self.device, transform_kind);
+                        (GPU_TAG_PRIM_IMAGE_RECT, shader)
+                    }
                     AlphaBatchKind::YuvImage => {
                         let shader = self.ps_yuv_image.get(&mut self.device, transform_kind);
                         (GPU_TAG_PRIM_YUV_IMAGE, shader)
                     }
                     AlphaBatchKind::Border => {
                         let shader = self.ps_border.get(&mut self.device, transform_kind);
                         (GPU_TAG_PRIM_BORDER, shader)
                     }
@@ -1454,21 +1476,37 @@ impl Renderer {
                          projection: &Matrix4D<f32>) {
         {
             let _gm = self.gpu_profile.add_marker(GPU_TAG_SETUP_TARGET);
             self.device.bind_draw_target(render_target, Some(target_size));
             self.device.disable_depth();
             self.device.enable_depth_write();
             self.device.set_blend(false);
             self.device.set_blend_mode_alpha();
-            self.device.clear_target(clear_color, Some(1.0));
+            match render_target {
+                Some(..) => {
+                    // TODO(gw): Applying a scissor rect and minimal clear here
+                    // is a very large performance win on the Intel and nVidia
+                    // GPUs that I have tested with. It's possible it may be a
+                    // performance penalty on other GPU types - we should test this
+                    // and consider different code paths.
+                    self.device.clear_target_rect(clear_color,
+                                                  Some(1.0),
+                                                  target.used_rect());
+                }
+                None => {
+                    self.device.clear_target(clear_color, Some(1.0));
+                }
+            }
 
             let isolate_clear_color = Some([0.0, 0.0, 0.0, 0.0]);
             for isolate_clear in &target.isolate_clears {
-                self.device.clear_target_rect(isolate_clear_color, None, *isolate_clear);
+                self.device.clear_target_rect(isolate_clear_color,
+                                              None,
+                                              *isolate_clear);
             }
 
             self.device.disable_depth_write();
         }
 
         // Draw any blurs for this target.
         // Blurs are rendered as a standard 2-pass
         // separable implementation.
@@ -1587,18 +1625,25 @@ impl Renderer {
                          target_size: DeviceUintSize,
                          projection: &Matrix4D<f32>) {
         {
             let _gm = self.gpu_profile.add_marker(GPU_TAG_SETUP_TARGET);
             self.device.bind_draw_target(Some(render_target), Some(target_size));
             self.device.disable_depth();
             self.device.disable_depth_write();
 
+            // TODO(gw): Applying a scissor rect and minimal clear here
+            // is a very large performance win on the Intel and nVidia
+            // GPUs that I have tested with. It's possible it may be a
+            // performance penalty on other GPU types - we should test this
+            // and consider different code paths.
             let clear_color = [1.0, 1.0, 1.0, 0.0];
-            self.device.clear_target(Some(clear_color), None);
+            self.device.clear_target_rect(Some(clear_color),
+                                          None,
+                                          target.used_rect());
         }
 
         // Draw the clip items into the tiled alpha mask.
         {
             let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_CLIP);
             let vao = self.clip_vao_id;
             // switch to multiplicative blending
             self.device.set_blend(true);
@@ -1637,26 +1682,34 @@ impl Renderer {
         if !frame.deferred_resolves.is_empty() {
             let handler = self.external_image_handler
                               .as_mut()
                               .expect("Found external image, but no handler set!");
 
             for deferred_resolve in &frame.deferred_resolves {
                 GpuMarker::fire(self.device.gl(), "deferred resolve");
                 let props = &deferred_resolve.image_properties;
-                let external_id = props.external_id
-                                       .expect("BUG: Deferred resolves must be external images!");
-                let image = handler.lock(external_id);
+                let ext_image = props.external_image
+                                     .expect("BUG: Deferred resolves must be external images!");
+                let image = handler.lock(ext_image.id);
+                let texture_target = match ext_image.image_type {
+                    ExternalImageType::Texture2DHandle => TextureTarget::Default,
+                    ExternalImageType::TextureRectHandle => TextureTarget::Rect,
+                    _ => {
+                        panic!("{:?} is not a suitable image type in update_deferred_resolves().",
+                            ext_image.image_type);
+                    }
+                };
 
                 let texture_id = match image.source {
-                    ExternalImageSource::NativeTexture(texture_id) => TextureId::new(texture_id),
+                    ExternalImageSource::NativeTexture(texture_id) => TextureId::new(texture_id, texture_target),
                     _ => panic!("No native texture found."),
                 };
 
-                self.external_images.insert(external_id, texture_id);
+                self.external_images.insert(ext_image.id, texture_id);
                 let resource_rect_index = deferred_resolve.resource_address.0 as usize;
                 let resource_rect = &mut frame.gpu_resource_rects[resource_rect_index];
                 resource_rect.uv0 = DevicePoint::new(image.u0, image.v0);
                 resource_rect.uv1 = DevicePoint::new(image.u1, image.v1);
             }
         }
     }
 
--- a/gfx/webrender/src/resource_cache.rs
+++ b/gfx/webrender/src/resource_cache.rs
@@ -19,21 +19,24 @@ use std::mem;
 use std::sync::{Arc, Barrier, Mutex};
 use std::sync::mpsc::{channel, Receiver, Sender};
 use std::thread;
 use texture_cache::{TextureCache, TextureCacheItemId};
 use thread_profiler::register_thread_with_profiler;
 use webrender_traits::{Epoch, FontKey, GlyphKey, ImageKey, ImageFormat, ImageRendering};
 use webrender_traits::{FontRenderMode, ImageData, GlyphDimensions, WebGLContextId};
 use webrender_traits::{DevicePoint, DeviceIntSize, DeviceUintRect, ImageDescriptor, ColorF};
-use webrender_traits::{ExternalImageId, GlyphOptions, GlyphInstance, TileOffset, TileSize};
+use webrender_traits::{GlyphOptions, GlyphInstance, TileOffset, TileSize};
 use webrender_traits::{BlobImageRenderer, BlobImageDescriptor, BlobImageError};
+use webrender_traits::{ExternalImageData, ExternalImageType};
 use threadpool::ThreadPool;
 use euclid::Point2D;
 
+const DEFAULT_TILE_SIZE: TileSize = 512;
+
 thread_local!(pub static FONT_CONTEXT: RefCell<FontContext> = RefCell::new(FontContext::new()));
 
 type GlyphCache = ResourceClassCache<RenderedGlyphKey, Option<TextureCacheItemId>>;
 
 /// Message sent from the resource cache to the glyph cache thread.
 enum GlyphCacheMsg {
     /// Begin the frame - pass ownership of the glyph cache to the thread.
     BeginFrame(FrameId, GlyphCache),
@@ -89,17 +92,17 @@ impl RenderedGlyphKey {
             render_mode: render_mode,
             glyph_options: glyph_options,
         }
     }
 }
 
 pub struct ImageProperties {
     pub descriptor: ImageDescriptor,
-    pub external_id: Option<ExternalImageId>,
+    pub external_image: Option<ExternalImageData>,
     pub tiling: Option<TileSize>,
 }
 
 #[derive(Debug, Copy, Clone, PartialEq)]
 enum State {
     Idle,
     AddResources,
     QueryResources,
@@ -249,16 +252,30 @@ impl ResourceCache {
             blob_image_requests: HashSet::new(),
         }
     }
 
     pub fn max_texture_size(&self) -> u32 {
         self.texture_cache.max_texture_size()
     }
 
+    fn should_tile(&self, descriptor: &ImageDescriptor, data: &ImageData) -> bool {
+        let limit = self.max_texture_size();
+        let size_check = descriptor.width > limit || descriptor.height > limit;
+        return match data {
+            &ImageData::Raw(_) => { size_check }
+            &ImageData::Blob(_) => { size_check }
+            &ImageData::External(info) => {
+                // External handles already represent existing textures so it does
+                // not make sense to tile them into smaller ones.
+                info.image_type == ExternalImageType::ExternalBuffer && size_check
+            },
+        };
+    }
+
     pub fn add_font_template(&mut self, font_key: FontKey, template: FontTemplate) {
         // Push the new font to the glyph cache thread, and also store
         // it locally for glyph metric requests.
         self.glyph_cache_tx
             .send(GlyphCacheMsg::AddFont(font_key, template.clone()))
             .unwrap();
         self.font_templates.insert(font_key, template);
     }
@@ -270,81 +287,84 @@ impl ResourceCache {
         self.font_templates.remove(&font_key);
     }
 
     pub fn add_image_template(&mut self,
                               image_key: ImageKey,
                               descriptor: ImageDescriptor,
                               data: ImageData,
                               mut tiling: Option<TileSize>) {
-        if descriptor.width > self.max_texture_size() || descriptor.height > self.max_texture_size() {
+        if tiling.is_none() && self.should_tile(&descriptor, &data) {
             // We aren't going to be able to upload a texture this big, so tile it, even
             // if tiling was not requested.
-            tiling = Some(512);
+            tiling = Some(DEFAULT_TILE_SIZE);
         }
 
         let resource = ImageResource {
             descriptor: descriptor,
             data: data,
             epoch: Epoch(0),
             tiling: tiling,
             dirty_rect: None,
         };
 
         self.image_templates.insert(image_key, resource);
     }
 
     pub fn update_image_template(&mut self,
                                  image_key: ImageKey,
                                  descriptor: ImageDescriptor,
-                                 bytes: Vec<u8>,
+                                 data: ImageData,
                                  dirty_rect: Option<DeviceUintRect>) {
-        let (next_epoch, prev_dirty_rect) = match self.image_templates.get(&image_key) {
-            Some(image) => {
-                // This image should not be an external image.
-                match image.data {
-                    ImageData::ExternalHandle(id) => {
-                        panic!("Update an external image with buffer, id={} image_key={:?}", id.0, image_key);
-                    },
-                    _ => {},
-                }
+        let resource = if let Some(image) = self.image_templates.get(&image_key) {
+            assert!(image.descriptor.width == descriptor.width);
+            assert!(image.descriptor.height == descriptor.height);
+            assert!(image.descriptor.format == descriptor.format);
 
-                let Epoch(current_epoch) = image.epoch;
-                (Epoch(current_epoch + 1), image.dirty_rect)
+            let next_epoch = Epoch(image.epoch.0 + 1);
+
+            let mut tiling = image.tiling;
+            if tiling.is_none() && self.should_tile(&descriptor, &data) {
+                tiling = Some(DEFAULT_TILE_SIZE);
             }
-            None => {
-                (Epoch(0), None)
+
+            ImageResource {
+                descriptor: descriptor,
+                data: data,
+                epoch: next_epoch,
+                tiling: tiling,
+                dirty_rect: match (dirty_rect, image.dirty_rect) {
+                    (Some(rect), Some(prev_rect)) => Some(rect.union(&prev_rect)),
+                    (Some(rect), None) => Some(rect),
+                    _ => None,
+                },
             }
-        };
-
-        let resource = ImageResource {
-            descriptor: descriptor,
-            data: ImageData::new(bytes),
-            epoch: next_epoch,
-            tiling: None,
-            dirty_rect: match (dirty_rect, prev_dirty_rect) {
-                (Some(rect), Some(prev_rect)) => Some(rect.union(&prev_rect)),
-                (Some(rect), None) => Some(rect),
-                _ => None,
-            },
+        } else {
+            panic!("Attempt to update non-existant image (key {:?}).", image_key);
         };
 
         self.image_templates.insert(image_key, resource);
     }
 
     pub fn delete_image_template(&mut self, image_key: ImageKey) {
         let value = self.image_templates.remove(&image_key);
 
         // If the key is associated to an external image, pass the external id to renderer for cleanup.
         if let Some(image) = value {
             match image.data {
-                ImageData::ExternalHandle(id) => {
-                    self.pending_external_image_update_list.push(id);
-                },
-                _ => {},
+                ImageData::External(ext_image) => {
+                    match ext_image.image_type {
+                        ExternalImageType::Texture2DHandle |
+                        ExternalImageType::TextureRectHandle => {
+                            self.pending_external_image_update_list.push(ext_image.id);
+                        }
+                        _ => {}
+                    }
+                }
+                _ => {}
             }
 
             return;
         }
 
         println!("Delete the non-exist key:{:?}", image_key);
     }
 
@@ -389,16 +409,17 @@ impl ResourceCache {
                         data.clone(),
                         &BlobImageDescriptor {
                             width: template.descriptor.width,
                             height: template.descriptor.height,
                             format: template.descriptor.format,
                             // TODO(nical): figure out the scale factor (should change with zoom).
                             scale_factor: 1.0,
                         },
+                        template.dirty_rect,
                     );
                 }
             }
         } else {
             self.pending_image_requests.push(request);
         }
     }
 
@@ -518,25 +539,34 @@ impl ResourceCache {
             uv1: DevicePoint::new(item.pixel_rect.bottom_right.x as f32,
                                   item.pixel_rect.bottom_right.y as f32),
         }
     }
 
     pub fn get_image_properties(&self, image_key: ImageKey) -> ImageProperties {
         let image_template = &self.image_templates[&image_key];
 
-        let external_id = match image_template.data {
-            ImageData::ExternalHandle(id) => Some(id),
-            // raw and externalBuffer are all use resource_cache.
-            ImageData::Raw(..) | ImageData::ExternalBuffer(..) | ImageData::Blob(..) => None,
+        let external_image = match image_template.data {
+            ImageData::External(ext_image) => {
+                match ext_image.image_type {
+                    ExternalImageType::Texture2DHandle |
+                    ExternalImageType::TextureRectHandle => {
+                        Some(ext_image)
+                    },
+                    // external buffer uses resource_cache.
+                    ExternalImageType::ExternalBuffer => None,
+                }
+            },
+            // raw and blob image are all using resource_cache.
+            ImageData::Raw(..) | ImageData::Blob(..) => None,
         };
 
         ImageProperties {
             descriptor: image_template.descriptor,
-            external_id: external_id,
+            external_image: external_image,
             tiling: image_template.tiling,
         }
     }
 
     #[inline]
     pub fn get_webgl_texture(&self, context_id: &WebGLContextId) -> CacheItem {
         let webgl_texture = &self.webgl_textures[context_id];
         CacheItem {
@@ -647,104 +677,122 @@ impl ResourceCache {
                     Err(BlobImageError::Other(msg)) => {
                         panic!("Vector image error {}", msg);
                     }
                 }
             }
         }
     }
 
-    fn finalize_image_request(&mut self,
-                              request: ImageRequest,
-                              image_data: Option<ImageData>,
-                              texture_cache_profile: &mut TextureCacheProfileCounters) {
+    fn update_texture_cache(&mut self,
+                            request: &ImageRequest,
+                            image_data: Option<ImageData>,
+                            texture_cache_profile: &mut TextureCacheProfileCounters) {
         let image_template = self.image_templates.get_mut(&request.key).unwrap();
         let image_data = image_data.unwrap_or_else(||{
             image_template.data.clone()
         });
 
-        match image_template.data {
-            ImageData::ExternalHandle(..) => {
-                // external handle doesn't need to update the texture_cache.
-            }
-            ImageData::Raw(..) | ImageData::ExternalBuffer(..) | ImageData::Blob(..) => {
-                let descriptor = if let Some(tile) = request.tile {
-                    let tile_size = image_template.tiling.unwrap() as u32;
-                    let image_descriptor = &image_template.descriptor;
-                    let stride = image_descriptor.compute_stride();
-                    let bpp = image_descriptor.format.bytes_per_pixel().unwrap();
+        let descriptor = if let Some(tile) = request.tile {
+            let tile_size = image_template.tiling.unwrap() as u32;
+            let image_descriptor = &image_template.descriptor;
+            let stride = image_descriptor.compute_stride();
+            let bpp = image_descriptor.format.bytes_per_pixel().unwrap();
 
-                    // Storage for the tiles on the right and bottom edges is shrunk to
-                    // fit the image data (See decompose_tiled_image in frame.rs).
-                    let actual_width = if (tile.x as u32) < image_descriptor.width / tile_size {
-                        tile_size
-                    } else {
-                        image_descriptor.width % tile_size
-                    };
+            // Storage for the tiles on the right and bottom edges is shrunk to
+            // fit the image data (See decompose_tiled_image in frame.rs).
+            let actual_width = if (tile.x as u32) < image_descriptor.width / tile_size {
+                tile_size
+            } else {
+                image_descriptor.width % tile_size
+            };
+
+            let actual_height = if (tile.y as u32) < image_descriptor.height / tile_size {
+                tile_size
+            } else {
+                image_descriptor.height % tile_size
+            };
+
+            let offset = image_descriptor.offset + tile.y as u32 * tile_size * stride
+                                                 + tile.x as u32 * tile_size * bpp;
 
-                    let actual_height = if (tile.y as u32) < image_descriptor.height / tile_size {
-                        tile_size
-                    } else {
-                        image_descriptor.height % tile_size
-                    };
+            ImageDescriptor {
+                width: actual_width,
+                height: actual_height,
+                stride: Some(stride),
+                offset: offset,
+                format: image_descriptor.format,
+                is_opaque: image_descriptor.is_opaque,
+            }
+        } else {
+            image_template.descriptor.clone()
+        };
 
-                    let offset = image_descriptor.offset + tile.y as u32 * tile_size * stride
-                                                         + tile.x as u32 * tile_size * bpp;
+        match self.cached_images.entry(request.clone(), self.current_frame_id) {
+            Occupied(entry) => {
+                let image_id = entry.get().texture_cache_id;
 
-                    ImageDescriptor {
-                        width: actual_width,
-                        height: actual_height,
-                        stride: Some(stride),
-                        offset: offset,
-                        format: image_descriptor.format,
-                        is_opaque: image_descriptor.is_opaque,
-                    }
-                } else {
-                    image_template.descriptor.clone()
+                if entry.get().epoch != image_template.epoch {
+                    self.texture_cache.update(image_id,
+                                              descriptor,
+                                              image_data,
+                                              image_template.dirty_rect);
+
+                    // Update the cached epoch
+                    *entry.into_mut() = CachedImageInfo {
+                        texture_cache_id: image_id,
+                        epoch: image_template.epoch,
+                    };
+                    image_template.dirty_rect = None;
+                }
+            }
+            Vacant(entry) => {
+                let image_id = self.texture_cache.new_item_id();
+
+                let filter = match request.rendering {
+                    ImageRendering::Pixelated => TextureFilter::Nearest,
+                    ImageRendering::Auto | ImageRendering::CrispEdges => TextureFilter::Linear,
                 };
 
-                match self.cached_images.entry(request.clone(), self.current_frame_id) {
-                    Occupied(entry) => {
-                        let image_id = entry.get().texture_cache_id;
-
-                        if entry.get().epoch != image_template.epoch {
-                            self.texture_cache.update(image_id,
-                                                      descriptor,
-                                                      image_data,
-                                                      image_template.dirty_rect);
+                self.texture_cache.insert(image_id,
+                                          descriptor,
+                                          filter,
+                                          image_data,
+                                          texture_cache_profile);
 
-                            // Update the cached epoch
-                            *entry.into_mut() = CachedImageInfo {
-                                texture_cache_id: image_id,
-                                epoch: image_template.epoch,
-                            };
-                            image_template.dirty_rect = None;
-                        }
+                entry.insert(CachedImageInfo {
+                    texture_cache_id: image_id,
+                    epoch: image_template.epoch,
+                });
+            }
+        }
+    }
+    fn finalize_image_request(&mut self,
+                              request: ImageRequest,
+                              image_data: Option<ImageData>,
+                              texture_cache_profile: &mut TextureCacheProfileCounters) {
+        match self.image_templates.get(&request.key).unwrap().data {
+            ImageData::External(ext_image) => {
+                match ext_image.image_type {
+                    ExternalImageType::Texture2DHandle |
+                    ExternalImageType::TextureRectHandle => {
+                        // external handle doesn't need to update the texture_cache.
                     }
-                    Vacant(entry) => {
-                        let image_id = self.texture_cache.new_item_id();
-
-                        let filter = match request.rendering {
-                            ImageRendering::Pixelated => TextureFilter::Nearest,
-                            ImageRendering::Auto | ImageRendering::CrispEdges => TextureFilter::Linear,
-                        };
-
-                        self.texture_cache.insert(image_id,
-                                                  descriptor,
-                                                  filter,
+                    ExternalImageType::ExternalBuffer => {
+                        self.update_texture_cache(&request,
                                                   image_data,
                                                   texture_cache_profile);
-
-                        entry.insert(CachedImageInfo {
-                            texture_cache_id: image_id,
-                            epoch: image_template.epoch,
-                        });
                     }
                 }
             }
+            ImageData::Raw(..) | ImageData::Blob(..) => {
+                self.update_texture_cache(&request,
+                                           image_data,
+                                           texture_cache_profile);
+            }
         }
     }
 
     pub fn end_frame(&mut self) {
         debug_assert!(self.state == State::QueryResources);
         self.state = State::Idle;
     }
 
--- a/gfx/webrender/src/scene.rs
+++ b/gfx/webrender/src/scene.rs
@@ -108,25 +108,25 @@ impl Scene {
             properties: SceneProperties::new(),
         }
     }
 
     pub fn set_root_pipeline_id(&mut self, pipeline_id: PipelineId) {
         self.root_pipeline_id = Some(pipeline_id);
     }
 
-    pub fn set_root_display_list(&mut self,
-                                 pipeline_id: PipelineId,
-                                 epoch: Epoch,
-                                 built_display_list: BuiltDisplayList,
-                                 background_color: Option<ColorF>,
-                                 viewport_size: LayerSize,
-                                 auxiliary_lists: AuxiliaryLists) {
+    pub fn set_display_list(&mut self,
+                            pipeline_id: PipelineId,
+                            epoch: Epoch,
+                            built_display_list: BuiltDisplayList,
+                            background_color: Option<ColorF>,
+                            viewport_size: LayerSize,
+                            auxiliary_lists: AuxiliaryLists) {
         self.pipeline_auxiliary_lists.insert(pipeline_id, auxiliary_lists);
-        self.display_lists.insert(pipeline_id, built_display_list.all_display_items().to_vec());
+        self.display_lists.insert(pipeline_id, built_display_list.into_display_items());
 
         let new_pipeline = ScenePipeline {
             pipeline_id: pipeline_id,
             epoch: epoch,
             viewport_size: viewport_size,
             background_color: background_color,
         };
 
--- a/gfx/webrender/src/texture_cache.rs
+++ b/gfx/webrender/src/texture_cache.rs
@@ -11,17 +11,17 @@ use profiler::TextureCacheProfileCounter
 use std::cmp::{self, Ordering};
 use std::collections::HashMap;
 use std::collections::hash_map::Entry;
 use std::hash::BuildHasherDefault;
 use std::mem;
 use std::slice::Iter;
 use time;
 use util;
-use webrender_traits::{ImageData, ImageFormat, DevicePixel, DeviceIntPoint};
+use webrender_traits::{ExternalImageType, ImageData, ImageFormat, DevicePixel, DeviceIntPoint};
 use webrender_traits::{DeviceUintRect, DeviceUintSize, DeviceUintPoint};
 use webrender_traits::ImageDescriptor;
 
 /// The number of bytes we're allowed to use for a texture.
 const MAX_BYTES_PER_TEXTURE: u32 = 1024 * 1024 * 256;  // 256MB
 
 /// The number of RGBA pixels we're allowed to use for a texture.
 const MAX_RGBA_PIXELS_PER_TEXTURE: u32 = MAX_BYTES_PER_TEXTURE / 4;
@@ -606,18 +606,18 @@ impl TextureCache {
         let (page_list, page_profile) = match format {
             ImageFormat::A8 => (&mut self.arena.pages_a8, &mut profile.pages_a8),
             ImageFormat::RGBA8 => (&mut self.arena.pages_rgba8, &mut profile.pages_rgba8),
             ImageFormat::RGB8 => (&mut self.arena.pages_rgb8, &mut profile.pages_rgb8),
             ImageFormat::Invalid | ImageFormat::RGBAF32 => unreachable!(),
         };
 
         // TODO(gw): Handle this sensibly (support failing to render items that can't fit?)
-        assert!(requested_size.width < self.max_texture_size);
-        assert!(requested_size.height < self.max_texture_size);
+        assert!(requested_size.width <= self.max_texture_size);
+        assert!(requested_size.height <= self.max_texture_size);
 
         let mut page_id = None; //using ID here to please the borrow checker
         for (i, page) in page_list.iter_mut().enumerate() {
             if page.can_allocate(&requested_size) {
                 page_id = Some(i);
                 break;
             }
             // try to coalesce it
@@ -710,44 +710,47 @@ impl TextureCache {
                   dirty_rect: Option<DeviceUintRect>) {
         let existing_item = self.items.get(image_id);
 
         // TODO(gw): Handle updates to size/format!
         debug_assert_eq!(existing_item.allocated_rect.size.width, descriptor.width);
         debug_assert_eq!(existing_item.allocated_rect.size.height, descriptor.height);
 
         let op = match data {
-            ImageData::ExternalHandle(..) | ImageData::ExternalBuffer(..)=> {
+            ImageData::External(..) => {
                 panic!("Doesn't support Update() for external image.");
             }
             ImageData::Blob(..) => {
                 panic!("The vector image should have been rasterized into a raw image.");
             }
             ImageData::Raw(bytes) => {
-                if let Some(dirty) = dirty_rect {
-                    let stride = descriptor.compute_stride();
-                    let offset = descriptor.offset + dirty.origin.y * stride + dirty.origin.x;
-                    TextureUpdateOp::Update {
-                        page_pos_x: existing_item.allocated_rect.origin.x + dirty.origin.x,
-                        page_pos_y: existing_item.allocated_rect.origin.y + dirty.origin.y,
-                        width: dirty.size.width,
-                        height: dirty.size.height,
-                        data: bytes,
-                        stride: Some(stride),
-                        offset: offset,
+                match dirty_rect {
+                    Some(dirty) => {
+                        let stride = descriptor.compute_stride();
+                        let offset = descriptor.offset + dirty.origin.y * stride + dirty.origin.x;
+                        TextureUpdateOp::Update {
+                            page_pos_x: existing_item.allocated_rect.origin.x + dirty.origin.x,
+                            page_pos_y: existing_item.allocated_rect.origin.y + dirty.origin.y,
+                            width: dirty.size.width,
+                            height: dirty.size.height,
+                            data: bytes,
+                            stride: Some(stride),
+                            offset: offset,
+                        }
                     }
-                } else {
-                    TextureUpdateOp::Update {
-                        page_pos_x: existing_item.allocated_rect.origin.x,
-                        page_pos_y: existing_item.allocated_rect.origin.y,
-                        width: descriptor.width,
-                        height: descriptor.height,
-                        data: bytes,
-                        stride: descriptor.stride,
-                        offset: descriptor.offset,
+                    None => {
+                        TextureUpdateOp::Update {
+                            page_pos_x: existing_item.allocated_rect.origin.x,
+                            page_pos_y: existing_item.allocated_rect.origin.y,
+                            width: descriptor.width,
+                            height: descriptor.height,
+                            data: bytes,
+                            stride: descriptor.stride,
+                            offset: descriptor.offset,
+                        }
                     }
                 }
             }
         };
 
         let update_op = TextureUpdate {
             id: existing_item.texture_id,
             op: op,
@@ -776,18 +779,35 @@ impl TextureCache {
                                    height,
                                    format,
                                    filter,
                                    profile);
 
         match result.kind {
             AllocationKind::TexturePage => {
                 match data {
-                    ImageData::ExternalHandle(..) => {
-                        panic!("External handle should not go through texture_cache.");
+                    ImageData::External(ext_image) => {
+                        match ext_image.image_type {
+                            ExternalImageType::Texture2DHandle |
+                            ExternalImageType::TextureRectHandle => {
+                                panic!("External texture handle should not go through texture_cache.");
+                            }
+                            ExternalImageType::ExternalBuffer => {
+                                let update_op = TextureUpdate {
+                                    id: result.item.texture_id,
+                                    op: TextureUpdateOp::UpdateForExternalBuffer {
+                                        rect: result.item.allocated_rect,
+                                        id: ext_image.id,
+                                        stride: stride,
+                                    },
+                                };
+
+                                self.pending_updates.push(update_op);
+                            }
+                        }
                     }
                     ImageData::Blob(..) => {
                         panic!("The vector image should have been rasterized.");
                     }
                     ImageData::Raw(bytes) => {
                         let update_op = TextureUpdate {
                             id: result.item.texture_id,
                             op: TextureUpdateOp::Update {
@@ -798,34 +818,42 @@ impl TextureCache {
                                 data: bytes,
                                 stride: stride,
                                 offset: descriptor.offset,
                             },
                         };
 
                         self.pending_updates.push(update_op);
                     }
-                    ImageData::ExternalBuffer(id) => {
-                        let update_op = TextureUpdate {
-                            id: result.item.texture_id,
-                            op: TextureUpdateOp::UpdateForExternalBuffer {
-                                rect: result.item.allocated_rect,
-                                id: id,
-                                stride: stride,
-                            },
-                        };
-
-                        self.pending_updates.push(update_op);
-                    }
                 }
             }
             AllocationKind::Standalone => {
                 match data {
-                    ImageData::ExternalHandle(..) => {
-                        panic!("External handle should not go through texture_cache.");
+                    ImageData::External(ext_image) => {
+                        match ext_image.image_type {
+                            ExternalImageType::Texture2DHandle |
+                            ExternalImageType::TextureRectHandle => {
+                                panic!("External texture handle should not go through texture_cache.");
+                            }
+                            ExternalImageType::ExternalBuffer => {
+                                let update_op = TextureUpdate {
+                                    id: result.item.texture_id,
+                                    op: TextureUpdateOp::Create {
+                                        width: width,
+                                        height: height,
+                                        format: format,
+                                        filter: filter,
+                                        mode: RenderTargetMode::None,
+                                        data: Some(data),
+                                    },
+                                };
+
+                                self.pending_updates.push(update_op);
+                            }
+                        }
                     }
                     _ => {
                         let update_op = TextureUpdate {
                             id: result.item.texture_id,
                             op: TextureUpdateOp::Create {
                                 width: width,
                                 height: height,
                                 format: format,
--- a/gfx/webrender/src/tiling.rs
+++ b/gfx/webrender/src/tiling.rs
@@ -18,20 +18,22 @@ use render_task::{RenderTaskId, RenderTa
 use render_task::RenderTaskLocation;
 use renderer::BlendMode;
 use resource_cache::ResourceCache;
 use std::{f32, i32, mem, usize};
 use std::collections::HashMap;
 use std::hash::BuildHasherDefault;
 use texture_cache::TexturePage;
 use util::{TransformedRect, TransformedRectKind};
-use webrender_traits::{AuxiliaryLists, ColorF, DeviceIntPoint, DeviceIntRect, DeviceUintPoint};
+use webrender_traits::{AuxiliaryLists, ColorF, DeviceIntPoint, DeviceIntRect};
+use webrender_traits::{DeviceIntSize, DeviceUintPoint};
 use webrender_traits::{DeviceUintSize, FontRenderMode, ImageRendering, LayerPoint, LayerRect};
 use webrender_traits::{LayerToWorldTransform, MixBlendMode, PipelineId, ScrollLayerId};
 use webrender_traits::{WorldPoint4D, WorldToLayerTransform};
+use webrender_traits::{ExternalImageType};
 
 // Special sentinel value recognized by the shader. It is considered to be
 // a dummy task that doesn't mask out anything.
 const OPAQUE_TASK_INDEX: RenderTaskIndex = RenderTaskIndex(i32::MAX as usize);
 
 
 pub type AuxiliaryListsMap = HashMap<PipelineId,
                                      AuxiliaryLists,
@@ -64,17 +66,34 @@ trait AlphaBatchHelpers {
                                        z_sort_index: i32);
 }
 
 impl AlphaBatchHelpers for PrimitiveStore {
     fn get_batch_kind(&self, metadata: &PrimitiveMetadata) -> AlphaBatchKind {
         let batch_kind = match metadata.prim_kind {
             PrimitiveKind::Border => AlphaBatchKind::Border,
             PrimitiveKind::BoxShadow => AlphaBatchKind::BoxShadow,
-            PrimitiveKind::Image => AlphaBatchKind::Image,
+            PrimitiveKind::Image => {
+                let image_cpu = &self.cpu_images[metadata.cpu_prim_index.0];
+
+                match image_cpu.color_texture_id {
+                    SourceTexture::External(ext_image) => {
+                        match ext_image.image_type {
+                            ExternalImageType::Texture2DHandle => AlphaBatchKind::Image,
+                            ExternalImageType::TextureRectHandle => AlphaBatchKind::ImageRect,
+                            _ => {
+                                panic!("Non-texture handle type should be handled in other way.");
+                            }
+                        }
+                    }
+                    _ => {
+                        AlphaBatchKind::Image
+                    }
+                }
+            }
             PrimitiveKind::YuvImage => AlphaBatchKind::YuvImage,
             PrimitiveKind::Rectangle => AlphaBatchKind::Rectangle,
             PrimitiveKind::AlignedGradient => AlphaBatchKind::AlignedGradient,
             PrimitiveKind::AngleGradient => AlphaBatchKind::AngleGradient,
             PrimitiveKind::RadialGradient => AlphaBatchKind::RadialGradient,
             PrimitiveKind::TextRun => {
                 let text_run_cpu = &self.cpu_text_runs[metadata.cpu_prim_index.0];
                 if text_run_cpu.blur_radius.0 == 0 {
@@ -268,17 +287,18 @@ impl AlphaBatchHelpers for PrimitiveStor
                                 global_prim_id: global_prim_id,
                                 prim_address: prim_address,
                                 sub_index: metadata.gpu_data_address.0 + glyph_index,
                                 user_data: [ text_cpu.resource_address.0 + glyph_index, 0 ],
                                 z_sort_index: z_sort_index,
                             });
                         }
                     }
-                    AlphaBatchKind::Image => {
+                    AlphaBatchKind::Image |
+                    AlphaBatchKind::ImageRect => {
                         let image_cpu = &self.cpu_images[metadata.cpu_prim_index.0];
 
                         data.push(PrimitiveInstance {
                             task_index: task_index,
                             clip_task_index: clip_task_index,
                             layer_index: packed_layer_index,
                             global_prim_id: global_prim_id,
                             prim_address: prim_address,
@@ -849,28 +869,69 @@ impl ClipBatcher {
 
 pub struct RenderTargetContext<'a> {
     pub stacking_context_store: &'a [StackingContext],
     pub clip_scroll_group_store: &'a [ClipScrollGroup],
     pub prim_store: &'a PrimitiveStore,
     pub resource_cache: &'a ResourceCache,
 }
 
+struct TextureAllocator {
+    // TODO(gw): Replace this with a simpler allocator for
+    // render target allocation - this use case doesn't need
+    // to deal with coalescing etc that the general texture
+    // cache allocator requires.
+    page_allocator: TexturePage,
+
+    // Track the used rect of the render target, so that
+    // we can set a scissor rect and only clear to the
+    // used portion of the target as an optimization.
+    used_rect: DeviceIntRect,
+}
+
+impl TextureAllocator {
+    fn new(size: DeviceUintSize) -> TextureAllocator {
+        TextureAllocator {
+            page_allocator: TexturePage::new(CacheTextureId(0), size),
+            used_rect: DeviceIntRect::zero(),
+        }
+    }
+
+    fn allocate(&mut self, size: &DeviceUintSize) -> Option<DeviceUintPoint> {
+        let origin = self.page_allocator.allocate(size);
+
+        if let Some(origin) = origin {
+            // TODO(gw): We need to make all the device rects
+            //           be consistent in the use of the
+            //           DeviceIntRect and DeviceUintRect types!
+            let origin = DeviceIntPoint::new(origin.x as i32,
+                                             origin.y as i32);
+            let size = DeviceIntSize::new(size.width as i32,
+                                          size.height as i32);
+            let rect = DeviceIntRect::new(origin, size);
+            self.used_rect = rect.union(&self.used_rect);
+        }
+
+        origin
+    }
+}
+
 pub trait RenderTarget {
     fn new(size: DeviceUintSize) -> Self;
     fn allocate(&mut self, size: DeviceUintSize) -> Option<DeviceUintPoint>;
     fn build(&mut self,
              _ctx: &RenderTargetContext,
              _render_tasks: &mut RenderTaskCollection,
              _child_pass_index: RenderPassIndex) {}
     fn add_task(&mut self,
                 task: RenderTask,
                 ctx: &RenderTargetContext,
                 render_tasks: &RenderTaskCollection,
                 pass_index: RenderPassIndex);
+    fn used_rect(&self) -> DeviceIntRect;
 }
 
 #[derive(Debug, Copy, Clone)]
 pub enum RenderTargetKind {
     Color,   // RGBA32
     Alpha,   // R8
 }
 
@@ -948,38 +1009,42 @@ pub struct ColorRenderTarget {
     //           be removed anyway.
     pub text_run_cache_prims: Vec<PrimitiveInstance>,
     pub text_run_textures: BatchTextures,
     // List of blur operations to apply for this render target.
     pub vertical_blurs: Vec<BlurCommand>,
     pub horizontal_blurs: Vec<BlurCommand>,
     pub readbacks: Vec<DeviceIntRect>,
     pub isolate_clears: Vec<DeviceIntRect>,
-    page_allocator: TexturePage,
+    allocator: TextureAllocator,
 }
 
 impl RenderTarget for ColorRenderTarget {
     fn allocate(&mut self, size: DeviceUintSize) -> Option<DeviceUintPoint> {
-        self.page_allocator.allocate(&size)
+        self.allocator.allocate(&size)
     }
 
     fn new(size: DeviceUintSize) -> ColorRenderTarget {
         ColorRenderTarget {
             alpha_batcher: AlphaBatcher::new(),
             box_shadow_cache_prims: Vec::new(),
             text_run_cache_prims: Vec::new(),
             text_run_textures: BatchTextures::no_texture(),
             vertical_blurs: Vec::new(),
             horizontal_blurs: Vec::new(),
             readbacks: Vec::new(),
             isolate_clears: Vec::new(),
-            page_allocator: TexturePage::new(CacheTextureId(0), size),
+            allocator: TextureAllocator::new(size),
         }
     }
 
+    fn used_rect(&self) -> DeviceIntRect {
+        self.allocator.used_rect
+    }
+
     fn build(&mut self,
              ctx: &RenderTargetContext,
              render_tasks: &mut RenderTaskCollection,
              child_pass_index: RenderPassIndex) {
         self.alpha_batcher.build(ctx,
                                  render_tasks,
                                  child_pass_index);
     }
@@ -1094,31 +1159,35 @@ impl RenderTarget for ColorRenderTarget 
                 self.readbacks.push(device_rect);
             }
         }
     }
 }
 
 pub struct AlphaRenderTarget {
     pub clip_batcher: ClipBatcher,
-    page_allocator: TexturePage,
+    allocator: TextureAllocator,
 }
 
 impl RenderTarget for AlphaRenderTarget {
     fn allocate(&mut self, size: DeviceUintSize) -> Option<DeviceUintPoint> {
-        self.page_allocator.allocate(&size)
+        self.allocator.allocate(&size)
     }
 
     fn new(size: DeviceUintSize) -> AlphaRenderTarget {
         AlphaRenderTarget {
             clip_batcher: ClipBatcher::new(),
-            page_allocator: TexturePage::new(CacheTextureId(0), size),
+            allocator: TextureAllocator::new(size),
         }
     }
 
+    fn used_rect(&self) -> DeviceIntRect {
+        self.allocator.used_rect
+    }
+
     fn add_task(&mut self,
                 task: RenderTask,
                 ctx: &RenderTargetContext,
                 render_tasks: &RenderTaskCollection,
                 pass_index: RenderPassIndex) {
         match task.kind {
             RenderTaskKind::Alpha(..) |
             RenderTaskKind::VerticalBlur(..) |
@@ -1256,16 +1325,17 @@ impl RenderPass {
 #[repr(u8)]
 pub enum AlphaBatchKind {
     Composite = 0,
     HardwareComposite,
     Blend,
     Rectangle,
     TextRun,
     Image,
+    ImageRect,
     YuvImage,
     Border,
     AlignedGradient,
     AngleGradient,
     RadialGradient,
     BoxShadow,
     CacheImage,
 }
@@ -1386,16 +1456,17 @@ pub struct PrimitiveBatch {
 }
 
 impl PrimitiveBatch {
     fn new_instances(batch_kind: AlphaBatchKind, key: AlphaBatchKey) -> PrimitiveBatch {
         let data = match batch_kind {
             AlphaBatchKind::Rectangle |
             AlphaBatchKind::TextRun |
             AlphaBatchKind::Image |
+            AlphaBatchKind::ImageRect |
             AlphaBatchKind::YuvImage |
             AlphaBatchKind::Border |
             AlphaBatchKind::AlignedGradient |
             AlphaBatchKind::AngleGradient |
             AlphaBatchKind::RadialGradient |
             AlphaBatchKind::BoxShadow |
             AlphaBatchKind::Blend |
             AlphaBatchKind::HardwareComposite |
--- a/gfx/webrender_bindings/Cargo.toml
+++ b/gfx/webrender_bindings/Cargo.toml
@@ -1,16 +1,16 @@
 [package]
 name = "webrender_bindings"
 version = "0.1.0"
 authors = ["The Mozilla Project Developers"]
 license = "MPL-2.0"
 
 [dependencies]
-webrender_traits = {path = "../webrender_traits", version = "0.27.0"}
+webrender_traits = {path = "../webrender_traits", version = "0.31.0"}
 euclid = "0.11"
 app_units = "0.4"
 gleam = "0.4"
 
 [dependencies.webrender]
 path = "../webrender"
-version = "0.26.0"
+version = "0.30.0"
 default-features = false
copy from gfx/webrender_bindings/RenderTextureHost.cpp
copy to gfx/webrender_bindings/RenderBufferTextureHost.cpp
--- a/gfx/webrender_bindings/RenderTextureHost.cpp
+++ b/gfx/webrender_bindings/RenderBufferTextureHost.cpp
@@ -1,118 +1,115 @@
 /* -*- 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 "RenderTextureHost.h"
+#include "RenderBufferTextureHost.h"
 
 #include "mozilla/gfx/Logging.h"
 #include "mozilla/layers/ImageDataSerializer.h"
 
 namespace mozilla {
-
-using namespace gfx;
-using namespace layers;
-
 namespace wr {
 
-RenderTextureHost::RenderTextureHost(uint8_t* aBuffer, const BufferDescriptor& aDescriptor)
+RenderBufferTextureHost::RenderBufferTextureHost(uint8_t* aBuffer,
+                                                 const layers::BufferDescriptor& aDescriptor)
   : mBuffer(aBuffer)
   , mDescriptor(aDescriptor)
   , mLocked(false)
 {
-  MOZ_COUNT_CTOR(RenderTextureHost);
+  MOZ_COUNT_CTOR_INHERITED(RenderBufferTextureHost, RenderTextureHost);
 
   switch (mDescriptor.type()) {
-    case BufferDescriptor::TYCbCrDescriptor: {
-      const YCbCrDescriptor& ycbcr = mDescriptor.get_YCbCrDescriptor();
+    case layers::BufferDescriptor::TYCbCrDescriptor: {
+      const layers::YCbCrDescriptor& ycbcr = mDescriptor.get_YCbCrDescriptor();
       mSize = ycbcr.ySize();
       mFormat = gfx::SurfaceFormat::YUV;
       break;
     }
-    case BufferDescriptor::TRGBDescriptor: {
-      const RGBDescriptor& rgb = mDescriptor.get_RGBDescriptor();
+    case layers::BufferDescriptor::TRGBDescriptor: {
+      const layers::RGBDescriptor& rgb = mDescriptor.get_RGBDescriptor();
       mSize = rgb.size();
       mFormat = rgb.format();
       break;
     }
     default:
       gfxCriticalError() << "Bad buffer host descriptor " << (int)mDescriptor.type();
       MOZ_CRASH("GFX: Bad descriptor");
   }
 }
 
-RenderTextureHost::~RenderTextureHost()
+RenderBufferTextureHost::~RenderBufferTextureHost()
 {
-  MOZ_COUNT_DTOR(RenderTextureHost);
+  MOZ_COUNT_DTOR_INHERITED(RenderBufferTextureHost, RenderTextureHost);
 }
 
 already_AddRefed<gfx::DataSourceSurface>
-RenderTextureHost::GetAsSurface()
+RenderBufferTextureHost::GetAsSurface()
 {
   RefPtr<gfx::DataSourceSurface> result;
   if (mFormat == gfx::SurfaceFormat::YUV) {
-    result = ImageDataSerializer::DataSourceSurfaceFromYCbCrDescriptor(
+    result = layers::ImageDataSerializer::DataSourceSurfaceFromYCbCrDescriptor(
       GetBuffer(), mDescriptor.get_YCbCrDescriptor());
     if (NS_WARN_IF(!result)) {
       return nullptr;
     }
   } else {
     result =
       gfx::Factory::CreateWrappingDataSourceSurface(GetBuffer(),
-        ImageDataSerializer::GetRGBStride(mDescriptor.get_RGBDescriptor()),
+          layers::ImageDataSerializer::GetRGBStride(mDescriptor.get_RGBDescriptor()),
         mSize, mFormat);
   }
   return result.forget();
 }
 
 bool
-RenderTextureHost::Lock()
+RenderBufferTextureHost::Lock()
 {
   MOZ_ASSERT(!mLocked);
 
   // XXX temporal workaround for YUV handling
   if (!mSurface) {
     mSurface = GetAsSurface();
     if (!mSurface) {
       return false;
     }
   }
 
-  if (NS_WARN_IF(!mSurface->Map(DataSourceSurface::MapType::READ_WRITE, &mMap))) {
+  if (NS_WARN_IF(!mSurface->Map(gfx::DataSourceSurface::MapType::READ_WRITE, &mMap))) {
     mSurface = nullptr;
     return false;
   }
 
   mLocked = true;
   return true;
 }
 
 void
-RenderTextureHost::Unlock()
+RenderBufferTextureHost::Unlock()
 {
   MOZ_ASSERT(mLocked);
   mLocked = false;
   if (mSurface) {
     mSurface->Unmap();
   }
   mSurface = nullptr;
 }
 
-uint8_t*
-RenderTextureHost::GetDataForRender() const
+const uint8_t*
+RenderBufferTextureHost::GetDataForRender() const
 {
   MOZ_ASSERT(mLocked);
   MOZ_ASSERT(mSurface);
   return mMap.mData;
 }
 
 size_t
-RenderTextureHost::GetBufferSizeForRender() const
+RenderBufferTextureHost::GetBufferSizeForRender() const
 {
   MOZ_ASSERT(mLocked);
   MOZ_ASSERT(mSurface);
   return mMap.mStride * mSurface->GetSize().height;
 }
 
 } // namespace wr
 } // namespace mozilla
copy from gfx/webrender_bindings/RenderTextureHost.h
copy to gfx/webrender_bindings/RenderBufferTextureHost.h
--- a/gfx/webrender_bindings/RenderTextureHost.h
+++ b/gfx/webrender_bindings/RenderBufferTextureHost.h
@@ -1,52 +1,61 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef MOZILLA_GFX_RENDERTEXTUREHOST_H
-#define MOZILLA_GFX_RENDERTEXTUREHOST_H
+#ifndef MOZILLA_GFX_RENDERBUFFERTEXTUREHOST_H
+#define MOZILLA_GFX_RENDERBUFFERTEXTUREHOST_H
 
-#include "nsISupportsImpl.h"
-#include "mozilla/gfx/2D.h"
-#include "mozilla/layers/LayersSurfaces.h"
-#include "mozilla/RefPtr.h"
+#include "RenderTextureHost.h"
 
 namespace mozilla {
 namespace wr {
 
-class RenderTextureHost
+class RenderBufferTextureHost final : public RenderTextureHost
 {
 public:
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RenderTextureHost)
+  RenderBufferTextureHost(uint8_t* aBuffer,
+                          const layers::BufferDescriptor& aDescriptor);
 
-  RenderTextureHost(uint8_t* aBuffer, const layers::BufferDescriptor& aDescriptor);
-
-  bool Lock();
+  virtual bool Lock() override;
+  virtual void Unlock() override;
 
-  void Unlock();
-
-  gfx::IntSize GetSize() const { return mSize; }
+  virtual gfx::IntSize GetSize() const override
+  {
+    return mSize;
+  }
+  virtual gfx::SurfaceFormat GetFormat() const override
+  {
+    return mFormat;
+  }
 
-  gfx::SurfaceFormat GetFormat() const { return mFormat; }
+  virtual RenderBufferTextureHost* AsBufferTextureHost() override
+  {
+    return this;
+  }
 
-  uint8_t* GetDataForRender() const;
+  const uint8_t* GetDataForRender() const;
   size_t GetBufferSizeForRender() const;
 
-protected:
-  ~RenderTextureHost();
+private:
+  virtual ~RenderBufferTextureHost();
+
   already_AddRefed<gfx::DataSourceSurface> GetAsSurface();
-  uint8_t* GetBuffer() const { return mBuffer; }
+  uint8_t* GetBuffer() const
+  {
+    return mBuffer;
+  }
 
   uint8_t* mBuffer;
   layers::BufferDescriptor mDescriptor;
   gfx::IntSize mSize;
   gfx::SurfaceFormat mFormat;
   RefPtr<gfx::DataSourceSurface> mSurface;
   gfx::DataSourceSurface::MappedSurface mMap;
   bool mLocked;
 };
 
 } // namespace wr
 } // namespace mozilla
 
-#endif // MOZILLA_GFX_RENDERTEXTUREHOST_H
+#endif // MOZILLA_GFX_RENDERBUFFERTEXTUREHOST_H
new file mode 100644
--- /dev/null
+++ b/gfx/webrender_bindings/RenderMacIOSurfaceTextureHostOGL.cpp
@@ -0,0 +1,103 @@
+/* -*- 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 "RenderMacIOSurfaceTextureHostOGL.h"
+
+#include "GLContextCGL.h"
+#include "mozilla/gfx/Logging.h"
+#include "ScopedGLHelpers.h"
+
+namespace mozilla {
+namespace wr {
+
+RenderMacIOSurfaceTextureHostOGL::RenderMacIOSurfaceTextureHostOGL(MacIOSurface* aSurface)
+  : mTextureHandle(0)
+{
+  MOZ_COUNT_CTOR_INHERITED(RenderMacIOSurfaceTextureHostOGL, RenderTextureHostOGL);
+
+  mSurface = aSurface;
+}
+
+RenderMacIOSurfaceTextureHostOGL::~RenderMacIOSurfaceTextureHostOGL()
+{
+  MOZ_COUNT_DTOR_INHERITED(RenderMacIOSurfaceTextureHostOGL, RenderTextureHostOGL);
+  DeleteTextureHandle();
+}
+
+bool
+RenderMacIOSurfaceTextureHostOGL::Lock()
+{
+  if (!mSurface || !mGL || !mGL->MakeCurrent()) {
+    return false;
+  }
+
+  if (!mTextureHandle) {
+    // xxx: should we need to handle the PlaneCount 3 iosurface?
+    MOZ_ASSERT(mSurface->GetPlaneCount() == 0);
+    MOZ_ASSERT(gl::GLContextCGL::Cast(mGL.get())->GetCGLContext());
+
+    mGL->fGenTextures(1, &mTextureHandle);
+    mGL->fActiveTexture(LOCAL_GL_TEXTURE0);
+    gl::ScopedBindTexture texture(mGL, mTextureHandle, LOCAL_GL_TEXTURE_RECTANGLE_ARB);
+    mGL->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
+    mGL->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
+    mSurface->CGLTexImageIOSurface2D(gl::GLContextCGL::Cast(mGL.get())->GetCGLContext(), 0);
+  }
+
+  return true;
+}
+
+void
+RenderMacIOSurfaceTextureHostOGL::Unlock()
+{
+
+}
+
+void
+RenderMacIOSurfaceTextureHostOGL::SetGLContext(gl::GLContext* aContext)
+{
+  if (mGL.get() != aContext) {
+    // release the texture handle in the previous gl context
+    DeleteTextureHandle();
+    mGL = aContext;
+  }
+}
+
+void
+RenderMacIOSurfaceTextureHostOGL::DeleteTextureHandle()
+{
+  if (mTextureHandle != 0 && mGL && mGL->MakeCurrent()) {
+    mGL->fDeleteTextures(1, &mTextureHandle);
+  }
+  mTextureHandle = 0;
+}
+
+GLuint
+RenderMacIOSurfaceTextureHostOGL::GetGLHandle()
+{
+  return mTextureHandle;
+}
+
+gfx::IntSize
+RenderMacIOSurfaceTextureHostOGL::GetSize() const
+{
+  if (!mSurface) {
+    return gfx::IntSize();
+  }
+  return gfx::IntSize(mSurface->GetDevicePixelWidth(),
+                      mSurface->GetDevicePixelHeight());
+}
+
+gfx::SurfaceFormat
+RenderMacIOSurfaceTextureHostOGL::GetFormat() const
+{
+  if (!mSurface) {
+    return gfx::SurfaceFormat::UNKNOWN;
+  }
+  return mSurface->GetReadFormat();
+}
+
+} // namespace wr
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/webrender_bindings/RenderMacIOSurfaceTextureHostOGL.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_RENDERMACIOSURFACETEXTUREHOSTOGL_H
+#define MOZILLA_GFX_RENDERMACIOSURFACETEXTUREHOSTOGL_H
+
+#include "mozilla/gfx/MacIOSurface.h"
+#include "mozilla/layers/TextureHostOGL.h"
+#include "RenderTextureHostOGL.h"
+
+namespace mozilla {
+
+namespace layers {
+class SurfaceDescriptorMacIOSurface;
+}
+
+namespace wr {
+
+class RenderMacIOSurfaceTextureHostOGL final : public RenderTextureHostOGL
+{
+public:
+  explicit RenderMacIOSurfaceTextureHostOGL(MacIOSurface* aSurface);
+
+  virtual bool Lock() override;
+  virtual void Unlock() override;
+
+  virtual gfx::IntSize GetSize() const override;
+  virtual gfx::SurfaceFormat GetFormat() const override;
+
+  virtual RenderMacIOSurfaceTextureHostOGL* AsMacIOSurfaceTextureHostOGL() override
+  {
+    return this;
+  }
+
+  virtual void SetGLContext(gl::GLContext* aContext) override;
+
+  virtual GLuint GetGLHandle() override;
+
+private:
+  virtual ~RenderMacIOSurfaceTextureHostOGL();
+  void DeleteTextureHandle();
+
+  RefPtr<MacIOSurface> mSurface;
+  RefPtr<gl::GLContext> mGL;
+  GLuint mTextureHandle;
+};
+
+} // namespace wr
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_RENDERMACIOSURFACETEXTUREHOSTOGL_H
--- a/gfx/webrender_bindings/RenderTextureHost.cpp
+++ b/gfx/webrender_bindings/RenderTextureHost.cpp
@@ -1,118 +1,22 @@
 /* -*- 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 "RenderTextureHost.h"
 
-#include "mozilla/gfx/Logging.h"
-#include "mozilla/layers/ImageDataSerializer.h"
-
 namespace mozilla {
-
-using namespace gfx;
-using namespace layers;
-
 namespace wr {
 
-RenderTextureHost::RenderTextureHost(uint8_t* aBuffer, const BufferDescriptor& aDescriptor)
-  : mBuffer(aBuffer)
-  , mDescriptor(aDescriptor)
-  , mLocked(false)
+RenderTextureHost::RenderTextureHost()
 {
   MOZ_COUNT_CTOR(RenderTextureHost);
-
-  switch (mDescriptor.type()) {
-    case BufferDescriptor::TYCbCrDescriptor: {
-      const YCbCrDescriptor& ycbcr = mDescriptor.get_YCbCrDescriptor();
-      mSize = ycbcr.ySize();
-      mFormat = gfx::SurfaceFormat::YUV;
-      break;
-    }
-    case BufferDescriptor::TRGBDescriptor: {
-      const RGBDescriptor& rgb = mDescriptor.get_RGBDescriptor();
-      mSize = rgb.size();
-      mFormat = rgb.format();
-      break;
-    }
-    default:
-      gfxCriticalError() << "Bad buffer host descriptor " << (int)mDescriptor.type();
-      MOZ_CRASH("GFX: Bad descriptor");
-  }
 }
 
 RenderTextureHost::~RenderTextureHost()
 {
   MOZ_COUNT_DTOR(RenderTextureHost);
 }
 
-already_AddRefed<gfx::DataSourceSurface>
-RenderTextureHost::GetAsSurface()
-{
-  RefPtr<gfx::DataSourceSurface> result;
-  if (mFormat == gfx::SurfaceFormat::YUV) {
-    result = ImageDataSerializer::DataSourceSurfaceFromYCbCrDescriptor(
-      GetBuffer(), mDescriptor.get_YCbCrDescriptor());
-    if (NS_WARN_IF(!result)) {
-      return nullptr;
-    }
-  } else {
-    result =
-      gfx::Factory::CreateWrappingDataSourceSurface(GetBuffer(),
-        ImageDataSerializer::GetRGBStride(mDescriptor.get_RGBDescriptor()),
-        mSize, mFormat);
-  }
-  return result.forget();
-}
-
-bool
-RenderTextureHost::Lock()
-{
-  MOZ_ASSERT(!mLocked);
-
-  // XXX temporal workaround for YUV handling
-  if (!mSurface) {
-    mSurface = GetAsSurface();
-    if (!mSurface) {
-      return false;
-    }
-  }
-
-  if (NS_WARN_IF(!mSurface->Map(DataSourceSurface::MapType::READ_WRITE, &mMap))) {
-    mSurface = nullptr;
-    return false;
-  }
-
-  mLocked = true;
-  return true;
-}
-
-void
-RenderTextureHost::Unlock()
-{
-  MOZ_ASSERT(mLocked);
-  mLocked = false;
-  if (mSurface) {
-    mSurface->Unmap();
-  }
-  mSurface = nullptr;
-}
-
-uint8_t*
-RenderTextureHost::GetDataForRender() const
-{
-  MOZ_ASSERT(mLocked);
-  MOZ_ASSERT(mSurface);
-  return mMap.mData;
-}
-
-size_t
-RenderTextureHost::GetBufferSizeForRender() const
-{
-  MOZ_ASSERT(mLocked);
-  MOZ_ASSERT(mSurface);
-  return mMap.mStride * mSurface->GetSize().height;
-}
-
 } // namespace wr
 } // namespace mozilla
--- a/gfx/webrender_bindings/RenderTextureHost.h
+++ b/gfx/webrender_bindings/RenderTextureHost.h
@@ -9,44 +9,35 @@
 #include "nsISupportsImpl.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/layers/LayersSurfaces.h"
 #include "mozilla/RefPtr.h"
 
 namespace mozilla {
 namespace wr {
 
+class RenderBufferTextureHost;
+class RenderTextureHostOGL;
+
 class RenderTextureHost
 {
-public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RenderTextureHost)
 
-  RenderTextureHost(uint8_t* aBuffer, const layers::BufferDescriptor& aDescriptor);
+public:
+  RenderTextureHost();
 
-  bool Lock();
-
-  void Unlock();
+  virtual bool Lock() = 0;
+  virtual void Unlock() = 0;
 
-  gfx::IntSize GetSize() const { return mSize; }
+  virtual gfx::IntSize GetSize() const = 0;
+  virtual gfx::SurfaceFormat GetFormat() const = 0;
 
-  gfx::SurfaceFormat GetFormat() const { return mFormat; }
-
-  uint8_t* GetDataForRender() const;
-  size_t GetBufferSizeForRender() const;
+  virtual RenderBufferTextureHost* AsBufferTextureHost() { return nullptr; }
+  virtual RenderTextureHostOGL* AsTextureHostOGL() { return nullptr; }
 
 protected:
-  ~RenderTextureHost();
-  already_AddRefed<gfx::DataSourceSurface> GetAsSurface();
-  uint8_t* GetBuffer() const { return mBuffer; }
-
-  uint8_t* mBuffer;
-  layers::BufferDescriptor mDescriptor;
-  gfx::IntSize mSize;
-  gfx::SurfaceFormat mFormat;
-  RefPtr<gfx::DataSourceSurface> mSurface;
-  gfx::DataSourceSurface::MappedSurface mMap;
-  bool mLocked;
+  virtual ~RenderTextureHost();
 };
 
 } // namespace wr
 } // namespace mozilla
 
 #endif // MOZILLA_GFX_RENDERTEXTUREHOST_H
new file mode 100644
--- /dev/null
+++ b/gfx/webrender_bindings/RenderTextureHostOGL.cpp
@@ -0,0 +1,22 @@
+/* -*- 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 "RenderTextureHostOGL.h"
+
+namespace mozilla {
+namespace wr {
+
+RenderTextureHostOGL::RenderTextureHostOGL()
+{
+  MOZ_COUNT_CTOR_INHERITED(RenderTextureHostOGL, RenderTextureHost);
+}
+
+RenderTextureHostOGL::~RenderTextureHostOGL()
+{
+  MOZ_COUNT_DTOR_INHERITED(RenderTextureHostOGL, RenderTextureHost);
+}
+
+} // namespace wr
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/webrender_bindings/RenderTextureHostOGL.h
@@ -0,0 +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/. */
+
+#ifndef MOZILLA_GFX_RENDERTEXTUREHOSTOGL_H
+#define MOZILLA_GFX_RENDERTEXTUREHOSTOGL_H
+
+#include "RenderTextureHost.h"
+
+namespace mozilla {
+namespace wr {
+
+class RenderMacIOSurfaceTextureHostOGL;
+
+class RenderTextureHostOGL : public RenderTextureHost
+{
+public:
+  RenderTextureHostOGL();
+
+  virtual void SetGLContext(gl::GLContext* aContext) = 0;
+
+  virtual GLuint GetGLHandle() = 0;
+
+  virtual RenderTextureHostOGL* AsTextureHostOGL() { return this; }
+  virtual RenderMacIOSurfaceTextureHostOGL* AsMacIOSurfaceTextureHostOGL() { return nullptr; }
+
+protected:
+  virtual ~RenderTextureHostOGL();
+};
+
+} // namespace wr
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_RENDERTEXTUREHOSTOGL_H
--- a/gfx/webrender_bindings/RenderThread.cpp
+++ b/gfx/webrender_bindings/RenderThread.cpp
@@ -192,16 +192,44 @@ RenderThread::UpdateAndRender(wr::Window
     &NotifyDidRender,
     renderer->GetCompositorBridge(),
     epochs,
     start, end
   ));
 }
 
 void
+RenderThread::Pause(wr::WindowId aWindowId)
+{
+  MOZ_ASSERT(IsInRenderThread());
+
+  auto it = mRenderers.find(aWindowId);
+  MOZ_ASSERT(it != mRenderers.end());
+  if (it == mRenderers.end()) {
+    return;
+  }
+  auto& renderer = it->second;
+  renderer->Pause();
+}
+
+bool
+RenderThread::Resume(wr::WindowId aWindowId)
+{
+  MOZ_ASSERT(IsInRenderThread());
+
+  auto it = mRenderers.find(aWindowId);
+  MOZ_ASSERT(it != mRenderers.end());
+  if (it == mRenderers.end()) {
+    return false;
+  }
+  auto& renderer = it->second;
+  return renderer->Resume();
+}
+
+void
 RenderThread::RegisterExternalImage(uint64_t aExternalImageId, RenderTextureHost* aTexture)
 {
   MutexAutoLock lock(mRenderTextureMapLock);
   MOZ_ASSERT(!mRenderTextures.Get(aExternalImageId));
   mRenderTextures.Put(aExternalImageId, aTexture);
 }
 
 void
--- a/gfx/webrender_bindings/RenderThread.h
+++ b/gfx/webrender_bindings/RenderThread.h
@@ -97,16 +97,19 @@ public:
   void PipelineSizeChanged(wr::WindowId aWindowId, uint64_t aPipelineId, float aWidth, float aHeight);
 
   /// Automatically forwarded to the render thread.
   void RunEvent(wr::WindowId aWindowId, UniquePtr<RendererEvent> aCallBack);
 
   /// Can only be called from the render thread.
   void UpdateAndRender(wr::WindowId aWindowId);
 
+  void Pause(wr::WindowId aWindowId);
+  bool Resume(wr::WindowId aWindowId);
+
   void RegisterExternalImage(uint64_t aExternalImageId, RenderTextureHost* aTexture);
 
   void UnregisterExternalImage(uint64_t aExternalImageId);
 
   RenderTextureHost* GetRenderTexture(uint64_t aExternalImageId);
 
 private:
   explicit RenderThread(base::Thread* aThread);
--- a/gfx/webrender_bindings/RendererOGL.cpp
+++ b/gfx/webrender_bindings/RendererOGL.cpp
@@ -4,30 +4,47 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "RendererOGL.h"
 #include "GLContext.h"
 #include "GLContextProvider.h"
 #include "mozilla/gfx/Logging.h"
 #include "mozilla/layers/CompositorBridgeParent.h"
 #include "mozilla/layers/CompositorThread.h"
-#include "mozilla/webrender/RenderTextureHost.h"
+#include "mozilla/webrender/RenderBufferTextureHost.h"
+#include "mozilla/webrender/RenderTextureHostOGL.h"
 #include "mozilla/widget/CompositorWidget.h"
 
 namespace mozilla {
 namespace wr {
 
 WrExternalImage LockExternalImage(void* aObj, WrExternalImageId aId)
 {
   RendererOGL* renderer = reinterpret_cast<RendererOGL*>(aObj);
   RenderTextureHost* texture = renderer->GetRenderTexture(aId.id);
-  MOZ_ASSERT(texture);
-  texture->Lock();
-  return WrExternalImage { WrExternalImageIdType::RawData, 0.0f, 0.0f, 0.0f, 0.0f, 0,
-                           texture->GetDataForRender(), texture->GetBufferSizeForRender() };
+
+  if (texture->AsBufferTextureHost()) {
+    RenderBufferTextureHost* bufferTexture = texture->AsBufferTextureHost();
+    MOZ_ASSERT(bufferTexture);
+    bufferTexture->Lock();
+
+    return RawDataToWrExternalImage(bufferTexture->GetDataForRender(),
+                                    bufferTexture->GetBufferSizeForRender());
+  } else {
+    // texture handle case
+    RenderTextureHostOGL* textureOGL = texture->AsTextureHostOGL();
+    MOZ_ASSERT(textureOGL);
+    gfx::IntSize size = textureOGL->GetSize();
+    textureOGL->SetGLContext(renderer->mGL);
+    textureOGL->Lock();
+
+    return NativeTextureToWrExternalImage(textureOGL->GetGLHandle(),
+                                          0, 0,
+                                          size.width, size.height);
+  }
 }
 
 void UnlockExternalImage(void* aObj, WrExternalImageId aId)
 {
   RendererOGL* renderer = reinterpret_cast<RendererOGL*>(aObj);
   RenderTextureHost* texture = renderer->GetRenderTexture(aId.id);
   MOZ_ASSERT(texture);
   texture->Unlock();
@@ -114,16 +131,42 @@ RendererOGL::Render()
 
   // TODO: Flush pending actions such as texture deletions/unlocks and
   //       textureHosts recycling.
 
   return true;
 }
 
 void
+RendererOGL::Pause()
+{
+#ifdef MOZ_WIDGET_ANDROID
+  if (!mGL || mGL->IsDestroyed()) {
+    return;
+  }
+  // ReleaseSurface internally calls MakeCurrent.
+  mGL->ReleaseSurface();
+#endif
+}
+
+bool
+RendererOGL::Resume()
+{
+#ifdef MOZ_WIDGET_ANDROID
+  if (!mGL || mGL->IsDestroyed()) {
+    return false;
+  }
+  // RenewSurface internally calls MakeCurrent.
+  return mGL->RenewSurface(mWidget);
+#else
+  return true;
+#endif
+}
+
+void
 RendererOGL::SetProfilerEnabled(bool aEnabled)
 {
   wr_renderer_set_profiler_enabled(mWrRenderer, aEnabled);
 }
 
 WrRenderedEpochs*
 RendererOGL::FlushRenderedEpochs()
 {
--- a/gfx/webrender_bindings/RendererOGL.h
+++ b/gfx/webrender_bindings/RendererOGL.h
@@ -65,16 +65,22 @@ public:
   /// This can be called on the render thread only.
   RendererOGL(RefPtr<RenderThread>&& aThread,
               RefPtr<gl::GLContext>&& aGL,
               RefPtr<widget::CompositorWidget>&&,
               wr::WindowId aWindowId,
               WrRenderer* aWrRenderer,
               layers::CompositorBridgeParentBase* aBridge);
 
+  /// This can be called on the render thread only.
+  void Pause();
+
+  /// This can be called on the render thread only.
+  bool Resume();
+
   layers::CompositorBridgeParentBase* GetCompositorBridge() { return mBridge; }
 
   WrRenderedEpochs* FlushRenderedEpochs();
 
   RenderTextureHost* GetRenderTexture(uint64_t aExternalImageId);
 
   WrRenderer* GetWrRenderer() { return mWrRenderer; }
 
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -41,28 +41,28 @@ public:
     MOZ_COUNT_DTOR(NewRenderer);
   }
 
   virtual void Run(RenderThread& aRenderThread, WindowId aWindowId) override
   {
     layers::AutoCompleteTask complete(mTask);
 
     RefPtr<gl::GLContext> gl;
-    if (gfxVars::UseWebRenderANGLE()) {
+    if (gfx::gfxVars::UseWebRenderANGLE()) {
       gl = gl::GLContextProviderEGL::CreateForCompositorWidget(mCompositorWidget, true);
       if (!gl || !gl->IsANGLE()) {
-        gfxCriticalNote << "Failed ANGLE GL context creation for WebRender: " << hexa(gl.get());
+        gfxCriticalNote << "Failed ANGLE GL context creation for WebRender: " << gfx::hexa(gl.get());
         return;
       }
     }
     if (!gl) {
       gl = gl::GLContextProvider::CreateForCompositorWidget(mCompositorWidget, true);
     }
     if (!gl || !gl->MakeCurrent()) {
-      gfxCriticalNote << "Failed GL context creation for WebRender: " << hexa(gl.get());
+      gfxCriticalNote << "Failed GL context creation for WebRender: " << gfx::hexa(gl.get());
       return;
     }
 
     gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, mMaxTextureSize);
     *mUseANGLE = gl->IsANGLE();
 
     WrRenderer* wrRenderer = nullptr;
     if (!wr_window_new(aWindowId, mSize.width, mSize.height, gl.get(),
@@ -259,16 +259,90 @@ WebRenderAPI::Readback(gfx::IntSize size
     // read-back event. Then, we could make sure this read-back event gets the
     // latest result.
     RunOnRenderThread(Move(event));
 
     task.Wait();
 }
 
 void
+WebRenderAPI::Pause()
+{
+    class PauseEvent : public RendererEvent
+    {
+        public:
+            explicit PauseEvent(layers::SynchronousTask* aTask)
+                : mTask(aTask)
+            {
+                MOZ_COUNT_CTOR(PauseEvent);
+            }
+
+            ~PauseEvent()
+            {
+                MOZ_COUNT_DTOR(PauseEvent);
+            }
+
+            virtual void Run(RenderThread& aRenderThread, WindowId aWindowId) override
+            {
+                aRenderThread.Pause(aWindowId);
+                layers::AutoCompleteTask complete(mTask);
+            }
+
+            layers::SynchronousTask* mTask;
+    };
+
+    layers::SynchronousTask task("Pause");
+    auto event = MakeUnique<PauseEvent>(&task);
+    // This event will be passed from wr_backend thread to renderer thread. That
+    // implies that all frame data have been processed when the renderer runs this event.
+    RunOnRenderThread(Move(event));
+
+    task.Wait();
+}
+
+bool
+WebRenderAPI::Resume()
+{
+    class ResumeEvent : public RendererEvent
+    {
+        public:
+            explicit ResumeEvent(layers::SynchronousTask* aTask, bool* aResult)
+                : mTask(aTask)
+                , mResult(aResult)
+            {
+                MOZ_COUNT_CTOR(ResumeEvent);
+            }
+
+            ~ResumeEvent()
+            {
+                MOZ_COUNT_DTOR(ResumeEvent);
+            }
+
+            virtual void Run(RenderThread& aRenderThread, WindowId aWindowId) override
+            {
+                *mResult = aRenderThread.Resume(aWindowId);
+                layers::AutoCompleteTask complete(mTask);
+            }
+
+            layers::SynchronousTask* mTask;
+            bool* mResult;
+    };
+
+    bool result = false;
+    layers::SynchronousTask task("Resume");
+    auto event = MakeUnique<ResumeEvent>(&task, &result);
+    // This event will be passed from wr_backend thread to renderer thread. That
+    // implies that all frame data have been processed when the renderer runs this event.
+    RunOnRenderThread(Move(event));
+
+    task.Wait();
+    return result;
+}
+
+void
 WebRenderAPI::WaitFlushed()
 {
     class WaitFlushedEvent : public RendererEvent
     {
         public:
             explicit WaitFlushedEvent(layers::SynchronousTask* aTask)
                 : mTask(aTask)
             {
@@ -286,19 +360,17 @@ WebRenderAPI::WaitFlushed()
             }
 
             layers::SynchronousTask* mTask;
     };
 
     layers::SynchronousTask task("WaitFlushed");
     auto event = MakeUnique<WaitFlushedEvent>(&task);
     // This event will be passed from wr_backend thread to renderer thread. That
-    // implies that all frame data have been processed when the renderer runs this
-    // read-back event. Then, we could make sure this read-back event gets the
-    // latest result.
+    // implies that all frame data have been processed when the renderer runs this event.
     RunOnRenderThread(Move(event));
 
     task.Wait();
 }
 
 void
 WebRenderAPI::SetRootPipeline(PipelineId aPipeline)
 {
@@ -322,24 +394,22 @@ WebRenderAPI::AddBlobImage(ImageKey key,
   wr_api_add_blob_image(mWrApi,
                         key,
                         &aDescriptor,
                         RangeToByteSlice(aBytes));
 }
 
 void
 WebRenderAPI::AddExternalImageHandle(ImageKey key,
-                                     gfx::IntSize aSize,
-                                     gfx::SurfaceFormat aFormat,
+                                     const ImageDescriptor& aDescriptor,
                                      uint64_t aHandle)
 {
-  auto format = SurfaceFormatToWrImageFormat(aFormat).value();
   wr_api_add_external_image_handle(mWrApi,
                                    key,
-                                   aSize.width, aSize.height, format,
+                                   &aDescriptor,
                                    aHandle);
 }
 
 void
 WebRenderAPI::AddExternalImageBuffer(ImageKey key,
                                      const ImageDescriptor& aDescriptor,
                                      uint64_t aHandle)
 {
@@ -517,27 +587,24 @@ DisplayListBuilder::PushLinearGradient(c
                              aStartPoint, aEndPoint,
                              aStops.Elements(), aStops.Length(),
                              aExtendMode);
 }
 
 void
 DisplayListBuilder::PushRadialGradient(const WrRect& aBounds,
                                        const WrClipRegion& aClip,
-                                       const WrPoint& aStartCenter,
-                                       const WrPoint& aEndCenter,
-                                       float aStartRadius,
-                                       float aEndRadius,
+                                       const WrPoint& aCenter,
+                                       const WrSize& aRadius,
                                        const nsTArray<WrGradientStop>& aStops,
                                        wr::GradientExtendMode aExtendMode)
 {
   wr_dp_push_radial_gradient(mWrState,
                              aBounds, aClip,
-                             aStartCenter, aEndCenter,
-                             aStartRadius, aEndRadius,
+                             aCenter, aRadius,
                              aStops.Elements(), aStops.Length(),
                              aExtendMode);
 }
 
 void
 DisplayListBuilder::PushImage(const WrRect& aBounds,
                               const WrClipRegion& aClip,
                               wr::ImageRendering aFilter,
--- a/gfx/webrender_bindings/WebRenderAPI.h
+++ b/gfx/webrender_bindings/WebRenderAPI.h
@@ -67,18 +67,17 @@ public:
                 const ImageDescriptor& aDescriptor,
                 Range<uint8_t> aBytes);
 
   void AddBlobImage(wr::ImageKey aKey,
                     const ImageDescriptor& aDescriptor,
                     Range<uint8_t> aBytes);
 
   void AddExternalImageHandle(ImageKey key,
-                              gfx::IntSize aSize,
-                              gfx::SurfaceFormat aFormat,
+                              const ImageDescriptor& aDescriptor,
                               uint64_t aHandle);
 
   void AddExternalImageBuffer(ImageKey key,
                               const ImageDescriptor& aDescriptor,
                               uint64_t aHandle);
 
   void UpdateImageBuffer(wr::ImageKey aKey,
                          const ImageDescriptor& aDescriptor,
@@ -90,16 +89,19 @@ public:
 
   void DeleteFont(wr::FontKey aKey);
 
   void SetProfilerEnabled(bool aEnabled);
 
   void RunOnRenderThread(UniquePtr<RendererEvent> aEvent);
   void Readback(gfx::IntSize aSize, uint8_t *aBuffer, uint32_t aBufferSize);
 
+  void Pause();
+  bool Resume();
+
   WrIdNamespace GetNamespace();
   GLint GetMaxTextureSize() const { return mMaxTextureSize; }
   bool GetUseANGLE() const { return mUseANGLE; }
 
 protected:
   WebRenderAPI(WrAPI* aRawApi, wr::WindowId aId, GLint aMaxTextureSize, bool aUseANGLE)
     : mWrApi(aRawApi)
     , mId(aId)
@@ -161,20 +163,18 @@ public:
                           const WrClipRegion& aClip,
                           const WrPoint& aStartPoint,
                           const WrPoint& aEndPoint,
                           const nsTArray<WrGradientStop>& aStops,
                           wr::GradientExtendMode aExtendMode);
 
   void PushRadialGradient(const WrRect& aBounds,
                           const WrClipRegion& aClip,
-                          const WrPoint& aStartCenter,
-                          const WrPoint& aEndCenter,
-                          float aStartRadius,
-                          float aEndRadius,
+                          const WrPoint& aCenter,
+                          const WrSize& aRadius,
                           const nsTArray<WrGradientStop>& aStops,
                           wr::GradientExtendMode aExtendMode);
 
   void PushImage(const WrRect& aBounds,
                  const WrClipRegion& aClip,
                  wr::ImageRendering aFilter,
                  wr::ImageKey aImage);
 
--- a/gfx/webrender_bindings/WebRenderTypes.h
+++ b/gfx/webrender_bindings/WebRenderTypes.h
@@ -39,16 +39,19 @@ inline Epoch NewEpoch(uint32_t aEpoch) {
   Epoch e;
   e.mHandle = aEpoch;
   return e;
 }
 
 inline Maybe<WrImageFormat>
 SurfaceFormatToWrImageFormat(gfx::SurfaceFormat aFormat) {
   switch (aFormat) {
+    case gfx::SurfaceFormat::R8G8B8X8:
+      // TODO: use RGBA + opaque flag
+      return Some(WrImageFormat::RGBA8);
     case gfx::SurfaceFormat::B8G8R8X8:
       // TODO: WebRender will have a BGRA + opaque flag for this but does not
       // have it yet (cf. issue #732).
     case gfx::SurfaceFormat::B8G8R8A8:
       return Some(WrImageFormat::RGBA8);
     case gfx::SurfaceFormat::B8G8R8:
       return Some(WrImageFormat::RGB8);
     case gfx::SurfaceFormat::A8:
@@ -345,16 +348,37 @@ static inline WrComplexClipRegion ToWrCo
 
 static inline WrExternalImageId ToWrExternalImageId(uint64_t aID)
 {
   WrExternalImageId id;
   id.id = aID;
   return id;
 }
 
+static inline WrExternalImage RawDataToWrExternalImage(const uint8_t* aBuff,
+                                                       size_t size)
+{
+  return WrExternalImage {
+    WrExternalImageIdType::RawData,
+    0, 0.0f, 0.0f, 0.0f, 0.0f,
+    aBuff, size
+  };
+}
+
+static inline WrExternalImage NativeTextureToWrExternalImage(uint8_t aHandle,
+                                                             float u0, float v0,
+                                                             float u1, float v1)
+{
+  return WrExternalImage {
+    WrExternalImageIdType::NativeTexture,
+    aHandle, u0, v0, u1, v1,
+    nullptr, 0
+  };
+}
+
 struct VecU8 {
   WrVecU8 inner;
   VecU8() {
     inner.data = nullptr;
     inner.capacity = 0;
   }
   VecU8(VecU8&) = delete;
   VecU8(VecU8&& src) {
--- a/gfx/webrender_bindings/moz.build
+++ b/gfx/webrender_bindings/moz.build
@@ -3,27 +3,39 @@
 # 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/.
 
 with Files('**'):
     BUG_COMPONENT = ('Core', 'Graphics: WebRender')
 
 EXPORTS.mozilla.webrender += [
+    'RenderBufferTextureHost.h',
     'RendererOGL.h',
     'RenderTextureHost.h',
+    'RenderTextureHostOGL.h',
     'RenderThread.h',
     'webrender_ffi.h',
     'WebRenderAPI.h',
     'WebRenderTypes.h',
 ]
 
 UNIFIED_SOURCES += [
     'Moz2DImageRenderer.cpp',
+    'RenderBufferTextureHost.cpp',
     'RendererOGL.cpp',
     'RenderTextureHost.cpp',
+    'RenderTextureHostOGL.cpp',
     'RenderThread.cpp',
     'WebRenderAPI.cpp',
 ]
 
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+    EXPORTS.mozilla.webrender += [
+        'RenderMacIOSurfaceTextureHostOGL.h',
+    ]
+    UNIFIED_SOURCES += [
+        'RenderMacIOSurfaceTextureHostOGL.cpp',
+    ]
+
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -1,28 +1,17 @@
 use std::ffi::CString;
 use std::{mem, slice};
 use std::path::PathBuf;
 use std::os::raw::{c_void, c_char};
 use std::sync::Arc;
 use std::collections::HashMap;
 use gleam::gl;
 
-use webrender_traits::{AuxiliaryLists, AuxiliaryListsDescriptor, BorderDetails, BorderRadius};
-use webrender_traits::{BorderSide, BorderStyle, BorderWidths, BoxShadowClipMode, BuiltDisplayList};
-use webrender_traits::{BuiltDisplayListDescriptor, ClipRegion, ColorF, ComplexClipRegion};
-use webrender_traits::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, Epoch, ExtendMode};
-use webrender_traits::{ExternalEvent, ExternalImageId, FilterOp, FontKey, GlyphInstance};
-use webrender_traits::{GradientStop, IdNamespace, ImageBorder, ImageData, ImageDescriptor};
-use webrender_traits::{ImageFormat, ImageKey, ImageMask, ImageRendering, ItemRange, LayerPixel};
-use webrender_traits::{LayoutPoint, LayoutRect, LayoutSize, LayoutTransform, MixBlendMode};
-use webrender_traits::{BlobImageData, BlobImageRenderer, BlobImageResult, BlobImageError};
-use webrender_traits::{BlobImageDescriptor, RasterizedBlobImage};
-use webrender_traits::{NinePatchDescriptor, NormalBorder, PipelineId, PropertyBinding, RenderApi};
-use webrender_traits::RepeatMode;
+use webrender_traits::*;
 use webrender::renderer::{Renderer, RendererOptions};
 use webrender::renderer::{ExternalImage, ExternalImageHandler, ExternalImageSource};
 use webrender::{ApiRecordingReceiver, BinaryRecorder};
 use app_units::Au;
 use euclid::{TypedPoint2D, SideOffsets2D};
 
 extern crate webrender_traits;
 
@@ -101,17 +90,17 @@ impl MutByteSlice {
         MutByteSlice { buffer: &mut slice[0], len: len }
     }
 
     pub fn as_mut_slice(&mut self) -> &mut [u8] {
         unsafe { slice::from_raw_parts_mut(self.buffer, self.len) }
     }
 }
 
-#[repr(C)]
+#[repr(u32)]
 pub enum WrGradientExtendMode {
     Clamp,
     Repeat,
 }
 
 impl WrGradientExtendMode {
     pub fn to_gradient_extend_mode(self) -> ExtendMode {
         match self {
@@ -312,17 +301,17 @@ pub struct WrSideOffsets2D<T>
 
 impl<T: Copy> WrSideOffsets2D<T>
 {
     pub fn to_side_offsets_2d(&self) -> SideOffsets2D<T> {
         SideOffsets2D::new(self.top, self.right, self.bottom, self.left)
     }
 }
 
-#[repr(C)]
+#[repr(u32)]
 pub enum WrRepeatMode
 {
     Stretch,
     Repeat,
     Round,
     Space,
 }
 
@@ -440,26 +429,25 @@ enum WrExternalImageType {
     NativeTexture,
     RawData,
 }
 
 #[repr(C)]
 struct WrExternalImageStruct {
     image_type: WrExternalImageType,
 
-    // Texture coordinate
+    // external texture handle
+    handle: u32,
+    // external texture coordinate
     u0: f32,
     v0: f32,
     u1: f32,
     v1: f32,
 
-    // external buffer handle
-    handle: u32,
-
-    // handle RawData.
+    // external image buffer
     buff: *const u8,
     size: usize,
 }
 
 type LockExternalImageCallback = fn(*mut c_void, ExternalImageId) -> WrExternalImageStruct;
 type UnlockExternalImageCallback = fn(*mut c_void, ExternalImageId);
 type ReleaseExternalImageCallback = fn(*mut c_void, ExternalImageId);
 
@@ -623,18 +611,18 @@ impl webrender_traits::RenderNotifier fo
     fn external_event(&mut self, event: ExternalEvent) {
         unsafe {
             wr_notifier_external_event(self.window_id, event.unwrap());
         }
     }
 }
 
 #[no_mangle]
-pub extern fn wr_renderer_set_external_image_handler(renderer: &mut Renderer,
-                                                     external_image_handler: *mut WrExternalImageHandler) {
+pub extern "C" fn wr_renderer_set_external_image_handler(renderer: &mut Renderer,
+                                                         external_image_handler: *mut WrExternalImageHandler) {
     if !external_image_handler.is_null() {
         renderer.set_external_image_handler(Box::new(
             unsafe {
                 WrExternalImageHandler {
                     external_image_obj: (*external_image_handler).external_image_obj,
                     lock_func: (*external_image_handler).lock_func,
                     unlock_func: (*external_image_handler).unlock_func,
                     release_func: (*external_image_handler).release_func,
@@ -811,55 +799,55 @@ pub extern "C" fn wr_api_add_blob_image(
                   descriptor.to_descriptor(),
                   ImageData::new_blob_image(copied_bytes),
                   None);
 }
 
 #[no_mangle]
 pub extern "C" fn wr_api_add_external_image_handle(api: &mut RenderApi,
                                                    image_key: ImageKey,
-                                                   width: u32,
-                                                   height: u32,
-                                                   format: ImageFormat,
+                                                   descriptor: &WrImageDescriptor,
                                                    external_image_id: u64) {
     assert!(unsafe { is_in_compositor_thread() });
     api.add_image(image_key,
-                  ImageDescriptor {
-                      width: width,
-                      height: height,
-                      stride: None,
-                      format: format,
-                      is_opaque: false,
-                      offset: 0,
-                  },
-                  ImageData::ExternalHandle(ExternalImageId(external_image_id)),
+                  descriptor.to_descriptor(),
+                  ImageData::External(ExternalImageData {
+                      id: ExternalImageId(external_image_id),
+                      image_type: ExternalImageType::Texture2DHandle
+                  }),
                   None);
 }
 
 #[no_mangle]
 pub extern "C" fn wr_api_add_external_image_buffer(api: &mut RenderApi,
                                                    image_key: ImageKey,
                                                    descriptor: &WrImageDescriptor,
                                                    external_image_id: u64) {
     assert!(unsafe { is_in_compositor_thread() });
     api.add_image(image_key,
                   descriptor.to_descriptor(),
-                  ImageData::ExternalBuffer(ExternalImageId(external_image_id)),
+                  ImageData::External(ExternalImageData {
+                      id: ExternalImageId(external_image_id),
+                      image_type: ExternalImageType::ExternalBuffer
+                  }),
                   None);
 }
 
 #[no_mangle]
 pub extern "C" fn wr_api_update_image(api: &mut RenderApi,
                                       key: ImageKey,
                                       descriptor: &WrImageDescriptor,
                                       bytes: ByteSlice) {
     assert!(unsafe { is_in_compositor_thread() });
     let copied_bytes = bytes.as_slice().to_owned();
 
-    api.update_image(key, descriptor.to_descriptor(), copied_bytes, None);
+    api.update_image(key,
+                     descriptor.to_descriptor(),
+                     ImageData::new(copied_bytes),
+                     None);
 }
 
 #[no_mangle]
 pub extern "C" fn wr_api_delete_image(api: &mut RenderApi, key: ImageKey) {
     assert!(unsafe { is_in_compositor_thread() });
     api.delete_image(key)
 }
 
@@ -883,53 +871,53 @@ pub unsafe extern "C" fn wr_api_set_root
                                                       pipeline_id: PipelineId,
                                                       dl_descriptor: BuiltDisplayListDescriptor,
                                                       dl_data: *mut u8,
                                                       dl_size: usize,
                                                       aux_descriptor: AuxiliaryListsDescriptor,
                                                       aux_data: *mut u8,
                                                       aux_size: usize) {
     let root_background_color = ColorF::new(0.3, 0.0, 0.0, 1.0);
-    // See the documentation of set_root_display_list in api.rs. I don't think
+    // See the documentation of set_display_list in api.rs. I don't think
     // it makes a difference in gecko at the moment(until APZ is figured out)
     // but I suppose it is a good default.
     let preserve_frame_state = true;
 
     let dl_slice = slice::from_raw_parts(dl_data, dl_size);
     let mut dl_vec = Vec::new();
     // XXX: see if we can get rid of the copy here
     dl_vec.extend_from_slice(dl_slice);
     let dl = BuiltDisplayList::from_data(dl_vec, dl_descriptor);
 
     let aux_slice = slice::from_raw_parts(aux_data, aux_size);
     let mut aux_vec = Vec::new();
     // XXX: see if we can get rid of the copy here
     aux_vec.extend_from_slice(aux_slice);
     let aux = AuxiliaryLists::from_data(aux_vec, aux_descriptor);
 
-    api.set_root_display_list(Some(root_background_color),
-                              epoch,
-                              LayoutSize::new(viewport_width, viewport_height),
-                              (pipeline_id, dl, aux),
-                              preserve_frame_state);
+    api.set_display_list(Some(root_background_color),
+                         epoch,
+                         LayoutSize::new(viewport_width, viewport_height),
+                         (pipeline_id, dl, aux),
+                         preserve_frame_state);
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn wr_api_clear_root_display_list(api: &mut RenderApi,
                                                         epoch: Epoch,
                                                         pipeline_id: PipelineId) {
     let root_background_color = ColorF::new(0.3, 0.0, 0.0, 1.0);
     let preserve_frame_state = true;
     let frame_builder = WebRenderFrameBuilder::new(pipeline_id);
 
-    api.set_root_display_list(Some(root_background_color),
-                              epoch,
-                              LayoutSize::new(0.0, 0.0),
-                              frame_builder.dl_builder.finalize(),
-                              preserve_frame_state);
+    api.set_display_list(Some(root_background_color),
+                         epoch,
+                         LayoutSize::new(0.0, 0.0),
+                         frame_builder.dl_builder.finalize(),
+                         preserve_frame_state);
 }
 
 #[no_mangle]
 pub extern "C" fn wr_api_generate_frame(api: &mut RenderApi) {
     api.generate_frame(None);
 }
 
 #[no_mangle]
@@ -1266,48 +1254,46 @@ pub extern "C" fn wr_dp_push_linear_grad
                                              stops: *const WrGradientStop,
                                              stops_count: usize,
                                              extend_mode: WrGradientExtendMode) {
     assert!(unsafe { is_in_main_thread() });
 
     let stops =
         WrGradientStop::to_gradient_stops(unsafe { slice::from_raw_parts(stops, stops_count) });
 
+    let gradient = state.frame_builder.dl_builder.create_gradient(start_point.to_point(),
+                                                                  end_point.to_point(),
+                                                                  stops,
+                                                                  extend_mode.to_gradient_extend_mode());
     state.frame_builder.dl_builder.push_gradient(rect.to_rect(),
                                                  clip.to_clip_region(),
-                                                 start_point.to_point(),
-                                                 end_point.to_point(),
-                                                 stops,
-                                                 extend_mode.to_gradient_extend_mode());
+                                                 gradient);
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_radial_gradient(state: &mut WrState,
                                              rect: WrRect,
                                              clip: WrClipRegion,
-                                             start_center: WrPoint,
-                                             end_center: WrPoint,
-                                             start_radius: f32,
-                                             end_radius: f32,
+                                             center: WrPoint,
+                                             radius: WrSize,
                                              stops: *const WrGradientStop,
                                              stops_count: usize,
                                              extend_mode: WrGradientExtendMode) {
     assert!(unsafe { is_in_main_thread() });
 
     let stops =
         WrGradientStop::to_gradient_stops(unsafe { slice::from_raw_parts(stops, stops_count) });
 
+    let gradient = state.frame_builder.dl_builder.create_radial_gradient(center.to_point(),
+                                                                         radius.to_size(),
+                                                                         stops,
+                                                                         extend_mode.to_gradient_extend_mode());
     state.frame_builder.dl_builder.push_radial_gradient(rect.to_rect(),
                                                         clip.to_clip_region(),
-                                                        start_center.to_point(),
-                                                        start_radius,
-                                                        end_center.to_point(),
-                                                        end_radius,
-                                                        stops,
-                                                        extend_mode.to_gradient_extend_mode());
+                                                        gradient);
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_box_shadow(state: &mut WrState,
                                         rect: WrRect,
                                         clip: WrClipRegion,
                                         box_bounds: WrRect,
                                         offset: WrPoint,
@@ -1365,17 +1351,18 @@ pub unsafe extern "C" fn wr_dp_push_buil
 struct Moz2dImageRenderer {
     images: HashMap<ImageKey, BlobImageResult>
 }
 
 impl BlobImageRenderer for Moz2dImageRenderer {
     fn request_blob_image(&mut self,
                           key: ImageKey,
                           data: Arc<BlobImageData>,
-                          descriptor: &BlobImageDescriptor) {
+                          descriptor: &BlobImageDescriptor,
+                          _dirty_rect: Option<DeviceUintRect>) {
         let result = self.render_blob_image(data, descriptor);
         self.images.insert(key, result);
     }
 
     fn resolve_blob_image(&mut self, key: ImageKey) -> BlobImageResult {
         return match self.images.remove(&key) {
             Some(result) => result,
             None => Err(BlobImageError::InvalidKey),
--- a/gfx/webrender_bindings/webrender_ffi.h
+++ b/gfx/webrender_bindings/webrender_ffi.h
@@ -408,25 +408,24 @@ struct WrExternalImageId
 {
   WrImageIdType id;
 };
 
 struct WrExternalImage
 {
   WrExternalImageIdType type;
 
-  // Texture coordinate
+  // external texture handle
+  uint32_t handle;
+  // external texture coordinate
   float u0, v0;
   float u1, v1;
 
-  // external buffer handle
-  uint32_t handle;
-
-  // handle RawData.
-  uint8_t* buff;
+  // external image buffer
+  const uint8_t* buff;
   size_t size;
 };
 
 typedef WrExternalImage (*LockExternalImageCallback)(void*, WrExternalImageId);
 typedef void (*UnlockExternalImageCallback)(void*, WrExternalImageId);
 typedef void (*ReleaseExternalImageCallback)(void*, WrExternalImageId);
 
 struct WrExternalImageHandler
@@ -488,17 +487,17 @@ wr_renderer_update(WrRenderer* renderer)
 WR_FUNC;
 
 WR_INLINE void
 wr_renderer_render(WrRenderer* renderer, uint32_t width, uint32_t height)
 WR_FUNC;
 
 // It is the responsibility of the caller to manage the dst_buffer memory
 // and also free it at the proper time.
-WR_INLINE const uint8_t*
+WR_INLINE void
 wr_renderer_readback(WrRenderer* renderer,
                      uint32_t width, uint32_t height,
                      uint8_t* dst_buffer, size_t buffer_length)
 WR_FUNC;
 
 WR_INLINE void
 wr_renderer_set_profiler_enabled(WrRenderer* renderer, bool enabled)
 WR_FUNC;
@@ -541,18 +540,19 @@ WR_INLINE void
 wr_api_add_image(WrAPI* api, WrImageKey key, const WrImageDescriptor* descriptor, const WrByteSlice aSlice)
 WR_FUNC;
 
 WR_INLINE void
 wr_api_add_blob_image(WrAPI* api, WrImageKey key, const WrImageDescriptor* descriptor, const WrByteSlice aSlice)
 WR_FUNC;
 
 WR_INLINE void
-wr_api_add_external_image_handle(WrAPI* api, WrImageKey key, uint32_t width, uint32_t height,
-                                 WrImageFormat format, uint64_t external_image_id)
+wr_api_add_external_image_handle(WrAPI* api, WrImageKey key,
+                                 const WrImageDescriptor* descriptor,
+                                 uint64_t external_image_id)
 WR_FUNC;
 
 WR_INLINE void
 wr_api_add_external_image_buffer(WrAPI* api, WrImageKey key,
                                  const WrImageDescriptor* descriptor,
                                  uint64_t external_image_id)
 WR_FUNC;
 
@@ -688,18 +688,17 @@ WR_INLINE void
 wr_dp_push_linear_gradient(WrState* wrState, WrRect bounds, WrClipRegion clip,
                            WrPoint startPoint, WrPoint endPoint,
                            const WrGradientStop* stops, size_t stopsCount,
                            WrGradientExtendMode extendMode)
 WR_FUNC;
 
 WR_INLINE void
 wr_dp_push_radial_gradient(WrState* wrState, WrRect bounds, WrClipRegion clip,
-                           WrPoint startCenter, WrPoint endCenter,
-                           float startRadius, float endRadius,
+                           WrPoint center, WrSize radius,
                            const WrGradientStop* stops, size_t stopsCount,
                            WrGradientExtendMode extendMode)
 WR_FUNC;
 
 WR_INLINE void
 wr_dp_push_box_shadow(WrState* wrState, WrRect rect, WrClipRegion clip,
                       WrRect box_bounds, WrPoint offset, WrColor color,
                       float blur_radius, float spread_radius, float border_radius,
--- a/gfx/webrender_traits/Cargo.toml
+++ b/gfx/webrender_traits/Cargo.toml
@@ -1,11 +1,11 @@
 [package]
 name = "webrender_traits"
-version = "0.27.0"
+version = "0.31.0"
 authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
 license = "MPL-2.0"
 repository = "https://github.com/servo/webrender"
 
 [features]
 nightly = ["euclid/unstable", "serde/unstable"]
 ipc = ["ipc-channel"]
 webgl = ["offscreen_gl_context"]
--- a/gfx/webrender_traits/src/api.rs
+++ b/gfx/webrender_traits/src/api.rs
@@ -1,14 +1,13 @@
 /* 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 byteorder::{LittleEndian, WriteBytesExt};
-use channel::{self, MsgSender, PayloadHelperMethods, PayloadSender};
+use channel::{self, MsgSender, Payload, PayloadSenderHelperMethods, PayloadSender};
 #[cfg(feature = "webgl")]
 use offscreen_gl_context::{GLContextAttributes, GLLimits};
 use std::cell::Cell;
 use std::fmt;
 use std::marker::PhantomData;
 use {AuxiliaryLists, AuxiliaryListsDescriptor, BuiltDisplayList, BuiltDisplayListDescriptor};
 use {ColorF, DeviceIntPoint, DeviceIntSize, DeviceUintRect, DeviceUintSize, FontKey};
 use {GlyphDimensions, GlyphKey, ImageData, ImageDescriptor, ImageKey, LayoutPoint, LayoutSize};
@@ -23,31 +22,31 @@ pub enum ApiMsg {
     AddRawFont(FontKey, Vec<u8>),
     AddNativeFont(FontKey, NativeFontHandle),
     DeleteFont(FontKey),
     /// Gets the glyph dimensions
     GetGlyphDimensions(Vec<GlyphKey>, MsgSender<Vec<Option<GlyphDimensions>>>),
     /// Adds an image from the resource cache.
     AddImage(ImageKey, ImageDescriptor, ImageData, Option<TileSize>),
     /// Updates the the resource cache with the new image data.
-    UpdateImage(ImageKey, ImageDescriptor, Vec<u8>, Option<DeviceUintRect>),
+    UpdateImage(ImageKey, ImageDescriptor, ImageData, Option<DeviceUintRect>),
     /// Drops an image from the resource cache.
     DeleteImage(ImageKey),
     CloneApi(MsgSender<IdNamespace>),
     /// Supplies a new frame to WebRender.
     ///
     /// After receiving this message, WebRender will read the display list, followed by the
     /// auxiliary lists, from the payload channel.
-    SetRootDisplayList(Option<ColorF>,
-                       Epoch,
-                       PipelineId,
-                       LayoutSize,
-                       BuiltDisplayListDescriptor,
-                       AuxiliaryListsDescriptor,
-                       bool),
+    SetDisplayList(Option<ColorF>,
+                   Epoch,
+                   PipelineId,
+                   LayoutSize,
+                   BuiltDisplayListDescriptor,
+                   AuxiliaryListsDescriptor,
+                   bool),
     SetPageZoom(ZoomFactor),
     SetPinchZoom(ZoomFactor),
     SetPan(DeviceIntPoint),
     SetRootPipeline(PipelineId),
     SetWindowParameters(DeviceUintSize, DeviceUintRect),
     Scroll(ScrollLocation, WorldPoint, ScrollEventPhase),
     ScrollLayerWithId(LayoutPoint, ScrollLayerId),
     TickScrollingBounce,
@@ -72,17 +71,17 @@ impl fmt::Debug for ApiMsg {
             &ApiMsg::AddRawFont(..) => { write!(f, "ApiMsg::AddRawFont") }
             &ApiMsg::AddNativeFont(..) => { write!(f, "ApiMsg::AddNativeFont") }
             &ApiMsg::DeleteFont(..) => { write!(f, "ApiMsg::DeleteFont") }
             &ApiMsg::GetGlyphDimensions(..) => { write!(f, "ApiMsg::GetGlyphDimensions") }
             &ApiMsg::AddImage(..) => { write!(f, "ApiMsg::AddImage") }
             &ApiMsg::UpdateImage(..) => { write!(f, "ApiMsg::UpdateImage") }
             &ApiMsg::DeleteImage(..) => { write!(f, "ApiMsg::DeleteImage") }
             &ApiMsg::CloneApi(..) => { write!(f, "ApiMsg::CloneApi") }
-            &ApiMsg::SetRootDisplayList(..) => { write!(f, "ApiMsg::SetRootDisplayList") }
+            &ApiMsg::SetDisplayList(..) => { write!(f, "ApiMsg::SetDisplayList") }
             &ApiMsg::SetRootPipeline(..) => { write!(f, "ApiMsg::SetRootPipeline") }
             &ApiMsg::Scroll(..) => { write!(f, "ApiMsg::Scroll") }
             &ApiMsg::ScrollLayerWithId(..) => { write!(f, "ApiMsg::ScrollLayerWithId") }
             &ApiMsg::TickScrollingBounce => { write!(f, "ApiMsg::TickScrollingBounce") }
             &ApiMsg::TranslatePointToLayerSpace(..) => { write!(f, "ApiMsg::TranslatePointToLayerSpace") }
             &ApiMsg::GetScrollLayerState(..) => { write!(f, "ApiMsg::GetScrollLayerState") }
             &ApiMsg::RequestWebGLContext(..) => { write!(f, "ApiMsg::RequestWebGLContext") }
             &ApiMsg::ResizeWebGLContext(..) => { write!(f, "ApiMsg::ResizeWebGLContext") }
@@ -245,19 +244,19 @@ impl RenderApi {
 
     /// Updates a specific image.
     ///
     /// Currently doesn't support changing dimensions or format by updating.
     // TODO: Support changing dimensions (and format) during image update?
     pub fn update_image(&self,
                         key: ImageKey,
                         descriptor: ImageDescriptor,
-                        bytes: Vec<u8>,
+                        data: ImageData,
                         dirty_rect: Option<DeviceUintRect>) {
-        let msg = ApiMsg::UpdateImage(key, descriptor, bytes, dirty_rect);
+        let msg = ApiMsg::UpdateImage(key, descriptor, data, dirty_rect);
         self.api_sender.send(msg).unwrap();
     }
 
     /// Deletes the specific image.
     pub fn delete_image(&self, key: ImageKey) {
         let msg = ApiMsg::DeleteImage(key);
         self.api_sender.send(msg).unwrap();
     }
@@ -294,36 +293,39 @@ impl RenderApi {
     /// * `viewport_size`: The size of the viewport for this frame.
     /// * `display_list`: The root Display list used in this frame.
     /// * `auxiliary_lists`: Various items that the display lists and stacking contexts reference.
     /// * `preserve_frame_state`: If a previous frame exists which matches this pipeline
     ///                           id, this setting determines if frame state (such as scrolling
     ///                           position) should be preserved for this new display list.
     ///
     /// [notifier]: trait.RenderNotifier.html#tymethod.new_frame_ready
-    pub fn set_root_display_list(&self,
-                                 background_color: Option<ColorF>,
-                                 epoch: Epoch,
-                                 viewport_size: LayoutSize,
-                                 (pipeline_id, display_list, auxiliary_lists): (PipelineId, BuiltDisplayList, AuxiliaryLists),
-                                 preserve_frame_state: bool) {
-        let msg = ApiMsg::SetRootDisplayList(background_color,
+    pub fn set_display_list(&self,
+                            background_color: Option<ColorF>,
+                            epoch: Epoch,
+                            viewport_size: LayoutSize,
+                            (pipeline_id, display_list, auxiliary_lists): (PipelineId, BuiltDisplayList, AuxiliaryLists),
+                            preserve_frame_state: bool) {
+        let (dl_data, dl_desc) = display_list.into_data();
+        let (aux_data, aux_desc) = auxiliary_lists.into_data();
+        let msg = ApiMsg::SetDisplayList(background_color,
                                              epoch,
                                              pipeline_id,
                                              viewport_size,
-                                             display_list.descriptor().clone(),
-                                             *auxiliary_lists.descriptor(),
+                                             dl_desc,
+                                             aux_desc,
                                              preserve_frame_state);
         self.api_sender.send(msg).unwrap();
 
-        let mut payload = vec![];
-        payload.write_u32::<LittleEndian>(epoch.0).unwrap();
-        payload.extend_from_slice(display_list.data());
-        payload.extend_from_slice(auxiliary_lists.data());
-        self.payload_sender.send_vec(payload).unwrap();
+        self.payload_sender.send_payload(Payload {
+            epoch: epoch,
+            pipeline_id: pipeline_id,
+            display_list_data: dl_data,
+            auxiliary_lists_data: aux_data
+        }).unwrap();
     }
 
     /// Scrolls the scrolling layer under the `cursor`
     ///
     /// Webrender looks for the layer closest to the user
     /// which has `ScrollPolicy::Scrollable` set.
     pub fn scroll(&self, scroll_location: ScrollLocation, cursor: WorldPoint, phase: ScrollEventPhase) {
         let msg = ApiMsg::Scroll(scroll_location, cursor, phase);
@@ -484,16 +486,25 @@ impl ZoomFactor {
 }
 
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, Eq, Hash)]
 pub struct PropertyBindingId {
     namespace: u32,
     uid: u32,
 }
 
+impl PropertyBindingId {
+    pub fn new(value: u64) -> Self {
+        PropertyBindingId {
+            namespace: (value>>32) as u32,
+            uid: value as u32,
+        }
+    }
+}
+
 /// A unique key that is used for connecting animated property
 /// values to bindings in the display list.
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct PropertyBindingKey<T> {
     pub id: PropertyBindingId,
     _phantom: PhantomData<T>,
 }
@@ -503,16 +514,25 @@ impl<T: Copy> PropertyBindingKey<T> {
     pub fn with(&self, value: T) -> PropertyValue<T> {
         PropertyValue {
             key: *self,
             value: value,
         }
     }
 }
 
+impl<T> PropertyBindingKey<T> {
+    pub fn new(value: u64) -> Self {
+        PropertyBindingKey {
+            id: PropertyBindingId::new(value),
+            _phantom: PhantomData,
+        }
+    }
+}
+
 /// A binding property can either be a specific value
 /// (the normal, non-animated case) or point to a binding location
 /// to fetch the current value from.
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub enum PropertyBinding<T> {
     Value(T),
     Binding(PropertyBindingKey<T>),
 }
--- a/gfx/webrender_traits/src/channel.rs
+++ b/gfx/webrender_traits/src/channel.rs
@@ -1,15 +1,86 @@
 /* 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/. */
 
-// A helper to handle the interface difference between
-// IpcBytesSender and Sender<Vec<u8>>
-pub trait PayloadHelperMethods {
-    fn send_vec(&self, data: Vec<u8>) -> Result<(), Error>;
+use byteorder::{LittleEndian, WriteBytesExt, ReadBytesExt};
+use std::io::{Cursor, Read};
+use api::{Epoch, PipelineId};
+use std::mem;
+
+#[derive(Clone)]
+pub struct Payload {
+    /// An epoch used to get the proper payload for a pipeline id frame request.
+    ///
+    /// TODO(emilio): Is this still relevant? We send the messages for the same
+    /// pipeline in order, so we shouldn't need it. Seems like this was only
+    /// wallpapering (in most cases) the underlying problem in #991.
+    pub epoch: Epoch,
+    /// A pipeline id to key the payload with, along with the epoch.
+    pub pipeline_id: PipelineId,
+    pub display_list_data: Vec<u8>,
+    pub auxiliary_lists_data: Vec<u8>
+}
+
+impl Payload {
+    /// Convert the payload to a raw byte vector, in order for it to be
+    /// efficiently shared via shmem, for example.
+    ///
+    /// TODO(emilio, #1049): Consider moving the IPC boundary to the
+    /// constellation in Servo and remove this complexity from WR.
+    pub fn to_data(&self) -> Vec<u8> {
+        let mut data = Vec::with_capacity(mem::size_of::<u32>() +
+                                          2 * mem::size_of::<u32>() +
+                                          mem::size_of::<u64>() +
+                                          self.display_list_data.len() +
+                                          mem::size_of::<u64>() +
+                                          self.auxiliary_lists_data.len());
+        data.write_u32::<LittleEndian>(self.epoch.0).unwrap();
+        data.write_u32::<LittleEndian>(self.pipeline_id.0).unwrap();
+        data.write_u32::<LittleEndian>(self.pipeline_id.1).unwrap();
+        data.write_u64::<LittleEndian>(self.display_list_data.len() as u64).unwrap();
+        data.extend_from_slice(&self.display_list_data);
+        data.write_u64::<LittleEndian>(self.auxiliary_lists_data.len() as u64).unwrap();
+        data.extend_from_slice(&self.auxiliary_lists_data);
+        data
+    }
+
+    /// Deserializes the given payload from a raw byte vector.
+    pub fn from_data(data: Vec<u8>) -> Payload {
+        let mut payload_reader = Cursor::new(&data[..]);
+        let epoch = Epoch(payload_reader.read_u32::<LittleEndian>().unwrap());
+        let pipeline_id = PipelineId(payload_reader.read_u32::<LittleEndian>().unwrap(),
+                                     payload_reader.read_u32::<LittleEndian>().unwrap());
+
+        let dl_size = payload_reader.read_u64::<LittleEndian>().unwrap() as usize;
+        let mut built_display_list_data = vec![0; dl_size];
+        payload_reader.read_exact(&mut built_display_list_data[..]).unwrap();
+
+        let aux_size = payload_reader.read_u64::<LittleEndian>().unwrap() as usize;
+        let mut auxiliary_lists_data = vec![0; aux_size];
+        payload_reader.read_exact(&mut auxiliary_lists_data[..]).unwrap();
+
+        Payload {
+            epoch: epoch,
+            pipeline_id: pipeline_id,
+            display_list_data: built_display_list_data,
+            auxiliary_lists_data: auxiliary_lists_data,
+        }
+    }
+}
+
+
+/// A helper to handle the interface difference between IpcBytesSender and
+/// Sender<Vec<u8>>.
+pub trait PayloadSenderHelperMethods {
+    fn send_payload(&self, data: Payload) -> Result<(), Error>;
+}
+
+pub trait PayloadReceiverHelperMethods {
+    fn recv_payload(&self) -> Result<Payload, Error>;
 }
 
 #[cfg(not(feature = "ipc"))]
 include!("channel_mpsc.rs");
 
 #[cfg(feature = "ipc")]
 include!("channel_ipc.rs");
--- a/gfx/webrender_traits/src/channel_ipc.rs
+++ b/gfx/webrender_traits/src/channel_ipc.rs
@@ -1,32 +1,41 @@
 /* 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 serde::{Deserialize, Serialize};
-use std::io::Error;
+use std::io::{Error, ErrorKind};
+use std::io;
+use std::error;
 
 use ipc_channel::ipc::{self, IpcSender, IpcReceiver, IpcBytesSender, IpcBytesReceiver};
 
 ///
 /// Handles the channel implementation when IPC is enabled.
 ///
 
 pub type MsgSender<T> = IpcSender<T>;
 
 pub type MsgReceiver<T> = IpcReceiver<T>;
 
 pub type PayloadSender = IpcBytesSender;
 
 pub type PayloadReceiver = IpcBytesReceiver;
 
-impl PayloadHelperMethods for PayloadSender {
-    fn send_vec(&self, data: Vec<u8>) -> Result<(), Error> {
-        self.send(&data)
+impl PayloadSenderHelperMethods for PayloadSender {
+    fn send_payload(&self, data: Payload) -> Result<(), Error> {
+        self.send(&data.to_data())
+    }
+}
+
+impl PayloadReceiverHelperMethods for PayloadReceiver {
+    fn recv_payload(&self) -> Result<Payload, Error> {
+        self.recv().map(|data| Payload::from_data(data) )
+                   .map_err(|e| io::Error::new(ErrorKind::Other, error::Error::description(&e)))
     }
 }
 
 pub fn msg_channel<T: Serialize + Deserialize>() -> Result<(MsgSender<T>, MsgReceiver<T>), Error> {
     ipc::channel()
 }
 
 pub fn payload_channel() -> Result<(PayloadSender, PayloadReceiver), Error> {
--- a/gfx/webrender_traits/src/channel_mpsc.rs
+++ b/gfx/webrender_traits/src/channel_mpsc.rs
@@ -1,30 +1,35 @@
 /* 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 serde::{Deserialize, Serialize};
 use std::io::{Error, ErrorKind};
-
 use serde::{Deserializer, Serializer};
 
 use std::sync::mpsc;
 
 ///
 /// Handles the channel implementation when in process channels are enabled.
 ///
 
-pub type PayloadSender = MsgSender<Vec<u8>>;
+pub type PayloadSender = MsgSender<Payload>;
 
-pub type PayloadReceiver = MsgReceiver<Vec<u8>>;
+pub type PayloadReceiver = MsgReceiver<Payload>;
 
-impl PayloadHelperMethods for PayloadSender {
-    fn send_vec(&self, data: Vec<u8>) -> Result<(), Error> {
-        self.send(data)
+impl PayloadSenderHelperMethods for PayloadSender {
+    fn send_payload(&self, payload: Payload) -> Result<(), Error> {
+        self.send(payload)
+    }
+}
+
+impl PayloadReceiverHelperMethods for PayloadReceiver {
+    fn recv_payload(&self) -> Result<Payload, Error> {
+        self.recv()
     }
 }
 
 pub struct MsgReceiver<T> {
     rx: mpsc::Receiver<T>,
 }
 
 impl<T> MsgReceiver<T> {
--- a/gfx/webrender_traits/src/display_item.rs
+++ b/gfx/webrender_traits/src/display_item.rs
@@ -222,16 +222,17 @@ pub struct GradientStop {
 known_heap_size!(0, GradientStop);
 
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct RadialGradient {
     pub start_center: LayoutPoint,
     pub start_radius: f32,
     pub end_center: LayoutPoint,
     pub end_radius: f32,
+    pub ratio_xy: f32,
     pub stops: ItemRange,
     pub extend_mode: ExtendMode,
 }
 
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct RadialGradientDisplayItem {
     pub gradient: RadialGradient,
 }
@@ -503,31 +504,31 @@ impl ComplexClipRegion {
         } else {
             None
         }
     }
 }
 
 #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
 pub enum ScrollLayerId {
-    Clip(usize, PipelineId),
-    ClipExternalId(usize, PipelineId),
-    ReferenceFrame(usize, PipelineId),
+    Clip(u64, PipelineId),
+    ClipExternalId(u64, PipelineId),
+    ReferenceFrame(u64, PipelineId),
 }
 
 impl ScrollLayerId {
     pub fn root_scroll_layer(pipeline_id: PipelineId) -> ScrollLayerId {
         ScrollLayerId::Clip(0, pipeline_id)
     }
 
     pub fn root_reference_frame(pipeline_id: PipelineId) -> ScrollLayerId {
         ScrollLayerId::ReferenceFrame(0, pipeline_id)
     }
 
-    pub fn new(id: usize, pipeline_id: PipelineId) -> ScrollLayerId {
+    pub fn new(id: u64, pipeline_id: PipelineId) -> ScrollLayerId {
         ScrollLayerId::ClipExternalId(id, pipeline_id)
     }
 
     pub fn pipeline_id(&self) -> PipelineId {
         match *self {
             ScrollLayerId::Clip(_, pipeline_id) => pipeline_id,
             ScrollLayerId::ClipExternalId(_, pipeline_id) => pipeline_id,
             ScrollLayerId::ReferenceFrame(_, pipeline_id) => pipeline_id,
@@ -536,15 +537,15 @@ impl ScrollLayerId {
 
     pub fn is_reference_frame(&self) -> bool {
         match *self {
             ScrollLayerId::ReferenceFrame(..) => true,
             _ => false,
         }
     }
 
-    pub fn external_id(&self) -> Option<usize> {
+    pub fn external_id(&self) -> Option<u64> {
         match *self {
             ScrollLayerId::ClipExternalId(id, _) => Some(id),
             _ => None,
         }
     }
 }
--- a/gfx/webrender_traits/src/display_list.rs
+++ b/gfx/webrender_traits/src/display_list.rs
@@ -80,25 +80,32 @@ impl BuiltDisplayList {
         &self.descriptor
     }
 
     pub fn all_display_items<'a>(&'a self) -> &'a [DisplayItem] {
         unsafe {
             convert_blob_to_pod(&self.data[0..self.descriptor.display_list_items_size])
         }
     }
+
+    pub fn into_display_items(self) -> Vec<DisplayItem> {
+        unsafe {
+            convert_vec_blob_to_pod(self.data)
+        }
+    }
+
 }
 
 #[derive(Clone)]
 pub struct DisplayListBuilder {
     pub list: Vec<DisplayItem>,
     auxiliary_lists_builder: AuxiliaryListsBuilder,
     pub pipeline_id: PipelineId,
     clip_stack: Vec<ScrollLayerId>,
-    next_scroll_layer_id: usize,
+    next_scroll_layer_id: u64,
 }
 
 impl DisplayListBuilder {
     pub fn new(pipeline_id: PipelineId) -> DisplayListBuilder {
         DisplayListBuilder {
             list: Vec::new(),
             auxiliary_lists_builder: AuxiliaryListsBuilder::new(),
             pipeline_id: pipeline_id,
@@ -211,41 +218,144 @@ impl DisplayListBuilder {
                 blur_radius: blur_radius,
                 glyph_options: glyph_options,
             });
 
             self.push_item(item, rect, clip);
         }
     }
 
+    // Gradients can be defined with stops outside the range of [0, 1]
+    // when this happens the gradient needs to be normalized by adjusting
+    // the gradient stops and gradient line into an equivalent gradient
+    // with stops in the range [0, 1]. this is done by moving the beginning
+    // of the gradient line to where stop[0] and the end of the gradient line
+    // to stop[n-1]. this function adjusts the stops in place, and returns
+    // the amount to adjust the gradient line start and stop
+    fn normalize_stops(stops: &mut Vec<GradientStop>,
+                       extend_mode: ExtendMode) -> (f32, f32) {
+        assert!(stops.len() >= 2);
+
+        let first = *stops.first().unwrap();
+        let last = *stops.last().unwrap();
+
+        assert!(first.offset <= last.offset);
+
+        let stops_origin = first.offset;
+        let stops_delta = last.offset - first.offset;
+
+        if stops_delta > 0.000001 {
+            for stop in stops {
+                stop.offset = (stop.offset - stops_origin) / stops_delta;
+            }
+
+            (first.offset, last.offset)
+        } else {
+            // We have a degenerate gradient and can't accurately transform the stops
+            // what happens here depends on the repeat behavior, but in any case
+            // we reconstruct the gradient stops to something simpler and equivalent
+            stops.clear();
+
+            match extend_mode {
+                ExtendMode::Clamp => {
+                    // This gradient is two colors split at the offset of the stops,
+                    // so create a gradient with two colors split at 0.5 and adjust
+                    // the gradient line so 0.5 is at the offset of the stops
+                    stops.push(GradientStop {
+                        color: first.color,
+                        offset: 0.0,
+                    });
+                    stops.push(GradientStop {
+                        color: first.color,
+                        offset: 0.5,
+                    });
+                    stops.push(GradientStop {
+                        color: last.color,
+                        offset: 0.5,
+                    });
+                    stops.push(GradientStop {
+                        color: last.color,
+                        offset: 1.0,
+                    });
+
+                    let offset = last.offset;
+
+                    (offset - 0.5, offset + 0.5)
+                }
+                ExtendMode::Repeat => {
+                    // A repeating gradient with stops that are all in the same
+                    // position should just display the last color. I believe the
+                    // spec says that it should be the average color of the gradient,
+                    // but this matches what Gecko and Blink does
+                    stops.push(GradientStop {
+                        color: last.color,
+                        offset: 0.0,
+                    });
+                    stops.push(GradientStop {
+                        color: last.color,
+                        offset: 1.0,
+                    });
+
+                    (0.0, 1.0)
+                }
+            }
+        }
+    }
+
     pub fn create_gradient(&mut self,
                            start_point: LayoutPoint,
                            end_point: LayoutPoint,
-                           stops: Vec<GradientStop>,
+                           mut stops: Vec<GradientStop>,
                            extend_mode: ExtendMode) -> Gradient {
+        let (start_offset,
+             end_offset) = DisplayListBuilder::normalize_stops(&mut stops, extend_mode);
+
+        let start_to_end = end_point - start_point;
+
         Gradient {
-            start_point: start_point,
-            end_point: end_point,
+            start_point: start_point + start_to_end * start_offset,
+            end_point: start_point + start_to_end * end_offset,
             stops: self.auxiliary_lists_builder.add_gradient_stops(&stops),
             extend_mode: extend_mode,
         }
     }
 
     pub fn create_radial_gradient(&mut self,
-                                  start_center: LayoutPoint,
-                                  start_radius: f32,
-                                  end_center: LayoutPoint,
-                                  end_radius: f32,
-                                  stops: Vec<GradientStop>,
+                                  center: LayoutPoint,
+                                  radius: LayoutSize,
+                                  mut stops: Vec<GradientStop>,
                                   extend_mode: ExtendMode) -> RadialGradient {
+        let (start_offset,
+             end_offset) = DisplayListBuilder::normalize_stops(&mut stops, extend_mode);
+
+        RadialGradient {
+            start_center: center,
+            start_radius: radius.width * start_offset,
+            end_center: center,
+            end_radius: radius.width * end_offset,
+            ratio_xy: radius.width / radius.height,
+            stops: self.auxiliary_lists_builder.add_gradient_stops(&stops),
+            extend_mode: extend_mode,
+        }
+    }
+
+    pub fn create_complex_radial_gradient(&mut self,
+                                          start_center: LayoutPoint,
+                                          start_radius: f32,
+                                          end_center: LayoutPoint,
+                                          end_radius: f32,
+                                          ratio_xy: f32,
+                                          stops: Vec<GradientStop>,
+                                          extend_mode: ExtendMode) -> RadialGradient {
         RadialGradient {
             start_center: start_center,
             start_radius: start_radius,
             end_center: end_center,
             end_radius: end_radius,
+            ratio_xy: ratio_xy,
             stops: self.auxiliary_lists_builder.add_gradient_stops(&stops),
             extend_mode: extend_mode,
         }
     }
 
     pub fn push_border(&mut self,
                        rect: LayoutRect,
                        clip: ClipRegion,
@@ -280,40 +390,30 @@ impl DisplayListBuilder {
         });
 
         self.push_item(item, rect, clip);
     }
 
     pub fn push_gradient(&mut self,
                          rect: LayoutRect,
                          clip: ClipRegion,
-                         start_point: LayoutPoint,
-                         end_point: LayoutPoint,
-                         stops: Vec<GradientStop>,
-                         extend_mode: ExtendMode) {
+                         gradient: Gradient) {
         let item = SpecificDisplayItem::Gradient(GradientDisplayItem {
-            gradient: self.create_gradient(start_point, end_point, stops, extend_mode),
+            gradient: gradient,
         });
 
         self.push_item(item, rect, clip);
     }
 
     pub fn push_radial_gradient(&mut self,
                                 rect: LayoutRect,
                                 clip: ClipRegion,
-                                start_center: LayoutPoint,
-                                start_radius: f32,
-                                end_center: LayoutPoint,
-                                end_radius: f32,
-                                stops: Vec<GradientStop>,
-                                extend_mode: ExtendMode) {
+                                gradient: RadialGradient) {
         let item = SpecificDisplayItem::RadialGradient(RadialGradientDisplayItem {
-            gradient: self.create_radial_gradient(start_center, start_radius,
-                                                  end_center, end_radius,
-                                                  stops, extend_mode),
+            gradient: gradient,
         });
 
         self.push_item(item, rect, clip);
     }
 
     pub fn push_stacking_context(&mut self,
                                  scroll_policy: ScrollPolicy,
                                  bounds: LayoutRect,
@@ -628,8 +728,14 @@ unsafe fn convert_vec_pod_to_blob<T>(mut
     mem::forget(data);
     v
 }
 
 unsafe fn convert_blob_to_pod<T>(blob: &[u8]) -> &[T] where T: Copy + 'static {
     slice::from_raw_parts(blob.as_ptr() as *const T, blob.len() / mem::size_of::<T>())
 }
 
+// this variant of the above lets us convert without needing to make a copy
+unsafe fn convert_vec_blob_to_pod<T>(mut data: Vec<u8>) -> Vec<T> where T: Copy + 'static {
+    let v = Vec::from_raw_parts(data.as_mut_ptr() as *mut T, data.len() / mem::size_of::<T>(), data.capacity() / mem::size_of::<T>());
+    mem::forget(data);
+    v
+}
--- a/gfx/webrender_traits/src/image.rs
+++ b/gfx/webrender_traits/src/image.rs
@@ -1,13 +1,14 @@
 /* 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::sync::Arc;
+use DeviceUintRect;
 
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
 pub struct ImageKey(pub u32, pub u32);
 
 impl ImageKey {
     pub fn new(key0: u32, key1: u32) -> ImageKey {
         ImageKey(key0, key1)
@@ -64,22 +65,34 @@ impl ImageDescriptor {
         }
     }
 
     pub fn compute_stride(&self) -> u32 {
         self.stride.unwrap_or(self.width * self.format.bytes_per_pixel().unwrap())
     }
 }
 
+#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
+pub enum ExternalImageType {
+    Texture2DHandle,    // gl TEXTURE_2D handle
+    TextureRectHandle,  // gl TEXTURE_RECT handle
+    ExternalBuffer,
+}
+
+#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
+pub struct ExternalImageData {
+    pub id: ExternalImageId,
+    pub image_type: ExternalImageType,
+}
+
 #[derive(Clone, Serialize, Deserialize)]
 pub enum ImageData {
     Raw(Arc<Vec<u8>>),
     Blob(Arc<BlobImageData>),
-    ExternalHandle(ExternalImageId),
-    ExternalBuffer(ExternalImageId),
+    External(ExternalImageData),
 }
 
 impl ImageData {
     pub fn new(bytes: Vec<u8>) -> ImageData {
         ImageData::Raw(Arc::new(bytes))
     }
 
     pub fn new_shared(bytes: Arc<Vec<u8>>) -> ImageData {
@@ -94,17 +107,18 @@ impl ImageData {
         ImageData::Blob(commands)
     }
 }
 
 pub trait BlobImageRenderer: Send {
     fn request_blob_image(&mut self,
                             key: ImageKey,
                             data: Arc<BlobImageData>,
-                            descriptor: &BlobImageDescriptor);
+                            descriptor: &BlobImageDescriptor,
+                            dirty_rect: Option<DeviceUintRect>);
     fn resolve_blob_image(&mut self, key: ImageKey) -> BlobImageResult;
 }
 
 pub type BlobImageData = Vec<u8>;
 
 pub type BlobImageResult = Result<RasterizedBlobImage, BlobImageError>;
 
 #[repr(C)]
--- a/image/decoders/nsPNGDecoder.cpp
+++ b/image/decoders/nsPNGDecoder.cpp
@@ -938,33 +938,59 @@ nsPNGDecoder::DoYield(png_structp aPNGSt
 
   MOZ_ASSERT(pendingBytes < mLastChunkLength);
   size_t consumedBytes = mLastChunkLength - min(pendingBytes, mLastChunkLength);
 
   mNextTransition =
     Transition::ContinueUnbufferedAfterYield(State::PNG_DATA, consumedBytes);
 }
 
+nsresult
+nsPNGDecoder::FinishInternal()
+{
+  // We shouldn't be called in error cases.
+  MOZ_ASSERT(!HasError(), "Can't call FinishInternal on error!");
+
+  if (IsMetadataDecode()) {
+    return NS_OK;
+  }
+
+  int32_t loop_count = 0;
+#ifdef PNG_APNG_SUPPORTED
+  if (png_get_valid(mPNG, mInfo, PNG_INFO_acTL)) {
+    int32_t num_plays = png_get_num_plays(mPNG, mInfo);
+    loop_count = num_plays - 1;
+  }
+#endif
+
+  if (InFrame()) {
+    EndImageFrame();
+  }
+  PostDecodeDone(loop_count);
+
+  return NS_OK;
+}
+
+
 #ifdef PNG_APNG_SUPPORTED
 // got the header of a new frame that's coming
 void
 nsPNGDecoder::frame_info_callback(png_structp png_ptr, png_uint_32 frame_num)
 {
   nsPNGDecoder* decoder =
                static_cast<nsPNGDecoder*>(png_get_progressive_ptr(png_ptr));
 
   // old frame is done
   decoder->EndImageFrame();
 
   const bool previousFrameWasHidden = decoder->mFrameIsHidden;
 
   if (!previousFrameWasHidden && decoder->IsFirstFrameDecode()) {
     // We're about to get a second non-hidden frame, but we only want the first.
     // Stop decoding now. (And avoid allocating the unnecessary buffers below.)
-    decoder->PostDecodeDone();
     return decoder->DoTerminate(png_ptr, TerminalState::SUCCESS);
   }
 
   // Only the first frame can be hidden, so unhide unconditionally here.
   decoder->mFrameIsHidden = false;
 
   // Save the information necessary to create the frame; we'll actually create
   // it when we return from the yield.
@@ -1020,27 +1046,16 @@ nsPNGDecoder::end_callback(png_structp p
    */
 
   nsPNGDecoder* decoder =
                static_cast<nsPNGDecoder*>(png_get_progressive_ptr(png_ptr));
 
   // We shouldn't get here if we've hit an error
   MOZ_ASSERT(!decoder->HasError(), "Finishing up PNG but hit error!");
 
-  int32_t loop_count = 0;
-#ifdef PNG_APNG_SUPPORTED
-  if (png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL)) {
-    int32_t num_plays = png_get_num_plays(png_ptr, info_ptr);
-    loop_count = num_plays - 1;
-  }
-#endif
-
-  // Send final notifications.
-  decoder->EndImageFrame();
-  decoder->PostDecodeDone(loop_count);
   return decoder->DoTerminate(png_ptr, TerminalState::SUCCESS);
 }
 
 
 void
 nsPNGDecoder::error_callback(png_structp png_ptr, png_const_charp error_msg)
 {
   MOZ_LOG(sPNGLog, LogLevel::Error, ("libpng error: %s\n", error_msg));
--- a/image/decoders/nsPNGDecoder.h
+++ b/image/decoders/nsPNGDecoder.h
@@ -22,16 +22,17 @@ class nsPNGDecoder : public Decoder
 public:
   virtual ~nsPNGDecoder();
 
   /// @return true if this PNG is a valid ICO resource.
   bool IsValidICO() const;
 
 protected:
   nsresult InitInternal() override;
+  nsresult FinishInternal() override;
   LexerResult DoDecode(SourceBufferIterator& aIterator,
                        IResumable* aOnResume) override;
 
   Maybe<Telemetry::HistogramID> SpeedHistogram() const override;
 
 private:
   friend class DecoderFactory;
 
--- a/image/test/mochitest/test_discardAnimatedImage.html
+++ b/image/test/mochitest/test_discardAnimatedImage.html
@@ -1,29 +1,30 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=686905
 -->
 <head>
   <title>Test that animated images can be discarded</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/WindowSnapshot.js"></script>
   <script type="text/javascript" src="imgutils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=686905">Mozilla Bug 686905</a>
 <p id="display"></p>
 <div id="content">
   <div id="container">
     <canvas id="canvas" width="100" height="100"></canvas>
     <img id="infinitepng" src="infinite-apng.png">
     <img id="infinitegif" src="animated1.gif">
     <img id="finitepng" src="restore-previous.png">
-    <img id="finitegif" src="restore-previous.gif">
+    <img id="finitegif" src="animated-gif.gif">
   </div>
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 /** Test for Bug 686905. **/
 SimpleTest.waitForExplicitFinish();
 
@@ -31,64 +32,126 @@ var gNumDiscards = 0;
 
 window.onload = function() {
   // Enable discarding for the test.
   SpecialPowers.pushPrefEnv({
     'set':[['image.mem.discardable',true]]
   }, runTest);
 }
 
-var gImgs = ['finitepng', 'finitegif', 'infinitepng', 'infinitegif'];
+var gImgs = ['infinitepng', 'infinitegif', 'finitepng', 'finitegif'];
+// If we are currently counting frame updates.
+var gCountingFrameUpdates = false;
+// The number of frame update notifications for the images in gImgs that happen
+// after discarding. (The last two images are finite looping so we don't expect
+// them to get incremented but it's possible if they don't finish their
+// animation before we discard them.)
+var gNumFrameUpdates = [0, 0, 0, 0];
+// The last snapshot of the image. Used to check that the image actually changes.
+var gLastSnapShot = [null, null, null, null];
+// Number of observed changes in the snapshot.
+var gNumSnapShotChanges = [0, 0, 0, 0];
+
+// 2 would probably be a good enough test, we arbitrarily choose 4.
+var kNumFrameUpdatesToExpect = 4;
 
 function runTest() {
   var animatedDiscardable =
     SpecialPowers.getBoolPref('image.mem.animated.discardable');
   if (!animatedDiscardable) {
-    ok(true, "discarding of animated images is disabled, nothing to test")
+    ok(true, "discarding of animated images is disabled, nothing to test");
     SimpleTest.finish();
     return;
   }
 
+  setTimeout(step2, 0);
+}
+
+function step2() {
   // Draw the images to canvas to force them to be decoded.
   for (var i = 0; i < gImgs.length; i++) {
     drawCanvas(document.getElementById(gImgs[i]));
   }
 
   for (var i = 0; i < gImgs.length; i++) {
-    addCallback(document.getElementById(gImgs[i]));
+    addCallbacks(document.getElementById(gImgs[i]), i);
   }
 
+  setTimeout(step3, 0);
+}
+
+function step3() {
   document.getElementById("container").style.display = "none";
+  document.documentElement.offsetLeft; // force that style to take effect
 
   for (var i = 0; i < gImgs.length; i++) {
     requestDiscard(document.getElementById(gImgs[i]));
   }
+
+  // the discard observers will call step4
 }
 
-function step2() {
+function step4() {
+  gCountingFrameUpdates = true;
   document.getElementById("container").style.display = "";
 
   // Draw the images to canvas to force them to be decoded again.
   for (var i = 0; i < gImgs.length; i++) {
     drawCanvas(document.getElementById(gImgs[i]));
   }
-
-  SimpleTest.finish();
 }
 
-function addCallback(anImage) {
+function checkIfFinished() {
+  if ((gNumFrameUpdates[0] >= kNumFrameUpdatesToExpect) &&
+      (gNumFrameUpdates[1] >= kNumFrameUpdatesToExpect) &&
+      (gNumSnapShotChanges[0] >= kNumFrameUpdatesToExpect) &&
+      (gNumSnapShotChanges[1] >= kNumFrameUpdatesToExpect)) {
+    ok(true, "got expected frame updates");
+    SimpleTest.finish();
+  }
+}
+
+// arrayIndex is the index into the arrays gNumFrameUpdates and gNumDecodes
+// to increment when a frame update notification is received.
+function addCallbacks(anImage, arrayIndex) {
   var observer = new ImageDecoderObserverStub();
   observer.discard = function () {
-    imgLoadingContent.removeObserver(scriptedObserver);
     gNumDiscards++;
     ok(true, "got image discard");
+    if (arrayIndex >= 2) {
+      // The last two images are finite, so we don't expect any frame updates,
+      // this image is done the test, so remove the observer.
+      imgLoadingContent.removeObserver(scriptedObserver);
+    }
     if (gNumDiscards == gImgs.length) {
-      step2();
+      step4();
+    }
+  };
+  observer.frameUpdate = function () {
+    if (!gCountingFrameUpdates) {
+      return;
     }
-  }
+    gNumFrameUpdates[arrayIndex]++;
+
+    var r = document.getElementById(gImgs[arrayIndex]).getBoundingClientRect();
+    var snapshot = snapshotRect(window, r, "rgba(0,0,0,0)");
+    if (gLastSnapShot[arrayIndex] != null) {
+      if (snapshot.toDataURL() != gLastSnapShot[arrayIndex].toDataURL()) {
+        gNumSnapShotChanges[arrayIndex]++;
+      }
+    }
+    gLastSnapShot[arrayIndex] = snapshot;
+
+    if (gNumFrameUpdates[arrayIndex] >= kNumFrameUpdatesToExpect &&
+        gNumSnapShotChanges[arrayIndex] >= kNumFrameUpdatesToExpect) {
+      imgLoadingContent.removeObserver(scriptedObserver);
+    }
+    ok(true, "got frame update");
+    checkIfFinished();
+  };
   observer = SpecialPowers.wrapCallbackObject(observer);
 
   var scriptedObserver = SpecialPowers.Cc["@mozilla.org/image/tools;1"]
                            .getService(SpecialPowers.Ci.imgITools)
                            .createScriptedObserver(observer);
 
   var imgLoadingContent =
     SpecialPowers.wrap(anImage)
@@ -99,21 +162,25 @@ function addCallback(anImage) {
 function requestDiscard(anImage) {
   var request = SpecialPowers.wrap(anImage)
       .QueryInterface(SpecialPowers.Ci.nsIImageLoadingContent)
       .getRequest(SpecialPowers.Ci.nsIImageLoadingContent.CURRENT_REQUEST);
   setTimeout(() => request.requestDiscard(), 0);
 }
 
 function drawCanvas(anImage) {
-  dump(anImage + "\n");
   var canvas = document.getElementById('canvas');
   var context = canvas.getContext('2d');
 
+  context.clearRect(0,0,100,100);
+  var cleared = canvas.toDataURL();
+
   context.drawImage(anImage, 0, 0);
   ok(true, "we got through the drawImage call without an exception being thrown");
+
+  ok(cleared != canvas.toDataURL(), "drawImage drew something");
 }
 
 </script>
 </pre>
 </body>
 </html>
 
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -1771,16 +1771,34 @@ GetNameIRGenerator::tryAttachGlobalNameG
         ObjOperandId holderId = writer.loadObject(holder);
         writer.guardShape(holderId, holder->lastProperty());
     }
 
     EmitCallGetterResultNoGuards(writer, &globalLexical->global(), holder, shape, globalId);
     return true;
 }
 
+static bool
+NeedEnvironmentShapeGuard(JSObject* envObj)
+{
+    if (!envObj->is<CallObject>())
+        return true;
+
+    // We can skip a guard on the call object if the script's bindings are
+    // guaranteed to be immutable (and thus cannot introduce shadowing
+    // variables). The function might have been relazified under rare
+    // conditions. In that case, we pessimistically create the guard.
+    CallObject* callObj = &envObj->as<CallObject>();
+    JSFunction* fun = &callObj->callee();
+    if (!fun->hasScript() || fun->nonLazyScript()->funHasExtensibleScope())
+        return true;
+
+    return false;
+}
+
 bool
 GetNameIRGenerator::tryAttachEnvironmentName(ObjOperandId objId, HandleId id)
 {
     if (IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope())
         return false;
 
     RootedObject env(cx_, env_);
     RootedShape shape(cx_);
@@ -1813,22 +1831,22 @@ GetNameIRGenerator::tryAttachEnvironment
     if (!IsCacheableGetPropReadSlotForIonOrCacheIR(holder, holder, PropertyResult(shape)))
         return false;
     if (holder->getSlot(shape->slot()).isMagic())
         return false;
 
     ObjOperandId lastObjId = objId;
     env = env_;
     while (env) {
-        if (env == holder) {
-            writer.guardShape(lastObjId, holder->maybeShape());
+        if (NeedEnvironmentShapeGuard(env))
+            writer.guardShape(lastObjId, env->maybeShape());
+
+        if (env == holder)
             break;
-        }
-
-        writer.guardShape(lastObjId, env->maybeShape());
+
         lastObjId = writer.loadEnclosingEnvironment(lastObjId);
         env = env->enclosingEnvironment();
     }
 
     if (holder->isFixedSlot(shape->slot())) {
         writer.loadEnvironmentFixedSlotResult(lastObjId, NativeObject::getFixedSlotOffset(shape->slot()));
     } else {
         size_t dynamicSlotOffset = holder->dynamicSlotIndex(shape->slot()) * sizeof(Value);
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -226,16 +226,21 @@ typedef bool (*IonGetPropertyICFn)(JSCon
 static const VMFunction IonGetPropertyICInfo =
     FunctionInfo<IonGetPropertyICFn>(IonGetPropertyIC::update, "IonGetPropertyIC::update");
 
 typedef bool (*IonSetPropertyICFn)(JSContext*, HandleScript, IonSetPropertyIC*, HandleObject,
                                    HandleValue, HandleValue);
 static const VMFunction IonSetPropertyICInfo =
     FunctionInfo<IonSetPropertyICFn>(IonSetPropertyIC::update, "IonSetPropertyIC::update");
 
+typedef bool (*IonGetNameICFn)(JSContext*, HandleScript, IonGetNameIC*, HandleObject,
+                               MutableHandleValue);
+static const VMFunction IonGetNameICInfo =
+    FunctionInfo<IonGetNameICFn>(IonGetNameIC::update, "IonGetNameIC::update");
+
 void
 CodeGenerator::visitOutOfLineICFallback(OutOfLineICFallback* ool)
 {
     LInstruction* lir = ool->lir();
     size_t cacheIndex = ool->cacheIndex();
     size_t cacheInfoIndex = ool->cacheInfoIndex();
 
     DataPtr<IonIC> ic(this, cacheIndex);
@@ -277,17 +282,33 @@ CodeGenerator::visitOutOfLineICFallback(
 
         callVM(IonSetPropertyICInfo, lir);
 
         restoreLive(lir);
 
         masm.jump(ool->rejoin());
         return;
       }
-      case CacheKind::GetName:
+      case CacheKind::GetName: {
+        IonGetNameIC* getNameIC = ic->asGetNameIC();
+
+        saveLive(lir);
+
+        pushArg(getNameIC->environment());
+        icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
+        pushArg(ImmGCPtr(gen->info().script()));
+
+        callVM(IonGetNameICInfo, lir);
+
+        StoreValueTo(getNameIC->output()).generate(this);
+        restoreLiveIgnore(lir, StoreValueTo(getNameIC->output()).clobbered());
+
+        masm.jump(ool->rejoin());
+        return;
+      }
       case CacheKind::In:
         MOZ_CRASH("Baseline-specific for now");
     }
     MOZ_CRASH();
 }
 
 StringObject*
 MNewStringObject::templateObj() const
@@ -10267,41 +10288,21 @@ CodeGenerator::visitStoreFixedSlotT(LSto
     }
 }
 
 void
 CodeGenerator::visitGetNameCache(LGetNameCache* ins)
 {
     LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
     Register envChain = ToRegister(ins->envObj());
-    TypedOrValueRegister output(GetValueOutput(ins));
-    bool isTypeOf = ins->mir()->accessKind() != MGetNameCache::NAME;
-
-    NameIC cache(liveRegs, isTypeOf, envChain, ins->mir()->name(), output);
-    cache.setProfilerLeavePC(ins->mir()->profilerLeavePc());
-    addCache(ins, allocateCache(cache));
-}
-
-typedef bool (*NameICFn)(JSContext*, HandleScript, size_t, HandleObject, MutableHandleValue);
-const VMFunction NameIC::UpdateInfo = FunctionInfo<NameICFn>(NameIC::update, "NameIC::update");
-
-void
-CodeGenerator::visitNameIC(OutOfLineUpdateCache* ool, DataPtr<NameIC>& ic)
-{
-    LInstruction* lir = ool->lir();
-    saveLive(lir);
-
-    pushArg(ic->environmentChainReg());
-    pushArg(Imm32(ool->getCacheIndex()));
-    pushArg(ImmGCPtr(gen->info().script()));
-    callVM(NameIC::UpdateInfo, lir);
-    StoreValueTo(ic->outputReg()).generate(this);
-    restoreLiveIgnore(lir, StoreValueTo(ic->outputReg()).clobbered());
-
-    masm.jump(ool->rejoin());
+    ValueOperand output(GetValueOutput(ins));
+    Register temp = ToRegister(ins->temp());
+
+    IonGetNameIC ic(liveRegs, envChain, output, temp);
+    addIC(ins, allocateIC(ic));
 }
 
 void
 CodeGenerator::addGetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs,
                                    TypedOrValueRegister value, const ConstantOrRegister& id,
                                    TypedOrValueRegister output, Register maybeTemp,
                                    bool monitoredResult, bool allowDoubleResult,
                                    jsbytecode* profilerLeavePc)
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -419,17 +419,16 @@ class CodeGenerator final : public CodeG
     void visitGetPropertyCacheV(LGetPropertyCacheV* ins);
     void visitGetPropertyCacheT(LGetPropertyCacheT* ins);
     void visitBindNameCache(LBindNameCache* ins);
     void visitCallSetProperty(LInstruction* ins);
     void visitSetPropertyCache(LSetPropertyCache* ins);
     void visitGetNameCache(LGetNameCache* ins);
 
     void visitBindNameIC(OutOfLineUpdateCache* ool, DataPtr<BindNameIC>& ic);
-    void visitNameIC(OutOfLineUpdateCache* ool, DataPtr<NameIC>& ic);
 
     void visitAssertRangeI(LAssertRangeI* ins);
     void visitAssertRangeD(LAssertRangeD* ins);
     void visitAssertRangeF(LAssertRangeF* ins);
     void visitAssertRangeV(LAssertRangeV* ins);
 
     void visitAssertResultV(LAssertResultV* ins);
     void visitAssertResultT(LAssertResultT* ins);
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -7442,22 +7442,17 @@ AbortReasonOr<Ok>
 IonBuilder::jsop_getname(PropertyName* name)
 {
     MDefinition* object;
     if (IsGlobalOp(JSOp(*pc)) && !script()->hasNonSyntacticScope())
         object = constant(ObjectValue(script()->global().lexicalEnvironment()));
     else
         object = current->environmentChain();
 
-    MGetNameCache* ins;
-    if (JSOp(*GetNextPc(pc)) == JSOP_TYPEOF)
-        ins = MGetNameCache::New(alloc(), object, name, MGetNameCache::NAMETYPEOF);
-    else
-        ins = MGetNameCache::New(alloc(), object, name, MGetNameCache::NAME);
-
+    MGetNameCache* ins = MGetNameCache::New(alloc(), object);
     current->add(ins);
     current->push(ins);
 
     MOZ_TRY(resumeAfter(ins));
 
     TemporaryTypeSet* types = bytecodeTypes(pc);
     return pushTypeBarrier(ins, types, BarrierKind::TypeSet);
 }
--- a/js/src/jit/IonCacheIRCompiler.cpp
+++ b/js/src/jit/IonCacheIRCompiler.cpp
@@ -405,17 +405,30 @@ IonCacheIRCompiler::init()
             allocator.initInputLocation(1, ic->rhs());
         } else {
             MOZ_ASSERT(numInputs == 3);
             allocator.initInputLocation(1, ic->id());
             allocator.initInputLocation(2, ic->rhs());
         }
         break;
       }
-      case CacheKind::GetName:
+      case CacheKind::GetName: {
+        IonGetNameIC* ic = ic_->asGetNameIC();
+        ValueOperand output = ic->output();
+
+        available.add(output);
+        available.add(ic->temp());
+
+        liveRegs_.emplace(ic->liveRegs());
+        outputUnchecked_.emplace(output);
+
+        MOZ_ASSERT(numInputs == 1);
+        allocator.initInputLocation(0, ic->environment(), JSVAL_TYPE_OBJECT);
+        break;
+      }
       case CacheKind::In:
         MOZ_CRASH("Invalid cache");
     }
 
     if (liveRegs_)
         liveFloatRegs_ = LiveFloatRegisterSet(liveRegs_->fpus());
 
     allocator.initAvailableRegs(available);
@@ -1015,23 +1028,54 @@ bool
 IonCacheIRCompiler::emitLoadFrameArgumentResult()
 {
     MOZ_CRASH("Baseline-specific op");
 }
 
 bool
 IonCacheIRCompiler::emitLoadEnvironmentFixedSlotResult()
 {
-    MOZ_CRASH("Baseline-specific op");
+    AutoOutputRegister output(*this);
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    int32_t offset = int32StubField(reader.stubOffset());
+
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    // Check for uninitialized lexicals.
+    Address slot(obj, offset);
+    masm.branchTestMagic(Assembler::Equal, slot, failure->label());
+
+    // Load the value.
+    masm.loadTypedOrValue(slot, output);
+    return true;
 }
 
 bool
 IonCacheIRCompiler::emitLoadEnvironmentDynamicSlotResult()
 {
-    MOZ_CRASH("Baseline-specific op");
+    AutoOutputRegister output(*this);
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    int32_t offset = int32StubField(reader.stubOffset());
+    AutoScratchRegisterMaybeOutput scratch(allocator, masm, output);
+
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), scratch);
+
+    // Check for uninitialized lexicals.
+    Address slot(scratch, offset);
+    masm.branchTestMagic(Assembler::Equal, slot, failure->label());
+
+    // Load the value.
+    masm.loadTypedOrValue(slot, output);
+    return true;
 }
 
 static bool
 GroupHasPropertyTypes(ObjectGroup* group, jsid* id, Value* v)
 {
     if (group->unknownProperties())
         return true;
     HeapTypeSet* propTypes = group->maybeGetProperty(*id);
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -401,59 +401,16 @@ jit::GetReturnAddressToIonCode(JSContext
     void* returnAddr = iter.returnAddress();
 #ifdef DEBUG
     ++iter;
     MOZ_ASSERT(iter.isIonJS());
 #endif
     return returnAddr;
 }
 
-static void
-GeneratePrototypeGuards(JSContext* cx, IonScript* ion, MacroAssembler& masm, JSObject* obj,
-                        JSObject* holder, Register objectReg, Register scratchReg,
-                        Label* failures)
-{
-    /*
-     * The guards here protect against the effects of JSObject::swap(). If the prototype chain
-     * is directly altered, then TI will toss the jitcode, so we don't have to worry about
-     * it, and any other change to the holder, or adding a shadowing property will result
-     * in reshaping the holder, and thus the failure of the shape guard.
-     */
-    MOZ_ASSERT(obj != holder);
-
-    if (obj->hasUncacheableProto()) {
-        // Note: objectReg and scratchReg may be the same register, so we cannot
-        // use objectReg in the rest of this function.
-        masm.loadPtr(Address(objectReg, JSObject::offsetOfGroup()), scratchReg);
-        Address proto(scratchReg, ObjectGroup::offsetOfProto());
-        masm.branchPtr(Assembler::NotEqual, proto, ImmGCPtr(obj->staticPrototype()), failures);
-    }
-
-    JSObject* pobj = obj->staticPrototype();
-    if (!pobj)
-        return;
-    while (pobj != holder) {
-        if (pobj->hasUncacheableProto()) {
-            masm.movePtr(ImmGCPtr(pobj), scratchReg);
-            Address groupAddr(scratchReg, JSObject::offsetOfGroup());
-            if (pobj->isSingleton()) {
-                // Singletons can have their group's |proto| mutated directly.
-                masm.loadPtr(groupAddr, scratchReg);
-                Address protoAddr(scratchReg, ObjectGroup::offsetOfProto());
-                masm.branchPtr(Assembler::NotEqual, protoAddr, ImmGCPtr(pobj->staticPrototype()),
-                              failures);
-            } else {
-                masm.branchPtr(Assembler::NotEqual, groupAddr, ImmGCPtr(pobj->group()), failures);
-            }
-        }
-
-        pobj = pobj->staticPrototype();
-    }
-}
-
 // Note: This differs from IsCacheableProtoChain in BaselineIC.cpp in that
 // Ion caches can deal with objects on the proto chain that have uncacheable
 // prototypes.
 bool
 jit::IsCacheableProtoChainForIonOrCacheIR(JSObject* obj, JSObject* holder)
 {
     while (obj != holder) {
         /*
@@ -477,56 +434,16 @@ jit::IsCacheableGetPropReadSlotForIonOrC
 
     Shape* shape = prop.shape();
     if (!shape->hasSlot() || !shape->hasDefaultGetter())
         return false;
 
     return true;
 }
 
-static bool
-IsCacheableNoProperty(JSObject* obj, JSObject* holder, PropertyResult prop, jsbytecode* pc,
-                      const TypedOrValueRegister& output)
-{
-    if (prop)
-        return false;
-
-    MOZ_ASSERT(!holder);
-
-    // Just because we didn't find the property on the object doesn't mean it
-    // won't magically appear through various engine hacks.
-    if (obj->getClass()->getGetProperty())
-        return false;
-
-    // Don't generate missing property ICs if we skipped a non-native object, as
-    // lookups may extend beyond the prototype chain (e.g. for DOMProxy
-    // proxies).
-    JSObject* obj2 = obj;
-    while (obj2) {
-        if (!obj2->isNative())
-            return false;
-        obj2 = obj2->staticPrototype();
-    }
-
-    // The pc is nullptr if the cache is idempotent. We cannot share missing
-    // properties between caches because TI can only try to prove that a type is
-    // contained, but does not attempts to check if something does not exists.
-    // So the infered type of getprop would be missing and would not contain
-    // undefined, as expected for missing properties.
-    if (!pc)
-        return false;
-
-    // TI has not yet monitored an Undefined value. The fallback path will
-    // monitor and invalidate the script.
-    if (!output.hasValue())
-        return false;
-
-    return true;
-}
-
 bool
 jit::IsCacheableGetPropCallNative(JSObject* obj, JSObject* holder, Shape* shape)
 {
     if (!shape || !IsCacheableProtoChainForIonOrCacheIR(obj, holder))
         return false;
 
     if (!shape->hasGetterValue() || !shape->getterValue().isObject())
         return false;
@@ -574,464 +491,16 @@ jit::IsCacheableGetPropCallScripted(JSOb
         if (isTemporarilyUnoptimizable)
             *isTemporarilyUnoptimizable = true;
         return false;
     }
 
     return true;
 }
 
-static bool
-IsCacheableGetPropCallPropertyOp(JSObject* obj, JSObject* holder, Shape* shape)
-{
-    if (!shape || !IsCacheableProtoChainForIonOrCacheIR(obj, holder))
-        return false;
-
-    if (shape->hasSlot() || shape->hasGetterValue() || shape->hasDefaultGetter())
-        return false;
-
-    return true;
-}
-
-static void
-TestMatchingReceiver(MacroAssembler& masm, IonCache::StubAttacher& attacher,
-                     Register object, JSObject* obj, Label* failure,
-                     bool alwaysCheckGroup = false)
-{
-    if (obj->is<UnboxedPlainObject>()) {
-        MOZ_ASSERT(failure);
-
-        masm.branchTestObjGroup(Assembler::NotEqual, object, obj->group(), failure);
-        Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando());
-        if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
-            masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failure);
-            Label success;
-            masm.push(object);
-            masm.loadPtr(expandoAddress, object);
-            masm.branchTestObjShape(Assembler::Equal, object, expando->lastProperty(),
-                                    &success);
-            masm.pop(object);
-            masm.jump(failure);
-            masm.bind(&success);
-            masm.pop(object);
-        } else {
-            masm.branchPtr(Assembler::NotEqual, expandoAddress, ImmWord(0), failure);
-        }
-    } else if (obj->is<UnboxedArrayObject>()) {
-        MOZ_ASSERT(failure);
-        masm.branchTestObjGroup(Assembler::NotEqual, object, obj->group(), failure);
-    } else if (obj->is<TypedObject>()) {
-        attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
-                                       Address(object, JSObject::offsetOfGroup()),
-                                       ImmGCPtr(obj->group()), failure);
-    } else {
-        Shape* shape = obj->maybeShape();
-        MOZ_ASSERT(shape);
-
-        attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
-                                       Address(object, ShapedObject::offsetOfShape()),
-                                       ImmGCPtr(shape), failure);
-
-        if (alwaysCheckGroup)
-            masm.branchTestObjGroup(Assembler::NotEqual, object, obj->group(), failure);
-    }
-}
-
-static inline void
-EmitLoadSlot(MacroAssembler& masm, NativeObject* holder, Shape* shape, Register holderReg,
-             TypedOrValueRegister output, Register scratchReg)
-{
-    MOZ_ASSERT(holder);
-    NativeObject::slotsSizeMustNotOverflow();
-    if (holder->isFixedSlot(shape->slot())) {
-        Address addr(holderReg, NativeObject::getFixedSlotOffset(shape->slot()));
-        masm.loadTypedOrValue(addr, output);
-    } else {
-        masm.loadPtr(Address(holderReg, NativeObject::offsetOfSlots()), scratchReg);
-
-        Address addr(scratchReg, holder->dynamicSlotIndex(shape->slot()) * sizeof(Value));
-        masm.loadTypedOrValue(addr, output);
-    }
-}
-
-static void
-GenerateReadSlot(JSContext* cx, IonScript* ion, MacroAssembler& masm,
-                 IonCache::StubAttacher& attacher, MaybeCheckTDZ checkTDZ,
-                 JSObject* obj, JSObject* holder, PropertyResult prop, Register object,
-                 TypedOrValueRegister output, Label* failures = nullptr)
-{
-    // If there's a single jump to |failures|, we can patch the shape guard
-    // jump directly. Otherwise, jump to the end of the stub, so there's a
-    // common point to patch.
-    bool multipleFailureJumps = (obj != holder)
-                             || obj->is<UnboxedPlainObject>()
-                             || (checkTDZ && output.hasValue())
-                             || (failures != nullptr && failures->used());
-
-    // If we have multiple failure jumps but didn't get a label from the
-    // outside, make one ourselves.
-    Label failures_;
-    if (multipleFailureJumps && !failures)
-        failures = &failures_;
-
-    TestMatchingReceiver(masm, attacher, object, obj, failures);
-
-    // If we need a scratch register, use either an output register or the
-    // object register. After this point, we cannot jump directly to
-    // |failures| since we may still have to pop the object register.
-    bool restoreScratch = false;
-    Register scratchReg = Register::FromCode(0); // Quell compiler warning.
-
-    if (obj != holder ||
-        obj->is<UnboxedPlainObject>() ||
-        !holder->as<NativeObject>().isFixedSlot(prop.shape()->slot()))
-    {
-        if (output.hasValue()) {
-            scratchReg = output.valueReg().scratchReg();
-        } else if (output.type() == MIRType::Double) {
-            scratchReg = object;
-            masm.push(scratchReg);
-            restoreScratch = true;
-        } else {
-            scratchReg = output.typedReg().gpr();
-        }
-    }
-
-    // Fast path: single failure jump, no prototype guards.
-    if (!multipleFailureJumps) {
-        EmitLoadSlot(masm, &holder->as<NativeObject>(), prop.shape(), object, output, scratchReg);
-        if (restoreScratch)
-            masm.pop(scratchReg);
-        attacher.jumpRejoin(masm);
-        return;
-    }
-
-    // Slow path: multiple jumps; generate prototype guards.
-    Label prototypeFailures;
-    Register holderReg;
-    if (obj != holder) {
-        // Note: this may clobber the object register if it's used as scratch.
-        GeneratePrototypeGuards(cx, ion, masm, obj, holder, object, scratchReg,
-                                &prototypeFailures);
-
-        if (holder) {
-            // Guard on the holder's shape.
-            holderReg = scratchReg;
-            masm.movePtr(ImmGCPtr(holder), holderReg);
-            masm.branchPtr(Assembler::NotEqual,
-                           Address(holderReg, ShapedObject::offsetOfShape()),
-                           ImmGCPtr(holder->as<NativeObject>().lastProperty()),
-                           &prototypeFailures);
-        } else {
-            // The property does not exist. Guard on everything in the
-            // prototype chain.
-            JSObject* proto = obj->staticPrototype();
-            Register lastReg = object;
-            MOZ_ASSERT(scratchReg != object);
-            while (proto) {
-                masm.loadObjProto(lastReg, scratchReg);
-
-                // Guard the shape of the current prototype.
-                MOZ_ASSERT(proto->hasStaticPrototype());
-                masm.branchPtr(Assembler::NotEqual,
-                               Address(scratchReg, ShapedObject::offsetOfShape()),
-                               ImmGCPtr(proto->as<NativeObject>().lastProperty()),
-                               &prototypeFailures);
-
-                proto = proto->staticPrototype();
-                lastReg = scratchReg;
-            }
-
-            holderReg = InvalidReg;
-        }
-    } else if (obj->is<UnboxedPlainObject>()) {
-        holder = obj->as<UnboxedPlainObject>().maybeExpando();
-        holderReg = scratchReg;
-        masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), holderReg);
-    } else {
-        holderReg = object;
-    }
-
-    // Slot access.
-    if (holder) {
-        EmitLoadSlot(masm, &holder->as<NativeObject>(), prop.shape(), holderReg, output,
-                     scratchReg);
-        if (checkTDZ && output.hasValue())
-            masm.branchTestMagic(Assembler::Equal, output.valueReg(), failures);
-    } else {
-        masm.moveValue(UndefinedValue(), output.valueReg());
-    }
-
-    // Restore scratch on success.
-    if (restoreScratch)
-        masm.pop(scratchReg);
-
-    attacher.jumpRejoin(masm);
-
-    masm.bind(&prototypeFailures);
-    if (restoreScratch)
-        masm.pop(scratchReg);
-    masm.bind(failures);
-
-    attacher.jumpNextStub(masm);
-}
-
-static bool
-EmitGetterCall(JSContext* cx, MacroAssembler& masm,
-               IonCache::StubAttacher& attacher, JSObject* obj,
-               JSObject* holder, HandleShape shape, bool holderIsReceiver,
-               LiveRegisterSet liveRegs, Register object,
-               TypedOrValueRegister output,
-               void* returnAddr)
-{
-    MOZ_ASSERT(output.hasValue());
-    MacroAssembler::AfterICSaveLive aic = masm.icSaveLive(liveRegs);
-
-    MOZ_ASSERT_IF(obj != holder, !holderIsReceiver);
-
-    // Remaining registers should basically be free, but we need to use |object| still
-    // so leave it alone.
-    AllocatableRegisterSet regSet(RegisterSet::All());
-    regSet.take(AnyRegister(object));
-
-    // This is a slower stub path, and we're going to be doing a call anyway.  Don't need
-    // to try so hard to not use the stack.  Scratch regs are just taken from the register
-    // set not including the input, current value saved on the stack, and restored when
-    // we're done with it.
-    Register scratchReg = regSet.takeAnyGeneral();
-
-    // Shape has a JSNative, PropertyOp or scripted getter function.
-    if (IsCacheableGetPropCallNative(obj, holder, shape)) {
-        Register argJSContextReg = regSet.takeAnyGeneral();
-        Register argUintNReg     = regSet.takeAnyGeneral();
-        Register argVpReg        = regSet.takeAnyGeneral();
-
-        JSFunction* target = &shape->getterValue().toObject().as<JSFunction>();
-        MOZ_ASSERT(target);
-        MOZ_ASSERT(target->isNative());
-
-        // Native functions have the signature:
-        //  bool (*)(JSContext*, unsigned, Value* vp)
-        // Where vp[0] is space for an outparam, vp[1] is |this|, and vp[2] onward
-        // are the function arguments.
-
-        // Construct vp array:
-        // Push object value for |this|
-        masm.Push(TypedOrValueRegister(MIRType::Object, AnyRegister(object)));
-        // Push callee/outparam.
-        masm.Push(ObjectValue(*target));
-
-        // Preload arguments into registers.
-        masm.loadJSContext(argJSContextReg);
-        masm.move32(Imm32(0), argUintNReg);
-        masm.moveStackPtrTo(argVpReg);
-
-        // Push marking data for later use.
-        masm.Push(argUintNReg);
-        attacher.pushStubCodePointer(masm);
-
-        if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
-            return false;
-        masm.enterFakeExitFrame(argJSContextReg, IonOOLNativeExitFrameLayoutToken);
-
-        // Construct and execute call.
-        masm.setupUnalignedABICall(scratchReg);
-        masm.passABIArg(argJSContextReg);
-        masm.passABIArg(argUintNReg);
-        masm.passABIArg(argVpReg);
-        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target->native()));
-
-        // Test for failure.
-        masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
-
-        // Load the outparam vp[0] into output register(s).
-        Address outparam(masm.getStackPointer(), IonOOLNativeExitFrameLayout::offsetOfResult());
-        masm.loadTypedOrValue(outparam, output);
-
-        // masm.leaveExitFrame & pop locals
-        masm.adjustStack(IonOOLNativeExitFrameLayout::Size(0));
-    } else if (IsCacheableGetPropCallPropertyOp(obj, holder, shape)) {
-        Register argJSContextReg = regSet.takeAnyGeneral();
-        Register argObjReg       = regSet.takeAnyGeneral();
-        Register argIdReg        = regSet.takeAnyGeneral();
-        Register argVpReg        = regSet.takeAnyGeneral();
-
-        GetterOp target = shape->getterOp();
-        MOZ_ASSERT(target);
-
-        // Push stubCode for marking.
-        attacher.pushStubCodePointer(masm);
-
-        // JSGetterOp: bool fn(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp)
-
-        // Push args on stack first so we can take pointers to make handles.
-        masm.Push(UndefinedValue());
-        masm.moveStackPtrTo(argVpReg);
-
-        // Push canonical jsid from shape instead of propertyname.
-        masm.Push(shape->propid(), scratchReg);
-        masm.moveStackPtrTo(argIdReg);
-
-        // Push the holder.
-        if (holderIsReceiver) {
-            // When the holder is also the current receiver, we just have a shape guard,
-            // so we might end up with a random object which is also guaranteed to have
-            // this JSGetterOp.
-            masm.Push(object);
-        } else {
-            // If the holder is on the prototype chain, the prototype-guarding
-            // only allows objects with the same holder.
-            masm.movePtr(ImmGCPtr(holder), scratchReg);
-            masm.Push(scratchReg);
-        }
-        masm.moveStackPtrTo(argObjReg);
-
-        masm.loadJSContext(argJSContextReg);
-
-        if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
-            return false;
-        masm.enterFakeExitFrame(argJSContextReg, IonOOLPropertyOpExitFrameLayoutToken);
-
-        // Make the call.
-        masm.setupUnalignedABICall(scratchReg);
-        masm.passABIArg(argJSContextReg);
-        masm.passABIArg(argObjReg);
-        masm.passABIArg(argIdReg);
-        masm.passABIArg(argVpReg);
-        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target));
-
-        // Test for failure.
-        masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
-
-        // Load the outparam vp[0] into output register(s).
-        Address outparam(masm.getStackPointer(), IonOOLPropertyOpExitFrameLayout::offsetOfResult());
-        masm.loadTypedOrValue(outparam, output);
-
-        // masm.leaveExitFrame & pop locals.
-        masm.adjustStack(IonOOLPropertyOpExitFrameLayout::Size());
-    } else {
-        MOZ_ASSERT(IsCacheableGetPropCallScripted(obj, holder, shape));
-
-        JSFunction* target = &shape->getterValue().toObject().as<JSFunction>();
-        uint32_t framePushedBefore = masm.framePushed();
-
-        // Construct IonICCallFrameLayout.
-        uint32_t descriptor = MakeFrameDescriptor(masm.framePushed(), JitFrame_IonJS,
-                                                  IonICCallFrameLayout::Size());
-        attacher.pushStubCodePointer(masm);
-        masm.Push(Imm32(descriptor));
-        masm.Push(ImmPtr(returnAddr));
-
-        // The JitFrameLayout pushed below will be aligned to JitStackAlignment,
-        // so we just have to make sure the stack is aligned after we push the
-        // |this| + argument Values.
-        uint32_t argSize = (target->nargs() + 1) * sizeof(Value);
-        uint32_t padding = ComputeByteAlignment(masm.framePushed() + argSize, JitStackAlignment);
-        MOZ_ASSERT(padding % sizeof(uintptr_t) == 0);
-        MOZ_ASSERT(padding < JitStackAlignment);
-        masm.reserveStack(padding);
-
-        for (size_t i = 0; i < target->nargs(); i++)
-            masm.Push(UndefinedValue());
-        masm.Push(TypedOrValueRegister(MIRType::Object, AnyRegister(object)));
-
-        masm.movePtr(ImmGCPtr(target), scratchReg);
-
-        descriptor = MakeFrameDescriptor(argSize + padding, JitFrame_IonICCall,
-                                         JitFrameLayout::Size());
-        masm.Push(Imm32(0)); // argc
-        masm.Push(scratchReg);
-        masm.Push(Imm32(descriptor));
-
-        // Check stack alignment. Add sizeof(uintptr_t) for the return address.
-        MOZ_ASSERT(((masm.framePushed() + sizeof(uintptr_t)) % JitStackAlignment) == 0);
-
-        // The getter has JIT code now and we will only discard the getter's JIT
-        // code when discarding all JIT code in the Zone, so we can assume it'll
-        // still have JIT code.
-        MOZ_ASSERT(target->hasJITCode());
-        masm.loadPtr(Address(scratchReg, JSFunction::offsetOfNativeOrScript()), scratchReg);
-        masm.loadBaselineOrIonRaw(scratchReg, scratchReg, nullptr);
-        masm.callJit(scratchReg);
-        masm.storeCallResultValue(output);
-
-        masm.freeStack(masm.framePushed() - framePushedBefore);
-    }
-
-    masm.icRestoreLive(liveRegs, aic);
-    return true;
-}
-
-static bool
-GenerateCallGetter(JSContext* cx, IonScript* ion, MacroAssembler& masm,
-                   IonCache::StubAttacher& attacher, JSObject* obj,
-                   JSObject* holder, HandleShape shape, LiveRegisterSet& liveRegs, Register object,
-                   TypedOrValueRegister output, void* returnAddr, Label* failures = nullptr)
-{
-    MOZ_ASSERT(output.hasValue());
-
-    // Use the passed in label if there was one. Otherwise, we'll have to make our own.
-    Label stubFailure;
-    failures = failures ? failures : &stubFailure;
-
-    TestMatchingReceiver(masm, attacher, object, obj, failures);
-
-    Register scratchReg = output.valueReg().scratchReg();
-    bool spillObjReg = scratchReg == object;
-    Label pop1AndFail;
-    Label* maybePopAndFail = failures;
-
-    // If we're calling a getter on the global, inline the logic for the
-    // 'this' hook on the global lexical env and manually push the global.
-    if (IsGlobalLexicalEnvironment(obj)) {
-        masm.extractObject(Address(object, EnvironmentObject::offsetOfEnclosingEnvironment()),
-                           object);
-    }
-
-    // Save off the object register if it aliases the scratchReg
-    if (spillObjReg) {
-        masm.push(object);
-        maybePopAndFail = &pop1AndFail;
-    }
-
-    // Note: this may clobber the object register if it's used as scratch.
-    if (obj != holder)
-        GeneratePrototypeGuards(cx, ion, masm, obj, holder, object, scratchReg, maybePopAndFail);
-
-    // Guard on the holder's shape.
-    Register holderReg = scratchReg;
-    masm.movePtr(ImmGCPtr(holder), holderReg);
-    masm.branchPtr(Assembler::NotEqual,
-                   Address(holderReg, ShapedObject::offsetOfShape()),
-                   ImmGCPtr(holder->as<NativeObject>().lastProperty()),
-                   maybePopAndFail);
-
-    if (spillObjReg)
-        masm.pop(object);
-
-    // Now we're good to go to invoke the native call.
-    bool holderIsReceiver = (obj == holder);
-    if (!EmitGetterCall(cx, masm, attacher, obj, holder, shape, holderIsReceiver, liveRegs, object,
-                        output, returnAddr))
-        return false;
-
-    // Rejoin jump.
-    attacher.jumpRejoin(masm);
-
-    // Jump to next stub.
-    if (spillObjReg) {
-        masm.bind(&pop1AndFail);
-        masm.pop(object);
-    }
-    masm.bind(failures);
-    attacher.jumpNextStub(masm);
-
-    return true;
-}
-
 bool
 jit::ValueToNameOrSymbolId(JSContext* cx, HandleValue idval, MutableHandleId id,
                            bool* nameOrSymbol)
 {
     *nameOrSymbol = false;
 
     if (!idval.isString() && !idval.isSymbol())
         return true;
@@ -1336,247 +805,8 @@ BindNameIC::update(JSContext* cx, Handle
         } else {
             JitSpew(JitSpew_IonIC, "BINDNAME uncacheable env chain");
         }
     }
 
     return holder;
 }
 
-bool
-NameIC::attachReadSlot(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                       HandleObject envChain, HandleObject holderBase,
-                       HandleNativeObject holder, Handle<PropertyResult> prop)
-{
-    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
-    Label failures;
-    StubAttacher attacher(*this);
-
-    Register scratchReg = outputReg().valueReg().scratchReg();
-
-    // Don't guard the base of the proto chain the name was found on. It will be guarded
-    // by GenerateReadSlot().
-    masm.mov(environmentChainReg(), scratchReg);
-    GenerateEnvironmentChainGuards(masm, envChain, holderBase, scratchReg, &failures,
-                             /* skipLastGuard = */true);
-
-    // GenerateEnvironmentChain leaves the last env chain in scratchReg, even though it
-    // doesn't generate the extra guard.
-    //
-    // NAME ops must do their own TDZ checks.
-    GenerateReadSlot(cx, ion, masm, attacher, CheckTDZ, holderBase, holder, prop, scratchReg,
-                     outputReg(), failures.used() ? &failures : nullptr);
-
-    return linkAndAttachStub(cx, masm, attacher, ion, "generic",
-                             JS::TrackedOutcome::ICNameStub_ReadSlot);
-}
-
-static bool
-IsCacheableEnvironmentChain(JSObject* envChain, JSObject* obj)
-{
-    JSObject* obj2 = envChain;
-    while (obj2) {
-        if (!IsCacheableEnvironment(obj2) && !obj2->is<GlobalObject>())
-            return false;
-
-        // Stop once we hit the global or target obj.
-        if (obj2->is<GlobalObject>() || obj2 == obj)
-            break;
-
-        obj2 = obj2->enclosingEnvironment();
-    }
-
-    return obj == obj2;
-}
-
-static bool
-IsCacheableNameReadSlot(JSContext* cx, HandleObject envChain, HandleObject obj,
-                        HandleObject holder, Handle<PropertyResult> prop, jsbytecode* pc,
-                        const TypedOrValueRegister& output)
-{
-    if (!prop)
-        return false;
-    if (!obj->isNative())
-        return false;
-
-    if (obj->is<GlobalObject>()) {
-        // Support only simple property lookups.
-        if (!IsCacheableGetPropReadSlotForIonOrCacheIR(obj, holder, prop) &&
-            !IsCacheableNoProperty(obj, holder, prop, pc, output))
-            return false;
-    } else if (obj->is<ModuleEnvironmentObject>()) {
-        // We don't yet support lookups in a module environment.
-        return false;
-    } else if (obj->is<CallObject>()) {
-        MOZ_ASSERT(obj == holder);
-        if (!prop.shape()->hasDefaultGetter())
-            return false;
-    } else {
-        // We don't yet support lookups on Block or DeclEnv objects.
-        return false;
-    }
-
-    return IsCacheableEnvironmentChain(envChain, obj);
-}
-
-bool
-NameIC::attachCallGetter(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                         HandleObject envChain, HandleObject obj, HandleObject holder,
-                         HandleShape shape, void* returnAddr)
-{
-    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
-    StubAttacher attacher(*this);
-
-    Label failures;
-    Register scratchReg = outputReg().valueReg().scratchReg();
-
-    // Don't guard the base of the proto chain the name was found on. It will be guarded
-    // by GenerateCallGetter().
-    masm.mov(environmentChainReg(), scratchReg);
-    GenerateEnvironmentChainGuards(masm, envChain, obj, scratchReg, &failures,
-                             /* skipLastGuard = */true);
-
-    // GenerateEnvironmentChain leaves the last env chain in scratchReg, even though it
-    // doesn't generate the extra guard.
-    if (!GenerateCallGetter(cx, ion, masm, attacher, obj, holder, shape, liveRegs_,
-                            scratchReg, outputReg(), returnAddr,
-                            failures.used() ? &failures : nullptr))
-    {
-         return false;
-    }
-
-    const char* attachKind = "name getter";
-    return linkAndAttachStub(cx, masm, attacher, ion, attachKind,
-                             JS::TrackedOutcome::ICNameStub_CallGetter);
-}
-
-static bool
-IsCacheableNameCallGetter(HandleObject envChain, HandleObject obj, HandleObject holder,
-                          Handle<PropertyResult> prop)
-{
-    if (!prop)
-        return false;
-    if (!obj->is<GlobalObject>())
-        return false;
-
-    if (!IsCacheableEnvironmentChain(envChain, obj))
-        return false;
-
-    if (!prop || !IsCacheableProtoChainForIonOrCacheIR(obj, holder))
-        return false;
-
-    Shape* shape = prop.shape();
-    return IsCacheableGetPropCallNative(obj, holder, shape) ||
-        IsCacheableGetPropCallPropertyOp(obj, holder, shape) ||
-        IsCacheableGetPropCallScripted(obj, holder, shape);
-}
-
-bool
-NameIC::attachTypeOfNoProperty(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                               HandleObject envChain)
-{
-    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
-    Label failures;
-    StubAttacher attacher(*this);
-
-    Register scratchReg = outputReg().valueReg().scratchReg();
-
-    masm.movePtr(environmentChainReg(), scratchReg);
-
-    // Generate env chain guards.
-    // Since the property was not defined on any object, iterate until reaching the global.
-    JSObject* tobj = envChain;
-    while (true) {
-        GenerateEnvironmentChainGuard(masm, tobj, scratchReg, nullptr, &failures);
-
-        if (tobj->is<GlobalObject>())
-            break;
-
-        // Load the next link.
-        tobj = &tobj->as<EnvironmentObject>().enclosingEnvironment();
-        masm.extractObject(Address(scratchReg, EnvironmentObject::offsetOfEnclosingEnvironment()),
-                           scratchReg);
-    }
-
-    masm.moveValue(UndefinedValue(), outputReg().valueReg());
-    attacher.jumpRejoin(masm);
-
-    masm.bind(&failures);
-    attacher.jumpNextStub(masm);
-
-    return linkAndAttachStub(cx, masm, attacher, ion, "generic",
-                             JS::TrackedOutcome::ICNameStub_TypeOfNoProperty);
-}
-
-static bool
-IsCacheableNameNoProperty(HandleObject envChain, HandleObject obj,
-                          HandleObject holder, Handle<PropertyResult> prop, jsbytecode* pc,
-                          NameIC& cache)
-{
-    if (cache.isTypeOf() && !prop) {
-        MOZ_ASSERT(!obj);
-        MOZ_ASSERT(!holder);
-        MOZ_ASSERT(envChain);
-
-        // Assert those extra things checked by IsCacheableNoProperty().
-        MOZ_ASSERT(cache.outputReg().hasValue());
-        MOZ_ASSERT(pc != nullptr);
-
-        return true;
-    }
-
-    return false;
-}
-
-bool
-NameIC::update(JSContext* cx, HandleScript outerScript, size_t cacheIndex, HandleObject envChain,
-               MutableHandleValue vp)
-{
-    IonScript* ion = outerScript->ionScript();
-
-    NameIC& cache = ion->getCache(cacheIndex).toName();
-    RootedPropertyName name(cx, cache.name());
-
-    RootedScript script(cx);
-    jsbytecode* pc;
-    cache.getScriptedLocation(&script, &pc);
-
-    RootedObject obj(cx);
-    RootedObject holder(cx);
-    Rooted<PropertyResult> prop(cx);
-    if (!LookupName(cx, name, envChain, &obj, &holder, &prop))
-        return false;
-
-    // Look first. Don't generate cache entries if the lookup fails.
-    if (cache.isTypeOf()) {
-        if (!FetchName<GetNameMode::TypeOf>(cx, obj, holder, name, prop, vp))
-            return false;
-    } else {
-        if (!FetchName<GetNameMode::Normal>(cx, obj, holder, name, prop, vp))
-            return false;
-    }
-
-    if (cache.canAttachStub()) {
-        if (IsCacheableNameReadSlot(cx, envChain, obj, holder, prop, pc, cache.outputReg())) {
-            if (!cache.attachReadSlot(cx, outerScript, ion, envChain, obj,
-                                      holder.as<NativeObject>(), prop))
-            {
-                return false;
-            }
-        } else if (IsCacheableNameCallGetter(envChain, obj, holder, prop)) {
-            void* returnAddr = GetReturnAddressToIonCode(cx);
-            RootedShape shape(cx, prop.shape());
-            if (!cache.attachCallGetter(cx, outerScript, ion, envChain, obj, holder, shape,
-                                        returnAddr))
-            {
-                return false;
-            }
-        } else if (IsCacheableNameNoProperty(envChain, obj, holder, prop, pc, cache)) {
-            if (!cache.attachTypeOfNoProperty(cx, outerScript, ion, envChain))
-                return false;
-        }
-    }
-
-    // Monitor changes to cache entry.
-    TypeScript::Monitor(cx, script, pc, vp);
-
-    return true;
-}
--- a/js/src/jit/IonCaches.h
+++ b/js/src/jit/IonCaches.h
@@ -24,18 +24,17 @@
 #include "vm/TypedArrayObject.h"
 
 namespace js {
 namespace jit {
 
 class LInstruction;
 
 #define IONCACHE_KIND_LIST(_)                                   \
-    _(BindName)                                                 \
-    _(Name)
+    _(BindName)
 
 // Forward declarations of Cache kinds.
 #define FORWARD_DECLARE(kind) class kind##IC;
 IONCACHE_KIND_LIST(FORWARD_DECLARE)
 #undef FORWARD_DECLARE
 
 class IonCacheVisitor
 {
@@ -391,72 +390,16 @@ class BindNameIC : public IonCache
 
     MOZ_MUST_USE bool attachNonGlobal(JSContext* cx, HandleScript outerScript, IonScript* ion,
                                       HandleObject envChain, HandleObject holder);
 
     static JSObject*
     update(JSContext* cx, HandleScript outerScript, size_t cacheIndex, HandleObject envChain);
 };
 
-class NameIC : public IonCache
-{
-  protected:
-    // Registers live after the cache, excluding output registers. The initial
-    // value of these registers must be preserved by the cache.
-    LiveRegisterSet liveRegs_;
-
-    bool typeOf_;
-    Register environmentChain_;
-    PropertyName* name_;
-    TypedOrValueRegister output_;
-
-  public:
-    NameIC(LiveRegisterSet liveRegs, bool typeOf,
-           Register envChain, PropertyName* name,
-           TypedOrValueRegister output)
-      : liveRegs_(liveRegs),
-        typeOf_(typeOf),
-        environmentChain_(envChain),
-        name_(name),
-        output_(output)
-    {
-    }
-
-    CACHE_HEADER(Name)
-
-    Register environmentChainReg() const {
-        return environmentChain_;
-    }
-    HandlePropertyName name() const {
-        return HandlePropertyName::fromMarkedLocation(&name_);
-    }
-    TypedOrValueRegister outputReg() const {
-        return output_;
-    }
-    bool isTypeOf() const {
-        return typeOf_;
-    }
-
-    MOZ_MUST_USE bool attachReadSlot(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                                     HandleObject envChain, HandleObject holderBase,
-                                     HandleNativeObject holder, Handle<PropertyResult> prop);
-
-    MOZ_MUST_USE bool attachCallGetter(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                                       HandleObject envChain, HandleObject obj,
-                                       HandleObject holder, HandleShape shape,
-                                       void* returnAddr);
-
-    MOZ_MUST_USE bool attachTypeOfNoProperty(JSContext* cx, HandleScript outerScript,
-                                             IonScript* ion, HandleObject envChain);
-
-    static MOZ_MUST_USE bool
-    update(JSContext* cx, HandleScript outerScript, size_t cacheIndex, HandleObject envChain,
-           MutableHandleValue vp);
-};
-
 #undef CACHE_HEADER
 
 // Implement cache casts now that the compiler can see the inheritance.
 #define CACHE_CASTS(ickind)                                             \
     ickind##IC& IonCache::to##ickind()                                  \
     {                                                                   \
         MOZ_ASSERT(is##ickind());                                       \
         return *static_cast<ickind##IC*>(this);                        \
--- a/js/src/jit/IonIC.cpp
+++ b/js/src/jit/IonIC.cpp
@@ -40,16 +40,17 @@ IonIC::scratchRegisterForEntryJump()
             return temp;
         TypedOrValueRegister output = asGetPropertyIC()->output();
         return output.hasValue() ? output.valueReg().scratchReg() : output.typedReg().gpr();
       }
       case CacheKind::SetProp:
       case CacheKind::SetElem:
         return asSetPropertyIC()->temp();
       case CacheKind::GetName:
+        return asGetNameIC()->temp();
       case CacheKind::In:
         MOZ_CRASH("Baseline-specific for now");
     }
 
     MOZ_CRASH("Invalid kind");
 }
 
 void
@@ -272,16 +273,59 @@ IonSetPropertyIC::update(JSContext* cx, 
 
         if (!attached && !isTemporarilyUnoptimizable)
             ic->state().trackNotAttached();
     }
 
     return true;
 }
 
+/* static */ bool
+IonGetNameIC::update(JSContext* cx, HandleScript outerScript, IonGetNameIC* ic,
+                     HandleObject envChain, MutableHandleValue res)
+{
+    IonScript* ionScript = outerScript->ionScript();
+
+    if (ic->state().maybeTransition())
+        ic->discardStubs(cx->zone());
+
+    jsbytecode* pc = ic->pc();
+    RootedPropertyName name(cx, ic->script()->getName(pc));
+
+    if (ic->state().canAttachStub()) {
+        bool attached = false;
+        RootedScript script(cx, ic->script());
+        GetNameIRGenerator gen(cx, script, pc, ic->state().mode(), envChain, name);
+        if (gen.tryAttachStub())
+            ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, &attached);
+
+        if (!attached)
+            ic->state().trackNotAttached();
+    }
+
+    RootedObject obj(cx);
+    RootedObject holder(cx);
+    Rooted<PropertyResult> prop(cx);
+    if (!LookupName(cx, name, envChain, &obj, &holder, &prop))
+        return false;
+
+    if (*GetNextPc(pc) == JSOP_TYPEOF) {
+        if (!FetchName<GetNameMode::TypeOf>(cx, obj, holder, name, prop, res))
+            return false;
+    } else {
+        if (!FetchName<GetNameMode::Normal>(cx, obj, holder, name, prop, res))
+            return false;
+    }
+
+    // No need to call TypeScript::Monitor, IonBuilder always inserts a type
+    // barrier after GetName ICs.
+
+    return true;
+}
+
 uint8_t*
 IonICStub::stubDataStart()
 {
     return reinterpret_cast<uint8_t*>(this) + stubInfo_->stubDataOffset();
 }
 
 void
 IonIC::attachStub(IonICStub* newStub, JitCode* code)
--- a/js/src/jit/IonIC.h
+++ b/js/src/jit/IonIC.h
@@ -53,16 +53,17 @@ class IonICStub
         nextCodeRaw_ = nullptr;
         next_ = nullptr;
         stubInfo_ = nullptr;
     }
 };
 
 class IonGetPropertyIC;
 class IonSetPropertyIC;
+class IonGetNameIC;
 
 class IonIC
 {
     // This either points at the OOL path for the fallback path, or the code for
     // the first stub.
     uint8_t* codeRaw_;
 
     // The first optimized stub, or nullptr.
@@ -134,16 +135,20 @@ class IonIC
     IonGetPropertyIC* asGetPropertyIC() {
         MOZ_ASSERT(kind_ == CacheKind::GetProp || kind_ == CacheKind::GetElem);
         return (IonGetPropertyIC*)this;
     }
     IonSetPropertyIC* asSetPropertyIC() {
         MOZ_ASSERT(kind_ == CacheKind::SetProp || kind_ == CacheKind::SetElem);
         return (IonSetPropertyIC*)this;
     }
+    IonGetNameIC* asGetNameIC() {
+        MOZ_ASSERT(kind_ == CacheKind::GetName);
+        return (IonGetNameIC*)this;
+    }
 
     void updateBaseAddress(JitCode* code, MacroAssembler& masm);
 
     // Returns the Register to use as scratch when entering IC stubs. This
     // should either be an output register or a temp.
     Register scratchRegisterForEntryJump();
 
     void trace(JSTracer* trc);
@@ -238,12 +243,39 @@ class IonSetPropertyIC : public IonIC
     bool needsPostBarrier() const { return needsPostBarrier_; }
     bool needsTypeBarrier() const { return needsTypeBarrier_; }
     bool guardHoles() const { return guardHoles_; }
 
     static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript, IonSetPropertyIC* ic,
                                     HandleObject obj, HandleValue idVal, HandleValue rhs);
 };
 
+class IonGetNameIC : public IonIC
+{
+    LiveRegisterSet liveRegs_;
+
+    Register environment_;
+    ValueOperand output_;
+    Register temp_;
+
+  public:
+    IonGetNameIC(LiveRegisterSet liveRegs, Register environment, ValueOperand output,
+                 Register temp)
+      : IonIC(CacheKind::GetName),
+        liveRegs_(liveRegs),
+        environment_(environment),
+        output_(output),
+        temp_(temp)
+    { }
+
+    Register environment() const { return environment_; }
+    ValueOperand output() const { return output_; }
+    Register temp() const { return temp_; }
+    LiveRegisterSet liveRegs() const { return liveRegs_; }
+
+    static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript, IonGetNameIC* ic,
+                                    HandleObject envChain, MutableHandleValue res);
+};
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_IonIC_h */
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -3694,17 +3694,17 @@ LIRGenerator::visitGetNameCache(MGetName
 {
     MOZ_ASSERT(ins->envObj()->type() == MIRType::Object);
 
     // Set the performs-call flag so that we don't omit the overrecursed check.
     // This is necessary because the cache can attach a scripted getter stub
     // that calls this script recursively.
     gen->setPerformsCall();
 
-    LGetNameCache* lir = new(alloc()) LGetNameCache(useRegister(ins->envObj()));
+    LGetNameCache* lir = new(alloc()) LGetNameCache(useRegister(ins->envObj()), temp());
     defineBox(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
 LIRGenerator::visitCallGetIntrinsicValue(MCallGetIntrinsicValue* ins)
 {
     LCallGetIntrinsicValue* lir = new(alloc()) LCallGetIntrinsicValue();
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -11687,48 +11687,27 @@ class MStoreSlot
 
     ALLOW_CLONE(MStoreSlot)
 };
 
 class MGetNameCache
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
 {
-  public:
-    enum AccessKind {
-        NAMETYPEOF,
-        NAME
-    };
-
   private:
-    CompilerPropertyName name_;
-    AccessKind kind_;
-
-    MGetNameCache(MDefinition* obj, PropertyName* name, AccessKind kind)
-      : MUnaryInstruction(obj),
-        name_(name),
-        kind_(kind)
+    explicit MGetNameCache(MDefinition* obj)
+      : MUnaryInstruction(obj)
     {
         setResultType(MIRType::Value);
     }
 
   public:
     INSTRUCTION_HEADER(GetNameCache)
     TRIVIAL_NEW_WRAPPERS
     NAMED_OPERANDS((0, envObj))
-
-    PropertyName* name() const {
-        return name_;
-    }
-    AccessKind accessKind() const {
-        return kind_;
-    }
-    bool appendRoots(MRootList& roots) const override {
-        return roots.append(name_);
-    }
 };
 
 class MCallGetIntrinsicValue : public MNullaryInstruction
 {
     CompilerPropertyName name_;
 
     explicit MCallGetIntrinsicValue(PropertyName* name)
       : name_(name)
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -6653,27 +6653,31 @@ class LStoreFixedSlotT : public LInstruc
         return getOperand(0);
     }
     const LAllocation* value() {
         return getOperand(1);
     }
 };
 
 // Note, Name ICs always return a Value. There are no V/T variants.
-class LGetNameCache : public LInstructionHelper<BOX_PIECES, 1, 0>
+class LGetNameCache : public LInstructionHelper<BOX_PIECES, 1, 1>
 {
   public:
     LIR_HEADER(GetNameCache)
 
-    explicit LGetNameCache(const LAllocation& envObj) {
+    LGetNameCache(const LAllocation& envObj, const LDefinition& temp) {
         setOperand(0, envObj);
+        setTemp(0, temp);
     }
     const LAllocation* envObj() {
         return getOperand(0);
     }
+    const LDefinition* temp() {
+        return getTemp(0);
+    }
     const MGetNameCache* mir() const {
         return mir_->toGetNameCache();
     }
 };
 
 class LCallGetIntrinsicValue : public LCallInstructionHelper<BOX_PIECES, 0, 0>
 {
   public:
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -8532,17 +8532,23 @@ LookupAsmJSModuleInCache(JSContext* cx, 
 
     // Due to the hash comparison made by openEntryForRead, this should succeed
     // with high probability.
     ModuleCharsForLookup moduleChars;
     cursor = moduleChars.deserialize(cursor);
     if (!moduleChars.match(parser))
         return true;
 
+    // Don't punish release users by crashing if there is a programmer error
+    // here, just gracefully return with a cache miss.
+#ifdef NIGHTLY_BUILD
     MOZ_RELEASE_ASSERT(cursor == entry.memory + entry.serializedSize);
+#endif
+    if (cursor != entry.memory + entry.serializedSize)
+        return true;
 
     // See AsmJSMetadata comment as well as ModuleValidator::init().
     asmJSMetadata->preludeStart = parser.pc->functionBox()->preludeStart;
     asmJSMetadata->srcStart = parser.pc->functionBox()->functionNode->pn_body->pn_pos.begin;
     asmJSMetadata->srcBodyStart = parser.tokenStream.currentToken().pos.end;
     asmJSMetadata->strict = parser.pc->sc()->strict() && !parser.pc->sc()->hasExplicitUseStrict();
     asmJSMetadata->scriptSource.reset(parser.ss);
 
--- a/layout/painting/nsCSSRenderingGradients.cpp
+++ b/layout/painting/nsCSSRenderingGradients.cpp
@@ -1024,21 +1024,19 @@ nsCSSGradientRenderer::BuildWebRenderDis
   for(uint32_t i = 0; i < mStops.Length(); i++) {
     stops[i].color.r = mStops[i].mColor.r;
     stops[i].color.g = mStops[i].mColor.g;
     stops[i].color.b = mStops[i].mColor.b;
     stops[i].color.a = mStops[i].mColor.a * aOpacity;
     stops[i].offset = mStops[i].mPosition;
   }
 
-  double firstStop = mStops[0].mPosition;
-  double lastStop = mStops[mStops.Length() - 1].mPosition;
-
   LayoutDevicePoint lineStart = LayoutDevicePoint(mLineStart.x, mLineStart.y);
   LayoutDevicePoint lineEnd = LayoutDevicePoint(mLineEnd.x, mLineEnd.y);
+  LayoutDeviceSize gradientRadius = LayoutDeviceSize(mRadiusX, mRadiusY);
 
   // Do a naive tiling of the gradient by making multiple display items
   // TODO: this should be done on the WebRender side eventually
 
   nscoord appUnitsPerDevPixel = mPresContext->AppUnitsPerDevPixel();
   LayoutDeviceRect firstTileBounds = LayoutDevicePixel::FromAppUnits(mDest, appUnitsPerDevPixel);
   LayoutDeviceRect clipBounds = LayoutDevicePixel::FromAppUnits(mFillArea, appUnitsPerDevPixel);
 
@@ -1068,27 +1066,21 @@ nsCSSGradientRenderer::BuildWebRenderDis
           aBuilder.BuildClipRegion(mozilla::wr::ToWrRect(clipBounds)),
           mozilla::wr::ToWrPoint(relativeGradientStart),
           mozilla::wr::ToWrPoint(relativeGradientEnd),
           stops,
           extendMode);
       } else {
         LayoutDevicePoint relativeGradientCenter = lineStart + tileOffset;
 
-        // TODO: ellipse gradients
-        double innerRadius = mRadiusX * firstStop;
-        double outerRadius = mRadiusX * lastStop;
-
         aBuilder.PushRadialGradient(
           mozilla::wr::ToWrRect(tileRect),
           aBuilder.BuildClipRegion(mozilla::wr::ToWrRect(clipBounds)),
           mozilla::wr::ToWrPoint(relativeGradientCenter),
-          mozilla::wr::ToWrPoint(relativeGradientCenter),
-          innerRadius,
-          outerRadius,
+          mozilla::wr::ToWrSize(gradientRadius),
           stops,
           extendMode);
       }
     }
   }
 }
 
 } // namespace mozilla
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -2749,16 +2749,50 @@ nsDisplayItem::BuildDisplayItemLayer(nsD
 
 nsRect
 nsDisplaySolidColor::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap)
 {
   *aSnap = true;
   return mBounds;
 }
 
+LayerState
+nsDisplaySolidColor::GetLayerState(nsDisplayListBuilder* aBuilder,
+                                   LayerManager* aManager,
+                                   const ContainerLayerParameters& aParameters)
+{
+  if (ForceActiveLayers() || gfxPrefs::LayersAllowSolidColorLayers()) {
+    return LAYER_ACTIVE;
+  }
+  return LAYER_NONE;
+}
+
+already_AddRefed<Layer>
+nsDisplaySolidColor::BuildLayer(nsDisplayListBuilder* aBuilder,
+                                LayerManager* aManager,
+                                const ContainerLayerParameters& aContainerParameters)
+{
+  RefPtr<ColorLayer> layer = static_cast<ColorLayer*>
+    (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this));
+  if (!layer) {
+    layer = aManager->CreateColorLayer();
+    if (!layer) {
+      return nullptr;
+    }
+  }
+  layer->SetColor(gfx::Color::FromABGR(mColor));
+
+  const int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
+  layer->SetBounds(mBounds.ToNearestPixels(appUnitsPerDevPixel));
+  layer->SetBaseTransform(gfx::Matrix4x4::Translation(aContainerParameters.mOffset.x,
+                                                      aContainerParameters.mOffset.y, 0));
+
+  return layer.forget();
+}
+
 void
 nsDisplaySolidColor::Paint(nsDisplayListBuilder* aBuilder,
                            nsRenderingContext* aCtx)
 {
   int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
   DrawTarget* drawTarget = aCtx->GetDrawTarget();
   Rect rect =
     NSRectToSnappedRect(mVisibleRect, appUnitsPerDevPixel, *drawTarget);
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -2978,16 +2978,23 @@ public:
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplaySolidColor() {
     MOZ_COUNT_DTOR(nsDisplaySolidColor);
   }
 #endif
 
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) override;
 
+  virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
+                                   LayerManager* aManager,
+                                   const ContainerLayerParameters& aParameters) override;
+  virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
+                                             LayerManager* aManager,
+                                             const ContainerLayerParameters& aContainerParameters) override;
+
   virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) override;
 
   virtual void WriteDebugInfo(std::stringstream& aStream) override;
 
   NS_DISPLAY_DECL_NAME("SolidColor", TYPE_SOLID_COLOR)
 
 private:
   nsRect  mBounds;
--- a/layout/reftests/box-shadow/reftest.list
+++ b/layout/reftests/box-shadow/reftest.list
@@ -6,17 +6,17 @@ random != boxshadow-blur-2.html boxshado
 == boxshadow-multiple.html boxshadow-multiple-ref.html
 == boxshadow-spread.html boxshadow-spread-ref.html
 == tableboxshadow-basic.html tableboxshadow-basic-ref.html
 == tableboxshadow-trshadow.html tableboxshadow-trshadow-ref.html
 == tableboxshadow-tdshadow.html tableboxshadow-tdshadow-ref.html
 == boxshadow-rounding.html boxshadow-rounding-ref.html
 # One uses old path, one uses WR box shadow.
 fails-if(Android) fuzzy-if(webrender,20,3310) == boxshadow-button.html boxshadow-button-ref.html
-fuzzy-if(OSX==1010,1,24) fuzzy-if(d2d,16,908) fuzzy-if(webrender,18,1680) == boxshadow-large-border-radius.html boxshadow-large-border-radius-ref.html # Bug 1209649
+fuzzy-if(OSX==1010,1,24) fuzzy-if(d2d,16,908) fuzzy-if(webrender,19,1680) == boxshadow-large-border-radius.html boxshadow-large-border-radius-ref.html # Bug 1209649
 
 fails-if(Android) == boxshadow-fileupload.html boxshadow-fileupload-ref.html
 == boxshadow-inner-basic.html boxshadow-inner-basic-ref.svg
 random-if(layersGPUAccelerated) == boxshadow-mixed.html boxshadow-mixed-ref.html
 random-if(d2d) == boxshadow-rounded-spread.html boxshadow-rounded-spread-ref.html
 fuzzy-if(skiaContent,1,50) HTTP(..) == boxshadow-dynamic.xul boxshadow-dynamic-ref.xul
 random-if(d2d) == boxshadow-onecorner.html boxshadow-onecorner-ref.html
 random-if(d2d) == boxshadow-twocorners.html boxshadow-twocorners-ref.html
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1932,17 +1932,17 @@ skip-if(!Android) fails-if(Android) == 1
 skip-if(!Android) == 1133905-4-vh-rtl.html 1133905-ref-vh-rtl.html
 skip-if(!Android) fails-if(Android) == 1133905-5-vh-rtl.html 1133905-ref-vh-rtl.html
 skip-if(!Android) fails-if(Android) == 1133905-6-vh-rtl.html 1133905-ref-vh-rtl.html
 == 1150021-1.xul 1150021-1-ref.xul
 == 1151145-1.html 1151145-1-ref.html
 == 1151306-1.html 1151306-1-ref.html
 == 1153845-1.html 1153845-1-ref.html
 == 1155828-1.html 1155828-1-ref.html
-fails-if(webrender) == 1156129-1.html 1156129-1-ref.html
+== 1156129-1.html 1156129-1-ref.html
 pref(dom.use_xbl_scopes_for_remote_xul,true) HTTP(..) == 1157127-1.html 1157127-1-ref.html
 fuzzy-if(Android,6,6) fails-if(webrender) == 1169331-1.html 1169331-1-ref.html
 fuzzy(1,74) fails-if(gtkWidget) == 1174332-1.html 1174332-1-ref.html # bug 1312658
 == 1179078-1.html 1179078-1-ref.html
 == 1179288-1.html 1179288-1-ref.html
 == 1190635-1.html 1190635-1-ref.html
 == 1202512-1.html 1202512-1-ref.html
 == 1202512-2.html 1202512-2-ref.html
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4906,16 +4906,22 @@ pref("dom.w3c_touch_events.enabled", 2);
 
 // W3C draft pointer events
 #if defined(XP_WIN) && defined(NIGHTLY_BUILD)
 pref("dom.w3c_pointer_events.enabled", true);
 #else
 pref("dom.w3c_pointer_events.enabled", false);
 #endif
 
+// Control firing WidgetMouseEvent by handling Windows pointer messages or mouse
+// messages.
+#if defined(XP_WIN)
+pref("dom.w3c_pointer_events.dispatch_by_pointer_messages", false);
+#endif
+
 // W3C pointer events draft
 pref("dom.w3c_pointer_events.implicit_capture", false);
 
 // W3C draft ImageCapture API
 pref("dom.imagecapture.enabled", false);
 
 // W3C MediaDevices devicechange event
 pref("media.ondevicechange.enabled", true);
new file mode 100644
--- /dev/null
+++ b/other-licenses/moz.build
@@ -0,0 +1,32 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+with Files('**'):
+    BUG_COMPONENT = ('Core', 'General')
+
+with Files('7zstub/**'):
+    BUG_COMPONENT = ('Firefox', 'Installer')
+
+with Files('atk-1.0/**'):
+    BUG_COMPONENT = ('Core', 'Disability Access APIs')
+
+with Files('bsdiff/**'):
+    BUG_COMPONENT = ('Core', 'XPCOM')
+
+with Files('ia2/**'):
+    BUG_COMPONENT = ('Core', 'Disability Access APIs')
+
+with Files('nsis/**'):
+    BUG_COMPONENT = ('Firefox', 'Installer')
+
+with Files('ply/**'):
+    BUG_COMPONENT = ('Core', 'JavaScript Engine')
+
+with Files('skia-npapi/**'):
+    BUG_COMPONENT = ('Core', 'Graphics')
+
+with Files('snappy/**'):
+    BUG_COMPONENT = ('Core', 'XPCOM')
--- a/security/manager/ssl/StaticHPKPins.h
+++ b/security/manager/ssl/StaticHPKPins.h
@@ -1152,9 +1152,9 @@ static const TransportSecurityPreload kP
   { "za.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
   { "zh.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
 };
 
 // Pinning Preload List Length = 477;
 
 static const int32_t kUnknownId = -1;
 
-static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1499784487081000);
+static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1499872531802000);
--- a/security/manager/ssl/nsSTSPreloadList.errors
+++ b/security/manager/ssl/nsSTSPreloadList.errors
@@ -43,16 +43,17 @@ 300m.com: did not receive HSTS header
 300mbmovies4u.cc: did not receive HSTS header
 301.website: could not connect to host
 302.nyc: could not connect to host
 33drugstore.com: did not receive HSTS header
 360ds.co.in: could not connect to host
 360gradus.com: did not receive HSTS header
 365.or.jp: could not connect to host
 368mibn.com: could not connect to host
+398.info: could not connect to host
 3chit.cf: could not connect to host
 3click-loan.com: could not connect to host
 3delivered.com: could not connect to host
 3sreporting.com: did not receive HSTS header
 3yearloans.com: max-age too low: 0
 404.sh: max-age too low: 0
 404404.info: could not connect to host
 420dongstorm.com: could not connect to host
@@ -71,16 +72,17 @@ 50millionablaze.org: could not connect t
 540.co: did not receive HSTS header
 56ct.com: could not connect to host
 60ych.net: did not receive HSTS header
 6120.eu: did not receive HSTS header
 69square.com: could not connect to host
 7kovrikov.ru: did not receive HSTS header
 808.lv: did not receive HSTS header
 83i.net: could not connect to host
+8ack.de: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 118"  data: no]
 8ox.ru: did not receive HSTS header
 911911.pw: could not connect to host
 922.be: could not connect to host
 960news.ca: could not connect to host
 9point6.com: could not connect to host
 a-plus.space: could not connect to host
 a-rickroll-n.pw: could not connect to host
 a-theme.com: could not connect to host
@@ -92,16 +94,17 @@ aapp.space: could not connect to host
 aaron-gustafson.com: did not receive HSTS header
 aati.info: did not receive HSTS header
 abe.cloud: could not connect to host
 abearofsoap.com: could not connect to host
 abecodes.net: did not receive HSTS header
 abeestrada.com: did not receive HSTS header
 abilitylist.org: did not receive HSTS header
 abioniere.de: could not connect to host
+ablogagency.net: could not connect to host
 abnarnro.com: could not connect to host
 abou.to: could not connect to host
 about.ge: did not receive HSTS header
 aboutmyip.info: did not receive HSTS header
 aboutmyproperty.ca: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 118"  data: no]
 aboutyou-deals.de: did not receive HSTS header
 abtom.de: did not receive HSTS header
 abury.fr: did not receive HSTS header
@@ -154,22 +157,24 @@ advancedstudio.ro: did not receive HSTS 
 adver.top: could not connect to host
 adviespuntklokkenluiders.nl: could not connect to host
 aemoria.com: could not connect to host
 aerialmediapro.net: could not connect to host
 aes256.ru: could not connect to host
 aether.pw: could not connect to host
 aevpn.net: could not connect to host
 aeyoun.com: did not receive HSTS header
+af-fotografie.net: did not receive HSTS header
 aficotroceni.ro: did not receive HSTS header
 afp548.tk: could not connect to host
 afyou.co.kr: could not connect to host
 agalaxyfarfaraway.co.uk: could not connect to host
 agbremen.de: did not receive HSTS header
 agentseeker.ca: did not receive HSTS header
+aggr.pw: did not receive HSTS header
 agrimap.com: did not receive HSTS header
 agrios.de: did not receive HSTS header
 agro-id.gov.ua: could not connect to host
 ahabingo.com: did not receive HSTS header
 ahoynetwork.com: did not receive HSTS header
 ahri.ovh: could not connect to host
 aidanwoods.com: did not receive HSTS header
 airbnb.com: did not receive HSTS header
@@ -201,17 +206,17 @@ alessandro.pw: did not receive HSTS head
 alethearose.com: did not receive HSTS header
 alexandre.sh: did not receive HSTS header
 alexisabarca.com: did not receive HSTS header
 alfa24.pro: could not connect to host
 alittlebitcheeky.com: did not receive HSTS header
 aljaspod.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 118"  data: no]
 alkami.com: did not receive HSTS header
 all-subtitles.com: could not connect to host
-all.tf: could not connect to host
+all.tf: did not receive HSTS header
 alldaymonitoring.com: could not connect to host
 allforyou.at: could not connect to host
 allinnote.com: could not connect to host
 allmbw.com: could not connect to host
 allstarswithus.com: could not connect to host
 alpha.irccloud.com: could not connect to host
 alphabit-secure.com: could not connect to host
 alphabuild.io: did not receive HSTS header
@@ -225,17 +230,17 @@ alza.cz: did not receive HSTS header
 alza.de: did not receive HSTS header
 alza.hu: did not receive HSTS header
 alza.sk: did not receive HSTS header
 alzashop.com: did not receive HSTS header
 amaforums.org: could not connect to host
 amandaonishi.com: could not connect to host
 amavis.org: did not receive HSTS header
 amazingfloridagulfhomes.com: could not connect to host
-ameho.me: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 118"  data: no]
+ameho.me: did not receive HSTS header
 american-truck-simulator.de: could not connect to host
 american-truck-simulator.net: could not connect to host
 americanworkwear.nl: did not receive HSTS header
 amigogeek.net: could not connect to host
 amihub.com: could not connect to host
 amilx.com: could not connect to host
 amilx.org: could not connect to host
 amimoto-ami.com: max-age too low: 3153600
@@ -272,17 +277,16 @@ animesharp.com: could not connect to hos
 animurecs.com: did not receive HSTS header
 aniplus.cf: could not connect to host
 aniplus.gq: could not connect to host
 aniplus.ml: could not connect to host
 anitklib.ml: could not connect to host
 ankakaak.com: could not connect to host
 ankaraprofesyonelnakliyat.com: did not receive HSTS header
 ankaraprofesyonelnakliyat.com.tr: did not receive HSTS header
-annarokina.com: could not connect to host
 anomaly.ws: did not receive HSTS header
 anonboards.com: did not receive HSTS header
 anonymo.co.uk: could not connect to host
 anonymo.uk: could not connect to host
 anonymousstatecollegelulzsec.com: could not connect to host
 anook.com: max-age too low: 0
 another.ch: could not connect to host
 anstoncs.com.au: max-age too low: 86400
@@ -325,16 +329,17 @@ aquilalab.com: could not connect to host
 arabdigitalexpression.org: did not receive HSTS header
 aradulconteaza.ro: could not connect to host
 aran.me.uk: could not connect to host
 arbeitskreis-asyl-eningen.de: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 118"  data: no]
 arboineuropa.nl: did not receive HSTS header
 arbu.eu: could not connect to host
 areafiftylan.nl: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 118"  data: no]
 arlen.se: could not connect to host
+armadaquadrat.com: could not connect to host
 armory.consulting: could not connect to host
 armory.supplies: could not connect to host
 armytricka.cz: did not receive HSTS header
 arnaudfeld.de: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 118"  data: no]
 arpa.ph: did not receive HSTS header
 arpr.co: did not receive HSTS header
 arrayify.com: could not connect to host
 arrow-cloud.nl: could not connect to host
@@ -436,17 +441,16 @@ babysaying.me: max-age too low: 6000
 bacchanallia.com: could not connect to host
 back-bone.nl: did not receive HSTS header
 badcronjob.com: could not connect to host
 badenhard.eu: could not connect to host
 badkamergigant.com: could not connect to host
 baff.lu: did not receive HSTS header
 baiduaccount.com: could not connect to host
 bakabt.info: could not connect to host
-bakaweb.fr: could not connect to host
 bakingstone.com: could not connect to host
 bakkerdesignandbuild.com: did not receive HSTS header
 balcan-underground.net: could not connect to host
 baldwinkoo.com: could not connect to host
 baleares.party: could not connect to host
 balloonphp.com: could not connect to host
 balonmano.co: could not connect to host
 bananabandy.com: could not connect to host
@@ -462,16 +466,17 @@ bashcode.ninja: could not connect to hos
 basicsolutionsus.com: did not receive HSTS header
 basilisk.io: could not connect to host
 bassh.net: could not connect to host
 baud.ninja: could not connect to host
 baum.ga: did not recei