merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 30 Nov 2015 13:19:02 +0100
changeset 274644 a18630f9ab42ddfde03ba8c7757a42069c48c7ed
parent 274538 2d385f1302a2a86dc22f6d10dad2ac15b03d5f3d (current diff)
parent 274643 b181b44bfeeba4e1115d8eedebbfe267226d9b65 (diff)
child 274645 620f5d248dabda25f0c118451e304a077cbb3311
child 274689 21f92638bb1464a42955ed0a9278685fd35e2ebc
child 274777 5a8a532f97cd82b06b2f45515b633958bda50c8e
push id16452
push usercbook@mozilla.com
push dateMon, 30 Nov 2015 12:46:40 +0000
treeherderfx-team@620f5d248dab [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone45.0a1
merge mozilla-inbound to mozilla-central a=merge
.hgignore
dom/media/omx/MediaCodecDecoder.cpp
dom/media/omx/MediaCodecDecoder.h
dom/media/omx/MediaCodecReader.cpp
dom/media/omx/MediaCodecReader.h
dom/media/omx/RtspMediaCodecReader.cpp
dom/media/omx/RtspMediaCodecReader.h
layout/style/test/test_flexbox_align_self_auto.html
mobile/android/confvars.sh
--- a/.hgignore
+++ b/.hgignore
@@ -99,16 +99,19 @@ GPATH
 # XCode project cruft
 ^embedding/ios/GeckoEmbed/GeckoEmbed.xcodeproj/project.xcworkspace/xcuserdata
 ^embedding/ios/GeckoEmbed/GeckoEmbed.xcodeproj/xcuserdata
 
 # Ignore mozharness execution files
 ^testing/mozharness/logs/
 ^testing/mozharness/build/
 
+# Ignore tox generated dir
+.tox/
+
 # Ignore node_modules from eslint-plugin-mozilla
 ^testing/eslint-plugin-mozilla/node_modules/
 
 # Ignore talos virtualenv and tp5n files.
 # The tp5n set is supposed to be decompressed at
 # testing/talos/talos/page_load_test/tp5n in order to run tests like tps
 # locally. Similarly, running talos requires a Python package virtual
 # environment. Both the virtual environment and tp5n files end up littering
--- a/accessible/mac/mozAccessible.mm
+++ b/accessible/mac/mozAccessible.mm
@@ -547,17 +547,17 @@ ConvertToNSArray(nsTArray<ProxyAccessibl
     return nil;
 
   // Convert the given screen-global point in the cocoa coordinate system (with
   // origin in the bottom-left corner of the screen) into point in the Gecko
   // coordinate system (with origin in a top-left screen point).
   NSScreen* mainView = [[NSScreen screens] objectAtIndex:0];
   NSPoint tmpPoint = NSMakePoint(point.x,
                                  [mainView frame].size.height - point.y);
-  nsIntPoint geckoPoint = nsCocoaUtils::
+  LayoutDeviceIntPoint geckoPoint = nsCocoaUtils::
     CocoaPointsToDevPixels(tmpPoint, nsCocoaUtils::GetBackingScaleFactor(mainView));
 
   mozAccessible* nativeChild = nil;
   if (accWrap) {
     Accessible* child = accWrap->ChildAtPoint(geckoPoint.x, geckoPoint.y,
                                   Accessible::eDeepestChild);
     if (child)
       nativeChild = GetNativeFromGeckoAccessible(child);
--- a/browser/components/migration/nsEdgeReadingListExtractor.cpp
+++ b/browser/components/migration/nsEdgeReadingListExtractor.cpp
@@ -8,16 +8,17 @@
 
 #include "nsCOMPtr.h"
 #include "nsIConsoleService.h"
 #include "nsIMutableArray.h"
 #include "nsIWritablePropertyBag2.h"
 #include "nsNetUtil.h"
 #include "nsServiceManagerUtils.h"
 #include "nsWindowsMigrationUtils.h"
+#include "nsStringGlue.h"
 
 #define NS_HANDLE_JET_ERROR(err) { \
   if (err < JET_errSuccess) {	\
     rv = ConvertJETError(err); \
     goto CloseDB; \
   } \
 }
 
@@ -194,17 +195,20 @@ nsEdgeReadingListExtractor::ConvertJETEr
     case JET_errAccessDenied:
       return NS_ERROR_FILE_ACCESS_DENIED;
     case JET_errInvalidFilename:
       return NS_ERROR_FILE_INVALID_PATH;
     case JET_errFileNotFound:
       return NS_ERROR_FILE_NOT_FOUND;
     case JET_errDatabaseDirtyShutdown:
       return NS_ERROR_FILE_CORRUPTED;
-    default:
-      nsCOMPtr<nsIConsoleService> consoleService = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
-      wchar_t* msg = new wchar_t[80];
-      swprintf(msg, 80, MOZ_UTF16("Unexpected JET error from ESE database: %ld"), aError);
-      consoleService->LogStringMessage(msg);
+    default: {
+      nsCOMPtr<nsIConsoleService>
+        consoleService = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
+      nsAutoString msg;
+      msg.AppendLiteral("Unexpected JET error from ESE database: ");
+      msg.AppendInt(aError);
+      consoleService->LogStringMessage(msg.get());
       return NS_ERROR_FAILURE;
+    }
   }
 }
 
--- a/build/sanitizers/lsan_suppressions.txt
+++ b/build/sanitizers/lsan_suppressions.txt
@@ -84,16 +84,20 @@ leak:processInternalEntity
 leak:nss_ClearErrorStack
 
 # Bug 1122045 - Leaks in MessageLoop::MessageLoop()
 leak:MessageLoop::MessageLoop
 # This may not actually be related to MessageLoop.
 leak:base::WaitableEvent::TimedWait
 leak:MessageLoop::PostTask_Helper
 
+# Bug 1189430 - DNS leaks in mochitest-chrome.
+leak:nsDNSService::AsyncResolveExtended
+leak:_GetAddrInfo_Portable
+
 # Bug 1189568 - Indirect leaks of IMContextWrapper and nsIntRect.
 leak:nsWindow::Create
 leak:nsBaseWidget::StoreWindowClipRegion
 
 
 ###
 ### Leaks with system libraries in their stacks. These show up across a number of tests.
 ### Better symbols and disabling fast stackwalking may help diagnose these.
--- a/devtools/server/actors/root.js
+++ b/devtools/server/actors/root.js
@@ -90,16 +90,17 @@ loader.lazyGetter(this, "ppmm", () => {
  * iteration: alliterative lazy live lists.
  */
 function RootActor(aConnection, aParameters) {
   this.conn = aConnection;
   this._parameters = aParameters;
   this._onTabListChanged = this.onTabListChanged.bind(this);
   this._onAddonListChanged = this.onAddonListChanged.bind(this);
   this._onWorkerListChanged = this.onWorkerListChanged.bind(this);
+  this._onServiceWorkerRegistrationListChanged = this.onServiceWorkerRegistrationListChanged.bind(this);
   this._extraActors = {};
 
   this._globalActorPool = new ActorPool(this.conn);
   this.conn.addActorPool(this._globalActorPool);
 
   this._chromeActor = null;
 }
 
@@ -381,16 +382,47 @@ RootActor.prototype = {
     });
   },
 
   onWorkerListChanged: function () {
     this.conn.send({ from: this.actorID, type: "workerListChanged" });
     this._parameters.workerList.onListChanged = null;
   },
 
+  onListServiceWorkerRegistrations: function () {
+    let registrationList = this._parameters.serviceWorkerRegistrationList;
+    if (!registrationList) {
+      return { from: this.actorID, error: "noServiceWorkerRegistrations",
+               message: "This root actor has no service worker registrations." };
+    }
+
+    return registrationList.getList().then(actors => {
+      let pool = new ActorPool(this.conn);
+      for (let actor of actors) {
+        pool.addActor(actor);
+      }
+
+      this.conn.removeActorPool(this._serviceWorkerRegistrationActorPool);
+      this._serviceWorkerRegistrationActorPool = pool;
+      this.conn.addActorPool(this._serviceWorkerRegistrationActorPool);
+
+      registrationList.onListChanged = this._onServiceWorkerRegistrationListChanged;
+
+      return {
+        "from": this.actorID,
+        "registrations": actors.map(actor => actor.form())
+      };
+    });
+  },
+
+  onServiceWorkerRegistrationListChanged: function () {
+    this.conn.send({ from: this.actorID, type: "serviceWorkerRegistrationListChanged" });
+    this._parameters.serviceWorkerRegistrationList.onListChanged = null;
+  },
+
   onListProcesses: function () {
     let processes = [];
     for (let i = 0; i < ppmm.childCount; i++) {
       processes.push({
         id: i, // XXX: may not be a perfect id, but process message manager doesn't expose anything...
         parent: i == 0, // XXX Weak, but appear to be stable
         tabCount: undefined, // TODO: exposes process message manager on frameloaders in order to compute this
       });
@@ -468,15 +500,16 @@ RootActor.prototype = {
    }
 };
 
 RootActor.prototype.requestTypes = {
   "listTabs": RootActor.prototype.onListTabs,
   "getTab": RootActor.prototype.onGetTab,
   "listAddons": RootActor.prototype.onListAddons,
   "listWorkers": RootActor.prototype.onListWorkers,
+  "listServiceWorkerRegistrations": RootActor.prototype.onListServiceWorkerRegistrations,
   "listProcesses": RootActor.prototype.onListProcesses,
   "getProcess": RootActor.prototype.onGetProcess,
   "echo": RootActor.prototype.onEcho,
   "protocolDescription": RootActor.prototype.onProtocolDescription
 };
 
 exports.RootActor = RootActor;
--- a/devtools/server/actors/webbrowser.js
+++ b/devtools/server/actors/webbrowser.js
@@ -7,27 +7,28 @@
 "use strict";
 
 var { Ci, Cu } = require("chrome");
 var Services = require("Services");
 var promise = require("promise");
 var { ActorPool, createExtraActors, appendExtraActors } = require("devtools/server/actors/common");
 var { DebuggerServer } = require("devtools/server/main");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
-var { assert  } = DevToolsUtils;
+var { assert } = DevToolsUtils;
 var { TabSources } = require("./utils/TabSources");
 var makeDebugger = require("./utils/make-debugger");
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 loader.lazyRequireGetter(this, "RootActor", "devtools/server/actors/root", true);
 loader.lazyRequireGetter(this, "ThreadActor", "devtools/server/actors/script", true);
 loader.lazyRequireGetter(this, "unwrapDebuggerObjectGlobal", "devtools/server/actors/script", true);
 loader.lazyRequireGetter(this, "BrowserAddonActor", "devtools/server/actors/addon", true);
 loader.lazyRequireGetter(this, "WorkerActorList", "devtools/server/actors/worker", true);
+loader.lazyRequireGetter(this, "ServiceWorkerRegistrationActorList", "devtools/server/actors/worker", true);
 loader.lazyImporter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
 
 // Assumptions on events module:
 // events needs to be dispatched synchronously,
 // by calling the listeners in the order or registration.
 loader.lazyRequireGetter(this, "events", "sdk/event/core");
 
 loader.lazyRequireGetter(this, "StyleSheetActor", "devtools/server/actors/stylesheets", true);
@@ -125,16 +126,17 @@ exports.sendShutdownEvent = sendShutdown
  */
 function createRootActor(aConnection)
 {
   return new RootActor(aConnection,
                        {
                          tabList: new BrowserTabList(aConnection),
                          addonList: new BrowserAddonList(aConnection),
                          workerList: new WorkerActorList({}),
+                         serviceWorkerRegistrationList: new ServiceWorkerRegistrationActorList(),
                          globalActorFactories: DebuggerServer.globalActorFactories,
                          onShutdown: sendShutdownEvent
                        });
 }
 
 /**
  * A live list of BrowserTabActors representing the current browser tabs,
  * to be provided to the root actor to answer 'listTabs' requests.
@@ -1341,17 +1343,36 @@ TabActor.prototype = {
     this._styleSheetActors.clear();
     this.conn.removeActorPool(this._tabPool);
     this._tabPool = null;
     if (this._tabActorPool) {
       this.conn.removeActorPool(this._tabActorPool);
       this._tabActorPool = null;
     }
 
+    // Make sure that no more workerListChanged notifications are sent.
+    if (this._workerActorList !== null) {
+      this._workerActorList.onListChanged = null;
+      this._workerActorList = null;
+    }
+
+    if (this._workerActorPool !== null) {
+      this.conn.removeActorPool(this._workerActorPool);
+      this._workerActorPool = null;
+    }
+
+    // Make sure that no more serviceWorkerRegistrationChanged notifications are
+    // sent.
+    if (this._mustNotifyServiceWorkerRegistrationChanged) {
+      swm.removeListener(this);
+      this._mustNotifyServiceWorkerRegistrationChanged = false;
+    }
+
     this._attached = false;
+
     return true;
   },
 
   // Protocol Request Handlers
 
   onAttach: function BTA_onAttach(aRequest) {
     if (this.exited) {
       return { type: "exited" };
--- a/devtools/server/actors/worker.js
+++ b/devtools/server/actors/worker.js
@@ -223,8 +223,111 @@ WorkerActorList.prototype = {
   onUnregister: function (dbg) {
     if (matchWorkerDebugger(dbg, this._options)) {
       this._notifyListChanged();
     }
   }
 };
 
 exports.WorkerActorList = WorkerActorList;
+
+function ServiceWorkerRegistrationActor(registration) {
+  this._registration = registration;
+};
+
+ServiceWorkerRegistrationActor.prototype = {
+  actorPrefix: "serviceWorkerRegistration",
+
+  form: function () {
+    return {
+      actor: this.actorID,
+      scope: this._registration.scope
+    };
+  }
+};
+
+function ServiceWorkerRegistrationActorList() {
+  this._actors = new Map();
+  this._onListChanged = null;
+  this._mustNotify = false;
+  this.onRegister = this.onRegister.bind(this);
+  this.onUnregister = this.onUnregister.bind(this);
+};
+
+ServiceWorkerRegistrationActorList.prototype = {
+  getList: function () {
+    // Create a set of registrations.
+    let registrations = new Set();
+    let array = swm.getAllRegistrations();
+    for (let index = 0; index < array.length; ++index) {
+      registrations.add(
+        array.queryElementAt(index, Ci.nsIServiceWorkerRegistrationInfo));
+    }
+
+    // Delete each actor for which we don't have a registration.
+    for (let [registration, ] of this._actors) {
+      if (!registrations.has(registration)) {
+        this._actors.delete(registration);
+      }
+    }
+
+    // Create an actor for each registration for which we don't have one.
+    for (let registration of registrations) {
+      if (!this._actors.has(registration)) {
+        this._actors.set(registration,
+          new ServiceWorkerRegistrationActor(registration));
+      }
+    }
+
+    if (!this._mustNotify) {
+      if (this._onListChanged !== null) {
+        swm.addListener(this);
+      }
+      this._mustNotify = true;
+    }
+
+    let actors = [];
+    for (let [, actor] of this._actors) {
+      actors.push(actor);
+    }
+
+    return Promise.resolve(actors);
+  },
+
+  get onListchanged() {
+    return this._onListchanged;
+  },
+
+  set onListChanged(onListChanged) {
+    if (typeof onListChanged !== "function" && onListChanged !== null) {
+      throw new Error("onListChanged must be either a function or null.");
+    }
+
+    if (this._mustNotify) {
+      if (this._onListChanged === null && onListChanged !== null) {
+        swm.addListener(this);
+      }
+      if (this._onListChanged !== null && onListChanged === null) {
+        swm.removeListener(this);
+      }
+    }
+    this._onListChanged = onListChanged;
+  },
+
+  _notifyListChanged: function () {
+    this._onListChanged();
+
+    if (this._onListChanged !== null) {
+      swm.removeListener(this);
+    }
+    this._mustNotify = false;
+  },
+
+  onRegister: function (registration) {
+    this._notifyListChanged();
+  },
+
+  onUnregister: function (registration) {
+    this._notifyListChanged();
+  }
+};
+
+exports.ServiceWorkerRegistrationActorList = ServiceWorkerRegistrationActorList;
--- a/devtools/shared/client/main.js
+++ b/devtools/shared/client/main.js
@@ -155,16 +155,17 @@ const UnsolicitedNotifications = {
   "networkEventUpdate": "networkEventUpdate",
   "newGlobal": "newGlobal",
   "newScript": "newScript",
   "tabDetached": "tabDetached",
   "tabListChanged": "tabListChanged",
   "reflowActivity": "reflowActivity",
   "addonListChanged": "addonListChanged",
   "workerListChanged": "workerListChanged",
+  "serviceWorkerRegistrationListChanged": "serviceWorkerRegistrationList",
   "tabNavigated": "tabNavigated",
   "frameUpdate": "frameUpdate",
   "pageError": "pageError",
   "documentLoad": "documentLoad",
   "enteredFrame": "enteredFrame",
   "exitedFrame": "exitedFrame",
   "appOpen": "appOpen",
   "appClose": "appClose",
@@ -1525,16 +1526,25 @@ RootClient.prototype = {
    *
    * @param function aOnResponse
    *        Called with the response packet.
    */
   listWorkers: DebuggerClient.requester({ type: "listWorkers" },
                                         { telemetry: "LISTWORKERS" }),
 
   /**
+   * List the registered service workers.
+   *
+   * @param function aOnResponse
+   *        Called with the response packet.
+   */
+  listServiceWorkerRegistrations: DebuggerClient.requester({ type: "listServiceWorkerRegistrations" },
+                                                           { telemetry: "LISTSERVICEWORKERREGISTRATIONS" }),
+
+  /**
    * List the running processes.
    *
    * @param function aOnResponse
    *        Called with the response packet.
    */
   listProcesses: DebuggerClient.requester({ type: "listProcesses" },
                                           { telemetry: "LISTPROCESSES" }),
 
--- a/dom/base/StructuredCloneHolder.cpp
+++ b/dom/base/StructuredCloneHolder.cpp
@@ -553,24 +553,25 @@ StructuredCloneHolder::WriteFullySeriali
 namespace {
 
 // Recursive!
 already_AddRefed<BlobImpl>
 EnsureBlobForBackgroundManager(BlobImpl* aBlobImpl,
                                PBackgroundChild* aManager = nullptr)
 {
   MOZ_ASSERT(aBlobImpl);
+  RefPtr<BlobImpl> blobImpl = aBlobImpl;
 
   if (!aManager) {
     aManager = BackgroundChild::GetForCurrentThread();
-    MOZ_ASSERT(aManager);
+    if (!aManager) {
+      return blobImpl.forget();
+    }
   }
 
-  RefPtr<BlobImpl> blobImpl = aBlobImpl;
-
   const nsTArray<RefPtr<BlobImpl>>* subBlobImpls =
     aBlobImpl->GetSubBlobImpls();
 
   if (!subBlobImpls || !subBlobImpls->Length()) {
     if (nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryObject(blobImpl)) {
       // Always make sure we have a blob from an actor we can use on this
       // thread.
       BlobChild* blobChild = BlobChild::GetOrCreate(aManager, blobImpl);
@@ -665,16 +666,18 @@ WriteBlob(JSStructuredCloneWriter* aWrit
 {
   MOZ_ASSERT(aWriter);
   MOZ_ASSERT(aBlob);
   MOZ_ASSERT(aHolder);
 
   RefPtr<BlobImpl> blobImpl = EnsureBlobForBackgroundManager(aBlob->Impl());
   MOZ_ASSERT(blobImpl);
 
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(blobImpl->SetMutable(false)));
+
   // We store the position of the blobImpl in the array as index.
   if (JS_WriteUint32Pair(aWriter, SCTAG_DOM_BLOB,
                          aHolder->BlobImpls().Length())) {
     aHolder->BlobImpls().AppendElement(blobImpl);
     return true;
   }
 
   return false;
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -2562,17 +2562,17 @@ CheckLeafLayers(Layer* aLayer, const nsI
   Layer* child = aLayer->GetFirstChild();
   if (child) {
     while (child) {
       if (!CheckLeafLayers(child, offset, aCoveredRegion))
         return false;
       child = child->GetNextSibling();
     }
   } else {
-    nsIntRegion rgn = aLayer->GetVisibleRegion();
+    nsIntRegion rgn = aLayer->GetVisibleRegion().ToUnknownRegion();
     rgn.MoveBy(offset);
     nsIntRegion tmp;
     tmp.And(rgn, *aCoveredRegion);
     if (!tmp.IsEmpty())
       return false;
     aCoveredRegion->Or(*aCoveredRegion, rgn);
   }
 
@@ -2598,17 +2598,17 @@ nsDOMWindowUtils::LeafLayersPartitionWin
   if (!root)
     return NS_ERROR_FAILURE;
 
   nsIntPoint offset(0, 0);
   nsIntRegion coveredRegion;
   if (!CheckLeafLayers(root, offset, &coveredRegion)) {
     *aResult = false;
   }
-  if (!coveredRegion.IsEqual(root->GetVisibleRegion())) {
+  if (!coveredRegion.IsEqual(root->GetVisibleRegion().ToUnknownRegion())) {
     *aResult = false;
   }
 #endif
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::CheckAndClearPaintedState(nsIDOMElement* aElement, bool* aResult)
--- a/dom/camera/GonkCameraControl.cpp
+++ b/dom/camera/GonkCameraControl.cpp
@@ -2208,37 +2208,28 @@ nsGonkCameraControl::LoadRecorderProfile
       name.AssignASCII("default");
       mRecorderProfiles.Put(name, profiles[bestIndexMatch]);
     }
   }
 
   return NS_OK;
 }
 
-/* static */ PLDHashOperator
-nsGonkCameraControl::Enumerate(const nsAString& aProfileName,
-                               RecorderProfile* aProfile,
-                               void* aUserArg)
-{
-  nsTArray<nsString>* profiles = static_cast<nsTArray<nsString>*>(aUserArg);
-  MOZ_ASSERT(profiles);
-  profiles->AppendElement(aProfileName);
-  return PL_DHASH_NEXT;
-}
-
 nsresult
 nsGonkCameraControl::GetRecorderProfiles(nsTArray<nsString>& aProfiles)
 {
   nsresult rv = LoadRecorderProfiles();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   aProfiles.Clear();
-  mRecorderProfiles.EnumerateRead(Enumerate, static_cast<void*>(&aProfiles));
+  for (auto iter = mRecorderProfiles.Iter(); !iter.Done(); iter.Next()) {
+    aProfiles.AppendElement(iter.Key());
+  }
   return NS_OK;
 }
 
 ICameraControl::RecorderProfile*
 nsGonkCameraControl::GetProfileInfo(const nsAString& aProfile)
 {
   RecorderProfile* profile;
   if (!mRecorderProfiles.Get(aProfile, &profile)) {
--- a/dom/camera/GonkCameraControl.h
+++ b/dom/camera/GonkCameraControl.h
@@ -151,19 +151,16 @@ protected:
                                        const Size& aMaxSize, uint32_t aCaptureSizeKey);
   nsresult MaybeAdjustVideoSize();
   nsresult PausePreview();
   nsresult GetSupportedSize(const Size& aSize, const nsTArray<Size>& supportedSizes, Size& best);
 
   void CreatePoster(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight, int32_t aRotation);
 
   nsresult LoadRecorderProfiles();
-  static PLDHashOperator Enumerate(const nsAString& aProfileName,
-                                   RecorderProfile* aProfile,
-                                   void* aUserArg);
 
   friend class SetPictureSize;
   friend class SetThumbnailSize;
   nsresult SetPictureSize(const Size& aSize);
   nsresult SetPictureSizeImpl(const Size& aSize);
   nsresult SetThumbnailSize(const Size& aSize);
   nsresult UpdateThumbnailSize();
   nsresult SetThumbnailSizeImpl(const Size& aSize);
--- a/dom/camera/GonkCameraParameters.cpp
+++ b/dom/camera/GonkCameraParameters.cpp
@@ -64,37 +64,29 @@ GonkCameraParameters::FindVendorSpecific
       // which indicates that this key exists.
       return aPotentialKeys[i];
     }
   }
 
   return nullptr;
 }
 
-/* static */ PLDHashOperator
-GonkCameraParameters::EnumerateFlatten(const nsACString& aKey,
-                                       nsCString* aValue,
-                                       void* aUserArg)
-{
-  nsCString* data = static_cast<nsCString*>(aUserArg);
-  if (!data->IsEmpty()) {
-    data->Append(';');
-  }
-  data->Append(aKey);
-  data->Append('=');
-  data->Append(*aValue);
-  return PL_DHASH_NEXT;
-}
-
 String8
 GonkCameraParameters::Flatten() const
 {
   MutexAutoLock lock(mLock);
   nsCString data;
-  mParams.EnumerateRead(EnumerateFlatten, static_cast<void*>(&data));
+  for (auto iter = mParams.ConstIter(); !iter.Done(); iter.Next()) {
+    if (!data.IsEmpty()) {
+      data.Append(';');
+    }
+    data.Append(iter.Key());
+    data.Append('=');
+    data.Append(*iter.UserData());
+  }
   return String8(data.Data());
 }
 
 nsresult
 GonkCameraParameters::Unflatten(const String8& aFlatParameters)
 {
   MutexAutoLock lock(mLock);
   mParams.Clear();
--- a/dom/camera/GonkCameraParameters.h
+++ b/dom/camera/GonkCameraParameters.h
@@ -88,18 +88,16 @@ protected:
   const char* mVendorSpecificKeySupportedIsoModes;
   nsTArray<int> mZoomRatios;
   nsTArray<nsString> mIsoModes;
   nsTArray<nsString> mSceneModes;
   nsTArray<nsString> mMeteringModes;
   nsClassHashtable<nsStringHashKey, nsCString> mIsoModeMap;
   nsClassHashtable<nsCStringHashKey, nsCString> mParams;
 
-  static PLDHashOperator EnumerateFlatten(const nsACString& aKey, nsCString* aValue, void* aUserArg);
-
   nsresult SetImpl(const char* aKey, const char* aValue)
   {
     if (!aValue || strchr(aValue, ';') || strchr(aValue, '=')) {
       return NS_ERROR_ILLEGAL_VALUE;
     }
     nsDependentCString key(aKey);
     mParams.Put(key, new nsCString(aValue));
     return NS_OK;
--- a/dom/camera/GonkRecorderProfiles.cpp
+++ b/dom/camera/GonkRecorderProfiles.cpp
@@ -264,28 +264,16 @@ GonkRecorderProfile::GonkRecorderProfile
 {
   mOutputFormat = static_cast<output_format>(GetProfileParameter("file.format"));
   bool isValid = Translate(mOutputFormat, mContainer);
   isValid = GetMimeType(mOutputFormat, mMimeType) ? isValid : false;
 
   mIsValid = isValid && mAudio.IsValid() && mVideo.IsValid();
 }
 
-/* static */ PLDHashOperator
-GonkRecorderProfile::Enumerate(const nsAString& aProfileName,
-                               GonkRecorderProfile* aProfile,
-                               void* aUserArg)
-{
-  nsTArray<RefPtr<ICameraControl::RecorderProfile>>* profiles =
-    static_cast<nsTArray<RefPtr<ICameraControl::RecorderProfile>>*>(aUserArg);
-  MOZ_ASSERT(profiles);
-  profiles->AppendElement(aProfile);
-  return PL_DHASH_NEXT;
-}
-
 /* static */
 already_AddRefed<GonkRecorderProfile>
 GonkRecorderProfile::CreateProfile(uint32_t aCameraId, int aQuality)
 {
   if (!IsProfileSupported(aCameraId, aQuality)) {
     DOM_CAMERA_LOGI("Profile %d not supported by platform\n", aQuality);
     return nullptr;
   }
@@ -374,17 +362,19 @@ GonkRecorderProfile::GetAll(uint32_t aCa
                             nsTArray<RefPtr<ICameraControl::RecorderProfile>>& aProfiles)
 {
   ProfileHashtable* profiles = GetProfileHashtable(aCameraId);
   if (!profiles) {
     return NS_ERROR_FAILURE;
   }
 
   aProfiles.Clear();
-  profiles->EnumerateRead(Enumerate, static_cast<void*>(&aProfiles));
+  for (auto iter = profiles->Iter(); !iter.Done(); iter.Next()) {
+    aProfiles.AppendElement(iter.UserData());
+  }
 
   return NS_OK;
 }
 
 #ifdef MOZ_WIDGET_GONK
 nsresult
 GonkRecorderProfile::ConfigureRecorder(GonkRecorder& aRecorder)
 {
--- a/dom/camera/GonkRecorderProfiles.h
+++ b/dom/camera/GonkRecorderProfiles.h
@@ -137,19 +137,16 @@ protected:
   bool IsValid() const { return mIsValid; };
 
 #ifdef MOZ_WIDGET_GONK
   nsresult ConfigureRecorder(android::GonkRecorder& aRecorder);
 #endif
   static already_AddRefed<GonkRecorderProfile> CreateProfile(uint32_t aCameraId,
                                                              int aQuality);
   static ProfileHashtable* GetProfileHashtable(uint32_t aCameraId);
-  static PLDHashOperator Enumerate(const nsAString& aProfileName,
-                                   GonkRecorderProfile* aProfile,
-                                   void* aUserArg);
 
   uint32_t mCameraId;
   int mQuality;
   bool mIsValid;
   android::output_format mOutputFormat;
 
   static nsClassHashtable<nsUint32HashKey, ProfileHashtable> sProfiles;
 
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -47,16 +47,17 @@
 #include "nsPresContext.h"
 #include "nsIDocShell.h"
 #include "nsNameSpaceManager.h"
 #include "nsError.h"
 #include "nsScriptLoader.h"
 #include "nsRuleData.h"
 #include "nsIPrincipal.h"
 #include "nsContainerFrame.h"
+#include "nsStyleUtil.h"
 
 #include "nsPresState.h"
 #include "nsILayoutHistoryState.h"
 
 #include "nsHTMLParts.h"
 #include "nsContentUtils.h"
 #include "mozilla/dom/DirectionalityUtils.h"
 #include "nsString.h"
@@ -1321,22 +1322,38 @@ nsGenericHTMLElement::MapCommonAttribute
         else if (value->Equals(nsGkAtoms::_false, eIgnoreCase)) {
             userModify->SetIntValue(NS_STYLE_USER_MODIFY_READ_ONLY,
                                     eCSSUnit_Enumerated);
         }
       }
     }
   }
 
-  if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Font)) {
-    nsCSSValue* lang = aData->ValueForLang();
-    if (lang->GetUnit() == eCSSUnit_Null) {
-      const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::lang);
-      if (value && value->Type() == nsAttrValue::eString) {
-        lang->SetStringValue(value->GetStringValue(), eCSSUnit_Ident);
+  const nsAttrValue* langValue = aAttributes->GetAttr(nsGkAtoms::lang);
+  if (langValue && langValue->Type() == nsAttrValue::eString) {
+    if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Font)) {
+      nsCSSValue* lang = aData->ValueForLang();
+      if (lang->GetUnit() == eCSSUnit_Null) {
+        lang->SetStringValue(langValue->GetStringValue(), eCSSUnit_Ident);
+      }
+    }
+    if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Text)) {
+      nsCSSValue* emphasisPos = aData->ValueForTextEmphasisPosition();
+      if (emphasisPos->GetUnit() == eCSSUnit_Null) {
+        const nsAString& lang = langValue->GetStringValue();
+        if (nsStyleUtil::MatchesLanguagePrefix(lang, MOZ_UTF16("zh"))) {
+          emphasisPos->SetIntValue(NS_STYLE_TEXT_EMPHASIS_POSITION_DEFAULT_ZH,
+                                   eCSSUnit_Enumerated);
+        } else if (nsStyleUtil::MatchesLanguagePrefix(lang, MOZ_UTF16("ja")) ||
+                   nsStyleUtil::MatchesLanguagePrefix(lang, MOZ_UTF16("mn"))) {
+          // This branch is currently no part of the spec.
+          // See bug 1040668 comment 69 and comment 75.
+          emphasisPos->SetIntValue(NS_STYLE_TEXT_EMPHASIS_POSITION_DEFAULT,
+                                   eCSSUnit_Enumerated);
+        }
       }
     }
   }
 }
 
 void
 nsGenericHTMLElement::MapCommonAttributesInto(const nsMappedAttributes* aAttributes,
                                               nsRuleData* aData)
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -14950,73 +14950,59 @@ void
 VersionChangeTransaction::UpdateMetadata(nsresult aResult)
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(GetDatabase());
   MOZ_ASSERT(mOpenDatabaseOp);
   MOZ_ASSERT(!!mActorWasAlive == !!mOpenDatabaseOp->mDatabase);
   MOZ_ASSERT_IF(mActorWasAlive, !mOpenDatabaseOp->mDatabaseId.IsEmpty());
 
-  class MOZ_STACK_CLASS Helper final
-  {
-  public:
-    static PLDHashOperator
-    Enumerate(const uint64_t& aKey,
-              RefPtr<FullObjectStoreMetadata>& aValue,
-              void* /* aClosure */)
-    {
-      MOZ_ASSERT(aKey);
-      MOZ_ASSERT(aValue);
-
-      if (aValue->mDeleted) {
-        return PL_DHASH_REMOVE;
-      }
-
-      aValue->mIndexes.Enumerate(Enumerate, nullptr);
-#ifdef DEBUG
-      aValue->mIndexes.MarkImmutable();
-#endif
-
-      return PL_DHASH_NEXT;
-    }
-
-  private:
-    static PLDHashOperator
-    Enumerate(const uint64_t& aKey,
-              RefPtr<FullIndexMetadata>& aValue,
-              void* /* aClosure */)
-    {
-      MOZ_ASSERT(aKey);
-      MOZ_ASSERT(aValue);
-
-      if (aValue->mDeleted) {
-        return PL_DHASH_REMOVE;
-      }
-
-      return PL_DHASH_NEXT;
-    }
-  };
-
   if (IsActorDestroyed() || !mActorWasAlive) {
     return;
   }
 
   RefPtr<FullDatabaseMetadata> oldMetadata;
   mOldMetadata.swap(oldMetadata);
 
   DatabaseActorInfo* info;
   if (!gLiveDatabaseHashtable->Get(oldMetadata->mDatabaseId, &info)) {
     return;
   }
 
   MOZ_ASSERT(!info->mLiveDatabases.IsEmpty());
 
   if (NS_SUCCEEDED(aResult)) {
     // Remove all deleted objectStores and indexes, then mark immutable.
-    info->mMetadata->mObjectStores.Enumerate(Helper::Enumerate, nullptr);
+    for (auto objectStoreIter = info->mMetadata->mObjectStores.Iter();
+         !objectStoreIter.Done();
+         objectStoreIter.Next()) {
+      MOZ_ASSERT(objectStoreIter.Key());
+      RefPtr<FullObjectStoreMetadata>& metadata = objectStoreIter.Data();
+      MOZ_ASSERT(metadata);
+
+      if (metadata->mDeleted) {
+        objectStoreIter.Remove();
+        continue;
+      }
+
+      for (auto indexIter = metadata->mIndexes.Iter();
+           !indexIter.Done();
+           indexIter.Next()) {
+        MOZ_ASSERT(indexIter.Key());
+        RefPtr<FullIndexMetadata>& index = indexIter.Data();
+        MOZ_ASSERT(index);
+
+        if (index->mDeleted) {
+          indexIter.Remove();
+        }
+      }
+#ifdef DEBUG
+      metadata->mIndexes.MarkImmutable();
+#endif
+    }
 #ifdef DEBUG
     info->mMetadata->mObjectStores.MarkImmutable();
 #endif
   } else {
     // Replace metadata pointers for all live databases.
     info->mMetadata = oldMetadata.forget();
 
     for (uint32_t count = info->mLiveDatabases.Length(), index = 0;
@@ -15904,43 +15890,34 @@ FileManager::Init(nsIFile* aDirectory,
   }
 
   return NS_OK;
 }
 
 nsresult
 FileManager::Invalidate()
 {
-  class MOZ_STACK_CLASS Helper final
-  {
-  public:
-    static PLDHashOperator
-    ClearDBRefs(const uint64_t& aKey, FileInfo*& aValue, void* aUserArg)
-    {
-      MOZ_ASSERT(aValue);
-
-      if (aValue->LockedClearDBRefs()) {
-        return PL_DHASH_NEXT;
-      }
-
-      return PL_DHASH_REMOVE;
-    }
-  };
-
   if (IndexedDatabaseManager::IsClosed()) {
     MOZ_ASSERT(false, "Shouldn't be called after shutdown!");
     return NS_ERROR_UNEXPECTED;
   }
 
   MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
 
   MOZ_ASSERT(!mInvalidated);
   mInvalidated = true;
 
-  mFileInfos.Enumerate(Helper::ClearDBRefs, nullptr);
+  for (auto iter = mFileInfos.Iter(); !iter.Done(); iter.Next()) {
+    FileInfo* info = iter.Data();
+    MOZ_ASSERT(info);
+
+    if (!info->LockedClearDBRefs()) {
+      iter.Remove();
+    }
+  }
 
   return NS_OK;
 }
 
 already_AddRefed<nsIFile>
 FileManager::GetDirectory()
 {
   return GetFileForPath(mDirectoryPath);
--- a/dom/indexedDB/IDBDatabase.cpp
+++ b/dom/indexedDB/IDBDatabase.cpp
@@ -1020,37 +1020,25 @@ IDBDatabase::GetOrCreateFileActorForBlob
 }
 
 void
 IDBDatabase::NoteFinishedFileActor(PBackgroundIDBDatabaseFileChild* aFileActor)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aFileActor);
 
-  class MOZ_STACK_CLASS Helper final
-  {
-  public:
-    static PLDHashOperator
-    Remove(nsISupports* aKey,
-           PBackgroundIDBDatabaseFileChild*& aValue,
-           void* aClosure)
-    {
-      MOZ_ASSERT(aKey);
-      MOZ_ASSERT(aValue);
-      MOZ_ASSERT(aClosure);
+  for (auto iter = mFileActors.Iter(); !iter.Done(); iter.Next()) {
+    MOZ_ASSERT(iter.Key());
+    PBackgroundIDBDatabaseFileChild* actor = iter.Data();
+    MOZ_ASSERT(actor);
 
-      if (aValue == static_cast<PBackgroundIDBDatabaseFileChild*>(aClosure)) {
-        return PL_DHASH_REMOVE;
-      }
-
-      return PL_DHASH_NEXT;
+    if (actor == aFileActor) {
+      iter.Remove();
     }
-  };
-
-  mFileActors.Enumerate(&Helper::Remove, aFileActor);
+  }
 }
 
 void
 IDBDatabase::NoteReceivedBlob(Blob* aBlob)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aBlob);
   MOZ_ASSERT(mBackgroundActor);
@@ -1158,53 +1146,40 @@ IDBDatabase::GetQuotaInfo(nsACString& aO
   MOZ_CRASH("Should never get here!");
 }
 
 void
 IDBDatabase::ExpireFileActors(bool aExpireAll)
 {
   AssertIsOnOwningThread();
 
-  class MOZ_STACK_CLASS Helper final
-  {
-  public:
-    static PLDHashOperator
-    MaybeExpireFileActors(nsISupports* aKey,
-                          PBackgroundIDBDatabaseFileChild*& aValue,
-                          void* aClosure)
-    {
-      MOZ_ASSERT(aKey);
-      MOZ_ASSERT(aValue);
-      MOZ_ASSERT(aClosure);
+  if (mBackgroundActor && mFileActors.Count()) {
+    for (auto iter = mFileActors.Iter(); !iter.Done(); iter.Next()) {
+      nsISupports* key = iter.Key();
+      PBackgroundIDBDatabaseFileChild* actor = iter.Data();
+      MOZ_ASSERT(key);
+      MOZ_ASSERT(actor);
 
-      const bool expiringAll = *static_cast<bool*>(aClosure);
-
-      bool shouldExpire = expiringAll;
+      bool shouldExpire = aExpireAll;
       if (!shouldExpire) {
-        nsCOMPtr<nsIWeakReference> weakRef = do_QueryInterface(aKey);
+        nsCOMPtr<nsIWeakReference> weakRef = do_QueryInterface(key);
         MOZ_ASSERT(weakRef);
 
         nsCOMPtr<nsISupports> referent = do_QueryReferent(weakRef);
         shouldExpire = !referent;
       }
 
       if (shouldExpire) {
-        PBackgroundIDBDatabaseFileChild::Send__delete__(aValue);
+        PBackgroundIDBDatabaseFileChild::Send__delete__(actor);
 
-        if (!expiringAll) {
-          return PL_DHASH_REMOVE;
+        if (!aExpireAll) {
+          iter.Remove();
         }
       }
-
-      return PL_DHASH_NEXT;
     }
-  };
-
-  if (mBackgroundActor && mFileActors.Count()) {
-    mFileActors.Enumerate(&Helper::MaybeExpireFileActors, &aExpireAll);
     if (aExpireAll) {
       mFileActors.Clear();
     }
   } else {
     MOZ_ASSERT(!mFileActors.Count());
   }
 
   if (mReceivedBlobs.Count()) {
--- a/dom/ipc/Blob.cpp
+++ b/dom/ipc/Blob.cpp
@@ -1748,48 +1748,43 @@ class BlobChild::RemoteBlobImpl
   , public nsIRemoteBlob
 {
 protected:
   class CreateStreamHelper;
 
   BlobChild* mActor;
   nsCOMPtr<nsIEventTarget> mActorTarget;
 
+  // We use this pointer to keep a live a blobImpl coming from a different
+  // process until this one is fully created. We set it to null when
+  // SendCreatedFromKnownBlob() is received. This is used only with KnownBlob
+  // params in the CTOR of a IPC BlobImpl.
+  RefPtr<BlobImpl> mDifferentProcessBlobImpl;
+
   RefPtr<BlobImpl> mSameProcessBlobImpl;
 
   const bool mIsSlice;
 
 public:
   // For File.
   RemoteBlobImpl(BlobChild* aActor,
+                 BlobImpl* aRemoteBlobImpl,
                  const nsAString& aName,
                  const nsAString& aContentType,
                  uint64_t aLength,
                  int64_t aModDate,
-                 BlobDirState aDirState);
+                 BlobDirState aDirState,
+                 bool aIsSameProcessBlob);
 
   // For Blob.
   RemoteBlobImpl(BlobChild* aActor,
-                 const nsAString& aContentType,
-                 uint64_t aLength);
-
-  // For same-process blobs.
-  RemoteBlobImpl(BlobChild* aActor,
-                 BlobImpl* aSameProcessBlobImpl,
-                 const nsAString& aName,
+                 BlobImpl* aRemoteBlobImpl,
                  const nsAString& aContentType,
                  uint64_t aLength,
-                 int64_t aModDate,
-                 BlobDirState aDirState);
-
-  // For same-process blobs.
-  RemoteBlobImpl(BlobChild* aActor,
-                 BlobImpl* aSameProcessBlobImpl,
-                 const nsAString& aContentType,
-                 uint64_t aLength);
+                 bool aIsSameProcessBlob);
 
   // For mystery blobs.
   explicit
   RemoteBlobImpl(BlobChild* aActor);
 
   void
   NoteDyingActor();
 
@@ -1852,16 +1847,23 @@ public:
   SetMutable(bool aMutable) override;
 
   virtual BlobChild*
   GetBlobChild() override;
 
   virtual BlobParent*
   GetBlobParent() override;
 
+  void
+  NullifyDifferentProcessBlobImpl()
+  {
+    MOZ_ASSERT(mDifferentProcessBlobImpl);
+    mDifferentProcessBlobImpl = nullptr;
+  }
+
 protected:
   // For SliceImpl.
   RemoteBlobImpl(const nsAString& aContentType, uint64_t aLength);
 
   ~RemoteBlobImpl()
   {
     MOZ_ASSERT_IF(mActorTarget,
                   EventTargetIsOnCurrentThread(mActorTarget));
@@ -2081,66 +2083,53 @@ private:
 };
 
 /*******************************************************************************
  * BlobChild::RemoteBlobImpl
  ******************************************************************************/
 
 BlobChild::
 RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor,
+                               BlobImpl* aRemoteBlobImpl,
                                const nsAString& aName,
                                const nsAString& aContentType,
                                uint64_t aLength,
                                int64_t aModDate,
-                               BlobDirState aDirState)
+                               BlobDirState aDirState,
+                               bool aIsSameProcessBlob)
   : BlobImplBase(aName, aContentType, aLength, aModDate, aDirState)
   , mIsSlice(false)
 {
-  CommonInit(aActor);
-}
-
-BlobChild::
-RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor,
-                               const nsAString& aContentType,
-                               uint64_t aLength)
-  : BlobImplBase(aContentType, aLength)
-  , mIsSlice(false)
-{
-  CommonInit(aActor);
-}
-
-BlobChild::
-RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor,
-                               BlobImpl* aSameProcessBlobImpl,
-                               const nsAString& aName,
-                               const nsAString& aContentType,
-                               uint64_t aLength,
-                               int64_t aModDate,
-                               BlobDirState aDirState)
-  : BlobImplBase(aName, aContentType, aLength, aModDate, aDirState)
-  , mSameProcessBlobImpl(aSameProcessBlobImpl)
-  , mIsSlice(false)
-{
-  MOZ_ASSERT(aSameProcessBlobImpl);
-  MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+  if (aIsSameProcessBlob) {
+    MOZ_ASSERT(aRemoteBlobImpl);
+    mSameProcessBlobImpl = aRemoteBlobImpl;
+    MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+  } else {
+    mDifferentProcessBlobImpl = aRemoteBlobImpl;
+  }
 
   CommonInit(aActor);
 }
 
 BlobChild::
 RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor,
-                               BlobImpl* aSameProcessBlobImpl,
+                               BlobImpl* aRemoteBlobImpl,
                                const nsAString& aContentType,
-                               uint64_t aLength)
+                               uint64_t aLength,
+                               bool aIsSameProcessBlob)
   : BlobImplBase(aContentType, aLength)
-  , mSameProcessBlobImpl(aSameProcessBlobImpl)
   , mIsSlice(false)
 {
-  MOZ_ASSERT(aSameProcessBlobImpl);
-  MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+  if (aIsSameProcessBlob) {
+    MOZ_ASSERT(aRemoteBlobImpl);
+    mSameProcessBlobImpl = aRemoteBlobImpl;
+    MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+  } else {
+    mDifferentProcessBlobImpl = aRemoteBlobImpl;
+  }
 
   CommonInit(aActor);
 }
 
 BlobChild::
 RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor)
   : BlobImplBase(EmptyString(), EmptyString(), UINT64_MAX, INT64_MAX)
   , mIsSlice(false)
@@ -3028,30 +3017,38 @@ BlobChild::CommonInit(BlobChild* aOther,
 
   nsString contentType;
   otherImpl->GetType(contentType);
 
   ErrorResult rv;
   uint64_t length = otherImpl->GetSize(rv);
   MOZ_ASSERT(!rv.Failed());
 
-  RefPtr<RemoteBlobImpl> remoteBlob;
+  RemoteBlobImpl* remoteBlob = nullptr;
   if (otherImpl->IsFile()) {
     nsString name;
     otherImpl->GetName(name);
 
     int64_t modDate = otherImpl->GetLastModified(rv);
     MOZ_ASSERT(!rv.Failed());
 
-    remoteBlob = new RemoteBlobImpl(this, name, contentType, length, modDate,
-                                    otherImpl->GetDirState());
+    remoteBlob = new RemoteBlobImpl(this, otherImpl, name, contentType, length,
+                                    modDate, otherImpl->GetDirState(),
+                                    false /* SameProcessBlobImpl */);
   } else {
-    remoteBlob = new RemoteBlobImpl(this, contentType, length);
+    remoteBlob = new RemoteBlobImpl(this, otherImpl, contentType, length,
+                                    false /* SameProcessBlobImpl */);
   }
 
+  // This RemoteBlob must be kept alive untill RecvCreatedFromKnownBlob is
+  // called because the parent will send this notification and we must be able
+  // to manage it.
+  MOZ_ASSERT(remoteBlob);
+  remoteBlob->AddRef();
+
   CommonInit(aOther->ParentID(), remoteBlob);
 }
 
 void
 BlobChild::CommonInit(const ChildBlobConstructorParams& aParams)
 {
   AssertIsOnOwningThread();
 
@@ -3068,29 +3065,32 @@ BlobChild::CommonInit(const ChildBlobCon
 
   RefPtr<RemoteBlobImpl> remoteBlob;
 
   switch (paramsType) {
     case AnyBlobConstructorParams::TNormalBlobConstructorParams: {
       const NormalBlobConstructorParams& params =
         blobParams.get_NormalBlobConstructorParams();
       remoteBlob =
-        new RemoteBlobImpl(this, params.contentType(), params.length());
+        new RemoteBlobImpl(this, nullptr, params.contentType(), params.length(),
+                           false /* SameProcessBlobImpl */);
       break;
     }
 
     case AnyBlobConstructorParams::TFileBlobConstructorParams: {
       const FileBlobConstructorParams& params =
         blobParams.get_FileBlobConstructorParams();
       remoteBlob = new RemoteBlobImpl(this,
+                                      nullptr,
                                       params.name(),
                                       params.contentType(),
                                       params.length(),
                                       params.modDate(),
-                                      BlobDirState(params.dirState()));
+                                      BlobDirState(params.dirState()),
+                                      false /* SameProcessBlobImpl */);
       break;
     }
 
     case AnyBlobConstructorParams::TSameProcessBlobConstructorParams: {
       MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
 
       const SameProcessBlobConstructorParams& params =
         blobParams.get_SameProcessBlobConstructorParams();
@@ -3115,19 +3115,21 @@ BlobChild::CommonInit(const ChildBlobCon
 
         remoteBlob =
           new RemoteBlobImpl(this,
                              blobImpl,
                              name,
                              contentType,
                              size,
                              lastModifiedDate,
-                             blobImpl->GetDirState());
+                             blobImpl->GetDirState(),
+                             true /* SameProcessBlobImpl */);
       } else {
-        remoteBlob = new RemoteBlobImpl(this, blobImpl, contentType, size);
+        remoteBlob = new RemoteBlobImpl(this, blobImpl, contentType, size,
+                                        true /* SameProcessBlobImpl */);
       }
 
       break;
     }
 
     case AnyBlobConstructorParams::TMysteryBlobConstructorParams: {
       remoteBlob = new RemoteBlobImpl(this);
       break;
@@ -3587,16 +3589,30 @@ bool
 BlobChild::DeallocPBlobStreamChild(PBlobStreamChild* aActor)
 {
   AssertIsOnOwningThread();
 
   delete static_cast<InputStreamChild*>(aActor);
   return true;
 }
 
+bool
+BlobChild::RecvCreatedFromKnownBlob()
+{
+  MOZ_ASSERT(mRemoteBlobImpl);
+
+  // Releasing the other blob now that this blob is fully created.
+  mRemoteBlobImpl->NullifyDifferentProcessBlobImpl();
+
+  // Release the additional reference to ourself that was added in order to
+  // receive this RecvCreatedFromKnownBlob.
+  mRemoteBlobImpl->Release();
+  return true;
+}
+
 /*******************************************************************************
  * BlobParent
  ******************************************************************************/
 
 BlobParent::BlobParent(nsIContentParent* aManager, IDTableEntry* aIDTableEntry)
   : mBackgroundManager(nullptr)
   , mContentManager(aManager)
 {
--- a/dom/ipc/BlobChild.h
+++ b/dom/ipc/BlobChild.h
@@ -215,16 +215,19 @@ private:
   ActorDestroy(ActorDestroyReason aWhy) override;
 
   virtual PBlobStreamChild*
   AllocPBlobStreamChild(const uint64_t& aStart,
                         const uint64_t& aLength) override;
 
   virtual bool
   DeallocPBlobStreamChild(PBlobStreamChild* aActor) override;
+
+  virtual bool
+  RecvCreatedFromKnownBlob() override;
 };
 
 // Only let ContentChild call BlobChild::Startup() and ensure that
 // ContentChild can't access any other BlobChild internals.
 class BlobChild::FriendKey final
 {
   friend class ContentChild;
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -3519,16 +3519,28 @@ ContentParent::AllocPBlobParent(const Bl
 }
 
 bool
 ContentParent::DeallocPBlobParent(PBlobParent* aActor)
 {
     return nsIContentParent::DeallocPBlobParent(aActor);
 }
 
+bool
+ContentParent::RecvPBlobConstructor(PBlobParent* aActor,
+                                    const BlobConstructorParams& aParams)
+{
+  const ParentBlobConstructorParams& params = aParams.get_ParentBlobConstructorParams();
+  if (params.blobParams().type() == AnyBlobConstructorParams::TKnownBlobConstructorParams) {
+    return aActor->SendCreatedFromKnownBlob();
+  }
+
+  return true;
+}
+
 mozilla::PRemoteSpellcheckEngineParent *
 ContentParent::AllocPRemoteSpellcheckEngineParent()
 {
     mozilla::RemoteSpellcheckEngineParent *parent = new mozilla::RemoteSpellcheckEngineParent();
     return parent;
 }
 
 bool
@@ -5716,29 +5728,31 @@ ContentParent::RecvGetAndroidSystemInfo(
   MOZ_CRASH("wrong platform!");
   return false;
 #endif
 }
 
 void
 ContentParent::StartProfiler(nsIProfilerStartParams* aParams)
 {
+#ifdef MOZ_ENABLE_PROFILER_SPS
     if (NS_WARN_IF(!aParams)) {
         return;
     }
 
     ProfilerInitParams ipcParams;
 
     ipcParams.enabled() = true;
     aParams->GetEntries(&ipcParams.entries());
     aParams->GetInterval(&ipcParams.interval());
     ipcParams.features() = aParams->GetFeatures();
     ipcParams.threadFilters() = aParams->GetThreadFilterNames();
 
     Unused << SendStartProfiler(ipcParams);
+#endif
 }
 
 } // namespace dom
 } // namespace mozilla
 
 NS_IMPL_ISUPPORTS(ParentIdleListener, nsIObserver)
 
 NS_IMETHODIMP
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -643,16 +643,19 @@ private:
 
     virtual bool
     DeallocPFileSystemRequestParent(PFileSystemRequestParent*) override;
 
     virtual PBlobParent* AllocPBlobParent(const BlobConstructorParams& aParams)
                                           override;
     virtual bool DeallocPBlobParent(PBlobParent* aActor) override;
 
+    virtual bool RecvPBlobConstructor(PBlobParent* aActor,
+                                      const BlobConstructorParams& params) override;
+
     virtual bool DeallocPCrashReporterParent(PCrashReporterParent* crashreporter) override;
 
     virtual bool RecvGetRandomValues(const uint32_t& length,
                                      InfallibleTArray<uint8_t>* randomValues) override;
 
     virtual bool RecvIsSecureURI(const uint32_t& aType, const URIParams& aURI,
                                  const uint32_t& aFlags, bool* aIsSecureURI) override;
 
--- a/dom/ipc/PBlob.ipdl
+++ b/dom/ipc/PBlob.ipdl
@@ -40,12 +40,17 @@ parent:
 
   // Use only for testing!
   sync GetFileId()
     returns (int64_t fileId);
 
   // Use only for testing!
   sync GetFilePath()
     returns (nsString filePath);
+
+child:
+  // This method must be called by the parent when the PBlobParent is fully
+  // created in order to release the known blob.
+  CreatedFromKnownBlob();
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/BufferMediaResource.h
+++ b/dom/media/BufferMediaResource.h
@@ -94,19 +94,19 @@ private:
     return NS_OK;
   }
 
   virtual nsresult Open(nsIStreamListener** aStreamListener) override
   {
     return NS_ERROR_FAILURE;
   }
 
-  virtual nsresult GetCachedRanges(nsTArray<MediaByteRange>& aRanges) override
+  virtual nsresult GetCachedRanges(MediaByteRangeSet& aRanges) override
   {
-    aRanges.AppendElement(MediaByteRange(0, mLength));
+    aRanges += MediaByteRange(0, int64_t(mLength));
     return NS_OK;
   }
 
   bool IsTransportSeekable() override { return true; }
 
   virtual const nsCString& GetContentType() const override
   {
     return mContentType;
--- a/dom/media/DecoderTraits.cpp
+++ b/dom/media/DecoderTraits.cpp
@@ -34,26 +34,18 @@
 #include "AndroidMediaReader.h"
 #include "AndroidMediaPluginHost.h"
 #endif
 #ifdef MOZ_OMX_DECODER
 #include "MediaOmxDecoder.h"
 #include "MediaOmxReader.h"
 #include "nsIPrincipal.h"
 #include "mozilla/dom/HTMLMediaElement.h"
-#if ANDROID_VERSION >= 18
-#include "MediaCodecDecoder.h"
-#include "MediaCodecReader.h"
-#endif
 #endif
 #ifdef NECKO_PROTOCOL_rtsp
-#if ANDROID_VERSION >= 18
-#include "RtspMediaCodecDecoder.h"
-#include "RtspMediaCodecReader.h"
-#endif
 #include "RtspOmxDecoder.h"
 #include "RtspOmxReader.h"
 #endif
 #ifdef MOZ_DIRECTSHOW
 #include "DirectShowDecoder.h"
 #include "DirectShowReader.h"
 #endif
 #ifdef MOZ_FMP4
@@ -587,35 +579,23 @@ InstantiateDecoder(const nsACString& aTy
       nsIPrincipal* principal = element->NodePrincipal();
       if (!principal) {
         return nullptr;
       }
       if (principal->GetAppStatus() < nsIPrincipal::APP_STATUS_PRIVILEGED) {
         return nullptr;
       }
     }
-#if ANDROID_VERSION >= 18
-    decoder = MediaDecoder::IsOmxAsyncEnabled()
-      ? static_cast<MediaDecoder*>(new MediaCodecDecoder(aOwner))
-      : static_cast<MediaDecoder*>(new MediaOmxDecoder(aOwner));
-#else
     decoder = new MediaOmxDecoder(aOwner);
-#endif
     return decoder.forget();
   }
 #endif
 #ifdef NECKO_PROTOCOL_rtsp
   if (IsRtspSupportedType(aType)) {
-#if ANDROID_VERSION >= 18
-    decoder = MediaDecoder::IsOmxAsyncEnabled()
-      ? static_cast<MediaDecoder*>(new RtspMediaCodecDecoder(aOwner))
-      : static_cast<MediaDecoder*>(new RtspOmxDecoder(aOwner));
-#else
     decoder = new RtspOmxDecoder(aOwner);
-#endif
     return decoder.forget();
   }
 #endif
 #ifdef MOZ_ANDROID_OMX
   if (MediaDecoder::IsAndroidMediaEnabled() &&
       EnsureAndroidMediaPluginHost()->FindDecoder(aType, nullptr)) {
     decoder = new AndroidMediaDecoder(aOwner, aType);
     return decoder.forget();
@@ -677,23 +657,17 @@ MediaDecoderReader* DecoderTraits::Creat
   } else
 #ifdef MOZ_WAVE
   if (IsWaveType(aType)) {
     decoderReader = new WaveReader(aDecoder);
   } else
 #endif
 #ifdef MOZ_OMX_DECODER
   if (IsOmxSupportedType(aType)) {
-#if ANDROID_VERSION >= 18
-    decoderReader = MediaDecoder::IsOmxAsyncEnabled()
-      ? static_cast<MediaDecoderReader*>(new MediaCodecReader(aDecoder))
-      : static_cast<MediaDecoderReader*>(new MediaOmxReader(aDecoder));
-#else
     decoderReader = new MediaOmxReader(aDecoder);
-#endif
   } else
 #endif
 #ifdef MOZ_ANDROID_OMX
   if (MediaDecoder::IsAndroidMediaEnabled() &&
       EnsureAndroidMediaPluginHost()->FindDecoder(aType, nullptr)) {
     decoderReader = new AndroidMediaReader(aDecoder, aType);
   } else
 #endif
--- a/dom/media/Intervals.h
+++ b/dom/media/Intervals.h
@@ -183,16 +183,19 @@ public:
 
   bool LeftOf(const SelfType& aOther) const
   {
     return mEnd - mFuzz <= aOther.mStart + aOther.mFuzz;
   }
 
   SelfType Span(const SelfType& aOther) const
   {
+    if (IsEmpty()) {
+      return aOther;
+    }
     SelfType result(*this);
     if (aOther.mStart < mStart) {
       result.mStart = aOther.mStart;
     }
     if (mEnd < aOther.mEnd) {
       result.mEnd = aOther.mEnd;
     }
     if (mFuzz < aOther.mFuzz) {
@@ -393,17 +396,17 @@ public:
 
   SelfType operator+ (const SelfType& aIntervals) const
   {
     SelfType intervals(*this);
     intervals.Add(aIntervals);
     return intervals;
   }
 
-  SelfType operator+ (const ElemType& aInterval)
+  SelfType operator+ (const ElemType& aInterval) const
   {
     SelfType intervals(*this);
     intervals.Add(aInterval);
     return intervals;
   }
 
   friend SelfType operator+ (const ElemType& aInterval,
                              const SelfType& aIntervals)
@@ -434,17 +437,24 @@ public:
   SelfType& operator-= (const SelfType& aIntervals)
   {
     for (const auto& interval : aIntervals.mIntervals) {
       *this -= interval;
     }
     return *this;
   }
 
-  SelfType operator- (const ElemType& aInterval)
+  SelfType operator- (const SelfType& aInterval) const
+  {
+    SelfType intervals(*this);
+    intervals -= aInterval;
+    return intervals;
+  }
+
+  SelfType operator- (const ElemType& aInterval) const
   {
     SelfType intervals(*this);
     intervals -= aInterval;
     return intervals;
   }
 
   // Mutate this IntervalSet to be the union of this and aOther.
   SelfType& Union(const SelfType& aOther)
@@ -647,16 +657,33 @@ public:
     return mIntervals.end();
   }
 
   typename ContainerType::const_iterator end() const
   {
     return mIntervals.end();
   }
 
+  ElemType& LastInterval()
+  {
+    MOZ_ASSERT(!mIntervals.IsEmpty());
+    return mIntervals.LastElement();
+  }
+
+  const ElemType& LastInterval() const
+  {
+    MOZ_ASSERT(!mIntervals.IsEmpty());
+    return mIntervals.LastElement();
+  }
+
+  void Clear()
+  {
+    mIntervals.Clear();
+  }
+
 protected:
   ContainerType mIntervals;
 
 private:
   void Normalize()
   {
     if (mIntervals.Length() >= 2) {
       ContainerType normalized;
--- a/dom/media/MP3FrameParser.h
+++ b/dom/media/MP3FrameParser.h
@@ -209,29 +209,11 @@ private:
     DEFINITELY_MP3, // We've hit at least one ID3 tag or MP3 frame.
     NOT_MP3 // Not found any evidence of the stream being MP3.
   };
 
   eIsMP3 mIsMP3;
 
 };
 
-class NotifyDataArrivedFilter {
-public:
-  media::IntervalSet<int64_t> NotifyDataArrived(uint32_t aLength, int64_t aOffset) {
-    media::Interval<int64_t> interval(aOffset, aOffset + aLength);
-    media::IntervalSet<int64_t> newIntervals(interval);
-    newIntervals -= mIntervals;
-    mIntervals += interval;
-    return newIntervals;
-  }
-
-  const media::IntervalSet<int64_t>& GetIntervals() {
-    return mIntervals;
-  }
-
-private:
-  media::IntervalSet<int64_t> mIntervals;
-};
-
 } // namespace mozilla
 
 #endif
--- a/dom/media/MediaCache.cpp
+++ b/dom/media/MediaCache.cpp
@@ -2437,32 +2437,32 @@ MediaCacheStream::InitAsClone(MediaCache
     // Every block is a readahead block for the clone because the clone's initial
     // stream offset is zero
     gMediaCache->AddBlockOwnerAsReadahead(cacheBlockIndex, this, i);
   }
 
   return NS_OK;
 }
 
-nsresult MediaCacheStream::GetCachedRanges(nsTArray<MediaByteRange>& aRanges)
+nsresult MediaCacheStream::GetCachedRanges(MediaByteRangeSet& aRanges)
 {
   // Take the monitor, so that the cached data ranges can't grow while we're
   // trying to loop over them.
   ReentrantMonitorAutoEnter mon(gMediaCache->GetReentrantMonitor());
 
   // We must be pinned while running this, otherwise the cached data ranges may
   // shrink while we're trying to loop over them.
   NS_ASSERTION(mPinCount > 0, "Must be pinned");
 
   int64_t startOffset = GetNextCachedDataInternal(0);
   while (startOffset >= 0) {
     int64_t endOffset = GetCachedDataEndInternal(startOffset);
     NS_ASSERTION(startOffset < endOffset, "Buffered range must end after its start");
     // Bytes [startOffset..endOffset] are cached.
-    aRanges.AppendElement(MediaByteRange(startOffset, endOffset));
+    aRanges += MediaByteRange(startOffset, endOffset);
     startOffset = GetNextCachedDataInternal(endOffset);
     NS_ASSERTION(startOffset == -1 || startOffset > endOffset,
       "Must have advanced to start of next range, or hit end of stream");
   }
   return NS_OK;
 }
 
 } // namespace mozilla
--- a/dom/media/MediaCache.h
+++ b/dom/media/MediaCache.h
@@ -6,23 +6,24 @@
 
 #ifndef MediaCache_h_
 #define MediaCache_h_
 
 #include "nsTArray.h"
 #include "nsCOMPtr.h"
 #include "nsHashKeys.h"
 #include "nsTHashtable.h"
+#include "Intervals.h"
 
 class nsIPrincipal;
 
 namespace mozilla {
 // defined in MediaResource.h
 class ChannelMediaResource;
-class MediaByteRange;
+typedef media::IntervalSet<int64_t> MediaByteRangeSet;
 class MediaResource;
 class ReentrantMonitorAutoEnter;
 
 /**
  * Media applications want fast, "on demand" random access to media data,
  * for pausing, seeking, etc. But we are primarily interested
  * in transporting media data using HTTP over the Internet, which has
  * high latency to open a connection, requires a new connection for every
@@ -295,17 +296,17 @@ public:
   int64_t GetCachedDataEnd(int64_t aOffset);
   // Returns the offset of the first byte of cached data at or after aOffset,
   // or -1 if there is no such cached data.
   int64_t GetNextCachedData(int64_t aOffset);
   // Fills aRanges with the ByteRanges representing the data which is currently
   // cached. Locks the media cache while running, to prevent any ranges
   // growing. The stream should be pinned while this runs and while its results
   // are used, to ensure no data is evicted.
-  nsresult GetCachedRanges(nsTArray<MediaByteRange>& aRanges);
+  nsresult GetCachedRanges(MediaByteRangeSet& aRanges);
 
   // Reads from buffered data only. Will fail if not all data to be read is
   // in the cache. Will not mark blocks as read. Can be called from the main
   // thread. It's the caller's responsibility to wrap the call in a pin/unpin,
   // and also to check that the range they want is cached before calling this.
   nsresult ReadFromCache(char* aBuffer,
                          int64_t aOffset,
                          int64_t aCount);
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -610,16 +610,22 @@ MediaDecoder::Shutdown()
   // This changes the decoder state to SHUTDOWN and does other things
   // necessary to unblock the state machine thread if it's blocked, so
   // the asynchronous shutdown in nsDestroyStateMachine won't deadlock.
   if (mDecoderStateMachine) {
     mDecoderStateMachine->DispatchShutdown();
     mTimedMetadataListener.Disconnect();
     mMetadataLoadedListener.Disconnect();
     mFirstFrameLoadedListener.Disconnect();
+    mOnPlaybackStart.Disconnect();
+    mOnPlaybackStop.Disconnect();
+    mOnPlaybackEnded.Disconnect();
+    mOnDecodeError.Disconnect();
+    mOnInvalidate.Disconnect();
+    mOnSeekingStart.Disconnect();
   }
 
   // Force any outstanding seek and byterange requests to complete
   // to prevent shutdown from deadlocking.
   if (mResource) {
     mResource->Close();
   }
 
@@ -693,16 +699,29 @@ MediaDecoder::SetStateMachineParameters(
     mDecoderStateMachine->DispatchMinimizePrerollUntilPlaybackStarts();
   }
   mTimedMetadataListener = mDecoderStateMachine->TimedMetadataEvent().Connect(
     AbstractThread::MainThread(), this, &MediaDecoder::OnMetadataUpdate);
   mMetadataLoadedListener = mDecoderStateMachine->MetadataLoadedEvent().Connect(
     AbstractThread::MainThread(), this, &MediaDecoder::MetadataLoaded);
   mFirstFrameLoadedListener = mDecoderStateMachine->FirstFrameLoadedEvent().Connect(
     AbstractThread::MainThread(), this, &MediaDecoder::FirstFrameLoaded);
+
+  mOnPlaybackStart = mDecoderStateMachine->OnPlaybackStart().Connect(
+    AbstractThread::MainThread(), this, &MediaDecoder::OnPlaybackStarted);
+  mOnPlaybackStop = mDecoderStateMachine->OnPlaybackStop().Connect(
+    AbstractThread::MainThread(), this, &MediaDecoder::OnPlaybackStopped);
+  mOnPlaybackEnded = mDecoderStateMachine->OnPlaybackEnded().Connect(
+    AbstractThread::MainThread(), this, &MediaDecoder::PlaybackEnded);
+  mOnDecodeError = mDecoderStateMachine->OnDecodeError().Connect(
+    AbstractThread::MainThread(), this, &MediaDecoder::DecodeError);
+  mOnInvalidate = mDecoderStateMachine->OnInvalidate().Connect(
+    AbstractThread::MainThread(), this, &MediaDecoder::Invalidate);
+  mOnSeekingStart = mDecoderStateMachine->OnSeekingStart().Connect(
+    AbstractThread::MainThread(), this, &MediaDecoder::SeekingStarted);
 }
 
 void
 MediaDecoder::SetMinimizePrerollUntilPlaybackStarts()
 {
   MOZ_ASSERT(NS_IsMainThread());
   DECODER_LOG("SetMinimizePrerollUntilPlaybackStarts()");
   mMinimizePreroll = true;
@@ -1636,26 +1655,16 @@ MediaDecoder::IsGStreamerEnabled()
 #endif
 
 #ifdef MOZ_OMX_DECODER
 bool
 MediaDecoder::IsOmxEnabled()
 {
   return Preferences::GetBool("media.omx.enabled", false);
 }
-
-bool
-MediaDecoder::IsOmxAsyncEnabled()
-{
-#if ANDROID_VERSION >= 16
-  return Preferences::GetBool("media.omx.async.enabled", false);
-#else
-  return false;
-#endif
-}
 #endif
 
 #ifdef MOZ_ANDROID_OMX
 bool
 MediaDecoder::IsAndroidMediaEnabled()
 {
   return Preferences::GetBool("media.plugins.enabled");
 }
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -565,36 +565,16 @@ private:
   // Fire timeupdate events if needed according to the time constraints
   // outlined in the specification.
   void FireTimeUpdate();
 
   // Something has changed that could affect the computed playback rate,
   // so recompute it. The monitor must be held.
   virtual void UpdatePlaybackRate();
 
-  // Used to estimate rates of data passing through the decoder's channel.
-  // Records activity stopping on the channel.
-  void DispatchPlaybackStarted() {
-    RefPtr<MediaDecoder> self = this;
-    nsCOMPtr<nsIRunnable> r =
-      NS_NewRunnableFunction([self] () { self->mPlaybackStatistics->Start(); });
-    AbstractThread::MainThread()->Dispatch(r.forget());
-  }
-
-  // Used to estimate rates of data passing through the decoder's channel.
-  // Records activity stopping on the channel.
-  void DispatchPlaybackStopped() {
-    RefPtr<MediaDecoder> self = this;
-    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self] () {
-      self->mPlaybackStatistics->Stop();
-      self->ComputePlaybackRate();
-    });
-    AbstractThread::MainThread()->Dispatch(r.forget());
-  }
-
   // The actual playback rate computation. The monitor must be held.
   void ComputePlaybackRate();
 
   // Returns true if we can play the entire media through without stopping
   // to buffer, given the current download and playback rates.
   bool CanPlayThrough();
 
   void SetAudioChannel(dom::AudioChannel aChannel) { mAudioChannel = aChannel; }
@@ -696,17 +676,16 @@ private:
 #endif
 
 #ifdef MOZ_GSTREAMER
   static bool IsGStreamerEnabled();
 #endif
 
 #ifdef MOZ_OMX_DECODER
   static bool IsOmxEnabled();
-  static bool IsOmxAsyncEnabled();
 #endif
 
 #ifdef MOZ_ANDROID_OMX
   static bool IsAndroidMediaEnabled();
 #endif
 
 #ifdef MOZ_WMF
   static bool IsWMFEnabled();
@@ -816,16 +795,28 @@ private:
   // state machine. Call on the main thread only.
   void MetadataLoaded(nsAutoPtr<MediaInfo> aInfo,
                       nsAutoPtr<MetadataTags> aTags,
                       MediaDecoderEventVisibility aEventVisibility);
 
   MediaEventSource<void>*
   DataArrivedEvent() override { return &mDataArrivedEvent; }
 
+  // Used to estimate rates of data passing through the decoder's channel.
+  // Records activity stopping on the channel.
+  void OnPlaybackStarted() { mPlaybackStatistics->Start(); }
+
+  // Used to estimate rates of data passing through the decoder's channel.
+  // Records activity stopping on the channel.
+  void OnPlaybackStopped()
+  {
+    mPlaybackStatistics->Stop();
+    ComputePlaybackRate();
+  }
+
   MediaEventProducer<void> mDataArrivedEvent;
 
   // The state machine object for handling the decoding. It is safe to
   // call methods of this object from other threads. Its internal data
   // is synchronised on a monitor. The lifetime of this object is
   // after mPlayState is LOADING and before mPlayState is SHUTDOWN. It
   // is safe to access it during this period.
   //
@@ -937,16 +928,23 @@ protected:
   nsCOMPtr<nsITimer> mDormantTimer;
 
   // A listener to receive metadata updates from MDSM.
   MediaEventListener mTimedMetadataListener;
 
   MediaEventListener mMetadataLoadedListener;
   MediaEventListener mFirstFrameLoadedListener;
 
+  MediaEventListener mOnPlaybackStart;
+  MediaEventListener mOnPlaybackStop;
+  MediaEventListener mOnPlaybackEnded;
+  MediaEventListener mOnDecodeError;
+  MediaEventListener mOnInvalidate;
+  MediaEventListener mOnSeekingStart;
+
 protected:
   // Whether the state machine is shut down.
   Mirror<bool> mStateMachineIsShutdown;
 
   // Buffered range, mirrored from the reader.
   Mirror<media::TimeIntervals> mBuffered;
 
   // NextFrameStatus, mirrored from the state machine.
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -1049,17 +1049,17 @@ nsresult MediaDecoderStateMachine::Init(
   return NS_OK;
 }
 
 void MediaDecoderStateMachine::StopPlayback()
 {
   MOZ_ASSERT(OnTaskQueue());
   DECODER_LOG("StopPlayback()");
 
-  mDecoder->DispatchPlaybackStopped();
+  mOnPlaybackStop.Notify();
 
   if (IsPlaying()) {
     mMediaSink->SetPlaying(false);
     MOZ_ASSERT(!IsPlaying());
   }
 
   DispatchDecodeTasksIfNeeded();
 }
@@ -1082,17 +1082,17 @@ void MediaDecoderStateMachine::MaybeStar
                 "mIsAudioPrerolling: %d, mIsVideoPrerolling: %d, "
                 "mAudioOffloading: %d]",
                 (int)playStatePermits, (int)mIsAudioPrerolling,
                 (int)mIsVideoPrerolling, (int)mAudioOffloading);
     return;
   }
 
   DECODER_LOG("MaybeStartPlayback() starting playback");
-  mDecoder->DispatchPlaybackStarted();
+  mOnPlaybackStart.Notify();
   StartMediaSink();
 
   if (!IsPlaying()) {
     mMediaSink->SetPlaying(true);
     MOZ_ASSERT(IsPlaying());
   }
 
   DispatchDecodeTasksIfNeeded();
@@ -1594,22 +1594,17 @@ MediaDecoderStateMachine::InitiateSeek()
   mCurrentTimeBeforeSeek = GetMediaTime();
 
   // Stop playback now to ensure that while we're outside the monitor
   // dispatching SeekingStarted, playback doesn't advance and mess with
   // mCurrentPosition that we've setting to seekTime here.
   StopPlayback();
   UpdatePlaybackPositionInternal(mCurrentSeek.mTarget.mTime);
 
-  nsCOMPtr<nsIRunnable> startEvent =
-      NS_NewRunnableMethodWithArg<MediaDecoderEventVisibility>(
-        mDecoder,
-        &MediaDecoder::SeekingStarted,
-        mCurrentSeek.mTarget.mEventVisibility);
-  AbstractThread::MainThread()->Dispatch(startEvent.forget());
+  mOnSeekingStart.Notify(mCurrentSeek.mTarget.mEventVisibility);
 
   // Reset our state machine and decoding pipeline before seeking.
   Reset();
 
   // Do the seek.
   RefPtr<MediaDecoderStateMachine> self = this;
   mSeekRequest.Begin(InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__,
                                  &MediaDecoderReader::Seek, mCurrentSeek.mTarget.mTime,
@@ -1892,19 +1887,17 @@ MediaDecoderStateMachine::DecodeError()
   // Change state to error, which will cause the state machine to wait until
   // the MediaDecoder shuts it down.
   SetState(DECODER_STATE_ERROR);
   ScheduleStateMachine();
   DECODER_WARN("Decode error, changed state to ERROR");
 
   // MediaDecoder::DecodeError notifies the owner, and then shuts down the state
   // machine.
-  nsCOMPtr<nsIRunnable> event =
-    NS_NewRunnableMethod(mDecoder, &MediaDecoder::DecodeError);
-  AbstractThread::MainThread()->Dispatch(event.forget());
+  mOnDecodeError.Notify();
 }
 
 void
 MediaDecoderStateMachine::OnMetadataRead(MetadataHolder* aMetadata)
 {
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(mState == DECODER_STATE_DECODING_METADATA);
   mMetadataRequest.Complete();
@@ -2153,19 +2146,17 @@ MediaDecoderStateMachine::SeekCompleted(
   // if we need to buffer after the seek.
   mQuickBuffering = false;
 
   mCurrentSeek.Resolve(mState == DECODER_STATE_COMPLETED, __func__);
   ScheduleStateMachine();
 
   if (video) {
     mMediaSink->Redraw();
-    nsCOMPtr<nsIRunnable> event =
-      NS_NewRunnableMethod(mDecoder, &MediaDecoder::Invalidate);
-    AbstractThread::MainThread()->Dispatch(event.forget());
+    mOnInvalidate.Notify();
   }
 }
 
 class DecoderDisposer
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DecoderDisposer)
   DecoderDisposer(MediaDecoder* aDecoder, MediaDecoderStateMachine* aStateMachine)
@@ -2408,19 +2399,17 @@ nsresult MediaDecoderStateMachine::RunSt
       {
         int64_t clockTime = std::max(AudioEndTime(), VideoEndTime());
         clockTime = std::max(int64_t(0), std::max(clockTime, Duration().ToMicroseconds()));
         UpdatePlaybackPosition(clockTime);
 
         // Ensure readyState is updated before firing the 'ended' event.
         UpdateNextFrameStatus();
 
-        nsCOMPtr<nsIRunnable> event =
-          NS_NewRunnableMethod(mDecoder, &MediaDecoder::PlaybackEnded);
-        AbstractThread::MainThread()->Dispatch(event.forget());
+        mOnPlaybackEnded.Notify();
 
         mSentPlaybackEndedEvent = true;
 
         // MediaSink::GetEndTime() must be called before stopping playback.
         StopMediaSink();
       }
 
       return NS_OK;
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -228,16 +228,25 @@ public:
                       nsAutoPtr<MetadataTags>,
                       MediaDecoderEventVisibility>&
   MetadataLoadedEvent() { return mMetadataLoadedEvent; }
 
   MediaEventSourceExc<nsAutoPtr<MediaInfo>,
                       MediaDecoderEventVisibility>&
   FirstFrameLoadedEvent() { return mFirstFrameLoadedEvent; }
 
+  MediaEventSource<void>& OnPlaybackStart() { return mOnPlaybackStart; }
+  MediaEventSource<void>& OnPlaybackStop() { return mOnPlaybackStop; }
+  MediaEventSource<void>& OnPlaybackEnded() { return mOnPlaybackEnded; }
+  MediaEventSource<void>& OnDecodeError() { return mOnDecodeError; }
+  MediaEventSource<void>& OnInvalidate() { return mOnInvalidate; }
+
+  MediaEventSource<MediaDecoderEventVisibility>&
+  OnSeekingStart() { return mOnSeekingStart; }
+
   // Immutable after construction - may be called on any thread.
   bool IsRealTime() const { return mRealTime; }
 
   size_t SizeOfVideoQueue() {
     if (mReader) {
       return mReader->SizeOfVideoQueueInBytes();
     }
     return 0;
@@ -1196,16 +1205,23 @@ private:
   MediaEventListener mVideoQueueListener;
 
   MediaEventProducerExc<nsAutoPtr<MediaInfo>,
                         nsAutoPtr<MetadataTags>,
                         MediaDecoderEventVisibility> mMetadataLoadedEvent;
   MediaEventProducerExc<nsAutoPtr<MediaInfo>,
                         MediaDecoderEventVisibility> mFirstFrameLoadedEvent;
 
+  MediaEventProducer<void> mOnPlaybackStart;
+  MediaEventProducer<void> mOnPlaybackStop;
+  MediaEventProducer<void> mOnPlaybackEnded;
+  MediaEventProducer<void> mOnDecodeError;
+  MediaEventProducer<void> mOnInvalidate;
+  MediaEventProducer<MediaDecoderEventVisibility> mOnSeekingStart;
+
   // True if audio is offloading.
   // Playback will not start when audio is offloading.
   bool mAudioOffloading;
 
 #ifdef MOZ_EME
   void OnCDMProxyReady(RefPtr<CDMProxy> aProxy);
   void OnCDMProxyNotReady();
   RefPtr<CDMProxy> mCDMProxy;
--- a/dom/media/MediaResource.cpp
+++ b/dom/media/MediaResource.cpp
@@ -231,17 +231,17 @@ ChannelMediaResource::OnStartRequest(nsI
       // Since that's bounded, we know we have a finite-length resource.
       dataIsBounded = true;
     }
 
     // Assume Range requests have a bounded upper limit unless the
     // Content-Range header tells us otherwise.
     bool boundedSeekLimit = true;
     // Check response code for byte-range requests (seeking, chunk requests).
-    if (!mByteRange.IsNull() && (responseStatus == HTTP_PARTIAL_RESPONSE_CODE)) {
+    if (!mByteRange.IsEmpty() && (responseStatus == HTTP_PARTIAL_RESPONSE_CODE)) {
       // Parse Content-Range header.
       int64_t rangeStart = 0;
       int64_t rangeEnd = 0;
       int64_t rangeTotal = 0;
       rv = ParseContentRangeHeader(hc, rangeStart, rangeEnd, rangeTotal);
       if (NS_FAILED(rv)) {
         // Content-Range header text should be parse-able.
         CMLOG("Error processing \'Content-Range' for "
@@ -268,17 +268,17 @@ ChannelMediaResource::OnStartRequest(nsI
       } else {
         mCacheStream.NotifyDataLength(rangeTotal);
       }
       mCacheStream.NotifyDataStarted(rangeStart);
 
       mOffset = rangeStart;
       // We received 'Content-Range', so the server accepts range requests.
       acceptsRanges = true;
-    } else if (((mOffset > 0) || !mByteRange.IsNull())
+    } else if (((mOffset > 0) || !mByteRange.IsEmpty())
                && (responseStatus == HTTP_OK_CODE)) {
       // If we get an OK response but we were seeking, or requesting a byte
       // range, then we have to assume that seeking doesn't work. We also need
       // to tell the cache that it's getting data for the start of the stream.
       mCacheStream.NotifyDataStarted(0);
       mOffset = 0;
 
       // The server claimed it supported range requests.  It lied.
@@ -529,17 +529,17 @@ nsresult ChannelMediaResource::OpenChann
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
   NS_ENSURE_TRUE(mChannel, NS_ERROR_NULL_POINTER);
   NS_ASSERTION(!mListener, "Listener should have been removed by now");
 
   if (aStreamListener) {
     *aStreamListener = nullptr;
   }
 
-  if (mByteRange.IsNull()) {
+  if (mByteRange.IsEmpty()) {
     // We're not making a byte range request, so set the content length,
     // if it's available as an HTTP header. This ensures that MediaResource
     // wrapping objects for platform libraries that expect to know
     // the length of a resource can get it before OnStartRequest() fires.
     nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(mChannel);
     if (hc) {
       int64_t cl = -1;
       if (NS_SUCCEEDED(hc->GetContentLength(&cl)) && cl != -1) {
@@ -578,24 +578,24 @@ nsresult ChannelMediaResource::SetupChan
   // of the resource.
   // This enables us to detect if the stream supports byte range
   // requests, and therefore seeking, early.
   nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(mChannel);
   if (hc) {
     // Use |mByteRange| for a specific chunk, or |mOffset| if seeking in a
     // complete file download.
     nsAutoCString rangeString("bytes=");
-    if (!mByteRange.IsNull()) {
+    if (!mByteRange.IsEmpty()) {
       rangeString.AppendInt(mByteRange.mStart);
       mOffset = mByteRange.mStart;
     } else {
       rangeString.AppendInt(mOffset);
     }
     rangeString.Append('-');
-    if (!mByteRange.IsNull()) {
+    if (!mByteRange.IsEmpty()) {
       rangeString.AppendInt(mByteRange.mEnd);
     }
     nsresult rv = hc->SetRequestHeader(NS_LITERAL_CSTRING("Range"), rangeString, false);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Send Accept header for video and audio types only (Bug 489071)
     NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
     MediaDecoderOwner* owner = mCallback->GetMediaOwner();
@@ -733,17 +733,17 @@ ChannelMediaResource::MediaReadAt(int64_
 
 int64_t ChannelMediaResource::Tell()
 {
   NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
 
   return mCacheStream.Tell();
 }
 
-nsresult ChannelMediaResource::GetCachedRanges(nsTArray<MediaByteRange>& aRanges)
+nsresult ChannelMediaResource::GetCachedRanges(MediaByteRangeSet& aRanges)
 {
   return mCacheStream.GetCachedRanges(aRanges);
 }
 
 void ChannelMediaResource::Suspend(bool aCloseImmediately)
 {
   NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
 
@@ -1197,17 +1197,17 @@ public:
     EnsureSizeInitialized();
     return std::max(aOffset, mSize);
   }
   virtual bool    IsDataCachedToEndOfResource(int64_t aOffset) override { return true; }
   virtual bool    IsSuspendedByCache() override { return true; }
   virtual bool    IsSuspended() override { return true; }
   virtual bool    IsTransportSeekable() override { return true; }
 
-  nsresult GetCachedRanges(nsTArray<MediaByteRange>& aRanges) override;
+  nsresult GetCachedRanges(MediaByteRangeSet& aRanges) override;
 
   virtual size_t SizeOfExcludingThis(
                         MallocSizeOf aMallocSizeOf) const override
   {
     // Might be useful to track in the future:
     // - mInput
     return BaseMediaResource::SizeOfExcludingThis(aMallocSizeOf);
   }
@@ -1269,25 +1269,25 @@ void FileMediaResource::EnsureSizeInitia
   uint64_t size;
   nsresult res = mInput->Available(&size);
   if (NS_SUCCEEDED(res) && size <= INT64_MAX) {
     mSize = (int64_t)size;
     mCallback->NotifyDataEnded(NS_OK);
   }
 }
 
-nsresult FileMediaResource::GetCachedRanges(nsTArray<MediaByteRange>& aRanges)
+nsresult FileMediaResource::GetCachedRanges(MediaByteRangeSet& aRanges)
 {
   MutexAutoLock lock(mLock);
 
   EnsureSizeInitialized();
   if (mSize == -1) {
     return NS_ERROR_FAILURE;
   }
-  aRanges.AppendElement(MediaByteRange(0, mSize));
+  aRanges += MediaByteRange(0, mSize);
   return NS_OK;
 }
 
 nsresult FileMediaResource::Open(nsIStreamListener** aStreamListener)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
   if (aStreamListener) {
--- a/dom/media/MediaResource.h
+++ b/dom/media/MediaResource.h
@@ -9,16 +9,17 @@
 #include "mozilla/Mutex.h"
 #include "nsIChannel.h"
 #include "nsIURI.h"
 #include "nsISeekableStream.h"
 #include "nsIStreamingProtocolController.h"
 #include "nsIStreamListener.h"
 #include "nsIChannelEventSink.h"
 #include "nsIInterfaceRequestor.h"
+#include "Intervals.h"
 #include "MediaCache.h"
 #include "MediaData.h"
 #include "MediaResourceCallback.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/TimeStamp.h"
 #include "nsThreadUtils.h"
 #include <algorithm>
@@ -123,97 +124,21 @@ public:
 private:
   ~MediaChannelStatistics() {}
   int64_t      mAccumulatedBytes;
   TimeDuration mAccumulatedTime;
   TimeStamp    mLastStartTime;
   bool         mIsStarted;
 };
 
-// Forward declaration for use in MediaByteRange.
-class TimestampedMediaByteRange;
-
 // Represents a section of contiguous media, with a start and end offset.
 // Used to denote ranges of data which are cached.
-class MediaByteRange {
-public:
-  MediaByteRange() : mStart(0), mEnd(0) {}
 
-  MediaByteRange(int64_t aStart, int64_t aEnd)
-    : mStart(aStart), mEnd(aEnd)
-  {
-    NS_ASSERTION(mStart <= mEnd, "Range should end after start!");
-  }
-
-  explicit MediaByteRange(TimestampedMediaByteRange& aByteRange);
-
-  bool IsNull() const {
-    return mStart == 0 && mEnd == 0;
-  }
-
-  bool operator==(const MediaByteRange& aRange) const {
-    return mStart == aRange.mStart && mEnd == aRange.mEnd;
-  }
-
-  // Clears byte range values.
-  void Clear() {
-    mStart = 0;
-    mEnd = 0;
-  }
-
-  bool Contains(const MediaByteRange& aByteRange) const {
-    return aByteRange.mStart >= mStart && aByteRange.mEnd <= mEnd;
-  }
-
-  MediaByteRange Extents(const MediaByteRange& aByteRange) const {
-    if (IsNull()) {
-      return aByteRange;
-    }
-    return MediaByteRange(std::min(mStart, aByteRange.mStart),
-                          std::max(mEnd, aByteRange.mEnd));
-  }
-
-  int64_t Length() const {
-    return mEnd - mStart;
-  }
-
-  int64_t mStart, mEnd;
-};
-
-// Represents a section of contiguous media, with a start and end offset, and
-// a timestamp representing the start time.
-class TimestampedMediaByteRange : public MediaByteRange {
-public:
-  TimestampedMediaByteRange() : MediaByteRange(), mStartTime(-1) {}
-
-  TimestampedMediaByteRange(int64_t aStart, int64_t aEnd, int64_t aStartTime)
-    : MediaByteRange(aStart, aEnd), mStartTime(aStartTime)
-  {
-    NS_ASSERTION(aStartTime >= 0, "Start time should not be negative!");
-  }
-
-  bool IsNull() const {
-    return MediaByteRange::IsNull() && mStartTime == -1;
-  }
-
-  // Clears byte range values.
-  void Clear() {
-    MediaByteRange::Clear();
-    mStartTime = -1;
-  }
-
-  // In usecs.
-  int64_t mStartTime;
-};
-
-inline MediaByteRange::MediaByteRange(TimestampedMediaByteRange& aByteRange)
-  : mStart(aByteRange.mStart), mEnd(aByteRange.mEnd)
-{
-  NS_ASSERTION(mStart < mEnd, "Range should end after start!");
-}
+typedef media::Interval<int64_t> MediaByteRange;
+typedef media::IntervalSet<int64_t> MediaByteRangeSet;
 
 class RtspMediaResource;
 
 /**
  * Provides a thread-safe, seek/read interface to resources
  * loaded from a URI. Uses MediaCache to cache data received over
  * Necko's async channel API, thus resolving the mismatch between clients
  * that need efficient random access to the data and protocols that do not
@@ -395,17 +320,17 @@ public:
    */
   virtual nsresult Open(nsIStreamListener** aStreamListener) = 0;
 
   /**
    * Fills aRanges with MediaByteRanges representing the data which is cached
    * in the media cache. Stream should be pinned during call and while
    * aRanges is being used.
    */
-  virtual nsresult GetCachedRanges(nsTArray<MediaByteRange>& aRanges) = 0;
+  virtual nsresult GetCachedRanges(MediaByteRangeSet& aRanges) = 0;
 
   // Ensure that the media cache writes any data held in its partial block.
   // Called on the main thread only.
   virtual void FlushCache() { }
 
   // Notify that the last data byte range was loaded.
   virtual void NotifyLastByteRange() { }
 
@@ -707,17 +632,17 @@ public:
 
     void Revoke() { mResource = nullptr; }
 
   private:
     RefPtr<ChannelMediaResource> mResource;
   };
   friend class Listener;
 
-  virtual nsresult GetCachedRanges(nsTArray<MediaByteRange>& aRanges) override;
+  virtual nsresult GetCachedRanges(MediaByteRangeSet& aRanges) override;
 
 protected:
   // These are called on the main thread by Listener.
   nsresult OnStartRequest(nsIRequest* aRequest);
   nsresult OnStopRequest(nsIRequest* aRequest, nsresult aStatus);
   nsresult OnDataAvailable(nsIRequest* aRequest,
                            nsIInputStream* aStream,
                            uint32_t aCount);
--- a/dom/media/RtspMediaResource.h
+++ b/dom/media/RtspMediaResource.h
@@ -157,17 +157,17 @@ public:
   virtual int64_t GetNextCachedData(int64_t aOffset) override { return 0; }
   // dummy
   virtual int64_t GetCachedDataEnd(int64_t aOffset) override { return 0; }
   // dummy
   virtual bool    IsDataCachedToEndOfResource(int64_t aOffset) override {
     return false;
   }
   // dummy
-  nsresult GetCachedRanges(nsTArray<MediaByteRange>& aRanges) override {
+  nsresult GetCachedRanges(MediaByteRangeSet& aRanges) override {
     return NS_ERROR_FAILURE;
   }
 
   // The following methods can be called on main thread only.
 
   virtual nsresult Open(nsIStreamListener** aStreamListener) override;
   virtual nsresult Close() override;
   virtual void     Suspend(bool aCloseImmediately) override;
--- a/dom/media/directshow/DirectShowReader.cpp
+++ b/dom/media/directshow/DirectShowReader.cpp
@@ -6,17 +6,16 @@
 
 #include "DirectShowReader.h"
 #include "MediaDecoderReader.h"
 #include "mozilla/RefPtr.h"
 #include "DirectShowUtils.h"
 #include "AudioSinkFilter.h"
 #include "SourceFilter.h"
 #include "SampleSink.h"
-#include "MediaResource.h"
 #include "VideoUtils.h"
 
 using namespace mozilla::media;
 
 namespace mozilla {
 
 LogModule*
 GetDirectShowLog() {
@@ -379,27 +378,29 @@ void
 DirectShowReader::NotifyDataArrivedInternal()
 {
   MOZ_ASSERT(OnTaskQueue());
   if (!mMP3FrameParser.NeedsData()) {
     return;
   }
 
   AutoPinned<MediaResource> resource(mDecoder->GetResource());
-  nsTArray<MediaByteRange> byteRanges;
+  MediaByteRangeSet byteRanges;
   nsresult rv = resource->GetCachedRanges(byteRanges);
 
   if (NS_FAILED(rv)) {
     return;
   }
 
-  IntervalSet<int64_t> intervals;
-  for (auto& range : byteRanges) {
-    intervals += mFilter.NotifyDataArrived(range.Length(), range.mStart);
+  if (byteRanges == mLastCachedRanges) {
+    return;
   }
+  MediaByteRangeSet intervals = byteRanges - mLastCachedRanges;
+  mLastCachedRanges = byteRanges;
+
   for (const auto& interval : intervals) {
     RefPtr<MediaByteBuffer> bytes =
       resource->MediaReadAt(interval.mStart, interval.Length());
     NS_ENSURE_TRUE_VOID(bytes);
     mMP3FrameParser.Parse(bytes->Elements(), interval.Length(), interval.mStart);
     if (!mMP3FrameParser.IsMP3()) {
       return;
     }
--- a/dom/media/directshow/DirectShowReader.h
+++ b/dom/media/directshow/DirectShowReader.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #if !defined(DirectShowReader_h_)
 #define DirectShowReader_h_
 
 #include "windows.h" // HRESULT, DWORD
 #include "MediaDecoderReader.h"
+#include "MediaResource.h"
 #include "mozilla/RefPtr.h"
 #include "MP3FrameParser.h"
 
 struct IGraphBuilder;
 struct IMediaControl;
 struct IMediaSeeking;
 
 namespace mozilla {
@@ -100,14 +101,14 @@ private:
   uint32_t mAudioRate;
 
   // Number of bytes per sample. Can be either 1 or 2.
   uint32_t mBytesPerSample;
 
   // Duration of the stream, in microseconds.
   int64_t mDuration;
 
-  NotifyDataArrivedFilter mFilter;
+  MediaByteRangeSet mLastCachedRanges;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/fmp4/MP4Demuxer.cpp
+++ b/dom/media/fmp4/MP4Demuxer.cpp
@@ -54,19 +54,16 @@ private:
   friend class MP4Demuxer;
   void NotifyDataArrived();
   void UpdateSamples(nsTArray<RefPtr<MediaRawData>>& aSamples);
   void EnsureUpToDateIndex();
   void SetNextKeyFrameTime();
   RefPtr<MP4Demuxer> mParent;
   RefPtr<mp4_demuxer::ResourceStream> mStream;
   UniquePtr<TrackInfo> mInfo;
-  // We do not actually need a monitor, however MoofParser (in mIndex) will
-  // assert if a monitor isn't held.
-  Monitor mMonitor;
   RefPtr<mp4_demuxer::Index> mIndex;
   UniquePtr<mp4_demuxer::SampleIterator> mIterator;
   Maybe<media::TimeUnit> mNextKeyframeTime;
   // Queued samples extracted by the demuxer, but not yet returned.
   RefPtr<MediaRawData> mQueuedSample;
   bool mNeedReIndex;
   bool mNeedSPSForTelemetry;
 };
@@ -223,22 +220,20 @@ MP4Demuxer::GetCrypto()
 }
 
 MP4TrackDemuxer::MP4TrackDemuxer(MP4Demuxer* aParent,
                                  UniquePtr<TrackInfo>&& aInfo,
                                  const nsTArray<mp4_demuxer::Index::Indice>& indices)
   : mParent(aParent)
   , mStream(new mp4_demuxer::ResourceStream(mParent->mResource))
   , mInfo(Move(aInfo))
-  , mMonitor("MP4TrackDemuxer")
   , mIndex(new mp4_demuxer::Index(indices,
                                   mStream,
                                   mInfo->mTrackId,
-                                  mInfo->IsAudio(),
-                                  &mMonitor))
+                                  mInfo->IsAudio()))
   , mIterator(MakeUnique<mp4_demuxer::SampleIterator>(mIndex))
   , mNeedReIndex(true)
 {
   EnsureUpToDateIndex(); // Force update of index
 
   // Collect telemetry from h264 AVCC SPS.
   if (mInfo->GetAsVideoInfo() &&
       (mInfo->mMimeType.EqualsLiteral("video/mp4") ||
@@ -259,33 +254,31 @@ MP4TrackDemuxer::GetInfo() const
 
 void
 MP4TrackDemuxer::EnsureUpToDateIndex()
 {
   if (!mNeedReIndex) {
     return;
   }
   AutoPinned<MediaResource> resource(mParent->mResource);
-  nsTArray<MediaByteRange> byteRanges;
+  MediaByteRangeSet byteRanges;
   nsresult rv = resource->GetCachedRanges(byteRanges);
   if (NS_FAILED(rv)) {
     return;
   }
-  MonitorAutoLock mon(mMonitor);
   mIndex->UpdateMoofIndex(byteRanges);
   mNeedReIndex = false;
 }
 
 RefPtr<MP4TrackDemuxer::SeekPromise>
 MP4TrackDemuxer::Seek(media::TimeUnit aTime)
 {
   int64_t seekTime = aTime.ToMicroseconds();
   mQueuedSample = nullptr;
 
-  MonitorAutoLock mon(mMonitor);
   mIterator->Seek(seekTime);
 
   // Check what time we actually seeked to.
   mQueuedSample = mIterator->GetNext();
   if (mQueuedSample) {
     seekTime = mQueuedSample->mTime;
   }
   SetNextKeyFrameTime();
@@ -302,17 +295,16 @@ MP4TrackDemuxer::GetSamples(int32_t aNum
     return SamplesPromise::CreateAndReject(DemuxerFailureReason::DEMUXER_ERROR, __func__);
   }
 
   if (mQueuedSample) {
     samples->mSamples.AppendElement(mQueuedSample);
     mQueuedSample = nullptr;
     aNumSamples--;
   }
-  MonitorAutoLock mon(mMonitor);
   RefPtr<MediaRawData> sample;
   while (aNumSamples && (sample = mIterator->GetNext())) {
     samples->mSamples.AppendElement(sample);
     aNumSamples--;
   }
 
   if (samples->mSamples.IsEmpty()) {
     return SamplesPromise::CreateAndReject(DemuxerFailureReason::END_OF_STREAM, __func__);
@@ -333,17 +325,16 @@ MP4TrackDemuxer::SetNextKeyFrameTime()
   }
 }
 
 void
 MP4TrackDemuxer::Reset()
 {
   mQueuedSample = nullptr;
   // TODO, Seek to first frame available, which isn't always 0.
-  MonitorAutoLock mon(mMonitor);
   mIterator->Seek(0);
   SetNextKeyFrameTime();
 }
 
 void
 MP4TrackDemuxer::UpdateSamples(nsTArray<RefPtr<MediaRawData>>& aSamples)
 {
   for (size_t i = 0; i < aSamples.Length(); i++) {
@@ -381,17 +372,16 @@ MP4TrackDemuxer::GetNextRandomAccessPoin
     *aTime = mNextKeyframeTime.value();
   }
   return NS_OK;
 }
 
 RefPtr<MP4TrackDemuxer::SkipAccessPointPromise>
 MP4TrackDemuxer::SkipToNextRandomAccessPoint(media::TimeUnit aTimeThreshold)
 {
-  MonitorAutoLock mon(mMonitor);
   mQueuedSample = nullptr;
   // Loop until we reach the next keyframe after the threshold.
   uint32_t parsed = 0;
   bool found = false;
   RefPtr<MediaRawData> sample;
   while (!found && (sample = mIterator->GetNext())) {
     parsed++;
     if (sample->mKeyframe && sample->mTime >= aTimeThreshold.ToMicroseconds()) {
@@ -408,34 +398,24 @@ MP4TrackDemuxer::SkipToNextRandomAccessP
   }
 }
 
 media::TimeIntervals
 MP4TrackDemuxer::GetBuffered()
 {
   EnsureUpToDateIndex();
   AutoPinned<MediaResource> resource(mParent->mResource);
-  nsTArray<MediaByteRange> byteRanges;
+  MediaByteRangeSet byteRanges;
   nsresult rv = resource->GetCachedRanges(byteRanges);
 
   if (NS_FAILED(rv)) {
     return media::TimeIntervals();
   }
-  nsTArray<mp4_demuxer::Interval<int64_t>> timeRanges;
 
-  MonitorAutoLock mon(mMonitor);
-  mIndex->ConvertByteRangesToTimeRanges(byteRanges, &timeRanges);
-  // convert timeRanges.
-  media::TimeIntervals ranges = media::TimeIntervals();
-  for (size_t i = 0; i < timeRanges.Length(); i++) {
-    ranges +=
-      media::TimeInterval(media::TimeUnit::FromMicroseconds(timeRanges[i].start),
-                          media::TimeUnit::FromMicroseconds(timeRanges[i].end));
-  }
-  return ranges;
+  return mIndex->ConvertByteRangesToTimeRanges(byteRanges);
 }
 
 void
 MP4TrackDemuxer::NotifyDataArrived()
 {
   mNeedReIndex = true;
 }
 
--- a/dom/media/gstreamer/GStreamerReader.cpp
+++ b/dom/media/gstreamer/GStreamerReader.cpp
@@ -880,17 +880,17 @@ media::TimeIntervals GStreamerReader::Ge
   if (!mInfo.HasValidMedia()) {
     return buffered;
   }
 
 #if GST_VERSION_MAJOR == 0
   GstFormat format = GST_FORMAT_TIME;
 #endif
   AutoPinned<MediaResource> resource(mDecoder->GetResource());
-  nsTArray<MediaByteRange> ranges;
+  MediaByteRangeSet ranges;
   resource->GetCachedRanges(ranges);
 
   if (resource->IsDataCachedToEndOfResource(0)) {
     /* fast path for local or completely cached files */
     gint64 duration =
        mDuration.Ref().refOr(media::TimeUnit::FromMicroseconds(0)).ToMicroseconds();
     LOG(LogLevel::Debug, "complete range [0, %f] for [0, %li]",
         (double) duration / GST_MSECOND, GetDataLength());
@@ -1281,27 +1281,28 @@ void GStreamerReader::NotifyDataArrivedI
   if (HasVideo()) {
     return;
   }
   if (!mMP3FrameParser.NeedsData()) {
     return;
   }
 
   AutoPinned<MediaResource> resource(mResource.GetResource());
-  nsTArray<MediaByteRange> byteRanges;
+  MediaByteRangeSet byteRanges;
   nsresult rv = resource->GetCachedRanges(byteRanges);
 
   if (NS_FAILED(rv)) {
     return;
   }
+  if (byteRanges == mLastCachedRanges) {
+    return;
+  }
+  MediaByteRangeSet intervals = byteRanges - mLastCachedRanges;
+  mLastCachedRanges = byteRanges;
 
-  IntervalSet<int64_t> intervals;
-  for (auto& range : byteRanges) {
-    intervals += mFilter.NotifyDataArrived(range.Length(), range.mStart);
-  }
   for (const auto& interval : intervals) {
     RefPtr<MediaByteBuffer> bytes =
       resource->MediaReadAt(interval.mStart, interval.Length());
     NS_ENSURE_TRUE_VOID(bytes);
     mMP3FrameParser.Parse(bytes->Elements(), interval.Length(), interval.mStart);
     if (!mMP3FrameParser.IsMP3()) {
       return;
     }
--- a/dom/media/gstreamer/GStreamerReader.h
+++ b/dom/media/gstreamer/GStreamerReader.h
@@ -251,14 +251,14 @@ private:
   bool mReachedVideoEos;
 #if GST_VERSION_MAJOR >= 1
   bool mConfigureAlignment;
 #endif
   int fpsNum;
   int fpsDen;
 
   MediaResourceIndex mResource;
-  NotifyDataArrivedFilter mFilter;
+  MediaByteRangeSet mLastCachedRanges;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/gtest/MockMediaResource.cpp
+++ b/dom/media/gtest/MockMediaResource.cpp
@@ -72,17 +72,17 @@ void
 MockMediaResource::MockClearBufferedRanges()
 {
   mRanges.Clear();
 }
 
 void
 MockMediaResource::MockAddBufferedRange(int64_t aStart, int64_t aEnd)
 {
-  mRanges.AppendElement(MediaByteRange(aStart, aEnd));
+  mRanges += MediaByteRange(aStart, aEnd);
 }
 
 int64_t
 MockMediaResource::GetNextCachedData(int64_t aOffset)
 {
   if (!aOffset) {
     return mRanges.Length() ? mRanges[0].mStart : -1;
   }
@@ -102,16 +102,15 @@ MockMediaResource::GetCachedDataEnd(int6
     if (aOffset == mRanges[i].mStart) {
       return mRanges[i].mEnd;
     }
   }
   return -1;
 }
 
 nsresult
-MockMediaResource::GetCachedRanges(nsTArray<MediaByteRange>& aRanges)
+MockMediaResource::GetCachedRanges(MediaByteRangeSet& aRanges)
 {
-  aRanges.Clear();
-  aRanges.AppendElements(mRanges);
+  aRanges = mRanges;
   return NS_OK;
 }
 
 } // namespace mozilla
--- a/dom/media/gtest/MockMediaResource.h
+++ b/dom/media/gtest/MockMediaResource.h
@@ -53,32 +53,32 @@ public:
     uint32_t bytesRead = 0;
     nsresult rv = ReadAt(aOffset, aBuffer, aCount, &bytesRead);
     NS_ENSURE_SUCCESS(rv, rv);
     return bytesRead == aCount ? NS_OK : NS_ERROR_FAILURE;
   }
 
   virtual bool IsTransportSeekable() override { return true; }
   virtual nsresult Open(nsIStreamListener** aStreamListener) override;
-  virtual nsresult GetCachedRanges(nsTArray<MediaByteRange>& aRanges)
+  virtual nsresult GetCachedRanges(MediaByteRangeSet& aRanges)
     override;
   virtual const nsCString& GetContentType() const override
   {
     return mContentType;
   }
 
   void MockClearBufferedRanges();
   void MockAddBufferedRange(int64_t aStart, int64_t aEnd);
 
 protected:
   virtual ~MockMediaResource();
 
 private:
   FILE* mFileHandle;
   const char* mFileName;
-  nsTArray<MediaByteRange> mRanges;
+  MediaByteRangeSet mRanges;
   Atomic<int> mEntry;
   nsCString mContentType;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/mediasource/ContainerParser.cpp
+++ b/dom/media/mediasource/ContainerParser.cpp
@@ -262,17 +262,17 @@ public:
     mOverlappedMapping.AppendElements(mapping.Elements() + completeIdx + 1,
                                       mapping.Length() - completeIdx - 1);
 
     if (completeIdx < 0) {
       mLastMapping.reset();
       return false;
     }
 
-    if (mCompleteMediaHeaderRange.IsNull()) {
+    if (mCompleteMediaHeaderRange.IsEmpty()) {
       mCompleteMediaHeaderRange = MediaByteRange(mapping[0].mSyncOffset,
                                                  mapping[0].mEndOffset);
     }
 
     if (foundNewCluster && mOffset >= mapping[endIdx].mEndOffset) {
       // We now have all information required to delimit a complete cluster.
       int64_t endOffset = mapping[endIdx+1].mSyncOffset;
       if (mapping[endIdx+1].mInitOffset > mapping[endIdx].mInitOffset) {
@@ -329,17 +329,16 @@ private:
   Maybe<WebMTimeDataOffset> mLastMapping;
 };
 
 #ifdef MOZ_FMP4
 class MP4ContainerParser : public ContainerParser {
 public:
   explicit MP4ContainerParser(const nsACString& aType)
     : ContainerParser(aType)
-    , mMonitor("MP4ContainerParser Index Monitor")
   {}
 
   bool IsInitSegmentPresent(MediaByteBuffer* aData) override
   {
     ContainerParser::IsInitSegmentPresent(aData);
     // Each MP4 atom has a chunk size and chunk type. The root chunk in an MP4
     // file is the 'ftyp' atom followed by a file type. We just check for a
     // vaguely valid 'ftyp' atom.
@@ -416,37 +415,34 @@ private:
     Maybe<size_t> mInitOffset;
     Maybe<size_t> mMediaOffset;
   };
 
 public:
   bool ParseStartAndEndTimestamps(MediaByteBuffer* aData,
                                   int64_t& aStart, int64_t& aEnd) override
   {
-    MonitorAutoLock mon(mMonitor); // We're not actually racing against anything,
-                                   // but mParser requires us to hold a monitor.
     bool initSegment = IsInitSegmentPresent(aData);
     if (initSegment) {
       mResource = new SourceBufferResource(NS_LITERAL_CSTRING("video/mp4"));
       mStream = new MP4Stream(mResource);
       // We use a timestampOffset of 0 for ContainerParser, and require
       // consumers of ParseStartAndEndTimestamps to add their timestamp offset
       // manually. This allows the ContainerParser to be shared across different
       // timestampOffsets.
-      mParser = new mp4_demuxer::MoofParser(mStream, 0, /* aIsAudio = */ false, &mMonitor);
+      mParser = new mp4_demuxer::MoofParser(mStream, 0, /* aIsAudio = */ false);
       mInitData = new MediaByteBuffer();
     } else if (!mStream || !mParser) {
       return false;
     }
 
     mResource->AppendData(aData);
-    nsTArray<MediaByteRange> byteRanges;
-    MediaByteRange mbr =
-      MediaByteRange(mParser->mOffset, mResource->GetLength());
-    byteRanges.AppendElement(mbr);
+    MediaByteRangeSet byteRanges;
+    byteRanges +=
+      MediaByteRange(int64_t(mParser->mOffset), mResource->GetLength());
     mParser->RebuildFragmentedIndex(byteRanges);
 
     if (initSegment || !HasCompleteInitData()) {
       MediaByteRange& range = mParser->mInitRange;
       if (range.Length()) {
         mCompleteInitSegmentRange = range;
         if (!mInitData->SetLength(range.Length(), fallible)) {
           // Super unlikely OOM
@@ -491,17 +487,16 @@ public:
   int64_t GetRoundingError() override
   {
     return 35000;
   }
 
 private:
   RefPtr<MP4Stream> mStream;
   nsAutoPtr<mp4_demuxer::MoofParser> mParser;
-  Monitor mMonitor;
 };
 #endif // MOZ_FMP4
 
 #ifdef MOZ_FMP4
 class ADTSContainerParser : public ContainerParser {
 public:
   explicit ADTSContainerParser(const nsACString& aType)
     : ContainerParser(aType)
@@ -605,17 +600,17 @@ public:
                                   int64_t& aStart, int64_t& aEnd) override
   {
     // ADTS header.
     Header header;
     if (!Parse(aData, header)) {
       return false;
     }
     mHasInitData = true;
-    mCompleteInitSegmentRange = MediaByteRange(0, header.header_length);
+    mCompleteInitSegmentRange = MediaByteRange(0, int64_t(header.header_length));
 
     // Cache raw header in case the caller wants a copy.
     mInitData = new MediaByteBuffer(header.header_length);
     mInitData->AppendElements(aData->Elements(), header.header_length);
 
     // Check that we have enough data for the frame body.
     if (aData->Length() < header.frame_length) {
       MSE_DEBUGV(ADTSContainerParser, "Not enough data for %llu byte frame"
--- a/dom/media/mediasource/MediaSourceResource.h
+++ b/dom/media/mediasource/MediaSourceResource.h
@@ -49,20 +49,20 @@ public:
   virtual nsresult ReadFromCache(char* aBuffer, int64_t aOffset, uint32_t aCount) override { UNIMPLEMENTED(); return NS_ERROR_FAILURE; }
   virtual nsresult Open(nsIStreamListener** aStreamListener) override { UNIMPLEMENTED(); return NS_ERROR_FAILURE; }
 
   virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal() override
   {
     return RefPtr<nsIPrincipal>(mPrincipal).forget();
   }
 
-  virtual nsresult GetCachedRanges(nsTArray<MediaByteRange>& aRanges) override
+  virtual nsresult GetCachedRanges(MediaByteRangeSet& aRanges) override
   {
     UNIMPLEMENTED();
-    aRanges.AppendElement(MediaByteRange(0, GetLength()));
+    aRanges += MediaByteRange(0, GetLength());
     return NS_OK;
   }
 
   virtual bool IsTransportSeekable() override { return true; }
   virtual const nsCString& GetContentType() const override { return mType; }
 
   virtual bool IsLiveStream() override
   {
--- a/dom/media/mediasource/SourceBufferResource.h
+++ b/dom/media/mediasource/SourceBufferResource.h
@@ -65,22 +65,22 @@ public:
   virtual int64_t GetCachedDataEnd(int64_t aOffset) override { UNIMPLEMENTED(); return -1; }
   virtual bool IsDataCachedToEndOfResource(int64_t aOffset) override { return false; }
   virtual bool IsSuspendedByCache() override { UNIMPLEMENTED(); return false; }
   virtual bool IsSuspended() override { UNIMPLEMENTED(); return false; }
   virtual nsresult ReadFromCache(char* aBuffer, int64_t aOffset, uint32_t aCount) override;
   virtual bool IsTransportSeekable() override { UNIMPLEMENTED(); return true; }
   virtual nsresult Open(nsIStreamListener** aStreamListener) override { UNIMPLEMENTED(); return NS_ERROR_FAILURE; }
 
-  virtual nsresult GetCachedRanges(nsTArray<MediaByteRange>& aRanges) override
+  virtual nsresult GetCachedRanges(MediaByteRangeSet& aRanges) override
   {
     ReentrantMonitorAutoEnter mon(mMonitor);
     if (mInputBuffer.GetLength()) {
-      aRanges.AppendElement(MediaByteRange(mInputBuffer.GetOffset(),
-                                           mInputBuffer.GetLength()));
+      aRanges += MediaByteRange(mInputBuffer.GetOffset(),
+                                mInputBuffer.GetLength());
     }
     return NS_OK;
   }
 
   virtual const nsCString& GetContentType() const override { return mType; }
 
   virtual size_t SizeOfExcludingThis(
                       MallocSizeOf aMallocSizeOf) const override
--- a/dom/media/mediasource/TrackBuffersManager.cpp
+++ b/dom/media/mediasource/TrackBuffersManager.cpp
@@ -675,17 +675,17 @@ TrackBuffersManager::SegmentParserLoop()
 
     int64_t start, end;
     bool newData = mParser->ParseStartAndEndTimestamps(mInputBuffer, start, end);
     mProcessedInput += mInputBuffer->Length();
 
     // 5. If the append state equals PARSING_INIT_SEGMENT, then run the
     // following steps:
     if (mAppendState == AppendState::PARSING_INIT_SEGMENT) {
-      if (mParser->InitSegmentRange().IsNull()) {
+      if (mParser->InitSegmentRange().IsEmpty()) {
         mInputBuffer = nullptr;
         NeedMoreData();
         return;
       }
       InitializationSegmentReceived();
       return;
     }
     if (mAppendState == AppendState::PARSING_MEDIA_SEGMENT) {
@@ -702,17 +702,17 @@ TrackBuffersManager::SegmentParserLoop()
       // monotonically increasing data.
       if (mNewMediaSegmentStarted) {
         if (newData && mLastParsedEndTime.isSome() &&
             start < mLastParsedEndTime.ref().ToMicroseconds()) {
           MSE_DEBUG("Re-creating demuxer");
           ResetDemuxingState();
           return;
         }
-        if (newData || !mParser->MediaSegmentRange().IsNull()) {
+        if (newData || !mParser->MediaSegmentRange().IsEmpty()) {
           if (mPendingInputBuffer) {
             // We now have a complete media segment header. We can resume parsing
             // the data.
             AppendDataToCurrentInputBuffer(mPendingInputBuffer);
             mPendingInputBuffer = nullptr;
           }
           mNewMediaSegmentStarted = false;
         } else {
@@ -1130,17 +1130,17 @@ TrackBuffersManager::OnDemuxerInitFailed
 
 RefPtr<TrackBuffersManager::CodedFrameProcessingPromise>
 TrackBuffersManager::CodedFrameProcessing()
 {
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(mProcessingPromise.IsEmpty());
 
   MediaByteRange mediaRange = mParser->MediaSegmentRange();
-  if (mediaRange.IsNull()) {
+  if (mediaRange.IsEmpty()) {
     AppendDataToCurrentInputBuffer(mInputBuffer);
     mInputBuffer = nullptr;
   } else {
     MOZ_ASSERT(mProcessedInput >= mInputBuffer->Length());
     if (int64_t(mProcessedInput - mInputBuffer->Length()) > mediaRange.mEnd) {
       // Something is not quite right with the data appended. Refuse it.
       // This would typically happen if the previous media segment was partial
       // yet a new complete media segment was added.
@@ -1304,17 +1304,17 @@ TrackBuffersManager::CompleteCodedFrameP
   // Return to step 6.4 of Segment Parser Loop algorithm
   // 4. If this SourceBuffer is full and cannot accept more media data, then set the buffer full flag to true.
   if (mSizeSourceBuffer >= mEvictionThreshold) {
     mBufferFull = true;
     mEvictionOccurred = false;
   }
 
   // 5. If the input buffer does not contain a complete media segment, then jump to the need more data step below.
-  if (mParser->MediaSegmentRange().IsNull()) {
+  if (mParser->MediaSegmentRange().IsEmpty()) {
     ResolveProcessing(true, __func__);
     return;
   }
 
   mLastParsedEndTime = Some(std::max(mAudioTracks.mLastParsedEndTime,
                                      mVideoTracks.mLastParsedEndTime));
 
   // 6. Remove the media segment bytes from the beginning of the input buffer.
--- a/dom/media/mediasource/gtest/TestContainerParser.cpp
+++ b/dom/media/mediasource/gtest/TestContainerParser.cpp
@@ -80,13 +80,13 @@ TEST(ContainerParser, ADTSHeader) {
   EXPECT_FALSE(parser->ParseStartAndEndTimestamps(header, start, end));
 
   EXPECT_TRUE(parser->HasInitData());
   EXPECT_TRUE(parser->HasCompleteInitData());
   MediaByteBuffer* init = parser->InitData();
   ASSERT_NE(init, nullptr);
   EXPECT_EQ(init->Length(), header->Length());
 
-  EXPECT_EQ(parser->InitSegmentRange(), MediaByteRange(0, header->Length()));
+  EXPECT_EQ(parser->InitSegmentRange(), MediaByteRange(0, int64_t(header->Length())));
   // Media segment range should be empty here.
   EXPECT_EQ(parser->MediaHeaderRange(), MediaByteRange());
   EXPECT_EQ(parser->MediaSegmentRange(), MediaByteRange());
 }
--- a/dom/media/ogg/OggReader.cpp
+++ b/dom/media/ogg/OggReader.cpp
@@ -1155,22 +1155,22 @@ int64_t OggReader::RangeEndTime(int64_t 
 
   return endTime;
 }
 
 nsresult OggReader::GetSeekRanges(nsTArray<SeekRange>& aRanges)
 {
   MOZ_ASSERT(OnTaskQueue());
   AutoPinned<MediaResource> resource(mDecoder->GetResource());
-  nsTArray<MediaByteRange> cached;
+  MediaByteRangeSet cached;
   nsresult res = resource->GetCachedRanges(cached);
   NS_ENSURE_SUCCESS(res, res);
 
   for (uint32_t index = 0; index < cached.Length(); index++) {
-    MediaByteRange& range = cached[index];
+    auto& range = cached[index];
     int64_t startTime = -1;
     int64_t endTime = -1;
     if (NS_FAILED(ResetDecode())) {
       return NS_ERROR_FAILURE;
     }
     int64_t startOffset = range.mStart;
     int64_t endOffset = range.mEnd;
     startTime = RangeStartTime(startOffset);
@@ -1836,17 +1836,17 @@ media::TimeIntervals OggReader::GetBuffe
   // a deadlock. Accessing mInfo doesn't require a lock - it doesn't change
   // after metadata is read.
   if (!mInfo.HasValidMedia()) {
     // No need to search through the file if there are no audio or video tracks
     return buffered;
   }
 
   AutoPinned<MediaResource> resource(mDecoder->GetResource());
-  nsTArray<MediaByteRange> ranges;
+  MediaByteRangeSet ranges;
   nsresult res = resource->GetCachedRanges(ranges);
   NS_ENSURE_SUCCESS(res, media::TimeIntervals::Invalid());
 
   // Traverse across the buffered byte ranges, determining the time ranges
   // they contain. MediaResource::GetNextCachedData(offset) returns -1 when
   // offset is after the end of the media resource, or there's no more cached
   // data after the offset. This loop will run until we've checked every
   // buffered range in the media, in increasing order of offset.
deleted file mode 100644
--- a/dom/media/omx/MediaCodecDecoder.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* 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 "MediaCodecDecoder.h"
-
-#include "MediaCodecReader.h"
-#include "MediaDecoderStateMachine.h"
-
-namespace mozilla {
-
-MediaDecoder*
-MediaCodecDecoder::Clone(MediaDecoderOwner* aOwner)
-{
-  return new MediaCodecDecoder(aOwner);
-}
-
-MediaOmxCommonReader*
-MediaCodecDecoder::CreateReader()
-{
-  return new MediaCodecReader(this);
-}
-
-MediaDecoderStateMachine*
-MediaCodecDecoder::CreateStateMachineFromReader(MediaOmxCommonReader* aReader)
-{
-  return new MediaDecoderStateMachine(this, aReader);
-}
-
-} // namespace mozilla
deleted file mode 100644
--- a/dom/media/omx/MediaCodecDecoder.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* 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 MEDIA_CODEC_DECODER_H
-#define MEDIA_CODEC_DECODER_H
-
-#include "MediaOmxCommonDecoder.h"
-
-namespace mozilla {
-
-// MediaDecoder that uses MediaCodecReader.
-class MediaCodecDecoder : public MediaOmxCommonDecoder
-{
-public:
-  explicit MediaCodecDecoder(MediaDecoderOwner* aOwner)
-    : MediaOmxCommonDecoder(aOwner) {}
-
-  virtual MediaDecoder* Clone(MediaDecoderOwner* aOwner);
-
-  virtual MediaOmxCommonReader* CreateReader();
-
-  virtual MediaDecoderStateMachine* CreateStateMachineFromReader(MediaOmxCommonReader* aReader);
-};
-
-} // namespace mozilla
-
-#endif // MEDIA_CODEC_DECODER_H
deleted file mode 100644
--- a/dom/media/omx/MediaCodecReader.cpp
+++ /dev/null
@@ -1,1928 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* 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 "MediaCodecReader.h"
-
-#include <OMX_IVCommon.h>
-
-#include <gui/Surface.h>
-#include <ICrypto.h>
-
-#include "GonkNativeWindow.h"
-
-#include <stagefright/foundation/ABuffer.h>
-#include <stagefright/foundation/ADebug.h>
-#include <stagefright/foundation/ALooper.h>
-#include <stagefright/foundation/AMessage.h>
-#include <stagefright/MediaBuffer.h>
-#include <stagefright/MediaCodec.h>
-#include <stagefright/MediaDefs.h>
-#include <stagefright/MediaExtractor.h>
-#include <stagefright/MediaSource.h>
-#include <stagefright/MetaData.h>
-#include <stagefright/Utils.h>
-
-#include "mozilla/TaskQueue.h"
-#include "mozilla/TimeStamp.h"
-#include "mozilla/layers/GrallocTextureClient.h"
-
-#include "gfx2DGlue.h"
-
-#include "MediaStreamSource.h"
-#include "MP3FrameParser.h"
-#include "nsMimeTypes.h"
-#include "nsThreadUtils.h"
-#include "ImageContainer.h"
-#include "mozilla/SharedThreadPool.h"
-#include "VideoFrameContainer.h"
-#include "VideoUtils.h"
-
-using namespace android;
-using namespace mozilla::layers;
-using namespace mozilla::media;
-
-namespace mozilla {
-
-static const int64_t sInvalidDurationUs = INT64_C(-1);
-static const int64_t sInvalidTimestampUs = INT64_C(-1);
-
-// Try not to spend more than this much time (in seconds) in a single call
-// to GetCodecOutputData.
-static const double sMaxAudioDecodeDurationS = 0.1;
-static const double sMaxVideoDecodeDurationS = 0.1;
-
-static CheckedUint32 sInvalidInputIndex = INT32_C(-1);
-
-inline bool
-IsValidDurationUs(int64_t aDuration)
-{
-  return aDuration >= INT64_C(0);
-}
-
-inline bool
-IsValidTimestampUs(int64_t aTimestamp)
-{
-  return aTimestamp >= INT64_C(0);
-}
-
-MediaCodecReader::TrackInputCopier::~TrackInputCopier()
-{
-}
-
-bool
-MediaCodecReader::TrackInputCopier::Copy(MediaBuffer* aSourceBuffer,
-                                         sp<ABuffer> aCodecBuffer)
-{
-  if (aSourceBuffer == nullptr ||
-      aCodecBuffer == nullptr ||
-      aSourceBuffer->range_length() > aCodecBuffer->capacity()) {
-    return false;
-  }
-
-  aCodecBuffer->setRange(0, aSourceBuffer->range_length());
-  memcpy(aCodecBuffer->data(),
-         (uint8_t*)aSourceBuffer->data() + aSourceBuffer->range_offset(),
-         aSourceBuffer->range_length());
-
-  return true;
-}
-
-MediaCodecReader::Track::Track(Type type)
-  : mType(type)
-  , mSourceIsStopped(true)
-  , mDurationUs(INT64_C(0))
-  , mInputIndex(sInvalidInputIndex)
-  , mInputEndOfStream(false)
-  , mOutputEndOfStream(false)
-  , mSeekTimeUs(sInvalidTimestampUs)
-  , mFlushed(false)
-  , mDiscontinuity(false)
-  , mTaskQueue(nullptr)
-  , mTrackMonitor("MediaCodecReader::mTrackMonitor")
-{
-  MOZ_ASSERT(mType != kUnknown, "Should have a valid Track::Type");
-}
-
-// Append the value of |kKeyValidSamples| to the end of each vorbis buffer.
-// https://github.com/mozilla-b2g/platform_frameworks_av/blob/master/media/libstagefright/OMXCodec.cpp#L3128
-// https://github.com/mozilla-b2g/platform_frameworks_av/blob/master/media/libstagefright/NuMediaExtractor.cpp#L472
-bool
-MediaCodecReader::VorbisInputCopier::Copy(MediaBuffer* aSourceBuffer,
-                                          sp<ABuffer> aCodecBuffer)
-{
-  if (aSourceBuffer == nullptr ||
-      aCodecBuffer == nullptr ||
-      aSourceBuffer->range_length() + sizeof(int32_t) > aCodecBuffer->capacity()) {
-    return false;
-  }
-
-  int32_t numPageSamples = 0;
-  if (!aSourceBuffer->meta_data()->findInt32(kKeyValidSamples, &numPageSamples)) {
-    numPageSamples = -1;
-  }
-
-  aCodecBuffer->setRange(0, aSourceBuffer->range_length() + sizeof(int32_t));
-  memcpy(aCodecBuffer->data(),
-         (uint8_t*)aSourceBuffer->data() + aSourceBuffer->range_offset(),
-         aSourceBuffer->range_length());
-  memcpy(aCodecBuffer->data() + aSourceBuffer->range_length(),
-         &numPageSamples, sizeof(numPageSamples));
-
-  return true;
-}
-
-MediaCodecReader::AudioTrack::AudioTrack()
-  : Track(kAudio)
-{
-  mAudioPromise.SetMonitor(&mTrackMonitor);
-}
-
-MediaCodecReader::VideoTrack::VideoTrack()
-  : Track(kVideo)
-  , mWidth(0)
-  , mHeight(0)
-  , mStride(0)
-  , mSliceHeight(0)
-  , mColorFormat(0)
-  , mRotation(0)
-{
-  mVideoPromise.SetMonitor(&mTrackMonitor);
-}
-
-MediaCodecReader::CodecBufferInfo::CodecBufferInfo()
-  : mIndex(0)
-  , mOffset(0)
-  , mSize(0)
-  , mTimeUs(0)
-  , mFlags(0)
-{
-}
-
-MediaCodecReader::SignalObject::SignalObject(const char* aName)
-  : mMonitor(aName)
-  , mSignaled(false)
-{
-}
-
-MediaCodecReader::SignalObject::~SignalObject()
-{
-}
-
-void
-MediaCodecReader::SignalObject::Wait()
-{
-  MonitorAutoLock al(mMonitor);
-  if (!mSignaled) {
-    mMonitor.Wait();
-  }
-}
-
-void
-MediaCodecReader::SignalObject::Signal()
-{
-  MonitorAutoLock al(mMonitor);
-  mSignaled = true;
-  mMonitor.Notify();
-}
-
-MediaCodecReader::ParseCachedDataRunnable::ParseCachedDataRunnable(RefPtr<MediaCodecReader> aReader,
-                                                                   const char* aBuffer,
-                                                                   uint32_t aLength,
-                                                                   int64_t aOffset,
-                                                                   RefPtr<SignalObject> aSignal)
-  : mReader(aReader)
-  , mBuffer(aBuffer)
-  , mLength(aLength)
-  , mOffset(aOffset)
-  , mSignal(aSignal)
-{
-  MOZ_ASSERT(mReader, "Should have a valid MediaCodecReader.");
-  MOZ_ASSERT(mBuffer, "Should have a valid buffer.");
-  MOZ_ASSERT(mOffset >= INT64_C(0), "Should have a valid offset.");
-}
-
-NS_IMETHODIMP
-MediaCodecReader::ParseCachedDataRunnable::Run()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
-
-  if (mReader->ParseDataSegment(mBuffer, mLength, mOffset)) {
-    MonitorAutoLock monLock(mReader->mParserMonitor);
-    if (mReader->mNextParserPosition >= mOffset + mLength &&
-        mReader->mParsedDataLength < mOffset + mLength) {
-      mReader->mParsedDataLength = mOffset + mLength;
-    }
-  }
-
-  if (mSignal != nullptr) {
-    mSignal->Signal();
-  }
-
-  return NS_OK;
-}
-
-MediaCodecReader::ProcessCachedDataTask::ProcessCachedDataTask(RefPtr<MediaCodecReader> aReader,
-                                                               int64_t aOffset)
-  : mReader(aReader)
-  , mOffset(aOffset)
-{
-  MOZ_ASSERT(mReader, "Should have a valid MediaCodecReader.");
-  MOZ_ASSERT(mOffset >= INT64_C(0), "Should have a valid offset.");
-}
-
-void
-MediaCodecReader::ProcessCachedDataTask::Run()
-{
-  mReader->ProcessCachedData(mOffset, nullptr);
-}
-
-MediaCodecReader::MediaCodecReader(AbstractMediaDecoder* aDecoder)
-  : MediaOmxCommonReader(aDecoder)
-  , mExtractor(nullptr)
-  , mTextureClientIndexesLock("MediaCodecReader::mTextureClientIndexesLock")
-  , mColorConverterBufferSize(0)
-  , mParserMonitor("MediaCodecReader::mParserMonitor")
-  , mParseDataFromCache(true)
-  , mNextParserPosition(INT64_C(0))
-  , mParsedDataLength(INT64_C(0))
-{
-}
-
-MediaCodecReader::~MediaCodecReader()
-{
-}
-
-void
-MediaCodecReader::ReleaseMediaResources()
-{
-  // Stop the mSource because we are in the dormant state and the stop function
-  // will rewind the mSource to the beginning of the stream.
-  if (mVideoTrack.mSource != nullptr && !mVideoTrack.mSourceIsStopped) {
-    mVideoTrack.mSource->stop();
-    mVideoTrack.mSourceIsStopped = true;
-  }
-  if (mAudioTrack.mSource != nullptr && !mAudioTrack.mSourceIsStopped) {
-    mAudioTrack.mSource->stop();
-    mAudioTrack.mSourceIsStopped = true;
-  }
-  ReleaseCriticalResources();
-}
-
-RefPtr<ShutdownPromise>
-MediaCodecReader::Shutdown()
-{
-  MOZ_ASSERT(mAudioTrack.mAudioPromise.IsEmpty());
-  MOZ_ASSERT(mVideoTrack.mVideoPromise.IsEmpty());
-  ReleaseResources();
-  return MediaDecoderReader::Shutdown();
-}
-
-void
-MediaCodecReader::DispatchAudioTask()
-{
-  if (mAudioTrack.mTaskQueue) {
-    RefPtr<nsIRunnable> task =
-      NS_NewRunnableMethod(this,
-                           &MediaCodecReader::DecodeAudioDataTask);
-    mAudioTrack.mTaskQueue->Dispatch(task.forget());
-  }
-}
-
-void
-MediaCodecReader::DispatchVideoTask(int64_t aTimeThreshold)
-{
-  if (mVideoTrack.mTaskQueue) {
-    RefPtr<nsIRunnable> task =
-      NS_NewRunnableMethodWithArg<int64_t>(this,
-                                           &MediaCodecReader::DecodeVideoFrameTask,
-                                           aTimeThreshold);
-    mVideoTrack.mTaskQueue->Dispatch(task.forget());
-  }
-}
-
-RefPtr<MediaDecoderReader::AudioDataPromise>
-MediaCodecReader::RequestAudioData()
-{
-  MOZ_ASSERT(OnTaskQueue());
-  MOZ_ASSERT(HasAudio());
-
-  MonitorAutoLock al(mAudioTrack.mTrackMonitor);
-  if (CheckAudioResources()) {
-    DispatchAudioTask();
-  }
-  MOZ_ASSERT(mAudioTrack.mAudioPromise.IsEmpty());
-  return mAudioTrack.mAudioPromise.Ensure(__func__);
-}
-
-RefPtr<MediaDecoderReader::VideoDataPromise>
-MediaCodecReader::RequestVideoData(bool aSkipToNextKeyframe,
-                                   int64_t aTimeThreshold)
-{
-  MOZ_ASSERT(OnTaskQueue());
-  MOZ_ASSERT(HasVideo());
-
-  int64_t threshold = sInvalidTimestampUs;
-  if (aSkipToNextKeyframe && IsValidTimestampUs(aTimeThreshold)) {
-    threshold = aTimeThreshold;
-  }
-
-  MonitorAutoLock al(mVideoTrack.mTrackMonitor);
-  if (CheckVideoResources()) {
-    DispatchVideoTask(threshold);
-  }
-  MOZ_ASSERT(mVideoTrack.mVideoPromise.IsEmpty());
-  return mVideoTrack.mVideoPromise.Ensure(__func__);
-}
-
-void
-MediaCodecReader::DecodeAudioDataSync()
-{
-  if (mAudioTrack.mCodec == nullptr || !mAudioTrack.mCodec->allocated() ||
-      mAudioTrack.mOutputEndOfStream) {
-    return;
-  }
-
-  // Get one audio output data from MediaCodec
-  CodecBufferInfo bufferInfo;
-  status_t status;
-  TimeStamp timeout = TimeStamp::Now() +
-                      TimeDuration::FromSeconds(sMaxAudioDecodeDurationS);
-  while (true) {
-    // Try to fill more input buffers and then get one output buffer.
-    // FIXME: use callback from MediaCodec
-    FillCodecInputData(mAudioTrack);
-
-    status = GetCodecOutputData(mAudioTrack, bufferInfo, sInvalidTimestampUs,
-                                timeout);
-    if (status == OK || status == ERROR_END_OF_STREAM) {
-      break;
-    } else if (status == -EAGAIN) {
-      if (TimeStamp::Now() > timeout) {
-        // Don't let this loop run for too long. Try it again later.
-        return;
-      }
-      continue; // Try it again now.
-    } else if (status == INFO_FORMAT_CHANGED) {
-      if (UpdateAudioInfo()) {
-        continue; // Try it again now.
-      } else {
-        return;
-      }
-    } else {
-      return;
-    }
-  }
-
-  if ((bufferInfo.mFlags & MediaCodec::BUFFER_FLAG_EOS) ||
-      (status == ERROR_END_OF_STREAM)) {
-    AudioQueue().Finish();
-  } else if (bufferInfo.mBuffer != nullptr && bufferInfo.mSize > 0 &&
-             bufferInfo.mBuffer->data() != nullptr) {
-    MOZ_ASSERT(mStreamSource);
-    // This is the approximate byte position in the stream.
-    int64_t pos = mStreamSource->Tell();
-
-    uint32_t frames = bufferInfo.mSize /
-                      (mInfo.mAudio.mChannels * sizeof(AudioDataValue));
-
-    mAudioCompactor.Push(
-      pos,
-      bufferInfo.mTimeUs,
-      mInfo.mAudio.mRate,
-      frames,
-      mInfo.mAudio.mChannels,
-      AudioCompactor::NativeCopy(
-        bufferInfo.mBuffer->data() + bufferInfo.mOffset,
-        bufferInfo.mSize,
-        mInfo.mAudio.mChannels));
-  }
-  mAudioTrack.mCodec->releaseOutputBuffer(bufferInfo.mIndex);
-}
-
-void
-MediaCodecReader::DecodeAudioDataTask()
-{
-  MOZ_ASSERT(mAudioTrack.mTaskQueue->IsCurrentThreadIn());
-  MonitorAutoLock al(mAudioTrack.mTrackMonitor);
-  if (mAudioTrack.mAudioPromise.IsEmpty()) {
-    // Clear the data in queue because the promise might be canceled by
-    // ResetDecode().
-    AudioQueue().Reset();
-    return;
-  }
-  if (AudioQueue().GetSize() == 0 && !AudioQueue().IsFinished()) {
-    MonitorAutoUnlock ul(mAudioTrack.mTrackMonitor);
-    DecodeAudioDataSync();
-  }
-  // Since we unlock the monitor above, we should check the promise again
-  // because the promise might be canceled by ResetDecode().
-  if (mAudioTrack.mAudioPromise.IsEmpty()) {
-    AudioQueue().Reset();
-    return;
-  }
-  if (AudioQueue().GetSize() > 0) {
-    RefPtr<AudioData> a = AudioQueue().PopFront();
-    if (a) {
-      if (mAudioTrack.mDiscontinuity) {
-        a->mDiscontinuity = true;
-        mAudioTrack.mDiscontinuity = false;
-      }
-      mAudioTrack.mAudioPromise.Resolve(a, __func__);
-    }
-  } else if (AudioQueue().AtEndOfStream()) {
-    mAudioTrack.mAudioPromise.Reject(END_OF_STREAM, __func__);
-  } else if (AudioQueue().GetSize() == 0) {
-    DispatchAudioTask();
-  }
-}
-
-void
-MediaCodecReader::DecodeVideoFrameTask(int64_t aTimeThreshold)
-{
-  MOZ_ASSERT(mVideoTrack.mTaskQueue->IsCurrentThreadIn());
-  MonitorAutoLock al(mVideoTrack.mTrackMonitor);
-  if (mVideoTrack.mVideoPromise.IsEmpty()) {
-    // Clear the data in queue because the promise might be canceled by
-    // ResetDecode().
-    VideoQueue().Reset();
-    return;
-  }
-  {
-    MonitorAutoUnlock ul(mVideoTrack.mTrackMonitor);
-    DecodeVideoFrameSync(aTimeThreshold);
-  }
-  // Since we unlock the monitor above, we should check the promise again
-  // because the promise might be canceled by ResetDecode().
-  if (mVideoTrack.mVideoPromise.IsEmpty()) {
-    VideoQueue().Reset();
-    return;
-  }
-  if (VideoQueue().GetSize() > 0) {
-    RefPtr<VideoData> v = VideoQueue().PopFront();
-    if (v) {
-      if (mVideoTrack.mDiscontinuity) {
-        v->mDiscontinuity = true;
-        mVideoTrack.mDiscontinuity = false;
-      }
-      mVideoTrack.mVideoPromise.Resolve(v, __func__);
-    }
-  } else if (VideoQueue().AtEndOfStream()) {
-    mVideoTrack.mVideoPromise.Reject(END_OF_STREAM, __func__);
-  } else if (VideoQueue().GetSize() == 0) {
-    DispatchVideoTask(aTimeThreshold);
-  }
-}
-
-bool
-MediaCodecReader::HasAudio()
-{
-  return mInfo.HasAudio();
-}
-
-bool
-MediaCodecReader::HasVideo()
-{
-  return mInfo.HasVideo();
-}
-
-void
-MediaCodecReader::NotifyDataArrivedInternal()
-{
-  AutoPinned<MediaResource> resource(mDecoder->GetResource());
-  nsTArray<MediaByteRange> byteRanges;
-  nsresult rv = resource->GetCachedRanges(byteRanges);
-
-  if (NS_FAILED(rv)) {
-    return;
-  }
-
-  IntervalSet<int64_t> intervals;
-  for (auto& range : byteRanges) {
-    intervals += mFilter.NotifyDataArrived(range.Length(), range.mStart);
-  }
-  for (const auto& interval : intervals) {
-    RefPtr<MediaByteBuffer> bytes =
-      resource->MediaReadAt(interval.mStart, interval.Length());
-    MonitorAutoLock monLock(mParserMonitor);
-    if (mNextParserPosition == mParsedDataLength &&
-        mNextParserPosition >= interval.mStart &&
-        mNextParserPosition <= interval.mEnd) {
-      // No pending parsing runnable currently. And available data are adjacent to
-      // parsed data.
-      int64_t shift = mNextParserPosition - interval.mStart;
-      const char* buffer = reinterpret_cast<const char*>(bytes->Elements()) + shift;
-      uint32_t length = interval.Length() - shift;
-      int64_t offset = mNextParserPosition;
-      if (length > 0) {
-        MonitorAutoUnlock monUnlock(mParserMonitor);
-        ParseDataSegment(buffer, length, offset);
-      }
-      mParseDataFromCache = false;
-      mParsedDataLength = offset + length;
-      mNextParserPosition = mParsedDataLength;
-    }
-  }
-}
-
-int64_t
-MediaCodecReader::ProcessCachedData(int64_t aOffset,
-                                    RefPtr<SignalObject> aSignal)
-{
-  // We read data in chunks of 32 KiB. We can reduce this
-  // value if media, such as sdcards, is too slow.
-  // Because of SD card's slowness, need to keep sReadSize to small size.
-  // See Bug 914870.
-  static const int64_t sReadSize = 32 * 1024;
-
-  MOZ_ASSERT(!NS_IsMainThread(), "Should not be on main thread.");
-
-  {
-    MonitorAutoLock monLock(mParserMonitor);
-    if (!mParseDataFromCache) {
-      // Skip cache processing since data can be continuously be parsed by
-      // ParseDataSegment() from NotifyDataArrived() directly.
-      return INT64_C(0);
-    }
-  }
-
-  MediaResource *resource = mDecoder->GetResource();
-  MOZ_ASSERT(resource);
-
-  int64_t resourceLength = resource->GetCachedDataEnd(0);
-  NS_ENSURE_TRUE(resourceLength >= 0, INT64_C(-1));
-
-  if (aOffset >= resourceLength) {
-    return INT64_C(0); // Cache is empty, nothing to do
-  }
-
-  int64_t bufferLength = std::min<int64_t>(resourceLength - aOffset, sReadSize);
-
-  nsAutoArrayPtr<char> buffer(new char[bufferLength]);
-
-  nsresult rv = resource->ReadFromCache(buffer.get(), aOffset, bufferLength);
-  NS_ENSURE_SUCCESS(rv, INT64_C(-1));
-
-  MonitorAutoLock monLock(mParserMonitor);
-  if (mParseDataFromCache) {
-    RefPtr<ParseCachedDataRunnable> runnable(
-      new ParseCachedDataRunnable(this,
-                                  buffer.forget(),
-                                  bufferLength,
-                                  aOffset,
-                                  aSignal));
-
-    rv = NS_DispatchToMainThread(runnable.get());
-    NS_ENSURE_SUCCESS(rv, INT64_C(-1));
-
-    mNextParserPosition = aOffset + bufferLength;
-    if (mNextParserPosition < resource->GetCachedDataEnd(0)) {
-      // We cannot read data in the main thread because it
-      // might block for too long. Instead we post an IO task
-      // to the IO thread if there is more data available.
-      XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
-          new ProcessCachedDataTask(this, mNextParserPosition));
-    }
-  }
-
-  return bufferLength;
-}
-
-bool
-MediaCodecReader::ParseDataSegment(const char* aBuffer,
-                                   uint32_t aLength,
-                                   int64_t aOffset)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
-
-  int64_t duration = INT64_C(-1);
-
-  {
-    MonitorAutoLock monLock(mParserMonitor);
-
-    // currently only mp3 files are supported for incremental parsing
-    if (mMP3FrameParser == nullptr) {
-      return false;
-    }
-
-    if (!mMP3FrameParser->IsMP3()) {
-      return true; // NO-OP
-    }
-
-    mMP3FrameParser->Parse(reinterpret_cast<const uint8_t*>(aBuffer), aLength, aOffset);
-
-    duration = mMP3FrameParser->GetDuration();
-  }
-
-  bool durationUpdateRequired = false;
-
-  {
-    MonitorAutoLock al(mAudioTrack.mTrackMonitor);
-    if (duration > mAudioTrack.mDurationUs) {
-      mAudioTrack.mDurationUs = duration;
-      durationUpdateRequired = true;
-    }
-  }
-
-  if (durationUpdateRequired && HasVideo()) {
-    MonitorAutoLock al(mVideoTrack.mTrackMonitor);
-    durationUpdateRequired = duration > mVideoTrack.mDurationUs;
-  }
-
-  if (durationUpdateRequired) {
-    MOZ_ASSERT(mDecoder);
-    mDecoder->DispatchUpdateEstimatedMediaDuration(duration);
-  }
-
-  return true;
-}
-
-RefPtr<MediaDecoderReader::MetadataPromise>
-MediaCodecReader::AsyncReadMetadata()
-{
-  MOZ_ASSERT(OnTaskQueue());
-
-  if (!ReallocateExtractorResources()) {
-    return MediaDecoderReader::MetadataPromise::CreateAndReject(
-             ReadMetadataFailureReason::METADATA_ERROR, __func__);
-  }
-
-  bool incrementalParserNeeded =
-    mDecoder->GetResource()->GetContentType().EqualsASCII(AUDIO_MP3);
-  if (incrementalParserNeeded && !TriggerIncrementalParser()) {
-    return MediaDecoderReader::MetadataPromise::CreateAndReject(
-             ReadMetadataFailureReason::METADATA_ERROR, __func__);
-  }
-
-  RefPtr<MediaDecoderReader::MetadataPromise> p = mMetadataPromise.Ensure(__func__);
-
-  RefPtr<MediaCodecReader> self = this;
-  mMediaResourceRequest.Begin(CreateMediaCodecs()
-    ->Then(OwnerThread(), __func__,
-      [self] (bool) -> void {
-        self->mMediaResourceRequest.Complete();
-        self->HandleResourceAllocated();
-      }, [self] (bool) -> void {
-        self->mMediaResourceRequest.Complete();
-        self->mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__);
-      }));
-
-  return p;
-}
-
-void
-MediaCodecReader::HandleResourceAllocated()
-{
-  // Configure video codec after the codecReserved.
-  if (mVideoTrack.mSource != nullptr) {
-    if (!ConfigureMediaCodec(mVideoTrack)) {
-      DestroyMediaCodec(mVideoTrack);
-      mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__);
-      return;
-    }
-  }
-
-  // TODO: start streaming
-
-  if (!UpdateDuration()) {
-    mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__);
-    return;
-  }
-
-  if (!UpdateAudioInfo()) {
-    mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__);
-    return;
-  }
-
-  if (!UpdateVideoInfo()) {
-    mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__);
-    return;
-  }
-
-  // Set the total duration (the max of the audio and video track).
-  int64_t audioDuration = INT64_C(-1);
-  {
-    MonitorAutoLock al(mAudioTrack.mTrackMonitor);
-    audioDuration = mAudioTrack.mDurationUs;
-  }
-  int64_t videoDuration = INT64_C(-1);
-  {
-    MonitorAutoLock al(mVideoTrack.mTrackMonitor);
-    videoDuration = mVideoTrack.mDurationUs;
-  }
-  int64_t duration = audioDuration > videoDuration ? audioDuration : videoDuration;
-  if (duration >= INT64_C(0)) {
-    mInfo.mMetadataDuration = Some(TimeUnit::FromMicroseconds(duration));
-  }
-
-  // Video track's frame sizes will not overflow. Activate the video track.
-  VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
-  if (container) {
-    container->ClearCurrentFrame(
-      gfx::IntSize(mInfo.mVideo.mDisplay.width, mInfo.mVideo.mDisplay.height));
-  }
-
-  RefPtr<MetadataHolder> metadata = new MetadataHolder();
-  metadata->mInfo = mInfo;
-  metadata->mTags = nullptr;
-
-#ifdef MOZ_AUDIO_OFFLOAD
-  CheckAudioOffload();
-#endif
-
-  mMetadataPromise.Resolve(metadata, __func__);
-}
-
-nsresult
-MediaCodecReader::ResetDecode()
-{
-  if (CheckAudioResources()) {
-    MonitorAutoLock al(mAudioTrack.mTrackMonitor);
-    if (!mAudioTrack.mAudioPromise.IsEmpty()) {
-      mAudioTrack.mAudioPromise.Reject(CANCELED, __func__);
-    }
-    FlushCodecData(mAudioTrack);
-    mAudioTrack.mDiscontinuity = true;
-  }
-  if (CheckVideoResources()) {
-    MonitorAutoLock al(mVideoTrack.mTrackMonitor);
-    if (!mVideoTrack.mVideoPromise.IsEmpty()) {
-      mVideoTrack.mVideoPromise.Reject(CANCELED, __func__);
-    }
-    FlushCodecData(mVideoTrack);
-    mVideoTrack.mDiscontinuity = true;
-  }
-
-  return MediaDecoderReader::ResetDecode();
-}
-
-void
-MediaCodecReader::TextureClientRecycleCallback(TextureClient* aClient,
-                                               void* aClosure)
-{
-  RefPtr<MediaCodecReader> reader = static_cast<MediaCodecReader*>(aClosure);
-  MOZ_ASSERT(reader, "reader should not be nullptr in TextureClientRecycleCallback()");
-
-  reader->TextureClientRecycleCallback(aClient);
-}
-
-void
-MediaCodecReader::TextureClientRecycleCallback(TextureClient* aClient)
-{
-  MOZ_ASSERT(aClient, "aClient should not be nullptr in RecycleCallback()");
-  MOZ_ASSERT(!aClient->IsDead());
-  size_t index = 0;
-
-  {
-    MutexAutoLock al(mTextureClientIndexesLock);
-
-    aClient->ClearRecycleCallback();
-
-    // aClient has been removed from mTextureClientIndexes by
-    // ReleaseAllTextureClients() on another thread.
-    if (!mTextureClientIndexes.Get(aClient, &index)) {
-      return;
-    }
-
-    FenceHandle handle = aClient->GetAndResetReleaseFenceHandle();
-    mPendingReleaseItems.AppendElement(ReleaseItem(index, handle));
-
-    mTextureClientIndexes.Remove(aClient);
-  }
-
-  if (mVideoTrack.mReleaseBufferTaskQueue->IsEmpty()) {
-    RefPtr<nsIRunnable> task =
-      NS_NewRunnableMethod(this,
-                           &MediaCodecReader::WaitFenceAndReleaseOutputBuffer);
-    mVideoTrack.mReleaseBufferTaskQueue->Dispatch(task.forget());
-  }
-}
-
-void
-MediaCodecReader::WaitFenceAndReleaseOutputBuffer()
-{
-  nsTArray<ReleaseItem> releasingItems;
-  {
-    MutexAutoLock autoLock(mTextureClientIndexesLock);
-    releasingItems.AppendElements(mPendingReleaseItems);
-    mPendingReleaseItems.Clear();
-  }
-
-  for (size_t i = 0; i < releasingItems.Length(); i++) {
-    if (releasingItems[i].mReleaseFence.IsValid()) {
-#if MOZ_WIDGET_GONK && ANDROID_VERSION >= 17
-      RefPtr<FenceHandle::FdObj> fdObj = releasingItems[i].mReleaseFence.GetAndResetFdObj();
-      sp<Fence> fence = new Fence(fdObj->GetAndResetFd());
-      fence->waitForever("MediaCodecReader");
-#endif
-    }
-    if (mVideoTrack.mCodec != nullptr) {
-      mVideoTrack.mCodec->releaseOutputBuffer(releasingItems[i].mReleaseIndex);
-    }
-  }
-}
-
-void
-MediaCodecReader::ReleaseAllTextureClients()
-{
-  MutexAutoLock al(mTextureClientIndexesLock);
-  MOZ_ASSERT(mTextureClientIndexes.Count(), "All TextureClients should be released already");
-
-  if (mTextureClientIndexes.Count() == 0) {
-    return;
-  }
-  printf_stderr("All TextureClients should be released already");
-
-  for (auto iter = mTextureClientIndexes.Iter(); !iter.Done(); iter.Next()) {
-    TextureClient* client = iter.Key();
-    size_t& index = iter.Data();
-
-    client->ClearRecycleCallback();
-
-    if (mVideoTrack.mCodec != nullptr) {
-      mVideoTrack.mCodec->releaseOutputBuffer(index);
-    }
-    iter.Remove();
-  }
-  mTextureClientIndexes.Clear();
-}
-
-void
-MediaCodecReader::DecodeVideoFrameSync(int64_t aTimeThreshold)
-{
-  if (mVideoTrack.mCodec == nullptr || !mVideoTrack.mCodec->allocated() ||
-      mVideoTrack.mOutputEndOfStream) {
-    return;
-  }
-
-  // Get one video output data from MediaCodec
-  CodecBufferInfo bufferInfo;
-  status_t status;
-  TimeStamp timeout = TimeStamp::Now() +
-                      TimeDuration::FromSeconds(sMaxVideoDecodeDurationS);
-  while (true) {
-    // Try to fill more input buffers and then get one output buffer.
-    // FIXME: use callback from MediaCodec
-    FillCodecInputData(mVideoTrack);
-
-    status = GetCodecOutputData(mVideoTrack, bufferInfo, aTimeThreshold,
-                                timeout);
-    if (status == OK || status == ERROR_END_OF_STREAM) {
-      break;
-    } else if (status == -EAGAIN) {
-      if (TimeStamp::Now() > timeout) {
-        // Don't let this loop run for too long. Try it again later.
-        return;
-      }
-      continue; // Try it again now.
-    } else if (status == INFO_FORMAT_CHANGED) {
-      if (UpdateVideoInfo()) {
-        continue; // Try it again now.
-      } else {
-        return;
-      }
-    } else {
-      return;
-    }
-  }
-
-  if ((bufferInfo.mFlags & MediaCodec::BUFFER_FLAG_EOS) ||
-      (status == ERROR_END_OF_STREAM)) {
-    VideoQueue().Finish();
-    mVideoTrack.mCodec->releaseOutputBuffer(bufferInfo.mIndex);
-    return;
-  }
-
-  RefPtr<VideoData> v;
-  RefPtr<TextureClient> textureClient;
-  sp<GraphicBuffer> graphicBuffer;
-  if (bufferInfo.mBuffer != nullptr) {
-    MOZ_ASSERT(mStreamSource);
-    // This is the approximate byte position in the stream.
-    int64_t pos = mStreamSource->Tell();
-
-    if (mVideoTrack.mNativeWindow != nullptr &&
-        mVideoTrack.mCodec->getOutputGraphicBufferFromIndex(bufferInfo.mIndex, &graphicBuffer) == OK &&
-        graphicBuffer != nullptr) {
-      textureClient = mVideoTrack.mNativeWindow->getTextureClientFromBuffer(graphicBuffer.get());
-      v = VideoData::Create(mInfo.mVideo,
-                            mDecoder->GetImageContainer(),
-                            pos,
-                            bufferInfo.mTimeUs,
-                            1, // We don't know the duration.
-                            textureClient,
-                            bufferInfo.mFlags & MediaCodec::BUFFER_FLAG_SYNCFRAME,
-                            -1,
-                            mVideoTrack.mRelativePictureRect);
-    } else if (bufferInfo.mSize > 0 &&
-        bufferInfo.mBuffer->data() != nullptr) {
-      uint8_t* yuv420p_buffer = bufferInfo.mBuffer->data();
-      int32_t stride = mVideoTrack.mStride;
-      int32_t slice_height = mVideoTrack.mSliceHeight;
-
-      // Converts to OMX_COLOR_FormatYUV420Planar
-      if (mVideoTrack.mColorFormat != OMX_COLOR_FormatYUV420Planar) {
-        ARect crop;
-        crop.top = 0;
-        crop.bottom = mVideoTrack.mHeight;
-        crop.left = 0;
-        crop.right = mVideoTrack.mWidth;
-
-        yuv420p_buffer = GetColorConverterBuffer(mVideoTrack.mWidth,
-                                                 mVideoTrack.mHeight);
-        if (mColorConverter.convertDecoderOutputToI420(
-              bufferInfo.mBuffer->data(), mVideoTrack.mWidth, mVideoTrack.mHeight,
-              crop, yuv420p_buffer) != OK) {
-          mVideoTrack.mCodec->releaseOutputBuffer(bufferInfo.mIndex);
-          NS_WARNING("Unable to convert color format");
-          return;
-        }
-
-        stride = mVideoTrack.mWidth;
-        slice_height = mVideoTrack.mHeight;
-      }
-
-      size_t yuv420p_y_size = stride * slice_height;
-      size_t yuv420p_u_size = ((stride + 1) / 2) * ((slice_height + 1) / 2);
-      uint8_t* yuv420p_y = yuv420p_buffer;
-      uint8_t* yuv420p_u = yuv420p_y + yuv420p_y_size;
-      uint8_t* yuv420p_v = yuv420p_u + yuv420p_u_size;
-
-      VideoData::YCbCrBuffer b;
-      b.mPlanes[0].mData = yuv420p_y;
-      b.mPlanes[0].mWidth = mVideoTrack.mWidth;
-      b.mPlanes[0].mHeight = mVideoTrack.mHeight;
-      b.mPlanes[0].mStride = stride;
-      b.mPlanes[0].mOffset = 0;
-      b.mPlanes[0].mSkip = 0;
-
-      b.mPlanes[1].mData = yuv420p_u;
-      b.mPlanes[1].mWidth = (mVideoTrack.mWidth + 1) / 2;
-      b.mPlanes[1].mHeight = (mVideoTrack.mHeight + 1) / 2;
-      b.mPlanes[1].mStride = (stride + 1) / 2;
-      b.mPlanes[1].mOffset = 0;
-      b.mPlanes[1].mSkip = 0;
-
-      b.mPlanes[2].mData = yuv420p_v;
-      b.mPlanes[2].mWidth =(mVideoTrack.mWidth + 1) / 2;
-      b.mPlanes[2].mHeight = (mVideoTrack.mHeight + 1) / 2;
-      b.mPlanes[2].mStride = (stride + 1) / 2;
-      b.mPlanes[2].mOffset = 0;
-      b.mPlanes[2].mSkip = 0;
-
-      v = VideoData::Create(mInfo.mVideo,
-                            mDecoder->GetImageContainer(),
-                            pos,
-                            bufferInfo.mTimeUs,
-                            1, // We don't know the duration.
-                            b,
-                            bufferInfo.mFlags & MediaCodec::BUFFER_FLAG_SYNCFRAME,
-                            -1,
-                            mVideoTrack.mRelativePictureRect);
-    }
-
-    if (v) {
-      // Notify mDecoder that we have decoded a video frame.
-      mDecoder->NotifyDecodedFrames(0, 1, 0);
-      VideoQueue().Push(v);
-    } else {
-      NS_WARNING("Unable to create VideoData");
-    }
-  }
-
-  if (v != nullptr && textureClient != nullptr && graphicBuffer != nullptr) {
-    MutexAutoLock al(mTextureClientIndexesLock);
-    mTextureClientIndexes.Put(textureClient.get(), bufferInfo.mIndex);
-    textureClient->SetRecycleCallback(MediaCodecReader::TextureClientRecycleCallback, this);
-  } else {
-    mVideoTrack.mCodec->releaseOutputBuffer(bufferInfo.mIndex);
-  }
-}
-
-RefPtr<MediaDecoderReader::SeekPromise>
-MediaCodecReader::Seek(int64_t aTime, int64_t aEndTime)
-{
-  MOZ_ASSERT(OnTaskQueue());
-
-  int64_t timestamp = sInvalidTimestampUs;
-
-  if (CheckVideoResources()) {
-    MonitorAutoLock al(mVideoTrack.mTrackMonitor);
-    mVideoTrack.mSeekTimeUs = aTime;
-    mVideoTrack.mInputEndOfStream = false;
-    mVideoTrack.mOutputEndOfStream = false;
-    mVideoTrack.mFlushed = false;
-
-    MediaBuffer* source_buffer = nullptr;
-    MediaSource::ReadOptions options;
-    options.setSeekTo(aTime, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
-    if (mVideoTrack.mSource->read(&source_buffer, &options) != OK ||
-        source_buffer == nullptr) {
-      return SeekPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
-    }
-    sp<MetaData> format = source_buffer->meta_data();
-    if (format != nullptr) {
-      if (format->findInt64(kKeyTime, &timestamp) &&
-          IsValidTimestampUs(timestamp)) {
-        mVideoTrack.mSeekTimeUs = timestamp;
-      }
-      format = nullptr;
-    }
-    source_buffer->release();
-  }
-
-  {
-    MonitorAutoLock al(mAudioTrack.mTrackMonitor);
-    mAudioTrack.mInputEndOfStream = false;
-    mAudioTrack.mOutputEndOfStream = false;
-    mAudioTrack.mFlushed = false;
-
-    if (IsValidTimestampUs(timestamp)) {
-      mAudioTrack.mSeekTimeUs = timestamp;
-    } else {
-      mAudioTrack.mSeekTimeUs = aTime;
-    }
-  }
-
-  return SeekPromise::CreateAndResolve(aTime, __func__);
-}
-
-bool
-MediaCodecReader::IsMediaSeekable()
-{
-  // Check the MediaExtract flag if the source is seekable.
-  return (mExtractor != nullptr) &&
-         (mExtractor->flags() & MediaExtractor::CAN_SEEK);
-}
-
-sp<MediaSource>
-MediaCodecReader::GetAudioOffloadTrack()
-{
-  return mAudioOffloadTrack.mSource;
-}
-
-bool
-MediaCodecReader::ReallocateExtractorResources()
-{
-  if (CreateLooper() &&
-      CreateExtractor() &&
-      CreateMediaSources() &&
-      CreateTaskQueues()) {
-    return true;
-  }
-
-  ReleaseResources();
-  return false;
-}
-
-void
-MediaCodecReader::ReleaseCriticalResources()
-{
-  mMediaResourceRequest.DisconnectIfExists();
-  mMediaResourcePromise.RejectIfExists(true, __func__);
-  mMetadataPromise.RejectIfExists(ReadMetadataFailureReason::METADATA_ERROR, __func__);
-  mVideoCodecRequest.DisconnectIfExists();
-
-  ResetDecode();
-  // Before freeing a video codec, all video buffers needed to be released
-  // even from graphics pipeline.
-  VideoFrameContainer* videoframe = mDecoder->GetVideoFrameContainer();
-  if (videoframe) {
-    videoframe->ClearCurrentFrame();
-  }
-  ReleaseAllTextureClients();
-
-  DestroyMediaCodecs();
-
-  ClearColorConverterBuffer();
-}
-
-void
-MediaCodecReader::ReleaseResources()
-{
-  ReleaseCriticalResources();
-  DestroyMediaSources();
-  DestroyExtractor();
-  DestroyLooper();
-  ShutdownTaskQueues();
-}
-
-bool
-MediaCodecReader::CreateLooper()
-{
-  if (mLooper != nullptr) {
-    return true;
-  }
-
-  // Create ALooper
-  sp<ALooper> looper = new ALooper;
-  looper->setName("MediaCodecReader::mLooper");
-
-  // Start ALooper thread.
-  if (looper->start() != OK) {
-    return false;
-  }
-
-  mLooper = looper;
-
-  return true;
-}
-
-void
-MediaCodecReader::DestroyLooper()
-{
-  if (mLooper == nullptr) {
-    return;
-  }
-
-  // Stop ALooper thread.
-  mLooper->stop();
-
-  // Clear ALooper
-  mLooper = nullptr;
-}
-
-bool
-MediaCodecReader::CreateExtractor()
-{
-  if (mExtractor != nullptr) {
-    return true;
-  }
-
-  //register sniffers, if they are not registered in this process.
-  DataSource::RegisterDefaultSniffers();
-
-  if (mExtractor == nullptr) {
-    sp<DataSource> dataSource = new MediaStreamSource(mDecoder->GetResource());
-
-    if (dataSource->initCheck() != OK) {
-      return false;
-    }
-    mStreamSource = static_cast<MediaStreamSource*>(dataSource.get());
-    mExtractor = MediaExtractor::Create(dataSource);
-  }
-
-  return mExtractor != nullptr;
-}
-
-void
-MediaCodecReader::DestroyExtractor()
-{
-  mExtractor = nullptr;
-}
-
-bool
-MediaCodecReader::CreateMediaSources()
-{
-  if (mExtractor == nullptr) {
-    return false;
-  }
-
-  mMetaData = mExtractor->getMetaData();
-
-  const ssize_t invalidTrackIndex = -1;
-  ssize_t audioTrackIndex = invalidTrackIndex;
-  ssize_t videoTrackIndex = invalidTrackIndex;
-
-  for (size_t i = 0; i < mExtractor->countTracks(); ++i) {
-    sp<MetaData> trackFormat = mExtractor->getTrackMetaData(i);
-
-    const char* mime;
-    if (!trackFormat->findCString(kKeyMIMEType, &mime)) {
-      continue;
-    }
-
-    if (audioTrackIndex == invalidTrackIndex &&
-        !strncasecmp(mime, "audio/", 6)) {
-      audioTrackIndex = i;
-    } else if (videoTrackIndex == invalidTrackIndex &&
-               !strncasecmp(mime, "video/", 6)) {
-      videoTrackIndex = i;
-    }
-  }
-
-  if (audioTrackIndex == invalidTrackIndex &&
-      videoTrackIndex == invalidTrackIndex) {
-    NS_WARNING("OMX decoder could not find audio or video tracks");
-    return false;
-  }
-
-  if (audioTrackIndex != invalidTrackIndex && mAudioTrack.mSource == nullptr) {
-    sp<MediaSource> audioSource = mExtractor->getTrack(audioTrackIndex);
-    if (audioSource != nullptr && audioSource->start() == OK) {
-      mAudioTrack.mSource = audioSource;
-      mAudioTrack.mSourceIsStopped = false;
-    }
-    // Get one another track instance for audio offload playback.
-    mAudioOffloadTrack.mSource = mExtractor->getTrack(audioTrackIndex);
-  }
-
-  if (videoTrackIndex != invalidTrackIndex && mVideoTrack.mSource == nullptr &&
-      mDecoder->GetImageContainer()) {
-    sp<MediaSource> videoSource = mExtractor->getTrack(videoTrackIndex);
-    if (videoSource != nullptr && videoSource->start() == OK) {
-      mVideoTrack.mSource = videoSource;
-      mVideoTrack.mSourceIsStopped = false;
-    }
-  }
-
-  return
-    (audioTrackIndex == invalidTrackIndex || mAudioTrack.mSource != nullptr) &&
-    (videoTrackIndex == invalidTrackIndex || mVideoTrack.mSource != nullptr);
-}
-
-void
-MediaCodecReader::DestroyMediaSources()
-{
-  mAudioTrack.mSource = nullptr;
-  mVideoTrack.mSource = nullptr;
-#if ANDROID_VERSION >= 21
-  mAudioOffloadTrack.mSource = nullptr;
-#endif
-}
-
-void
-MediaCodecReader::ShutdownTaskQueues()
-{
-  if (mAudioTrack.mTaskQueue) {
-    mAudioTrack.mTaskQueue->BeginShutdown();
-    mAudioTrack.mTaskQueue->AwaitShutdownAndIdle();
-    mAudioTrack.mTaskQueue = nullptr;
-  }
-  if (mVideoTrack.mTaskQueue) {
-    mVideoTrack.mTaskQueue->BeginShutdown();
-    mVideoTrack.mTaskQueue->AwaitShutdownAndIdle();
-    mVideoTrack.mTaskQueue = nullptr;
-  }
-  if (mVideoTrack.mReleaseBufferTaskQueue) {
-    mVideoTrack.mReleaseBufferTaskQueue->BeginShutdown();
-    mVideoTrack.mReleaseBufferTaskQueue->AwaitShutdownAndIdle();
-    mVideoTrack.mReleaseBufferTaskQueue = nullptr;
-  }
-}
-
-bool
-MediaCodecReader::CreateTaskQueues()
-{
-  if (mAudioTrack.mSource != nullptr && !mAudioTrack.mTaskQueue) {
-    mAudioTrack.mTaskQueue = CreateMediaDecodeTaskQueue();
-    NS_ENSURE_TRUE(mAudioTrack.mTaskQueue, false);
-  }
-  if (mVideoTrack.mSource != nullptr && !mVideoTrack.mTaskQueue) {
-    mVideoTrack.mTaskQueue = CreateMediaDecodeTaskQueue();
-    NS_ENSURE_TRUE(mVideoTrack.mTaskQueue, false);
-    mVideoTrack.mReleaseBufferTaskQueue = CreateMediaDecodeTaskQueue();
-    NS_ENSURE_TRUE(mVideoTrack.mReleaseBufferTaskQueue, false);
-  }
-
-  return true;
-}
-
-RefPtr<MediaOmxCommonReader::MediaResourcePromise>
-MediaCodecReader::CreateMediaCodecs()
-{
-  bool isWaiting = false;
-  RefPtr<MediaResourcePromise> p = mMediaResourcePromise.Ensure(__func__);
-
-  if (!CreateMediaCodec(mLooper, mAudioTrack, isWaiting)) {
-    mMediaResourcePromise.Reject(true, __func__);
-    return p;
-  }
-
-  if (!CreateMediaCodec(mLooper, mVideoTrack, isWaiting)) {
-    mMediaResourcePromise.Reject(true, __func__);
-    return p;
-  }
-
-  if (!isWaiting) {
-    // No MediaCodec allocation wait.
-    mMediaResourcePromise.Resolve(true, __func__);
-  }
-
-  return p;
-}
-
-bool
-MediaCodecReader::CreateMediaCodec(sp<ALooper>& aLooper,
-                                   Track& aTrack,
-                                   bool& aIsWaiting)
-{
-  if (aTrack.mSource != nullptr && aTrack.mCodec == nullptr) {
-    sp<MetaData> sourceFormat = aTrack.mSource->getFormat();
-
-    const char* mime;
-    if (sourceFormat->findCString(kKeyMIMEType, &mime)) {
-      aTrack.mCodec = MediaCodecProxy::CreateByType(aLooper, mime, false);
-    }
-
-    if (aTrack.mCodec == nullptr) {
-      NS_WARNING("Couldn't create MediaCodecProxy");
-      return false;
-    }
-
-    if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) {
-      aTrack.mInputCopier = new VorbisInputCopier;
-    } else {
-      aTrack.mInputCopier = new TrackInputCopier;
-    }
-
-    uint32_t capability = MediaCodecProxy::kEmptyCapability;
-    if (aTrack.mType == Track::kVideo &&
-        aTrack.mCodec->getCapability(&capability) == OK &&
-        (capability & MediaCodecProxy::kCanExposeGraphicBuffer) == MediaCodecProxy::kCanExposeGraphicBuffer) {
-#if ANDROID_VERSION >= 21
-      android::sp<android::IGraphicBufferProducer> producer;
-      android::sp<android::IGonkGraphicBufferConsumer> consumer;
-      GonkBufferQueue::createBufferQueue(&producer, &consumer);
-      aTrack.mNativeWindow = new GonkNativeWindow(consumer);
-      aTrack.mGraphicBufferProducer = producer;
-#else
-      aTrack.mNativeWindow = new GonkNativeWindow();
-#endif
-    }
-
-    if (aTrack.mType == Track::kAudio && aTrack.mCodec->AllocateAudioMediaCodec()) {
-      // Pending configure() and start() to codecReserved() if the creation
-      // should be asynchronous.
-      if (!aTrack.mCodec->allocated() || !ConfigureMediaCodec(aTrack)){
-        NS_WARNING("Couldn't create and configure MediaCodec synchronously");
-        DestroyMediaCodec(aTrack);
-        return false;
-      }
-    } else if (aTrack.mType == Track::kVideo) {
-      aIsWaiting = true;
-      RefPtr<MediaCodecReader> self = this;
-      mVideoCodecRequest.Begin(aTrack.mCodec->AsyncAllocateVideoMediaCodec()
-        ->Then(OwnerThread(), __func__,
-          [self] (bool) -> void {
-            self->mVideoCodecRequest.Complete();
-            self->mMediaResourcePromise.ResolveIfExists(true, __func__);
-          }, [self] (bool) -> void {
-            self->mVideoCodecRequest.Complete();
-            self->mMediaResourcePromise.RejectIfExists(true, __func__);
-          }));
-    }
-  }
-
-  return true;
-}
-
-bool
-MediaCodecReader::ConfigureMediaCodec(Track& aTrack)
-{
-  if (aTrack.mSource != nullptr && aTrack.mCodec != nullptr) {
-    if (!aTrack.mCodec->allocated()) {
-      return false;
-    }
-
-    sp<Surface> surface;
-    if (aTrack.mNativeWindow != nullptr) {
-#if ANDROID_VERSION >= 21
-      surface = new Surface(aTrack.mGraphicBufferProducer);
-#else
-      surface = new Surface(aTrack.mNativeWindow->getBufferQueue());
-#endif
-    }
-
-    sp<MetaData> sourceFormat = aTrack.mSource->getFormat();
-    sp<AMessage> codecFormat;
-    convertMetaDataToMessage(sourceFormat, &codecFormat);
-
-    bool allpass = true;
-    if (allpass && aTrack.mCodec->configure(codecFormat, surface, nullptr, 0) != OK) {
-      NS_WARNING("Couldn't configure MediaCodec");
-      allpass = false;
-    }
-    if (allpass && aTrack.mCodec->start() != OK) {
-      NS_WARNING("Couldn't start MediaCodec");
-      allpass = false;
-    }
-    if (allpass && aTrack.mCodec->getInputBuffers(&aTrack.mInputBuffers) != OK) {
-      NS_WARNING("Couldn't get input buffers from MediaCodec");
-      allpass = false;
-    }
-    if (allpass && aTrack.mCodec->getOutputBuffers(&aTrack.mOutputBuffers) != OK) {
-      NS_WARNING("Couldn't get output buffers from MediaCodec");
-      allpass = false;
-    }
-    if (!allpass) {
-      DestroyMediaCodec(aTrack);
-      return false;
-    }
-  }
-
-  return true;
-}
-
-void
-MediaCodecReader::DestroyMediaCodecs()
-{
-  DestroyMediaCodec(mAudioTrack);
-  DestroyMediaCodec(mVideoTrack);
-}
-
-void
-MediaCodecReader::DestroyMediaCodec(Track& aTrack)
-{
-  aTrack.mCodec = nullptr;
-  aTrack.mNativeWindow = nullptr;
-#if ANDROID_VERSION >= 21
-  aTrack.mGraphicBufferProducer = nullptr;
-#endif
-}
-
-bool
-MediaCodecReader::TriggerIncrementalParser()
-{
-  if (mMetaData == nullptr) {
-    return false;
-  }
-
-  int64_t duration = INT64_C(-1);
-
-  {
-    MonitorAutoLock monLock(mParserMonitor);
-
-    // only support incremental parsing for mp3 currently.
-    if (mMP3FrameParser != nullptr) {
-      return true;
-    }
-
-    mParseDataFromCache = true;
-    mNextParserPosition = INT64_C(0);
-    mParsedDataLength = INT64_C(0);
-
-    // MP3 file duration
-    const char* mime = nullptr;
-    if (mMetaData->findCString(kKeyMIMEType, &mime) &&
-        !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) {
-      mMP3FrameParser = new MP3FrameParser(mDecoder->GetResource()->GetLength());
-      {
-        MonitorAutoUnlock monUnlock(mParserMonitor);
-        // trigger parsing logic and wait for finishing parsing data in the beginning.
-        RefPtr<SignalObject> signalObject = new SignalObject("MediaCodecReader::UpdateDuration()");
-        if (ProcessCachedData(INT64_C(0), signalObject) > INT64_C(0)) {
-          signalObject->Wait();
-        }
-      }
-      duration = mMP3FrameParser->GetDuration();
-    }
-  }
-
-  {
-    MonitorAutoLock al(mAudioTrack.mTrackMonitor);
-    if (duration > mAudioTrack.mDurationUs) {
-      mAudioTrack.mDurationUs = duration;
-    }
-  }
-
-  return true;
-}
-
-bool
-MediaCodecReader::UpdateDuration()
-{
-  // read audio duration
-  if (mAudioTrack.mSource != nullptr) {
-    sp<MetaData> audioFormat = mAudioTrack.mSource->getFormat();
-    if (audioFormat != nullptr) {
-      int64_t duration = INT64_C(0);
-      if (audioFormat->findInt64(kKeyDuration, &duration)) {
-        MonitorAutoLock al(mAudioTrack.mTrackMonitor);
-        if (duration > mAudioTrack.mDurationUs) {
-          mAudioTrack.mDurationUs = duration;
-        }
-      }
-    }
-  }
-
-  // read video duration
-  if (mVideoTrack.mSource != nullptr) {
-    sp<MetaData> videoFormat = mVideoTrack.mSource->getFormat();
-    if (videoFormat != nullptr) {
-      int64_t duration = INT64_C(0);
-      if (videoFormat->findInt64(kKeyDuration, &duration)) {
-        MonitorAutoLock al(mVideoTrack.mTrackMonitor);
-        if (duration > mVideoTrack.mDurationUs) {
-          mVideoTrack.mDurationUs = duration;
-        }
-      }
-    }
-  }
-
-  return true;
-}
-
-bool
-MediaCodecReader::UpdateAudioInfo()
-{
-  if (mAudioTrack.mSource == nullptr && mAudioTrack.mCodec == nullptr) {
-    // No needs to update AudioInfo if no audio streams.
-    return true;
-  }
-
-  if (mAudioTrack.mSource == nullptr || mAudioTrack.mCodec == nullptr ||
-      !mAudioTrack.mCodec->allocated()) {
-    // Something wrong.
-    MOZ_ASSERT(mAudioTrack.mSource != nullptr, "mAudioTrack.mSource should not be nullptr");
-    MOZ_ASSERT(mAudioTrack.mCodec != nullptr, "mAudioTrack.mCodec should not be nullptr");
-    MOZ_ASSERT(mAudioTrack.mCodec->allocated(), "mAudioTrack.mCodec->allocated() should not be false");
-    return false;
-  }
-
-  // read audio metadata from MediaSource
-  sp<MetaData> audioSourceFormat = mAudioTrack.mSource->getFormat();
-  if (audioSourceFormat == nullptr) {
-    return false;
-  }
-
-  // ensure audio metadata from MediaCodec has been parsed
-  if (!EnsureCodecFormatParsed(mAudioTrack)){
-    return false;
-  }
-
-  // read audio metadata from MediaCodec
-  sp<AMessage> audioCodecFormat;
-  if (mAudioTrack.mCodec->getOutputFormat(&audioCodecFormat) != OK ||
-      audioCodecFormat == nullptr) {
-    return false;
-  }
-
-  AString codec_mime;
-  int32_t codec_channel_count = 0;
-  int32_t codec_sample_rate = 0;
-  if (!audioCodecFormat->findString("mime", &codec_mime) ||
-      !audioCodecFormat->findInt32("channel-count", &codec_channel_count) ||
-      !audioCodecFormat->findInt32("sample-rate", &codec_sample_rate)) {
-    return false;
-  }
-
-  // Update AudioInfo
-  mInfo.mAudio.mChannels = codec_channel_count;
-  mInfo.mAudio.mRate = codec_sample_rate;
-
-  return true;
-}
-
-bool
-MediaCodecReader::UpdateVideoInfo()
-{
-  if (mVideoTrack.mSource == nullptr && mVideoTrack.mCodec == nullptr) {
-    // No needs to update VideoInfo if no video streams.
-    return true;
-  }
-
-  if (mVideoTrack.mSource == nullptr || mVideoTrack.mCodec == nullptr ||
-      !mVideoTrack.mCodec->allocated()) {
-    // Something wrong.
-    MOZ_ASSERT(mVideoTrack.mSource != nullptr, "mVideoTrack.mSource should not be nullptr");
-    MOZ_ASSERT(mVideoTrack.mCodec != nullptr, "mVideoTrack.mCodec should not be nullptr");
-    MOZ_ASSERT(mVideoTrack.mCodec->allocated(), "mVideoTrack.mCodec->allocated() should not be false");
-    return false;
-  }
-
-  // read video metadata from MediaSource
-  sp<MetaData> videoSourceFormat = mVideoTrack.mSource->getFormat();
-  if (videoSourceFormat == nullptr) {
-    return false;
-  }
-  int32_t container_width = 0;
-  int32_t container_height = 0;
-  int32_t container_rotation = 0;
-  if (!videoSourceFormat->findInt32(kKeyWidth, &container_width) ||
-      !videoSourceFormat->findInt32(kKeyHeight, &container_height)) {
-    return false;
-  }
-  mVideoTrack.mFrameSize = nsIntSize(container_width, container_height);
-  if (videoSourceFormat->findInt32(kKeyRotation, &container_rotation)) {
-    mVideoTrack.mRotation = container_rotation;
-  }
-
-  // ensure video metadata from MediaCodec has been parsed
-  if (!EnsureCodecFormatParsed(mVideoTrack)){
-    return false;
-  }
-
-  // read video metadata from MediaCodec
-  sp<AMessage> videoCodecFormat;
-  if (mVideoTrack.mCodec->getOutputFormat(&videoCodecFormat) != OK ||
-      videoCodecFormat == nullptr) {
-    return false;
-  }
-  AString codec_mime;
-  int32_t codec_width = 0;
-  int32_t codec_height = 0;
-  int32_t codec_stride = 0;
-  int32_t codec_slice_height = 0;
-  int32_t codec_color_format = 0;
-  int32_t codec_crop_left = 0;
-  int32_t codec_crop_top = 0;
-  int32_t codec_crop_right = 0;
-  int32_t codec_crop_bottom = 0;
-  if (!videoCodecFormat->findString("mime", &codec_mime) ||
-      !videoCodecFormat->findInt32("width", &codec_width) ||
-      !videoCodecFormat->findInt32("height", &codec_height) ||
-      !videoCodecFormat->findInt32("stride", &codec_stride) ||
-      !videoCodecFormat->findInt32("slice-height", &codec_slice_height) ||
-      !videoCodecFormat->findInt32("color-format", &codec_color_format) ||
-      !videoCodecFormat->findRect("crop", &codec_crop_left, &codec_crop_top,
-                                  &codec_crop_right, &codec_crop_bottom)) {
-    return false;
-  }
-
-  mVideoTrack.mWidth = codec_width;
-  mVideoTrack.mHeight = codec_height;
-  mVideoTrack.mStride = codec_stride;
-  mVideoTrack.mSliceHeight = codec_slice_height;
-  mVideoTrack.mColorFormat = codec_color_format;
-
-  // Validate the container-reported frame and pictureRect sizes. This ensures
-  // that our video frame creation code doesn't overflow.
-  int32_t display_width = codec_crop_right - codec_crop_left + 1;
-  int32_t display_height = codec_crop_bottom - codec_crop_top + 1;
-  nsIntRect picture_rect(0, 0, mVideoTrack.mWidth, mVideoTrack.mHeight);
-  nsIntSize display_size(display_width, display_height);
-  if (!IsValidVideoRegion(mVideoTrack.mFrameSize, picture_rect, display_size)) {
-    return false;
-  }
-
-  // Relative picture size
-  gfx::IntRect relative_picture_rect = picture_rect;
-  if (mVideoTrack.mWidth != mVideoTrack.mFrameSize.width ||
-      mVideoTrack.mHeight != mVideoTrack.mFrameSize.height) {
-    // Frame size is different from what the container reports. This is legal,
-    // and we will preserve the ratio of the crop rectangle as it
-    // was reported relative to the picture size reported by the container.
-    relative_picture_rect.x = (picture_rect.x * mVideoTrack.mWidth) /
-                              mVideoTrack.mFrameSize.width;
-    relative_picture_rect.y = (picture_rect.y * mVideoTrack.mHeight) /
-                              mVideoTrack.mFrameSize.height;
-    relative_picture_rect.width = (picture_rect.width * mVideoTrack.mWidth) /
-                                  mVideoTrack.mFrameSize.width;
-    relative_picture_rect.height = (picture_rect.height * mVideoTrack.mHeight) /
-                                   mVideoTrack.mFrameSize.height;
-  }
-
-  // Update VideoInfo
-  mVideoTrack.mPictureRect = picture_rect;
-  mInfo.mVideo.mDisplay = display_size;
-  mVideoTrack.mRelativePictureRect = relative_picture_rect;
-
-  return true;
-}
-
-status_t
-MediaCodecReader::FlushCodecData(Track& aTrack)
-{
-  if (aTrack.mType == Track::kVideo) {
-    // TODO: if we do release TextureClient on a separate thread in the future,
-    // we will have to explicitly cleanup TextureClients which have been
-    // recycled through TextureClient::mRecycleCallback.
-    // Just NO-OP for now.
-  }
-
-  if (aTrack.mSource == nullptr || aTrack.mCodec == nullptr ||
-      !aTrack.mCodec->allocated()) {
-    return UNKNOWN_ERROR;
-  }
-
-  status_t status = aTrack.mCodec->flush();
-  aTrack.mFlushed = (status == OK);
-  if (aTrack.mFlushed) {
-    aTrack.mInputIndex = sInvalidInputIndex;
-  }
-
-  return status;
-}
-
-// Keep filling data if there are available buffers.
-// FIXME: change to non-blocking read
-status_t
-MediaCodecReader::FillCodecInputData(Track& aTrack)
-{
-  if (aTrack.mSource == nullptr || aTrack.mCodec == nullptr ||
-      !aTrack.mCodec->allocated()) {
-    return UNKNOWN_ERROR;
-  }
-
-  if (aTrack.mInputEndOfStream) {
-    return ERROR_END_OF_STREAM;
-  }
-
-  if (IsValidTimestampUs(aTrack.mSeekTimeUs) && !aTrack.mFlushed) {
-    FlushCodecData(aTrack);
-  }
-
-  size_t index = 0;
-  while (aTrack.mInputIndex.isValid() ||
-         aTrack.mCodec->dequeueInputBuffer(&index) == OK) {
-    if (!aTrack.mInputIndex.isValid()) {
-      aTrack.mInputIndex = index;
-    }
-    MOZ_ASSERT(aTrack.mInputIndex.isValid(), "aElement.mInputIndex should be valid");
-
-    // Start the mSource before we read it.
-    if (aTrack.mSourceIsStopped) {
-      if (aTrack.mSource->start() == OK) {
-        aTrack.mSourceIsStopped = false;
-      } else {
-        return UNKNOWN_ERROR;
-      }
-    }
-    MediaBuffer* source_buffer = nullptr;
-    status_t status = OK;
-    if (IsValidTimestampUs(aTrack.mSeekTimeUs)) {
-      MediaSource::ReadOptions options;
-      options.setSeekTo(aTrack.mSeekTimeUs);
-      status = aTrack.mSource->read(&source_buffer, &options);
-    } else {
-      status = aTrack.mSource->read(&source_buffer);
-    }
-
-    // read() fails
-    if (status == INFO_FORMAT_CHANGED) {
-      return INFO_FORMAT_CHANGED;
-    } else if (status == ERROR_END_OF_STREAM) {
-      aTrack.mInputEndOfStream = true;
-      aTrack.mCodec->queueInputBuffer(aTrack.mInputIndex.value(),
-                                      0, 0, 0,
-                                      MediaCodec::BUFFER_FLAG_EOS);
-      return ERROR_END_OF_STREAM;
-    } else if (status == -ETIMEDOUT) {
-      return OK; // try it later
-    } else if (status != OK) {
-      return status;
-    } else if (source_buffer == nullptr) {
-      return UNKNOWN_ERROR;
-    }
-
-    // read() successes
-    aTrack.mInputEndOfStream = false;
-    aTrack.mSeekTimeUs = sInvalidTimestampUs;
-
-    sp<ABuffer> input_buffer = nullptr;
-    if (aTrack.mInputIndex.value() < aTrack.mInputBuffers.size()) {
-      input_buffer = aTrack.mInputBuffers[aTrack.mInputIndex.value()];
-    }
-    if (input_buffer != nullptr &&
-        aTrack.mInputCopier != nullptr &&
-        aTrack.mInputCopier->Copy(source_buffer, input_buffer)) {
-      int64_t timestamp = sInvalidTimestampUs;
-      sp<MetaData> codec_format = source_buffer->meta_data();
-      if (codec_format != nullptr) {
-        codec_format->findInt64(kKeyTime, &timestamp);
-      }
-
-      status = aTrack.mCodec->queueInputBuffer(
-        aTrack.mInputIndex.value(), input_buffer->offset(),
-        input_buffer->size(), timestamp, 0);
-      if (status == OK) {
-        aTrack.mInputIndex = sInvalidInputIndex;
-      }
-    }
-    source_buffer->release();
-
-    if (status != OK) {
-      return status;
-    }
-  }
-
-  return OK;
-}
-
-status_t
-MediaCodecReader::GetCodecOutputData(Track& aTrack,
-                                     CodecBufferInfo& aBuffer,
-                                     int64_t aThreshold,
-                                     const TimeStamp& aTimeout)
-{
-  // Read next frame.
-  CodecBufferInfo info;
-  status_t status = OK;
-  while (status == OK || status == INFO_OUTPUT_BUFFERS_CHANGED ||
-         status == -EAGAIN) {
-
-    int64_t duration = (int64_t)(aTimeout - TimeStamp::Now()).ToMicroseconds();
-    if (!IsValidDurationUs(duration)) {
-      return -EAGAIN;
-    }
-
-    status = aTrack.mCodec->dequeueOutputBuffer(&info.mIndex, &info.mOffset,
-      &info.mSize, &info.mTimeUs, &info.mFlags, duration);
-    // Check EOS first.
-    if (status == ERROR_END_OF_STREAM ||
-        (info.mFlags & MediaCodec::BUFFER_FLAG_EOS)) {
-      aBuffer = info;
-      aBuffer.mBuffer = aTrack.mOutputBuffers[info.mIndex];
-      aTrack.mOutputEndOfStream = true;
-      return ERROR_END_OF_STREAM;
-    }
-
-    if (status == OK) {
-      // Notify mDecoder that we have parsed a video frame.
-      if (aTrack.mType == Track::kVideo) {
-        mDecoder->NotifyDecodedFrames(1, 0, 0);
-      }
-      if (!IsValidTimestampUs(aThreshold) || info.mTimeUs >= aThreshold) {
-        // Get a valid output buffer.
-        break;
-      } else {
-        aTrack.mCodec->releaseOutputBuffer(info.mIndex);
-      }
-    } else if (status == INFO_OUTPUT_BUFFERS_CHANGED) {
-      // Update output buffers of MediaCodec.
-      if (aTrack.mCodec->getOutputBuffers(&aTrack.mOutputBuffers) != OK) {
-        NS_WARNING("Couldn't get output buffers from MediaCodec");
-        return UNKNOWN_ERROR;
-      }
-    }
-
-    if (TimeStamp::Now() > aTimeout) {
-      // Don't let this loop run for too long. Try it again later.
-      return -EAGAIN;
-    }
-  }
-
-  if (status != OK) {
-    // Something wrong.
-    return status;
-  }
-
-  if (info.mIndex >= aTrack.mOutputBuffers.size()) {
-    NS_WARNING("Couldn't get proper index of output buffers from MediaCodec");
-    aTrack.mCodec->releaseOutputBuffer(info.mIndex);
-    return UNKNOWN_ERROR;
-  }
-
-  aBuffer = info;
-  aBuffer.mBuffer = aTrack.mOutputBuffers[info.mIndex];
-
-  return OK;
-}
-
-bool
-MediaCodecReader::EnsureCodecFormatParsed(Track& aTrack)
-{
-  if (aTrack.mSource == nullptr || aTrack.mCodec == nullptr ||
-      !aTrack.mCodec->allocated()) {
-    return false;
-  }
-
-  sp<AMessage> format;
-  if (aTrack.mCodec->getOutputFormat(&format) == OK) {
-    return true;
-  }
-
-  status_t status = OK;
-  size_t index = 0;
-  size_t offset = 0;
-  size_t size = 0;
-  int64_t timeUs = INT64_C(0);
-  uint32_t flags = 0;
-  FillCodecInputData(aTrack);
-  while ((status = aTrack.mCodec->dequeueOutputBuffer(&index, &offset, &size,
-                     &timeUs, &flags)) != INFO_FORMAT_CHANGED) {
-    if (status == OK) {
-      aTrack.mCodec->releaseOutputBuffer(index);
-    } else if (status == INFO_OUTPUT_BUFFERS_CHANGED) {
-      // Update output buffers of MediaCodec.
-      if (aTrack.mCodec->getOutputBuffers(&aTrack.mOutputBuffers) != OK) {
-        NS_WARNING("Couldn't get output buffers from MediaCodec");
-        return false;
-      }
-    } else if (status != -EAGAIN) {
-      return false; // something wrong!!!
-    }
-    FillCodecInputData(aTrack);
-  }
-  aTrack.mCodec->releaseOutputBuffer(index);
-  return aTrack.mCodec->getOutputFormat(&format) == OK;
-}
-
-uint8_t*
-MediaCodecReader::GetColorConverterBuffer(int32_t aWidth, int32_t aHeight)
-{
-  // Allocate a temporary YUV420Planer buffer.
-  size_t yuv420p_y_size = aWidth * aHeight;
-  size_t yuv420p_u_size = ((aWidth + 1) / 2) * ((aHeight + 1) / 2);
-  size_t yuv420p_v_size = yuv420p_u_size;
-  size_t yuv420p_size = yuv420p_y_size + yuv420p_u_size + yuv420p_v_size;
-  if (mColorConverterBufferSize != yuv420p_size) {
-    mColorConverterBuffer = nullptr; // release the previous buffer first
-    mColorConverterBuffer = new uint8_t[yuv420p_size];
-    mColorConverterBufferSize = yuv420p_size;
-  }
-  return mColorConverterBuffer.get();
-}
-
-void
-MediaCodecReader::ClearColorConverterBuffer()
-{
-  mColorConverterBuffer = nullptr;
-  mColorConverterBufferSize = 0;
-}
-
-
-} // namespace mozilla
deleted file mode 100644
--- a/dom/media/omx/MediaCodecReader.h
+++ /dev/null
@@ -1,414 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* 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 MEDIA_CODEC_READER_H
-#define MEDIA_CODEC_READER_H
-
-#include <utils/threads.h>
-
-#include <base/message_loop.h>
-
-#include <mozilla/CheckedInt.h>
-#include <mozilla/Mutex.h>
-#include <mozilla/Monitor.h>
-
-#include <nsDataHashtable.h>
-
-#include "MediaData.h"
-
-#include "I420ColorConverterHelper.h"
-#include "MediaCodecProxy.h"
-#include "MediaOmxCommonReader.h"
-#include "mozilla/layers/FenceUtils.h"
-#if MOZ_WIDGET_GONK && ANDROID_VERSION >= 17
-#include <ui/Fence.h>
-#endif
-
-#include "MP3FrameParser.h"
-
-namespace android {
-struct ALooper;
-struct AMessage;
-
-class MOZ_EXPORT MediaExtractor;
-class MOZ_EXPORT MetaData;
-class MOZ_EXPORT MediaBuffer;
-struct MOZ_EXPORT MediaSource;
-
-class GonkNativeWindow;
-} // namespace android
-
-namespace mozilla {
-
-class FlushableTaskQueue;
-class MP3FrameParser;
-
-namespace layers {
-class TextureClient;
-} // namespace mozilla::layers
-
-class MediaCodecReader : public MediaOmxCommonReader
-{
-  typedef mozilla::layers::TextureClient TextureClient;
-  typedef mozilla::layers::FenceHandle FenceHandle;
-  typedef MediaOmxCommonReader::MediaResourcePromise MediaResourcePromise;
-
-public:
-  MediaCodecReader(AbstractMediaDecoder* aDecoder);
-  virtual ~MediaCodecReader();
-
-  // Release media resources they should be released in dormant state
-  virtual void ReleaseMediaResources();
-
-  // Destroys the decoding state. The reader cannot be made usable again.
-  // This is different from ReleaseMediaResources() as Shutdown() is
-  // irreversible, whereas ReleaseMediaResources() is reversible.
-  virtual RefPtr<ShutdownPromise> Shutdown();
-
-protected:
-  // Used to retrieve some special information that can only be retrieved after
-  // all contents have been continuously parsed. (ex. total duration of some
-  // variable-bit-rate MP3 files.)
-  virtual void NotifyDataArrivedInternal() override;
-public:
-
-  // Flush the TaskQueue, flush MediaCodec and raise the mDiscontinuity.
-  virtual nsresult ResetDecode() override;
-
-  // Disptach a DecodeVideoFrameTask to decode video data.
-  virtual RefPtr<VideoDataPromise>
-  RequestVideoData(bool aSkipToNextKeyframe,
-                   int64_t aTimeThreshold) override;
-
-  // Disptach a DecodeAduioDataTask to decode video data.
-  virtual RefPtr<AudioDataPromise> RequestAudioData() override;
-
-  virtual RefPtr<MediaDecoderReader::MetadataPromise> AsyncReadMetadata() override;
-
-  // Moves the decode head to aTime microseconds. aStartTime and aEndTime
-  // denote the start and end times of the media in usecs, and aCurrentTime
-  // is the current playback position in microseconds.
-  virtual RefPtr<SeekPromise>
-  Seek(int64_t aTime, int64_t aEndTime) override;
-
-  virtual bool IsMediaSeekable() override;
-
-  virtual android::sp<android::MediaSource> GetAudioOffloadTrack();
-
-  virtual bool IsAsync() const override { return true; }
-
-protected:
-  struct TrackInputCopier
-  {
-    virtual ~TrackInputCopier();
-
-    virtual bool Copy(android::MediaBuffer* aSourceBuffer,
-                      android::sp<android::ABuffer> aCodecBuffer);
-  };
-
-  struct Track
-  {
-    enum Type
-    {
-      kUnknown = 0,
-      kAudio,
-      kVideo,
-    };
-
-    Track(Type type=kUnknown);
-
-    const Type mType;
-
-    // pipeline parameters
-    android::sp<android::MediaSource> mSource;
-    bool mSourceIsStopped;
-    android::sp<android::MediaCodecProxy> mCodec;
-    android::Vector<android::sp<android::ABuffer> > mInputBuffers;
-    android::Vector<android::sp<android::ABuffer> > mOutputBuffers;
-    android::sp<android::GonkNativeWindow> mNativeWindow;
-#if ANDROID_VERSION >= 21
-    android::sp<android::IGraphicBufferProducer> mGraphicBufferProducer;
-#endif
-
-    // pipeline copier
-    nsAutoPtr<TrackInputCopier> mInputCopier;
-
-    // Protected by mTrackMonitor.
-    // mDurationUs might be read or updated from multiple threads.
-    int64_t mDurationUs;
-
-    // playback parameters
-    CheckedUint32 mInputIndex;
-
-    bool mInputEndOfStream;
-    bool mOutputEndOfStream;
-    int64_t mSeekTimeUs;
-    bool mFlushed; // meaningless when mSeekTimeUs is invalid.
-    bool mDiscontinuity;
-    RefPtr<TaskQueue> mTaskQueue;
-    Monitor mTrackMonitor;
-
-  private:
-    // Forbidden
-    Track(const Track &rhs) = delete;
-    const Track &operator=(const Track&) = delete;
-  };
-
-  // Receive a message from MessageHandler.
-  // Called on MediaCodecReader::mLooper thread.
-  void onMessageReceived(const android::sp<android::AMessage>& aMessage);
-
-  virtual bool CreateExtractor();
-
-  virtual void HandleResourceAllocated();
-
-  android::sp<android::MediaExtractor> mExtractor;
-
-  MozPromiseHolder<MediaDecoderReader::MetadataPromise> mMetadataPromise;
-  // XXX Remove after bug 1168008 land.
-  MozPromiseRequestHolder<MediaResourcePromise> mMediaResourceRequest;
-  MozPromiseHolder<MediaResourcePromise> mMediaResourcePromise;
-
-  MozPromiseRequestHolder<android::MediaCodecProxy::CodecPromise> mVideoCodecRequest;
-
-private:
-  virtual bool HasAudio() override;
-  virtual bool HasVideo() override;
-
-  class VorbisInputCopier : public TrackInputCopier
-  {
-    virtual bool Copy(android::MediaBuffer* aSourceBuffer,
-                      android::sp<android::ABuffer> aCodecBuffer);
-  };
-
-  struct AudioTrack : public Track
-  {
-    AudioTrack();
-    // Protected by mTrackMonitor.
-    MozPromiseHolder<AudioDataPromise> mAudioPromise;
-
-  private:
-    // Forbidden
-    AudioTrack(const AudioTrack &rhs) = delete;
-    const AudioTrack &operator=(const AudioTrack &rhs) = delete;
-  };
-
-  struct VideoTrack : public Track
-  {
-    VideoTrack();
-
-    int32_t mWidth;
-    int32_t mHeight;
-    int32_t mStride;
-    int32_t mSliceHeight;
-    int32_t mColorFormat;
-    int32_t mRotation;
-    nsIntSize mFrameSize;
-    nsIntRect mPictureRect;
-    gfx::IntRect mRelativePictureRect;
-    // Protected by mTrackMonitor.
-    MozPromiseHolder<VideoDataPromise> mVideoPromise;
-
-    RefPtr<TaskQueue> mReleaseBufferTaskQueue;
-  private:
-    // Forbidden
-    VideoTrack(const VideoTrack &rhs) = delete;
-    const VideoTrack &operator=(const VideoTrack &rhs) = delete;
-  };
-
-  struct CodecBufferInfo
-  {
-    CodecBufferInfo();
-
-    android::sp<android::ABuffer> mBuffer;
-    size_t mIndex;
-    size_t mOffset;
-    size_t mSize;
-    int64_t mTimeUs;
-    uint32_t mFlags;
-  };
-
-  class SignalObject
-  {
-  public:
-    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SignalObject)
-
-    SignalObject(const char* aName);
-    void Wait();
-    void Signal();
-
-  protected:
-    ~SignalObject();
-
-  private:
-    // Forbidden
-    SignalObject() = delete;
-    SignalObject(const SignalObject &rhs) = delete;
-    const SignalObject &operator=(const SignalObject &rhs) = delete;
-
-    Monitor mMonitor;
-    bool mSignaled;
-  };
-
-  class ParseCachedDataRunnable : public nsRunnable
-  {
-  public:
-    ParseCachedDataRunnable(RefPtr<MediaCodecReader> aReader,
-                            const char* aBuffer,
-                            uint32_t aLength,
-                            int64_t aOffset,
-                            RefPtr<SignalObject> aSignal);
-
-    NS_IMETHOD Run() override;
-
-  private:
-    // Forbidden
-    ParseCachedDataRunnable() = delete;
-    ParseCachedDataRunnable(const ParseCachedDataRunnable &rhs) = delete;
-    const ParseCachedDataRunnable &operator=(const ParseCachedDataRunnable &rhs) = delete;
-
-    RefPtr<MediaCodecReader> mReader;
-    nsAutoArrayPtr<const char> mBuffer;
-    uint32_t mLength;
-    int64_t mOffset;
-    RefPtr<SignalObject> mSignal;
-  };
-  friend class ParseCachedDataRunnable;
-
-  class ProcessCachedDataTask : public Task
-  {
-  public:
-    ProcessCachedDataTask(RefPtr<MediaCodecReader> aReader,
-                          int64_t aOffset);
-
-    void Run() override;
-
-  private:
-    // Forbidden
-    ProcessCachedDataTask() = delete;
-    ProcessCachedDataTask(const ProcessCachedDataTask &rhs) = delete;
-    const ProcessCachedDataTask &operator=(const ProcessCachedDataTask &rhs) = delete;
-
-    RefPtr<MediaCodecReader> mReader;
-    int64_t mOffset;
-  };
-  friend class ProcessCachedDataTask;
-
-  // Forbidden
-  MediaCodecReader() = delete;
-  const MediaCodecReader& operator=(const MediaCodecReader& rhs) = delete;
-
-  bool ReallocateExtractorResources();
-  void ReleaseCriticalResources();
-  void ReleaseResources();
-
-  bool CreateLooper();
-  void DestroyLooper();
-
-  void DestroyExtractor();
-
-  bool CreateMediaSources();
-  void DestroyMediaSources();
-
-  RefPtr<MediaResourcePromise> CreateMediaCodecs();
-  bool CreateMediaCodec(android::sp<android::ALooper>& aLooper,
-                        Track& aTrack,
-                        bool& aIsWaiting);
-  static bool ConfigureMediaCodec(Track& aTrack);
-  void DestroyMediaCodecs();
-  static void DestroyMediaCodec(Track& aTrack);
-
-  bool CreateTaskQueues();
-  void ShutdownTaskQueues();
-  void DecodeVideoFrameTask(int64_t aTimeThreshold);
-  void DecodeVideoFrameSync(int64_t aTimeThreshold);
-  void DecodeAudioDataTask();
-  void DecodeAudioDataSync();
-  void DispatchVideoTask(int64_t aTimeThreshold);
-  void DispatchAudioTask();
-  inline bool CheckVideoResources() {
-    return (HasVideo() && mVideoTrack.mSource != nullptr &&
-            mVideoTrack.mTaskQueue);
-  }
-
-  inline bool CheckAudioResources() {
-    return (HasAudio() && mAudioTrack.mSource != nullptr &&
-            mAudioTrack.mTaskQueue);
-  }
-
-  bool TriggerIncrementalParser();
-
-  bool UpdateDuration();
-  bool UpdateAudioInfo();
-  bool UpdateVideoInfo();
-
-  android::status_t FlushCodecData(Track& aTrack);
-  android::status_t FillCodecInputData(Track& aTrack);
-  android::status_t GetCodecOutputData(Track& aTrack,
-                                       CodecBufferInfo& aBuffer,
-                                       int64_t aThreshold,
-                                       const TimeStamp& aTimeout);
-  bool EnsureCodecFormatParsed(Track& aTrack);
-
-  uint8_t* GetColorConverterBuffer(int32_t aWidth, int32_t aHeight);
-  void ClearColorConverterBuffer();
-
-  int64_t ProcessCachedData(int64_t aOffset,
-                            RefPtr<SignalObject> aSignal);
-  bool ParseDataSegment(const char* aBuffer,
-                        uint32_t aLength,
-                        int64_t aOffset);
-
-  static void TextureClientRecycleCallback(TextureClient* aClient,
-                                           void* aClosure);
-  void TextureClientRecycleCallback(TextureClient* aClient);
-  void WaitFenceAndReleaseOutputBuffer();
-
-  void ReleaseRecycledTextureClients();
-
-  void ReleaseAllTextureClients();
-
-  android::sp<android::ALooper> mLooper;
-  android::sp<android::MetaData> mMetaData;
-
-  Mutex mTextureClientIndexesLock;
-  nsDataHashtable<nsPtrHashKey<TextureClient>, size_t> mTextureClientIndexes;
-
-  // media tracks
-  AudioTrack mAudioTrack;
-  VideoTrack mVideoTrack;
-  AudioTrack mAudioOffloadTrack; // only Track::mSource is valid
-
-  // color converter
-  android::I420ColorConverterHelper mColorConverter;
-  nsAutoArrayPtr<uint8_t> mColorConverterBuffer;
-  size_t mColorConverterBufferSize;
-
-  // incremental parser
-  Monitor mParserMonitor;
-  bool mParseDataFromCache;
-  int64_t mNextParserPosition;
-  int64_t mParsedDataLength;
-  nsAutoPtr<MP3FrameParser> mMP3FrameParser;
-  // mReleaseIndex corresponding to a graphic buffer, and the mReleaseFence is
-  // the graohic buffer's fence. We must wait for the fence signaled by
-  // compositor, otherwise we will see the flicker because the HW decoder and
-  // compositor use the buffer concurrently.
-  struct ReleaseItem {
-    ReleaseItem(size_t aIndex, const FenceHandle& aFence)
-    : mReleaseIndex(aIndex)
-    , mReleaseFence(aFence) {}
-    size_t mReleaseIndex;
-    FenceHandle mReleaseFence;
-  };
-  nsTArray<ReleaseItem> mPendingReleaseItems;
-
-  NotifyDataArrivedFilter mFilter;
-};
-
-} // namespace mozilla
-
-#endif // MEDIA_CODEC_READER_H
--- a/dom/media/omx/MediaOmxReader.cpp
+++ b/dom/media/omx/MediaOmxReader.cpp
@@ -459,27 +459,29 @@ void MediaOmxReader::NotifyDataArrivedIn
   if (HasVideo()) {
     return;
   }
   if (!mMP3FrameParser.NeedsData()) {
     return;
   }
 
   AutoPinned<MediaResource> resource(mDecoder->GetResource());
-  nsTArray<MediaByteRange> byteRanges;
+  MediaByteRangeSet byteRanges;
   nsresult rv = resource->GetCachedRanges(byteRanges);
 
   if (NS_FAILED(rv)) {
     return;
   }
 
-  IntervalSet<int64_t> intervals;
-  for (auto& range : byteRanges) {
-    intervals += mFilter.NotifyDataArrived(range.Length(), range.mStart);
+  if (byteRanges == mLastCachedRanges) {
+    return;
   }
+  MediaByteRangeSet intervals = byteRanges - mLastCachedRanges;
+  mLastCachedRanges = byteRanges;
+
   for (const auto& interval : intervals) {
     RefPtr<MediaByteBuffer> bytes =
       resource->MediaReadAt(interval.mStart, interval.Length());
     NS_ENSURE_TRUE_VOID(bytes);
     mMP3FrameParser.Parse(bytes->Elements(), interval.Length(), interval.mStart);
     if (!mMP3FrameParser.IsMP3()) {
       return;
     }
--- a/dom/media/omx/MediaOmxReader.h
+++ b/dom/media/omx/MediaOmxReader.h
@@ -115,14 +115,14 @@ private:
     MutexAutoLock lock(mShutdownMutex);
     return mIsShutdown;
   }
 
   int64_t ProcessCachedData(int64_t aOffset);
 
   already_AddRefed<AbstractMediaDecoder> SafeGetDecoder();
 
-  NotifyDataArrivedFilter mFilter;
+  MediaByteRangeSet mLastCachedRanges;
 };
 
 } // namespace mozilla
 
 #endif
deleted file mode 100644
--- a/dom/media/omx/RtspMediaCodecReader.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* 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 "RtspMediaCodecReader.h"
-
-#include "RtspExtractor.h"
-#include "RtspMediaResource.h"
-#include "RtspMediaCodecDecoder.h"
-
-using namespace android;
-
-namespace mozilla {
-
-RtspMediaCodecReader::RtspMediaCodecReader(AbstractMediaDecoder* aDecoder)
-  : MediaCodecReader(aDecoder)
-{
-  NS_ASSERTION(mDecoder, "RtspMediaCodecReader mDecoder is null.");
-  NS_ASSERTION(mDecoder->GetResource(),
-               "RtspMediaCodecReader mDecoder->GetResource() is null.");
-  mRtspResource = mDecoder->GetResource()->GetRtspPointer();
-  MOZ_ASSERT(mRtspResource);
-}
-
-RtspMediaCodecReader::~RtspMediaCodecReader() {}
-
-bool
-RtspMediaCodecReader::CreateExtractor()
-{
-  if (mExtractor != nullptr) {
-    return true;
-  }
-
-  mExtractor = new RtspExtractor(mRtspResource);
-
-  return mExtractor != nullptr;
-}
-
-RefPtr<MediaDecoderReader::SeekPromise>
-RtspMediaCodecReader::Seek(int64_t aTime, int64_t aEndTime)
-{
-  // The seek function of Rtsp is time-based, we call the SeekTime function in
-  // RtspMediaResource. The SeekTime function finally send a seek command to
-  // Rtsp stream server through network and also clear the buffer data in
-  // RtspMediaResource.
-  mRtspResource->SeekTime(aTime);
-
-  return MediaCodecReader::Seek(aTime, aEndTime);
-}
-
-void
-RtspMediaCodecReader::SetIdle()
-{
-  nsIStreamingProtocolController* controller =
-    mRtspResource->GetMediaStreamController();
-  if (controller) {
-    controller->Pause();
-  }
-  mRtspResource->SetSuspend(true);
-}
-
-void
-RtspMediaCodecReader::EnsureActive()
-{
-  // Need to start RTSP streaming OMXCodec decoding.
-  nsIStreamingProtocolController* controller =
-    mRtspResource->GetMediaStreamController();
-  if (controller) {
-    controller->Play();
-  }
-  mRtspResource->SetSuspend(false);
-}
-
-RefPtr<MediaDecoderReader::AudioDataPromise>
-RtspMediaCodecReader::RequestAudioData()
-{
-  EnsureActive();
-  return MediaCodecReader::RequestAudioData();
-}
-
-RefPtr<MediaDecoderReader::VideoDataPromise>
-RtspMediaCodecReader::RequestVideoData(bool aSkipToNextKeyframe,
-                                       int64_t aTimeThreshold)
-{
-  EnsureActive();
-  return MediaCodecReader::RequestVideoData(aSkipToNextKeyframe, aTimeThreshold);
-}
-
-RefPtr<MediaDecoderReader::MetadataPromise>
-RtspMediaCodecReader::AsyncReadMetadata()
-{
-  mRtspResource->DisablePlayoutDelay();
-  EnsureActive();
-
-  RefPtr<MediaDecoderReader::MetadataPromise> p =
-    MediaCodecReader::AsyncReadMetadata();
-
-  // Send a PAUSE to the RTSP server because the underlying media resource is
-  // not ready.
-  SetIdle();
-
-  return p;
-}
-
-void
-RtspMediaCodecReader::HandleResourceAllocated()
-{
-  EnsureActive();
-  MediaCodecReader::HandleResourceAllocated();
-  mRtspResource->EnablePlayoutDelay();;
-}
-
-} // namespace mozilla
deleted file mode 100644
--- a/dom/media/omx/RtspMediaCodecReader.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* 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 !defined(RtspMediaCodecReader_h_)
-#define RtspMediaCodecReader_h_
-
-#include "MediaCodecReader.h"
-
-namespace mozilla {
-
-class AbstractMediaDecoder;
-class RtspMediaResource;
-
-/* RtspMediaCodecReader is a subclass of MediaCodecReader.
- * The major reason that RtspMediaCodecReader inherit from MediaCodecReader is
- * the same video/audio decoding logic we can reuse.
- */
-class RtspMediaCodecReader final : public MediaCodecReader
-{
-protected:
-  // Provide a Rtsp extractor.
-  virtual bool CreateExtractor() override;
-  // Ensure the play command is sent to Rtsp server.
-  void EnsureActive();
-
-public:
-  RtspMediaCodecReader(AbstractMediaDecoder* aDecoder);
-
-  virtual ~RtspMediaCodecReader();
-
-  // Implement a time-based seek instead of byte-based.
-  virtual RefPtr<SeekPromise>
-  Seek(int64_t aTime, int64_t aEndTime) override;
-
-  // Override GetBuffered() to do nothing for below reasons:
-  // 1. Because the Rtsp stream is a/v separated. The buffered data in a/v
-  // tracks are not consistent with time stamp.
-  // For example: audio buffer: 1~2s, video buffer: 1.5~2.5s
-  // 2. Since the Rtsp is a realtime streaming, the buffer we made for
-  // RtspMediaResource is quite small. The small buffer implies the time ranges
-  // we returned are not useful for the MediaDecodeStateMachine. Unlike the
-  // ChannelMediaResource, it has a "cache" that can store the whole streaming
-  // data so the |GetBuffered| function can retrieve useful time ranges.
-  virtual media::TimeIntervals GetBuffered() override {
-    return media::TimeIntervals::Invalid();
-  }
-
-  virtual void SetIdle() override;
-
-  // Disptach a DecodeVideoFrameTask to decode video data.
-  virtual RefPtr<VideoDataPromise>
-  RequestVideoData(bool aSkipToNextKeyframe,
-                   int64_t aTimeThreshold) override;
-
-  // Disptach a DecodeAudioDataTask to decode audio data.
-  virtual RefPtr<AudioDataPromise> RequestAudioData() override;
-
-  virtual RefPtr<MediaDecoderReader::MetadataPromise> AsyncReadMetadata()
-    override;
-
-  virtual void HandleResourceAllocated() override;
-
-private:
-  // A pointer to RtspMediaResource for calling the Rtsp specific function.
-  // The lifetime of mRtspResource is controlled by MediaDecoder. MediaDecoder
-  // holds the MediaDecoderStateMachine and RtspMediaResource.
-  // And MediaDecoderStateMachine holds this RtspMediaCodecReader.
-  RtspMediaResource* mRtspResource;
-};
-
-} // namespace mozilla
-
-#endif
--- a/dom/media/omx/moz.build
+++ b/dom/media/omx/moz.build
@@ -49,38 +49,25 @@ if 'rtsp' in CONFIG['NECKO_PROTOCOLS']:
         'RtspOmxDecoder.h',
         'RtspOmxReader.h',
     ]
     SOURCES += [
         'RtspExtractor.cpp',
         'RtspOmxDecoder.cpp',
         'RtspOmxReader.cpp',
     ]
-    if CONFIG['ANDROID_VERSION'] >= '18':
-        EXPORTS += [
-            'RtspMediaCodecDecoder.h',
-            'RtspMediaCodecReader.h',
-        ]
-        SOURCES += [
-            'RtspMediaCodecDecoder.cpp',
-            'RtspMediaCodecReader.cpp',
-        ]
 
 if CONFIG['ANDROID_VERSION'] >= '18':
     EXPORTS += [
         'I420ColorConverterHelper.h',
-        'MediaCodecDecoder.h',
         'MediaCodecProxy.h',
-        'MediaCodecReader.h',
     ]
     SOURCES += [
         'I420ColorConverterHelper.cpp',
-        'MediaCodecDecoder.cpp',
         'MediaCodecProxy.cpp',
-        'MediaCodecReader.cpp',
     ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 # Suppress some GCC/clang warnings being treated as errors:
 #  - about attributes on forward declarations for types that are already
 #    defined, which complains about an important MOZ_EXPORT for android::AString
 #  - about multi-character constants which are used in codec-related code
--- a/dom/media/platforms/agnostic/BlankDecoderModule.cpp
+++ b/dom/media/platforms/agnostic/BlankDecoderModule.cpp
@@ -1,24 +1,25 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "ImageContainer.h"
 #include "MediaDecoderReader.h"
-#include "PlatformDecoderModule.h"
-#include "nsRect.h"
-#include "mozilla/RefPtr.h"
+#include "MediaInfo.h"
 #include "mozilla/CheckedInt.h"
+#include "mozilla/mozalloc.h" // for operator new, and new (fallible)
+#include "mozilla/RefPtr.h"
+#include "mozilla/TaskQueue.h"
+#include "nsRect.h"
+#include "PlatformDecoderModule.h"
+#include "TimeUnits.h"
 #include "VideoUtils.h"
-#include "ImageContainer.h"
-#include "MediaInfo.h"
-#include "mozilla/TaskQueue.h"
-#include "TimeUnits.h"
 
 namespace mozilla {
 
 // Decoder that uses a passed in object's Create function to create blank
 // MediaData objects.
 template<class BlankMediaDataCreator>
 class BlankMediaDataDecoder : public MediaDataDecoder {
 public:
@@ -53,16 +54,19 @@ public:
     {
     }
     NS_IMETHOD Run() override
     {
       RefPtr<MediaData> data =
         mCreator->Create(media::TimeUnit::FromMicroseconds(mSample->mTime),
                          media::TimeUnit::FromMicroseconds(mSample->mDuration),
                          mSample->mOffset);
+      if (!data) {
+        return NS_ERROR_OUT_OF_MEMORY;
+      }
       mCallback->Output(data);
       return NS_OK;
     }
   private:
     RefPtr<MediaRawData> mSample;
     BlankMediaDataCreator* mCreator;
     MediaDataDecoderCallback* mCallback;
   };
@@ -178,17 +182,21 @@ public:
     CheckedInt64 frames =
       UsecsToFrames(aDuration.ToMicroseconds()+1, mSampleRate);
     if (!frames.isValid() ||
         !mChannelCount ||
         !mSampleRate ||
         frames.value() > (UINT32_MAX / mChannelCount)) {
       return nullptr;
     }
-    auto samples = MakeUnique<AudioDataValue[]>(frames.value() * mChannelCount);
+    UniquePtr<AudioDataValue[]> samples(
+      new (fallible) AudioDataValue[frames.value() * mChannelCount]);
+    if (!samples) {
+      return nullptr;
+    }
     // Fill the sound buffer with an A4 tone.
     static const float pi = 3.14159265f;
     static const float noteHz = 440.0f;
     for (int i = 0; i < frames.value(); i++) {
       float f = sin(2 * pi * noteHz * mFrameSum / mSampleRate);
       for (unsigned c = 0; c < mChannelCount; c++) {
         samples[i * mChannelCount + c] = AudioDataValue(f);
       }
--- a/dom/media/webm/WebMBufferedParser.cpp
+++ b/dom/media/webm/WebMBufferedParser.cpp
@@ -378,17 +378,17 @@ void WebMBufferedState::NotifyDataArrive
   mLastBlockOffset = mRangeParsers.LastElement().mBlockEndOffset;
 }
 
 void WebMBufferedState::Reset() {
   mRangeParsers.Clear();
   mTimeMapping.Clear();
 }
 
-void WebMBufferedState::UpdateIndex(const nsTArray<MediaByteRange>& aRanges, MediaResource* aResource)
+void WebMBufferedState::UpdateIndex(const MediaByteRangeSet& aRanges, MediaResource* aResource)
 {
   for (uint32_t index = 0; index < aRanges.Length(); index++) {
     const MediaByteRange& range = aRanges[index];
     int64_t offset = range.mStart;
     uint32_t length = range.mEnd - range.mStart;
 
     uint32_t idx = mRangeParsers.IndexOfFirstElementGt(offset - 1);
     if (!idx || !(mRangeParsers[idx-1] == offset)) {
--- a/dom/media/webm/WebMBufferedParser.h
+++ b/dom/media/webm/WebMBufferedParser.h
@@ -261,17 +261,17 @@ public:
     : mReentrantMonitor("WebMBufferedState")
     , mLastBlockOffset(-1)
   {
     MOZ_COUNT_CTOR(WebMBufferedState);
   }
 
   void NotifyDataArrived(const unsigned char* aBuffer, uint32_t aLength, int64_t aOffset);
   void Reset();
-  void UpdateIndex(const nsTArray<MediaByteRange>& aRanges, MediaResource* aResource);
+  void UpdateIndex(const MediaByteRangeSet& aRanges, MediaResource* aResource);
   bool CalculateBufferedForRange(int64_t aStartOffset, int64_t aEndOffset,
                                  uint64_t* aStartTime, uint64_t* aEndTime);
 
   // Returns true if aTime is is present in mTimeMapping and sets aOffset to
   // the latest offset for which decoding can resume without data
   // dependencies to arrive at aTime.
   bool GetOffsetForTime(uint64_t aTime, int64_t* aOffset);
 
--- a/dom/media/webm/WebMDemuxer.cpp
+++ b/dom/media/webm/WebMDemuxer.cpp
@@ -434,17 +434,17 @@ WebMDemuxer::IsSeekable() const
 
 void
 WebMDemuxer::EnsureUpToDateIndex()
 {
   if (!mNeedReIndex || !mInitData) {
     return;
   }
   AutoPinned<MediaResource> resource(mResource.GetResource());
-  nsTArray<MediaByteRange> byteRanges;
+  MediaByteRangeSet byteRanges;
   nsresult rv = resource->GetCachedRanges(byteRanges);
   if (NS_FAILED(rv) || !byteRanges.Length()) {
     return;
   }
   mBufferedState->UpdateIndex(byteRanges, resource);
 
   mNeedReIndex = false;
 
@@ -727,17 +727,17 @@ WebMDemuxer::SeekInternal(const media::T
 media::TimeIntervals
 WebMDemuxer::GetBuffered()
 {
   EnsureUpToDateIndex();
   AutoPinned<MediaResource> resource(mResource.GetResource());
 
   media::TimeIntervals buffered;
 
-  nsTArray<MediaByteRange> ranges;
+  MediaByteRangeSet ranges;
   nsresult rv = resource->GetCachedRanges(ranges);
   if (NS_FAILED(rv)) {
     return media::TimeIntervals();
   }
   uint64_t duration = 0;
   uint64_t startOffset = 0;
   if (!nestegg_duration(mContext, &duration)) {
     if(mBufferedState->GetStartTime(&startOffset)) {
--- a/dom/media/webm/WebMReader.cpp
+++ b/dom/media/webm/WebMReader.cpp
@@ -744,17 +744,17 @@ media::TimeIntervals WebMReader::GetBuff
         media::TimeInterval(media::TimeUnit::FromSeconds(0),
                             media::TimeUnit::FromSeconds(duration / NS_PER_S));
       return buffered;
     }
   }
 
   // Either we the file is not fully cached, or we couldn't find a duration in
   // the WebM bitstream.
-  nsTArray<MediaByteRange> ranges;
+  MediaByteRangeSet ranges;
   nsresult res = resource->GetCachedRanges(ranges);
   NS_ENSURE_SUCCESS(res, media::TimeIntervals::Invalid());
 
   for (uint32_t index = 0; index < ranges.Length(); index++) {
     uint64_t start, end;
     bool rv = mBufferedState->CalculateBufferedForRange(ranges[index].mStart,
                                                         ranges[index].mEnd,
                                                         &start, &end);
@@ -783,17 +783,17 @@ media::TimeIntervals WebMReader::GetBuff
 
   return buffered;
 }
 
 void WebMReader::NotifyDataArrivedInternal()
 {
   MOZ_ASSERT(OnTaskQueue());
   AutoPinned<MediaResource> resource(mDecoder->GetResource());
-  nsTArray<MediaByteRange> byteRanges;
+  MediaByteRangeSet byteRanges;
   nsresult rv = resource->GetCachedRanges(byteRanges);
 
   if (NS_FAILED(rv)) {
     return;
   }
 
   for (auto& range : byteRanges) {
     RefPtr<MediaByteBuffer> bytes =
--- a/embedding/browser/nsWebBrowser.cpp
+++ b/embedding/browser/nsWebBrowser.cpp
@@ -1732,17 +1732,17 @@ nsWebBrowser::PaintWindow(nsIWidget* aWi
 {
   LayerManager* layerManager = aWidget->GetLayerManager();
   NS_ASSERTION(layerManager, "Must be in paint event");
 
   layerManager->BeginTransaction();
   RefPtr<PaintedLayer> root = layerManager->CreatePaintedLayer();
   if (root) {
     nsIntRect dirtyRect = aRegion.GetBounds();
-    root->SetVisibleRegion(dirtyRect);
+    root->SetVisibleRegion(LayerIntRegion::FromUnknownRegion(dirtyRect));
     layerManager->SetRoot(root);
   }
 
   layerManager->EndTransaction(DrawPaintedLayer, &mBackgroundColor);
   return true;
 }
 
 NS_IMETHODIMP
--- a/gfx/ipc/GfxMessageUtils.h
+++ b/gfx/ipc/GfxMessageUtils.h
@@ -391,19 +391,21 @@ struct RegionParamTraits
       if (rect.IsEmpty())
         return true;
       result->Or(*result, rect);
     }
     return false;
   }
 };
 
-template<>
-struct ParamTraits<nsIntRegion>
-  : RegionParamTraits<nsIntRegion, mozilla::gfx::IntRect, nsIntRegionRectIterator>
+template<class Units>
+struct ParamTraits<mozilla::gfx::IntRegionTyped<Units>>
+  : RegionParamTraits<mozilla::gfx::IntRegionTyped<Units>,
+                      mozilla::gfx::IntRectTyped<Units>,
+                      typename mozilla::gfx::IntRegionTyped<Units>::RectIterator>
 {};
 
 template<>
 struct ParamTraits<mozilla::gfx::IntSize>
 {
   typedef mozilla::gfx::IntSize paramType;
 
   static void Write(Message* msg, const paramType& param)
--- a/gfx/layers/LayerMetricsWrapper.h
+++ b/gfx/layers/LayerMetricsWrapper.h
@@ -338,24 +338,24 @@ public:
     MOZ_ASSERT(IsValid());
 
     if (AtBottomLayer()) {
       return mLayer->AsRefLayer();
     }
     return nullptr;
   }
 
-  nsIntRegion GetVisibleRegion() const
+  LayerIntRegion GetVisibleRegion() const
   {
     MOZ_ASSERT(IsValid());
 
     if (AtBottomLayer()) {
       return mLayer->GetVisibleRegion();
     }
-    nsIntRegion region = mLayer->GetVisibleRegion();
+    LayerIntRegion region = mLayer->GetVisibleRegion();
     region.Transform(mLayer->GetTransform());
     return region;
   }
 
   const Maybe<ParentLayerIntRect>& GetClipRect() const
   {
     MOZ_ASSERT(IsValid());
 
--- a/gfx/layers/LayerSorter.cpp
+++ b/gfx/layers/LayerSorter.cpp
@@ -71,18 +71,18 @@ static gfxFloat RecoverZDepth(const Matr
  * depths and use this point to determine an ordering for the two layers.
  * For layers that are intersecting in 3d space, this essentially guesses an 
  * order. In a lot of cases we only intersect right at the edge point (3d cubes
  * in particular) and this generates the 'correct' looking ordering. For planes
  * that truely intersect, then there is no correct ordering and this remains
  * unsolved without changing our rendering code.
  */
 static LayerSortOrder CompareDepth(Layer* aOne, Layer* aTwo) {
-  gfxRect ourRect = ThebesRect(aOne->GetEffectiveVisibleRegion().GetBounds());
-  gfxRect otherRect = ThebesRect(aTwo->GetEffectiveVisibleRegion().GetBounds());
+  gfxRect ourRect = ThebesRect(aOne->GetEffectiveVisibleRegion().ToUnknownRegion().GetBounds());
+  gfxRect otherRect = ThebesRect(aTwo->GetEffectiveVisibleRegion().ToUnknownRegion().GetBounds());
 
   MOZ_ASSERT(aOne->GetParent() && aOne->GetParent()->Extend3DContext() &&
              aTwo->GetParent() && aTwo->GetParent()->Extend3DContext());
   // Effective transform of leaves may had been projected to 2D.
   Matrix4x4 ourTransform =
     aOne->GetLocalTransform() * aOne->GetParent()->GetEffectiveTransform();
   Matrix4x4 otherTransform =
     aTwo->GetLocalTransform() * aTwo->GetParent()->GetEffectiveTransform();
--- a/gfx/layers/LayerTreeInvalidation.cpp
+++ b/gfx/layers/LayerTreeInvalidation.cpp
@@ -120,25 +120,25 @@ NotifySubdocumentInvalidationRecursive(L
   if (!container) {
     return;
   }
 
   for (Layer* child = container->GetFirstChild(); child; child = child->GetNextSibling()) {
     NotifySubdocumentInvalidationRecursive(child, aCallback);
   }
 
-  aCallback(container, container->GetVisibleRegion());
+  aCallback(container, container->GetVisibleRegion().ToUnknownRegion());
 }
 
 struct LayerPropertiesBase : public LayerProperties
 {
   explicit LayerPropertiesBase(Layer* aLayer)
     : mLayer(aLayer)
     , mMaskLayer(nullptr)
-    , mVisibleRegion(aLayer->GetVisibleRegion())
+    , mVisibleRegion(aLayer->GetVisibleRegion().ToUnknownRegion())
     , mInvalidRegion(aLayer->GetInvalidRegion())
     , mPostXScale(aLayer->GetPostXScale())
     , mPostYScale(aLayer->GetPostYScale())
     , mOpacity(aLayer->GetLocalOpacity())
     , mUseClipRect(!!aLayer->GetEffectiveClipRect())
   {
     MOZ_COUNT_CTOR(LayerPropertiesBase);
     if (aLayer->GetMaskLayer()) {
@@ -230,23 +230,23 @@ struct LayerPropertiesBase : public Laye
     }
 
     mLayer->ClearInvalidRect();
     return result;
   }
 
   IntRect NewTransformedBounds()
   {
-    return TransformRect(mLayer->GetVisibleRegion().GetBounds(),
+    return TransformRect(mLayer->GetVisibleRegion().ToUnknownRegion().GetBounds(),
                          GetTransformForInvalidation(mLayer));
   }
 
   IntRect OldTransformedBounds()
   {
-    return TransformRect(mVisibleRegion.GetBounds(), mTransform);
+    return TransformRect(mVisibleRegion.ToUnknownRegion().GetBounds(), mTransform);
   }
 
   virtual nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback,
                                             bool& aGeometryChanged)
   {
     return IntRect();
   }
 
@@ -342,17 +342,17 @@ struct ContainerLayerProperties : public
           invalidateChildsCurrentArea = true;
         }
       } else {
         // |child| is new, or was reordered to a higher index
         invalidateChildsCurrentArea = true;
       }
       if (invalidateChildsCurrentArea) {
         aGeometryChanged = true;
-        AddTransformedRegion(result, child->GetVisibleRegion(),
+        AddTransformedRegion(result, child->GetVisibleRegion().ToUnknownRegion(),
                              GetTransformForInvalidation(child));
         if (aCallback) {
           NotifySubdocumentInvalidationRecursive(child, aCallback);
         } else {
           ClearInvalidations(child);
         }
       }
       childrenChanged |= invalidateChildsCurrentArea;
@@ -449,17 +449,17 @@ struct ImageLayerProperties : public Lay
     }
   }
 
   virtual nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback,
                                             bool& aGeometryChanged)
   {
     ImageLayer* imageLayer = static_cast<ImageLayer*>(mLayer.get());
     
-    if (!imageLayer->GetVisibleRegion().IsEqual(mVisibleRegion)) {
+    if (!imageLayer->GetVisibleRegion().ToUnknownRegion().IsEqual(mVisibleRegion)) {
       aGeometryChanged = true;
       IntRect result = NewTransformedBounds();
       result = result.Union(OldTransformedBounds());
       return result;
     }
 
     ImageContainer* container = imageLayer->GetContainer();
     ImageHost* host = GetImageHost(imageLayer);
@@ -591,17 +591,17 @@ LayerPropertiesBase::ComputeDifferences(
 {
   NS_ASSERTION(aRoot, "Must have a layer tree to compare against!");
   if (mLayer != aRoot) {
     if (aCallback) {
       NotifySubdocumentInvalidationRecursive(aRoot, aCallback);
     } else {
       ClearInvalidations(aRoot);
     }
-    IntRect result = TransformRect(aRoot->GetVisibleRegion().GetBounds(),
+    IntRect result = TransformRect(aRoot->GetVisibleRegion().ToUnknownRegion().GetBounds(),
                                      aRoot->GetLocalTransform());
     result = result.Union(OldTransformedBounds());
     if (aGeometryChanged != nullptr) {
       *aGeometryChanged = true;
     }
     return result;
   } else {
     bool geometryChanged = (aGeometryChanged != nullptr) ? *aGeometryChanged : false;
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -614,17 +614,17 @@ const Maybe<ParentLayerIntRect>&
 Layer::GetEffectiveClipRect()
 {
   if (LayerComposite* shadow = AsLayerComposite()) {
     return shadow->GetShadowClipRect();
   }
   return GetClipRect();
 }
 
-const nsIntRegion&
+const LayerIntRegion&
 Layer::GetEffectiveVisibleRegion()
 {
   if (LayerComposite* shadow = AsLayerComposite()) {
     return shadow->GetShadowVisibleRegion();
   }
   return GetVisibleRegion();
 }
 
@@ -1018,17 +1018,17 @@ Layer::GetVisibleRegionRelativeToRootLay
 {
   MOZ_ASSERT(aLayerOffset, "invalid offset pointer");
 
   if (!GetParent()) {
     return false;
   }
 
   IntPoint offset;
-  aResult = GetEffectiveVisibleRegion();
+  aResult = GetEffectiveVisibleRegion().ToUnknownRegion();
   for (Layer* layer = this; layer; layer = layer->GetParent()) {
     gfx::Matrix matrix;
     if (!layer->GetLocalTransform().Is2D(&matrix) ||
         !matrix.IsTranslation()) {
       return false;
     }
 
     // The offset of |layer| to its parent.
@@ -1055,17 +1055,17 @@ Layer::GetVisibleRegionRelativeToRootLay
       if (!sibling->GetLocalTransform().Is2D(&siblingMatrix) ||
           !siblingMatrix.IsTranslation()) {
         return false;
       }
 
       // Retreive the translation from sibling to |layer|. The accumulated
       // visible region is currently oriented with |layer|.
       IntPoint siblingOffset = RoundedToInt(siblingMatrix.GetTranslation());
-      nsIntRegion siblingVisibleRegion(sibling->GetEffectiveVisibleRegion());
+      nsIntRegion siblingVisibleRegion(sibling->GetEffectiveVisibleRegion().ToUnknownRegion());
       // Translate the siblings region to |layer|'s origin.
       siblingVisibleRegion.MoveBy(-siblingOffset.x, -siblingOffset.y);
       // Apply the sibling's clip.
       // Layer clip rects are not affected by the layer's transform.
       Maybe<ParentLayerIntRect> clipRect = sibling->GetEffectiveClipRect();
       if (clipRect) {
         siblingVisibleRegion.AndWith(clipRect->ToUnknownRect());
       }
@@ -1889,17 +1889,17 @@ Layer::PrintInfo(std::stringstream& aStr
   }
   if (mTransformIsPerspective) {
     aStream << " [perspective]";
   }
   if (!mLayerBounds.IsEmpty()) {
     AppendToString(aStream, mLayerBounds, " [bounds=", "]");
   }
   if (!mVisibleRegion.IsEmpty()) {
-    AppendToString(aStream, mVisibleRegion, " [visible=", "]");
+    AppendToString(aStream, mVisibleRegion.ToUnknownRegion(), " [visible=", "]");
   } else {
     aStream << " [not visible]";
   }
   if (!mEventRegions.IsEmpty()) {
     AppendToString(aStream, mEventRegions, " ", "");
   }
   if (1.0 != mOpacity) {
     aStream << nsPrintfCString(" [opacity=%g]", mOpacity).get();
@@ -2008,30 +2008,30 @@ Layer::DumpPacket(layerscope::LayersPack
     LayersPacket::Layer::Shadow* s = layer->mutable_shadow();
     if (const Maybe<ParentLayerIntRect>& clipRect = lc->GetShadowClipRect()) {
       DumpRect(s->mutable_clip(), *clipRect);
     }
     if (!lc->GetShadowTransform().IsIdentity()) {
       DumpTransform(s->mutable_transform(), lc->GetShadowTransform());
     }
     if (!lc->GetShadowVisibleRegion().IsEmpty()) {
-      DumpRegion(s->mutable_vregion(), lc->GetShadowVisibleRegion());
+      DumpRegion(s->mutable_vregion(), lc->GetShadowVisibleRegion().ToUnknownRegion());
     }
   }
   // Clip
   if (mClipRect) {
     DumpRect(layer->mutable_clip(), *mClipRect);
   }
   // Transform
   if (!mTransform.IsIdentity()) {
     DumpTransform(layer->mutable_transform(), mTransform);
   }
   // Visible region
-  if (!mVisibleRegion.IsEmpty()) {
-    DumpRegion(layer->mutable_vregion(), mVisibleRegion);
+  if (!mVisibleRegion.ToUnknownRegion().IsEmpty()) {
+    DumpRegion(layer->mutable_vregion(), mVisibleRegion.ToUnknownRegion());
   }
   // EventRegions
   if (!mEventRegions.IsEmpty()) {
     const EventRegions &e = mEventRegions;
     if (!e.mHitRegion.IsEmpty()) {
       DumpRegion(layer->mutable_hitregion(), e.mHitRegion);
     }
     if (!e.mDispatchToContentHitRegion.IsEmpty()) {
@@ -2417,30 +2417,30 @@ PrintInfo(std::stringstream& aStream, La
   }
   if (const Maybe<ParentLayerIntRect>& clipRect = aLayerComposite->GetShadowClipRect()) {
     AppendToString(aStream, *clipRect, " [shadow-clip=", "]");
   }
   if (!aLayerComposite->GetShadowTransform().IsIdentity()) {
     AppendToString(aStream, aLayerComposite->GetShadowTransform(), " [shadow-transform=", "]");
   }
   if (!aLayerComposite->GetShadowVisibleRegion().IsEmpty()) {
-    AppendToString(aStream, aLayerComposite->GetShadowVisibleRegion(), " [shadow-visible=", "]");
+    AppendToString(aStream, aLayerComposite->GetShadowVisibleRegion().ToUnknownRegion(), " [shadow-visible=", "]");
   }
 }
 
 void
 SetAntialiasingFlags(Layer* aLayer, DrawTarget* aTarget)
 {
   bool permitSubpixelAA = !(aLayer->GetContentFlags() & Layer::CONTENT_DISABLE_SUBPIXEL_AA);
   if (aTarget->GetFormat() != SurfaceFormat::B8G8R8A8) {
     aTarget->SetPermitSubpixelAA(permitSubpixelAA);
     return;
   }
 
-  const IntRect& bounds = aLayer->GetVisibleRegion().GetBounds();
+  const IntRect& bounds = aLayer->GetVisibleRegion().ToUnknownRegion().GetBounds();
   gfx::Rect transformedBounds = aTarget->GetTransform().TransformBounds(gfx::Rect(Float(bounds.x), Float(bounds.y),
                                                                                   Float(bounds.width), Float(bounds.height)));
   transformedBounds.RoundOut();
   IntRect intTransformedBounds;
   transformedBounds.ToIntRect(&intTransformedBounds);
   permitSubpixelAA &= !(aLayer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA) ||
                       aTarget->GetOpaqueRect().Contains(intTransformedBounds);
   aTarget->SetPermitSubpixelAA(permitSubpixelAA);
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -840,17 +840,17 @@ public:
    * that are covered by opaque contents of other layers, and it can
    * exclude areas where this layer simply contains no content at all.
    * (This can be an overapproximation to the "true" visible region.)
    *
    * There is no general guarantee that drawing outside the bounds of the
    * visible region will be ignored. So if a layer draws outside the bounds
    * of its visible region, it needs to ensure that what it draws is valid.
    */
-  virtual void SetVisibleRegion(const nsIntRegion& aRegion)
+  virtual void SetVisibleRegion(const LayerIntRegion& aRegion)
   {
     if (!mVisibleRegion.IsEqual(aRegion)) {
       MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) VisibleRegion was %s is %s", this,
         mVisibleRegion.ToString().get(), aRegion.ToString().get()));
       mVisibleRegion = aRegion;
       Mutated();
     }
   }
@@ -1256,17 +1256,17 @@ public:
   }
 
   // These getters can be used anytime.
   float GetOpacity() { return mOpacity; }
   gfx::CompositionOp GetMixBlendMode() const { return mMixBlendMode; }
   const Maybe<ParentLayerIntRect>& GetClipRect() const { return mClipRect; }
   uint32_t GetContentFlags() { return mContentFlags; }
   const gfx::IntRect& GetLayerBounds() const { return mLayerBounds; }
-  const nsIntRegion& GetVisibleRegion() const { return mVisibleRegion; }
+  const LayerIntRegion& GetVisibleRegion() const { return mVisibleRegion; }
   const FrameMetrics& GetFrameMetrics(uint32_t aIndex) const;
   uint32_t GetFrameMetricsCount() const { return mFrameMetrics.Length(); }
   const nsTArray<FrameMetrics>& GetAllFrameMetrics() { return mFrameMetrics; }
   bool HasScrollableFrameMetrics() const;
   bool IsScrollInfoLayer() const;
   const EventRegions& GetEventRegions() const { return mEventRegions; }
   ContainerLayer* GetParent() { return mParent; }
   Layer* GetNextSibling() { return mNextSibling; }
@@ -1468,17 +1468,17 @@ public:
    * ShadowableLayer.  Can be used anytime.
    */
   virtual ShadowableLayer* AsShadowableLayer() { return nullptr; }
 
   // These getters can be used anytime.  They return the effective
   // values that should be used when drawing this layer to screen,
   // accounting for this layer possibly being a shadow.
   const Maybe<ParentLayerIntRect>& GetEffectiveClipRect();
-  const nsIntRegion& GetEffectiveVisibleRegion();
+  const LayerIntRegion& GetEffectiveVisibleRegion();
 
   bool Extend3DContext() {
     return GetContentFlags() & CONTENT_EXTEND_3D_CONTEXT;
   }
   bool Combines3DTransformWithAncestors() {
     return GetParent() &&
       reinterpret_cast<Layer*>(GetParent())->Extend3DContext();
   }
@@ -1639,17 +1639,17 @@ public:
   const nsIntRegion& GetInvalidRegion() { return mInvalidRegion; }
   const void AddInvalidRegion(const nsIntRegion& aRegion) {
     mInvalidRegion.Or(mInvalidRegion, aRegion);
   }
 
   /**
    * Mark the entirety of the layer's visible region as being invalid.
    */
-  void SetInvalidRectToVisibleRegion() { mInvalidRegion = GetVisibleRegion(); }
+  void SetInvalidRectToVisibleRegion() { mInvalidRegion = GetVisibleRegion().ToUnknownRegion(); }
 
   /**
    * Adds to the current invalid rect.
    */
   void AddInvalidRect(const gfx::IntRect& aRect) { mInvalidRegion.Or(mInvalidRegion, aRect); }
 
   /**
    * Clear the invalid rect, marking the layer as being identical to what is currently
@@ -1782,17 +1782,17 @@ protected:
   ContainerLayer* mParent;
   Layer* mNextSibling;
   Layer* mPrevSibling;
   void* mImplData;
   RefPtr<Layer> mMaskLayer;
   nsTArray<RefPtr<Layer>> mAncestorMaskLayers;
   gfx::UserData mUserData;
   gfx::IntRect mLayerBounds;
-  nsIntRegion mVisibleRegion;
+  LayerIntRegion mVisibleRegion;
   nsTArray<FrameMetrics> mFrameMetrics;
   EventRegions mEventRegions;
   gfx::Matrix4x4 mTransform;
   // A mutation of |mTransform| that we've queued to be applied at the
   // end of the next transaction (if nothing else overrides it in the
   // meantime).
   nsAutoPtr<gfx::Matrix4x4> mPendingTransform;
   float mPostXScale;
@@ -2083,17 +2083,17 @@ public:
    * in this layer's coordinate system.
    *
    * NOTE: Since this layer has an intermediate surface it follows
    *       that LayerPixel == RenderTargetPixel
    */
   RenderTargetIntRect GetIntermediateSurfaceRect()
   {
     NS_ASSERTION(mUseIntermediateSurface, "Must have intermediate surface");
-    return RenderTargetIntRect::FromUnknownRect(mVisibleRegion.GetBounds());
+    return RenderTargetIntRect::FromUnknownRect(GetEffectiveVisibleRegion().ToUnknownRegion().GetBounds());
   }
 
   /**
    * Returns true if this container has more than one non-empty child
    */
   bool HasMultipleChildren();
 
   /**
--- a/gfx/layers/ReadbackProcessor.cpp
+++ b/gfx/layers/ReadbackProcessor.cpp
@@ -58,17 +58,17 @@ FindBackgroundLayer(ReadbackLayer* aLaye
   for (Layer* l = aLayer->GetPrevSibling(); l; l = l->GetPrevSibling()) {
     gfx::Matrix backgroundTransform;
     if (!l->GetTransform().Is2D(&backgroundTransform) ||
         gfx::ThebesMatrix(backgroundTransform).HasNonIntegerTranslation())
       return nullptr;
 
     nsIntPoint backgroundOffset(int32_t(backgroundTransform._31), int32_t(backgroundTransform._32));
     IntRect rectInBackground(transformOffset - backgroundOffset, aLayer->GetSize());
-    const nsIntRegion& visibleRegion = l->GetEffectiveVisibleRegion();
+    const nsIntRegion visibleRegion = l->GetEffectiveVisibleRegion().ToUnknownRegion();
     if (!visibleRegion.Intersects(rectInBackground))
       continue;
     // Since l is present in the background, from here on we either choose l
     // or nothing.
     if (!visibleRegion.Contains(rectInBackground))
       return nullptr;
 
     if (l->GetEffectiveOpacity() != 1.0 ||
--- a/gfx/layers/RotatedBuffer.cpp
+++ b/gfx/layers/RotatedBuffer.cpp
@@ -216,24 +216,24 @@ RotatedContentBuffer::DrawTo(PaintedLaye
   bool clipped = false;
 
   // If the entire buffer is valid, we can just draw the whole thing,
   // no need to clip. But we'll still clip if clipping is cheap ---
   // that might let us copy a smaller region of the buffer.
   // Also clip to the visible region if we're told to.
   if (!aLayer->GetValidRegion().Contains(BufferRect()) ||
       (ToData(aLayer)->GetClipToVisibleRegion() &&
-       !aLayer->GetVisibleRegion().Contains(BufferRect())) ||
-      IsClippingCheap(aTarget, aLayer->GetEffectiveVisibleRegion())) {
+       !aLayer->GetVisibleRegion().ToUnknownRegion().Contains(BufferRect())) ||
+      IsClippingCheap(aTarget, aLayer->GetEffectiveVisibleRegion().ToUnknownRegion())) {
     // We don't want to draw invalid stuff, so we need to clip. Might as
     // well clip to the smallest area possible --- the visible region.
     // Bug 599189 if there is a non-integer-translation transform in aTarget,
     // we might sample pixels outside GetEffectiveVisibleRegion(), which is wrong
     // and may cause gray lines.
-    gfxUtils::ClipToRegion(aTarget, aLayer->GetEffectiveVisibleRegion());
+    gfxUtils::ClipToRegion(aTarget, aLayer->GetEffectiveVisibleRegion().ToUnknownRegion());
     clipped = true;
   }
 
   DrawBufferWithRotation(aTarget, BUFFER_BLACK, aOpacity, aOp, aMask, aMaskTransform);
   if (clipped) {
     aTarget->PopClip();
   }
 }
@@ -449,17 +449,17 @@ RotatedContentBuffer::BeginPaint(Painted
   SurfaceMode mode;
   nsIntRegion neededRegion;
   IntRect destBufferRect;
 
   bool canReuseBuffer = HaveBuffer();
 
   while (true) {
     mode = aLayer->GetSurfaceMode();
-    neededRegion = aLayer->GetVisibleRegion();
+    neededRegion = aLayer->GetVisibleRegion().ToUnknownRegion();
     canReuseBuffer &= BufferSizeOkFor(neededRegion.GetBounds().Size());
     result.mContentType = layerContentType;
 
     if (canReuseBuffer) {
       if (mBufferRect.Contains(neededRegion.GetBounds())) {
         // We don't need to adjust mBufferRect.
         destBufferRect = mBufferRect;
       } else if (neededRegion.GetBounds().Size() <= mBufferRect.Size()) {
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -703,17 +703,17 @@ APZCTreeManager::ReceiveInputEvent(Input
     } case MOUSE_INPUT: {
       MouseInput& mouseInput = aEvent.AsMouseInput();
 
       RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(mouseInput.mOrigin,
                                                             &hitResult);
 
       // When the mouse is outside the window we still want to handle dragging
       // but we won't find an APZC. Fallback to root APZC then.
-      if (!apzc) {
+      if (!apzc && mRootNode) {
         apzc = mRootNode->GetApzc();
       }
 
       if (apzc) {
         result = mInputQueue->ReceiveInputEvent(
           apzc,
           /* aTargetConfirmed = */ false,
           mouseInput, aOutInputBlockId);
--- a/gfx/layers/apz/test/gtest/TestAsyncPanZoomController.cpp
+++ b/gfx/layers/apz/test/gtest/TestAsyncPanZoomController.cpp
@@ -2075,17 +2075,17 @@ protected:
   static void SetScrollableFrameMetrics(Layer* aLayer, FrameMetrics::ViewID aScrollId,
                                         CSSRect aScrollableRect = CSSRect(-1, -1, -1, -1)) {
     FrameMetrics metrics;
     metrics.SetScrollId(aScrollId);
     // By convention in this test file, START_SCROLL_ID is the root, so mark it as such.
     if (aScrollId == FrameMetrics::START_SCROLL_ID) {
       metrics.SetIsLayersIdRoot(true);
     }
-    IntRect layerBound = aLayer->GetVisibleRegion().GetBounds();
+    IntRect layerBound = aLayer->GetVisibleRegion().ToUnknownRegion().GetBounds();
     metrics.SetCompositionBounds(ParentLayerRect(layerBound.x, layerBound.y,
                                                  layerBound.width, layerBound.height));
     metrics.SetScrollableRect(aScrollableRect);
     metrics.SetScrollOffset(CSSPoint(0, 0));
     metrics.SetPageScrollAmount(LayoutDeviceIntSize(50, 100));
     metrics.SetAllowVerticalScrollWithWheel();
     aLayer->SetFrameMetrics(metrics);
     aLayer->SetClipRect(Some(ViewAs<ParentLayerPixel>(layerBound)));
@@ -2750,17 +2750,17 @@ TEST_F(APZHitTestingTester, Bug1148350) 
 
   uint64_t blockId;
   TouchDown(manager, 100, 100, mcc->Time(), &blockId);
   if (gfxPrefs::TouchActionEnabled()) {
     SetDefaultAllowedTouchBehavior(manager, blockId);
   }
   mcc->AdvanceByMillis(100);
 
-  layers[0]->SetVisibleRegion(nsIntRegion(IntRect(0,50,200,150)));
+  layers[0]->SetVisibleRegion(LayerIntRegion(LayerIntRect(0,50,200,150)));
   layers[0]->SetBaseTransform(Matrix4x4::Translation(0, 50, 0));
   manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
 
   TouchUp(manager, 100, 100, mcc->Time());
   mcc->RunThroughDelayedTasks();
   check.Call("Tapped with interleaved transform");
 }
 
--- a/gfx/layers/basic/BasicCanvasLayer.h
+++ b/gfx/layers/basic/BasicCanvasLayer.h
@@ -19,17 +19,17 @@ namespace layers {
 class BasicCanvasLayer : public CopyableCanvasLayer,
                          public BasicImplData
 {
 public:
   explicit BasicCanvasLayer(BasicLayerManager* aLayerManager) :
     CopyableCanvasLayer(aLayerManager, static_cast<BasicImplData*>(this))
   { }
 
-  virtual void SetVisibleRegion(const nsIntRegion& aRegion) override
+  virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
   {
     NS_ASSERTION(BasicManager()->InConstruction(),
                  "Can only set properties in construction phase");
     CanvasLayer::SetVisibleRegion(aRegion);
   }
 
   virtual void Paint(gfx::DrawTarget* aDT,
                      const gfx::Point& aDeviceOffset,
--- a/gfx/layers/basic/BasicColorLayer.cpp
+++ b/gfx/layers/basic/BasicColorLayer.cpp
@@ -34,17 +34,17 @@ public:
 
 protected:
   virtual ~BasicColorLayer()
   {
     MOZ_COUNT_DTOR(BasicColorLayer);
   }
 
 public:
-  virtual void SetVisibleRegion(const nsIntRegion& aRegion) override
+  virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
   {
     NS_ASSERTION(BasicManager()->InConstruction(),
                  "Can only set properties in construction phase");
     ColorLayer::SetVisibleRegion(aRegion);
   }
 
   virtual void Paint(DrawTarget* aDT,
                      const gfx::Point& aDeviceOffset,
--- a/gfx/layers/basic/BasicContainerLayer.cpp
+++ b/gfx/layers/basic/BasicContainerLayer.cpp
@@ -104,29 +104,29 @@ bool
 BasicContainerLayer::ChildrenPartitionVisibleRegion(const gfx::IntRect& aInRect)
 {
   Matrix transform;
   if (!GetEffectiveTransform().CanDraw2D(&transform) ||
       ThebesMatrix(transform).HasNonIntegerTranslation())
     return false;
 
   nsIntPoint offset(int32_t(transform._31), int32_t(transform._32));
-  gfx::IntRect rect = aInRect.Intersect(GetEffectiveVisibleRegion().GetBounds() + offset);
+  gfx::IntRect rect = aInRect.Intersect(GetEffectiveVisibleRegion().ToUnknownRegion().GetBounds() + offset);
   nsIntRegion covered;
 
   for (Layer* l = mFirstChild; l; l = l->GetNextSibling()) {
     if (ToData(l)->IsHidden())
       continue;
 
     Matrix childTransform;
     if (!l->GetEffectiveTransform().CanDraw2D(&childTransform) ||
         ThebesMatrix(childTransform).HasNonIntegerTranslation() ||
         l->GetEffectiveOpacity() != 1.0)
       return false;
-    nsIntRegion childRegion = l->GetEffectiveVisibleRegion();
+    nsIntRegion childRegion = l->GetEffectiveVisibleRegion().ToUnknownRegion();
     childRegion.MoveBy(int32_t(childTransform._31), int32_t(childTransform._32));
     childRegion.And(childRegion, rect);
     if (l->GetClipRect()) {
       childRegion.And(childRegion, l->GetClipRect()->ToUnknownRect() + offset);
     }
     nsIntRegion intersection;
     intersection.And(covered, childRegion);
     if (!intersection.IsEmpty())
--- a/gfx/layers/basic/BasicContainerLayer.h
+++ b/gfx/layers/basic/BasicContainerLayer.h
@@ -24,17 +24,17 @@ public:
   {
     MOZ_COUNT_CTOR(BasicContainerLayer);
     mSupportsComponentAlphaChildren = true;
   }
 protected:
   virtual ~BasicContainerLayer();
 
 public:
-  virtual void SetVisibleRegion(const nsIntRegion& aRegion) override
+  virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
   {
     NS_ASSERTION(BasicManager()->InConstruction(),
                  "Can only set properties in construction phase");
     ContainerLayer::SetVisibleRegion(aRegion);
   }
   virtual bool InsertAfter(Layer* aChild, Layer* aAfter) override
   {
     if (!BasicManager()->InConstruction()) {
--- a/gfx/layers/basic/BasicImageLayer.cpp
+++ b/gfx/layers/basic/BasicImageLayer.cpp
@@ -33,17 +33,17 @@ public:
   }
 protected:
   virtual ~BasicImageLayer()
   {
     MOZ_COUNT_DTOR(BasicImageLayer);
   }
 
 public:
-  virtual void SetVisibleRegion(const nsIntRegion& aRegion) override
+  virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
   {
     NS_ASSERTION(BasicManager()->InConstruction(),
                  "Can only set properties in construction phase");
     ImageLayer::SetVisibleRegion(aRegion);
   }
 
   virtual void Paint(DrawTarget* aDT,
                      const gfx::Point& aDeviceOffset,
--- a/gfx/layers/basic/BasicLayerManager.cpp
+++ b/gfx/layers/basic/BasicLayerManager.cpp
@@ -123,26 +123,16 @@ BasicLayerManager::PushGroupForLayer(gfx
       group.mMaskSurface = GetMaskForLayer(aLayer, &group.mMaskTransform);
       return group;
     }
   }
 
   Matrix maskTransform;
   RefPtr<SourceSurface> maskSurf = GetMaskForLayer(aLayer, &maskTransform);
 
-  if (maskSurf) {
-    // The returned transform will transform the mask to device space on the
-    // destination. Since the User->Device space transform will be applied
-    // to the mask by PopGroupAndBlend we need to adjust the transform to
-    // transform the mask to user space.
-    Matrix currentTransform = ToMatrix(group.mFinalTarget->CurrentMatrix());
-    currentTransform.Invert();
-    maskTransform = maskTransform * currentTransform;
-  }
-
   if (aLayer->CanUseOpaqueSurface() &&
       ((didCompleteClip && aRegion.GetNumRects() == 1) ||
        !aContext->CurrentMatrix().HasNonIntegerTranslation())) {
     // If the layer is opaque in its visible region we can push a gfxContentType::COLOR
     // group. We need to make sure that only pixels inside the layer's visible
     // region are copied back to the destination. Remember if we've already
     // clipped precisely to the visible region.
     group.mNeedsClipToVisibleRegion = !didCompleteClip || aRegion.GetNumRects() > 1;
@@ -177,22 +167,18 @@ BasicLayerManager::PopGroupForLayer(Push
 
   DrawTarget* dt = group.mFinalTarget->GetDrawTarget();
   RefPtr<DrawTarget> sourceDT = group.mGroupTarget->GetDrawTarget();
   group.mGroupTarget = nullptr;
 
   RefPtr<SourceSurface> src = sourceDT->Snapshot();
 
   if (group.mMaskSurface) {
-    Point finalOffset = group.mFinalTarget->GetDeviceOffset();
-    dt->SetTransform(group.mMaskTransform * Matrix::Translation(-finalOffset));
-    Matrix surfTransform = group.mMaskTransform;
-    surfTransform.Invert();
-    dt->MaskSurface(SurfacePattern(src, ExtendMode::CLAMP, surfTransform *
-                                                           Matrix::Translation(group.mGroupOffset.x, group.mGroupOffset.y)),
+    dt->SetTransform(group.mMaskTransform * Matrix::Translation(-group.mFinalTarget->GetDeviceOffset()));
+    dt->MaskSurface(SurfacePattern(src, ExtendMode::CLAMP, Matrix::Translation(group.mGroupOffset.x, group.mGroupOffset.y)),
                     group.mMaskSurface, Point(0, 0), DrawOptions(group.mOpacity, group.mOperator));
   } else {
     // For now this is required since our group offset is in device space of the final target,
     // context but that may still have its own device offset. Once PushGroup/PopGroup logic is
     // migrated to DrawTargets this can go as gfxContext::GetDeviceOffset will essentially
     // always become null.
     dt->SetTransform(Matrix::Translation(-group.mFinalTarget->GetDeviceOffset()));
     dt->DrawSurface(src, Rect(group.mGroupOffset.x, group.mGroupOffset.y, src->GetSize().width, src->GetSize().height),
@@ -254,17 +240,17 @@ public:
   void Apply2DTransform()
   {
     mTarget->SetMatrix(ThebesMatrix(mTransform));
   }
 
   // Set the opaque rect to match the bounds of the visible region.
   void AnnotateOpaqueRect()
   {
-    const nsIntRegion& visibleRegion = mLayer->GetEffectiveVisibleRegion();
+    const nsIntRegion visibleRegion = mLayer->GetEffectiveVisibleRegion().ToUnknownRegion();
     const IntRect& bounds = visibleRegion.GetBounds();
 
     DrawTarget *dt = mTarget->GetDrawTarget();
     const IntRect& targetOpaqueRect = dt->GetOpaqueRect();
 
     // Try to annotate currentSurface with a region of pixels that have been
     // (or will be) painted opaque, if no such region is currently set.
     if (targetOpaqueRect.IsEmpty() && visibleRegion.GetNumRects() == 1 &&
@@ -441,17 +427,17 @@ MarkLayersHidden(Layer* aLayer, const In
 
   if (!aLayer->AsContainerLayer()) {
     Matrix transform;
     if (!aLayer->GetEffectiveTransform().CanDraw2D(&transform)) {
       data->SetHidden(false);
       return;
     }
 
-    nsIntRegion region = aLayer->GetEffectiveVisibleRegion();
+    nsIntRegion region = aLayer->GetEffectiveVisibleRegion().ToUnknownRegion();
     IntRect r = region.GetBounds();
     TransformIntRect(r, transform, ToOutsideIntRect);
     r.IntersectRect(r, aDirtyRect);
     data->SetHidden(aOpaqueRegion.Contains(r));
 
     // Allow aLayer to cover underlying layers only if aLayer's
     // content is opaque
     if ((aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) &&
@@ -929,17 +915,17 @@ BasicLayerManager::FlushGroup(PaintLayer
   // those temporary surfaces from being composited to aTarget.
   // ApplyDoubleBuffering guarantees that this container layer can't
   // intersect any other leaf layers, so if the transaction is not yet marked
   // incomplete, the contents of this container layer are the final contents
   // for the window.
   if (!mTransactionIncomplete) {
     if (aNeedsClipToVisibleRegion) {
       gfxUtils::ClipToRegion(aPaintContext.mTarget,
-                             aPaintContext.mLayer->GetEffectiveVisibleRegion());
+                             aPaintContext.mLayer->GetEffectiveVisibleRegion().ToUnknownRegion());
     }
 
     CompositionOp op = GetEffectiveOperator(aPaintContext.mLayer);
     AutoSetOperator setOperator(aPaintContext.mTarget, op);
 
     PaintWithMask(aPaintContext.mTarget, aPaintContext.mLayer->GetEffectiveOpacity(),
                   aPaintContext.mLayer->GetMaskLayer());
   }
@@ -1041,17 +1027,17 @@ BasicLayerManager::PaintLayer(gfxContext
     InstallLayerClipPreserves3D(aTarget, aLayer);
     for (Layer* l = parent; l && l->Extend3DContext(); l = l->GetParent()) {
       InstallLayerClipPreserves3D(aTarget, l);
     }
   }
 
   paintLayerContext.Apply2DTransform();
 
-  const nsIntRegion& visibleRegion = aLayer->GetEffectiveVisibleRegion();
+  const nsIntRegion visibleRegion = aLayer->GetEffectiveVisibleRegion().ToUnknownRegion();
   // If needsGroup is true, we'll clip to the visible region after we've popped the group
   if (needsClipToVisibleRegion && !needsGroup) {
     gfxUtils::ClipToRegion(aTarget, visibleRegion);
     // Don't need to clip to visible region again
     needsClipToVisibleRegion = false;
   }
   
   if (is2D) {
@@ -1062,17 +1048,17 @@ BasicLayerManager::PaintLayer(gfxContext
   if (clipIsEmpty) {
     PaintSelfOrChildren(paintLayerContext, aTarget);
     return;
   }
 
   if (is2D) {
     if (needsGroup) {
       PushedGroup pushedGroup =
-        PushGroupForLayer(aTarget, aLayer, aLayer->GetEffectiveVisibleRegion());
+        PushGroupForLayer(aTarget, aLayer, aLayer->GetEffectiveVisibleRegion().ToUnknownRegion());
       PaintSelfOrChildren(paintLayerContext, pushedGroup.mGroupTarget);
       PopGroupForLayer(pushedGroup);
     } else {
       PaintSelfOrChildren(paintLayerContext, aTarget);
     }
   } else {
     if (!needsGroup && container) {
       PaintSelfOrChildren(paintLayerContext, aTarget);
--- a/gfx/layers/basic/BasicLayersImpl.h
+++ b/gfx/layers/basic/BasicLayersImpl.h
@@ -57,17 +57,17 @@ public:
 
 protected:
   virtual ~BasicReadbackLayer()
   {
     MOZ_COUNT_DTOR(BasicReadbackLayer);
   }
 
 public:
-  virtual void SetVisibleRegion(const nsIntRegion& aRegion)
+  virtual void SetVisibleRegion(const LayerIntRegion& aRegion)
   {
     NS_ASSERTION(BasicManager()->InConstruction(),
                  "Can only set properties in construction phase");
     ReadbackLayer::SetVisibleRegion(aRegion);
   }
 
 protected:
   BasicLayerManager* BasicManager()
--- a/gfx/layers/basic/BasicPaintedLayer.cpp
+++ b/gfx/layers/basic/BasicPaintedLayer.cpp
@@ -58,17 +58,17 @@ BasicPaintedLayer::PaintThebes(gfxContex
 
   float opacity = GetEffectiveOpacity();
   CompositionOp effectiveOperator = GetEffectiveOperator(this);
 
   if (!BasicManager()->IsRetained()) {
     mValidRegion.SetEmpty();
     mContentClient->Clear();
 
-    nsIntRegion toDraw = IntersectWithClip(GetEffectiveVisibleRegion(), aContext);
+    nsIntRegion toDraw = IntersectWithClip(GetEffectiveVisibleRegion().ToUnknownRegion(), aContext);
 
     RenderTraceInvalidateStart(this, "FFFF00", toDraw.GetBounds());
 
     if (!toDraw.IsEmpty() && !IsHidden()) {
       if (!aCallback) {
         BasicManager()->SetTransactionIncomplete();
         return;
       }
@@ -163,17 +163,17 @@ BasicPaintedLayer::Validate(LayerManager
   mValidRegion.Sub(mValidRegion, state.mRegionToInvalidate);
 
   if (DrawTarget* target = mContentClient->BorrowDrawTargetForPainting(state)) {
     // The area that became invalid and is visible needs to be repainted
     // (this could be the whole visible area if our buffer switched
     // from RGB to RGBA, because we might need to repaint with
     // subpixel AA)
     state.mRegionToInvalidate.And(state.mRegionToInvalidate,
-                                  GetEffectiveVisibleRegion());
+                                  GetEffectiveVisibleRegion().ToUnknownRegion());
     SetAntialiasingFlags(this, target);
 
     RenderTraceInvalidateStart(this, "FFFF00", state.mRegionToDraw.GetBounds());
 
     RefPtr<gfxContext> ctx = gfxContext::ContextForDrawTarget(target);
     PaintBuffer(ctx,
                 state.mRegionToDraw, state.mRegionToDraw, state.mRegionToInvalidate,
                 state.mDidSelfCopy,
--- a/gfx/layers/basic/BasicPaintedLayer.h
+++ b/gfx/layers/basic/BasicPaintedLayer.h
@@ -39,17 +39,17 @@ public:
 
 protected:
   virtual ~BasicPaintedLayer()
   {
     MOZ_COUNT_DTOR(BasicPaintedLayer);
   }
 
 public:
-  virtual void SetVisibleRegion(const nsIntRegion& aRegion) override
+  virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
   {
     NS_ASSERTION(BasicManager()->InConstruction(),
                  "Can only set properties in construction phase");
     PaintedLayer::SetVisibleRegion(aRegion);
   }
   virtual void InvalidateRegion(const nsIntRegion& aRegion) override
   {
     NS_ASSERTION(BasicManager()->InConstruction(),
@@ -114,17 +114,17 @@ protected:
     }
     aCallback(this, aContext, aExtendedRegionToDraw, aExtendedRegionToDraw,
               aClip, aRegionToInvalidate, aCallbackData);
     // Everything that's visible has been validated. Do this instead of just
     // OR-ing with aRegionToDraw, since that can lead to a very complex region
     // here (OR doesn't automatically simplify to the simplest possible
     // representation of a region.)
     nsIntRegion tmp;
-    tmp.Or(mVisibleRegion, aExtendedRegionToDraw);
+    tmp.Or(mVisibleRegion.ToUnknownRegion(), aExtendedRegionToDraw);
     mValidRegion.Or(mValidRegion, tmp);
   }
 
   RefPtr<ContentClientBasic> mContentClient;
 };
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/client/ClientCanvasLayer.h
+++ b/gfx/layers/client/ClientCanvasLayer.h
@@ -39,17 +39,17 @@ public:
   {
     MOZ_COUNT_CTOR(ClientCanvasLayer);
   }
 
 protected:
   virtual ~ClientCanvasLayer();
 
 public:
-  virtual void SetVisibleRegion(const nsIntRegion& aRegion) override
+  virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
   {
     NS_ASSERTION(ClientManager()->InConstruction(),
                  "Can only set properties in construction phase");
     CanvasLayer::SetVisibleRegion(aRegion);
   }
 
   virtual void Initialize(const Data& aData) override;
 
--- a/gfx/layers/client/ClientColorLayer.cpp
+++ b/gfx/layers/client/ClientColorLayer.cpp
@@ -29,17 +29,17 @@ public:
 
 protected:
   virtual ~ClientColorLayer()
   {
     MOZ_COUNT_DTOR(ClientColorLayer);
   }
 
 public:
-  virtual void SetVisibleRegion(const nsIntRegion& aRegion)
+  virtual void SetVisibleRegion(const LayerIntRegion& aRegion)
   {
     NS_ASSERTION(ClientManager()->InConstruction(),
                  "Can only set properties in construction phase");
     ColorLayer::SetVisibleRegion(aRegion);
   }
 
   virtual void RenderLayer()
   {
--- a/gfx/layers/client/ClientContainerLayer.h
+++ b/gfx/layers/client/ClientContainerLayer.h
@@ -66,17 +66,17 @@ public:
 
       if (!ClientManager()->GetRepeatTransaction() &&
           !child->GetInvalidRegion().IsEmpty()) {
         child->Mutated();
       }
     }
   }
 
-  virtual void SetVisibleRegion(const nsIntRegion& aRegion) override
+  virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
   {
     NS_ASSERTION(ClientManager()->InConstruction(),
                  "Can only set properties in construction phase");
     ContainerLayer::SetVisibleRegion(aRegion);
   }
   virtual bool InsertAfter(Layer* aChild, Layer* aAfter) override
   {
     if(!ClientManager()->InConstruction()) {
--- a/gfx/layers/client/ClientImageLayer.cpp
+++ b/gfx/layers/client/ClientImageLayer.cpp
@@ -41,17 +41,17 @@ protected:
   }
 
   virtual void SetContainer(ImageContainer* aContainer) override
   {
     ImageLayer::SetContainer(aContainer);
     mImageClientTypeContainer = CompositableType::UNKNOWN;
   }
 
-  virtual void SetVisibleRegion(const nsIntRegion& aRegion) override
+  virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
   {
     NS_ASSERTION(ClientManager()->InConstruction(),
                  "Can only set properties in construction phase");
     ImageLayer::SetVisibleRegion(aRegion);
   }
 
   virtual void RenderLayer() override;
   
--- a/gfx/layers/client/ClientPaintedLayer.cpp
+++ b/gfx/layers/client/ClientPaintedLayer.cpp
@@ -70,17 +70,17 @@ ClientPaintedLayer::PaintThebes()
     return;
   }
 
   // The area that became invalid and is visible needs to be repainted
   // (this could be the whole visible area if our buffer switched
   // from RGB to RGBA, because we might need to repaint with
   // subpixel AA)
   state.mRegionToInvalidate.And(state.mRegionToInvalidate,
-                                GetEffectiveVisibleRegion());
+                                GetEffectiveVisibleRegion().ToUnknownRegion());
 
   bool didUpdate = false;
   RotatedContentBuffer::DrawIterator iter;
   while (DrawTarget* target = mContentClient->BorrowDrawTargetForPainting(state, &iter)) {
     SetAntialiasingFlags(this, target);
 
     RefPtr<gfxContext> ctx = gfxContext::ContextForDrawTarget(target);
 
@@ -105,17 +105,17 @@ ClientPaintedLayer::PaintThebes()
     ContentClientRemote* contentClientRemote = static_cast<ContentClientRemote*>(mContentClient.get());
     MOZ_ASSERT(contentClientRemote->GetIPDLActor());
 
     // Hold(this) ensures this layer is kept alive through the current transaction
     // The ContentClient assumes this layer is kept alive (e.g., in CreateBuffer),
     // so deleting this Hold for whatever reason will break things.
     ClientManager()->Hold(this);
     contentClientRemote->Updated(state.mRegionToDraw,
-                                 mVisibleRegion,
+                                 mVisibleRegion.ToUnknownRegion(),
                                  state.mDidSelfCopy);
   }
 }
 
 void
 ClientPaintedLayer::RenderLayerWithReadback(ReadbackProcessor *aReadback)
 {
   RenderMaskLayers(this);
--- a/gfx/layers/client/ClientPaintedLayer.h
+++ b/gfx/layers/client/ClientPaintedLayer.h
@@ -45,17 +45,17 @@ protected:
     if (mContentClient) {
       mContentClient->OnDetach();
       mContentClient = nullptr;
     }
     MOZ_COUNT_DTOR(ClientPaintedLayer);
   }
 
 public:
-  virtual void SetVisibleRegion(const nsIntRegion& aRegion) override
+  virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
   {
     NS_ASSERTION(ClientManager()->InConstruction(),
                  "Can only set properties in construction phase");
     PaintedLayer::SetVisibleRegion(aRegion);
   }
   virtual void InvalidateRegion(const nsIntRegion& aRegion) override
   {
     NS_ASSERTION(ClientManager()->InConstruction(),
--- a/gfx/layers/client/ClientTiledPaintedLayer.cpp
+++ b/gfx/layers/client/ClientTiledPaintedLayer.cpp
@@ -405,17 +405,17 @@ ClientTiledPaintedLayer::EndPaint()
 
 void
 ClientTiledPaintedLayer::RenderLayer()
 {
   LayerManager::DrawPaintedLayerCallback callback =
     ClientManager()->GetPaintedLayerCallback();
   void *data = ClientManager()->GetPaintedLayerCallbackData();
 
-  IntSize layerSize = mVisibleRegion.GetBounds().Size();
+  IntSize layerSize = mVisibleRegion.ToUnknownRegion().GetBounds().Size();
   if (mContentClient && !mContentClient->SupportsLayerSize(layerSize, ClientManager())) {
     ClearCachedResources();
     MOZ_ASSERT(!mContentClient);
   }
 
   if (!mContentClient) {
     if (mCreationHint == LayerManager::NONE &&
         SingleTiledContentClient::ClientSupportsLayerSize(layerSize, ClientManager()) &&
@@ -434,17 +434,17 @@ ClientTiledPaintedLayer::RenderLayer()
     mValidRegion = nsIntRegion();
     mContentClient->GetTiledBuffer()->ResetPaintedAndValidState();
   }
 
   TILING_LOG("TILING %p: Initial visible region %s\n", this, Stringify(mVisibleRegion).c_str());
   TILING_LOG("TILING %p: Initial valid region %s\n", this, Stringify(mValidRegion).c_str());
   TILING_LOG("TILING %p: Initial low-precision valid region %s\n", this, Stringify(mLowPrecisionValidRegion).c_str());
 
-  nsIntRegion neededRegion = mVisibleRegion;
+  nsIntRegion neededRegion = mVisibleRegion.ToUnknownRegion();
 #ifndef MOZ_IGNORE_PAINT_WILL_RESAMPLE
   // This is handled by PadDrawTargetOutFromRegion in TiledContentClient for mobile
   if (MayResample()) {
     // If we're resampling then bilinear filtering can read up to 1 pixel
     // outside of our texture coords. Make the visible region a single rect,
     // and pad it out by 1 pixel (restricted to tile boundaries) so that
     // we always have valid content or transparent pixels to sample from.
     IntRect bounds = neededRegion.GetBounds();
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -78,30 +78,30 @@ static void DrawLayerInfo(const RenderTa
     // is hard to do, since it will often get superimposed over the first
     // child of the layer, which is bad.
     return;
   }
 
   std::stringstream ss;
   aLayer->PrintInfo(ss, "");
 
-  nsIntRegion visibleRegion = aLayer->GetVisibleRegion();
+  LayerIntRegion visibleRegion = aLayer->GetVisibleRegion();
 
   uint32_t maxWidth = std::min<uint32_t>(visibleRegion.GetBounds().width, 500);
 
-  IntPoint topLeft = visibleRegion.GetBounds().TopLeft();
+  IntPoint topLeft = visibleRegion.ToUnknownRegion().GetBounds().TopLeft();
   aManager->GetTextRenderer()->RenderText(ss.str().c_str(), topLeft,
                                           aLayer->GetEffectiveTransform(), 16,
                                           maxWidth);
 }
 
 template<class ContainerT>
 static gfx::IntRect ContainerVisibleRect(ContainerT* aContainer)
 {
-  gfx::IntRect surfaceRect = aContainer->GetEffectiveVisibleRegion().GetBounds();
+  gfx::IntRect surfaceRect = aContainer->GetEffectiveVisibleRegion().ToUnknownRegion().GetBounds();
   return surfaceRect;
 }
 
 static void PrintUniformityInfo(Layer* aLayer)
 {
 #ifdef MOZ_ENABLE_PROFILER_SPS
   if (!profiler_is_active()) {
     return;
@@ -228,17 +228,17 @@ ContainerRenderVR(ContainerT* aContainer
       // XXX this is a hack! Canvas layers aren't reporting the
       // proper bounds here (visible region bounds are 0,0,0,0)
       // and I'm not sure if this is the bounds we want anyway.
       if (layer->GetType() == Layer::TYPE_CANVAS) {
         layerBounds =
           IntRectToRect(static_cast<CanvasLayer*>(layer)->GetBounds());
       } else {
         layerBounds =
-          IntRectToRect(layer->GetEffectiveVisibleRegion().GetBounds());
+          IntRectToRect(layer->GetEffectiveVisibleRegion().ToUnknownRegion().GetBounds());
       }
       const gfx::Matrix4x4 childTransform = layer->GetEffectiveTransform();
       layerBounds = childTransform.TransformBounds(layerBounds);
 
       DUMP("  layer %p [type %d] bounds [%f %f %f %f] surfaceRect [%d %d %d %d]\n", layer, (int) layer->GetType(),
            XYWH(layerBounds), XYWH(surfaceRect));
 
       bool restoreTransform = false;
@@ -637,17 +637,17 @@ CreateOrRecycleTarget(ContainerT* aConta
   }
 }
 
 template<class ContainerT> RefPtr<CompositingRenderTarget>
 CreateTemporaryTargetAndCopyFromBackground(ContainerT* aContainer,
                                            LayerManagerComposite* aManager)
 {
   Compositor* compositor = aManager->GetCompositor();
-  gfx::IntRect visibleRect = aContainer->GetEffectiveVisibleRegion().GetBounds();
+  gfx::IntRect visibleRect = aContainer->GetEffectiveVisibleRegion().ToUnknownRegion().GetBounds();
   RefPtr<CompositingRenderTarget> previousTarget = compositor->GetCurrentRenderTarget();
   gfx::IntRect surfaceRect = gfx::IntRect(visibleRect.x, visibleRect.y,
                                           visibleRect.width, visibleRect.height);
 
   gfx::IntPoint sourcePoint = gfx::IntPoint(visibleRect.x, visibleRect.y);
 
   gfx::Matrix4x4 transform = aContainer->GetEffectiveTransform();
   DebugOnly<gfx::Matrix> transform2d;
@@ -705,17 +705,17 @@ ContainerRender(ContainerT* aContainer,
       surface = aContainer->mPrepared->mTmpTarget;
     }
 
     if (!surface) {
       aContainer->mPrepared = nullptr;
       return;
     }
 
-    gfx::Rect visibleRect(aContainer->GetEffectiveVisibleRegion().GetBounds());
+    gfx::Rect visibleRect(aContainer->GetEffectiveVisibleRegion().ToUnknownRegion().GetBounds());
     RefPtr<Compositor> compositor = aManager->GetCompositor();
 #ifdef MOZ_DUMP_PAINTING
     if (gfxEnv::DumpCompositorTextures()) {
       RefPtr<gfx::DataSourceSurface> surf = surface->Dump(compositor);
       if (surf) {
         WriteSnapshotToDumpFile(aContainer, surf);
       }
     }
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -200,61 +200,89 @@ LayerManagerComposite::BeginTransactionW
 
   mIsCompositorReady = true;
   mCompositor->SetTargetContext(aTarget, aRect);
   mTarget = aTarget;
   mTargetBounds = aRect;
 }
 
 void
-LayerManagerComposite::ApplyOcclusionCulling(Layer* aLayer, nsIntRegion& aOpaqueRegion)
+LayerManagerComposite::PostProcessLayers(Layer* aLayer,
+                                         nsIntRegion& aOpaqueRegion,
+                                         LayerIntRegion& aVisibleRegion)
 {
   nsIntRegion localOpaque;
   Matrix transform2d;
-  bool isTranslation = false;
+  Maybe<nsIntPoint> integerTranslation;
   // If aLayer has a simple transform (only an integer translation) then we
   // can easily convert aOpaqueRegion into pre-transform coordinates and include
   // that region.
   if (aLayer->GetLocalTransform().Is2D(&transform2d)) {
     if (transform2d.IsIntegerTranslation()) {
-      isTranslation = true;
+      integerTranslation = Some(TruncatedToInt(transform2d.GetTranslation()));
       localOpaque = aOpaqueRegion;
-      localOpaque.MoveBy(-transform2d._31, -transform2d._32);
+      localOpaque.MoveBy(-*integerTranslation);
     }
   }
 
-  // Subtract any areas that we know to be opaque from our
-  // visible region.
-  LayerComposite *composite = aLayer->AsLayerComposite();
-  if (!localOpaque.IsEmpty()) {
-    nsIntRegion visible = composite->GetShadowVisibleRegion();
-    visible.Sub(visible, localOpaque);
-    composite->SetShadowVisibleRegion(visible);
+  // Save the value of localOpaque, which currently stores the region obscured
+  // by siblings (and uncles and such), before our descendants contribute to it.
+  nsIntRegion obscured = localOpaque;
+
+  // Recurse on our descendants, in front-to-back order. In this process:
+  //  - Occlusions are computed for them, and they contribute to localOpaque.
+  //  - They recalculate their visible regions, and accumulate them into
+  //    descendantsVisibleRegion.
+  LayerIntRegion descendantsVisibleRegion;
+  for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) {
+    PostProcessLayers(child, localOpaque, descendantsVisibleRegion);
   }
 
-  // Compute occlusions for our descendants (in front-to-back order) and allow them to
-  // contribute to localOpaque.
-  for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) {
-    ApplyOcclusionCulling(child, localOpaque);
+  // Recalculate our visible region.
+  LayerComposite* composite = aLayer->AsLayerComposite();
+  LayerIntRegion visible = composite->GetShadowVisibleRegion();
+
+  // If we have descendants, throw away the visible region stored on this
+  // layer, and use the region accumulated by our descendants instead.
+  if (aLayer->GetFirstChild()) {
+    visible = descendantsVisibleRegion;
+  }
+
+  // Subtract any areas that we know to be opaque.
+  if (!obscured.IsEmpty()) {
+    visible.SubOut(LayerIntRegion::FromUnknownRegion(obscured));
   }
 
+  composite->SetShadowVisibleRegion(visible);
+
+  // Transform the newly calculated visible region into our parent's space,
+  // apply our clip to it (if any), and accumulate it into |aVisibleRegion|
+  // for the caller to use.
+  ParentLayerIntRegion visibleParentSpace = TransformTo<ParentLayerPixel>(
+      aLayer->GetLocalTransform(), visible);
+  if (const Maybe<ParentLayerIntRect>& clipRect = composite->GetShadowClipRect()) {
+    visibleParentSpace.AndWith(*clipRect);
+  }
+  aVisibleRegion.OrWith(ViewAs<LayerPixel>(visibleParentSpace,
+      PixelCastJustification::MovingDownToChildren));
+
   // If we have a simple transform, then we can add our opaque area into
   // aOpaqueRegion.
-  if (isTranslation &&
+  if (integerTranslation &&
       !aLayer->HasMaskLayers() &&
       aLayer->IsOpaqueForVisibility()) {
     if (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) {
-      localOpaque.Or(localOpaque, composite->GetFullyRenderedRegion());
+      localOpaque.OrWith(composite->GetFullyRenderedRegion());
     }
-    localOpaque.MoveBy(transform2d._31, transform2d._32);
+    localOpaque.MoveBy(*integerTranslation);
     const Maybe<ParentLayerIntRect>& clip = aLayer->GetEffectiveClipRect();
     if (clip) {
-      localOpaque.And(localOpaque, clip->ToUnknownRect());
+      localOpaque.AndWith(clip->ToUnknownRect());
     }
-    aOpaqueRegion.Or(aOpaqueRegion, localOpaque);
+    aOpaqueRegion.OrWith(localOpaque);
   }
 }
 
 void
 LayerManagerComposite::EndTransaction(const TimeStamp& aTimeStamp,
                                       EndTransactionFlags aFlags)
 {
   NS_ASSERTION(mInTransaction, "Didn't call BeginTransaction?");
@@ -356,17 +384,18 @@ LayerManagerComposite::UpdateAndRender()
 
   if (!didEffectiveTransforms) {
     // The results of our drawing always go directly into a pixel buffer,
     // so we don't need to pass any global transform here.
     mRoot->ComputeEffectiveTransforms(gfx::Matrix4x4());
   }
 
   nsIntRegion opaque;
-  ApplyOcclusionCulling(mRoot, opaque);
+  LayerIntRegion visible;
+  PostProcessLayers(mRoot, opaque, visible);
 
   Render(invalid);
 #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
   RenderToPresentationSurface();
 #endif
   mGeometryChanged = false;
   mWindowOverlayChanged = false;
 }
@@ -1015,17 +1044,18 @@ LayerManagerComposite::RenderToPresentat
                                                   rotation);
   viewMatrix.Invert(); // unrotate
   viewMatrix.PostScale(scale, scale);
   viewMatrix.PostTranslate(offset.x, offset.y);
   Matrix4x4 matrix = Matrix4x4::From2D(viewMatrix);
 
   mRoot->ComputeEffectiveTransforms(matrix);
   nsIntRegion opaque;
-  ApplyOcclusionCulling(mRoot, opaque);
+  LayerIntRegion visible;
+  PostProcessLayers(mRoot, opaque, visible);
 
   nsIntRegion invalid;
   Rect bounds(0.0f, 0.0f, scale * pageWidth, (float)actualHeight);
   Rect rect, actualBounds;
 
   mCompositor->BeginFrame(invalid, nullptr, bounds, &rect, &actualBounds);
 
   // The Java side of Fennec sets a scissor rect that accounts for
@@ -1105,17 +1135,17 @@ LayerManagerComposite::ComputeRenderInte
 
   // Only painted layers can be incomplete
   PaintedLayer* paintedLayer = aLayer->AsPaintedLayer();
   if (!paintedLayer) {
     return;
   }
 
   // See if there's any incomplete rendering
-  nsIntRegion incompleteRegion = aLayer->GetEffectiveVisibleRegion();
+  nsIntRegion incompleteRegion = aLayer->GetEffectiveVisibleRegion().ToUnknownRegion();
   incompleteRegion.Sub(incompleteRegion, paintedLayer->GetValidRegion());
 
   if (!incompleteRegion.IsEmpty()) {
     // Calculate the transform to get between screen and layer space
     Matrix4x4 transformToScreen = aLayer->GetEffectiveTransform();
     transformToScreen = aTransform * transformToScreen;
 
     SubtractTransformedRegion(aScreenRegion, incompleteRegion, transformToScreen);
@@ -1440,24 +1470,24 @@ LayerManagerComposite::AsyncPanZoomEnabl
 {
   return mCompositor->GetWidget()->AsyncPanZoomEnabled();
 }
 
 nsIntRegion
 LayerComposite::GetFullyRenderedRegion() {
   if (TiledContentHost* tiled = GetCompositableHost() ? GetCompositableHost()->AsTiledContentHost()
                                                         : nullptr) {
-    nsIntRegion shadowVisibleRegion = GetShadowVisibleRegion();
+    nsIntRegion shadowVisibleRegion = GetShadowVisibleRegion().ToUnknownRegion();
     // Discard the region which hasn't been drawn yet when doing
     // progressive drawing. Note that if the shadow visible region
     // shrunk the tiled valig region may not have discarded this yet.
     shadowVisibleRegion.And(shadowVisibleRegion, tiled->GetValidRegion());
     return shadowVisibleRegion;
   } else {
-    return GetShadowVisibleRegion();
+    return GetShadowVisibleRegion().ToUnknownRegion();
   }
 }
 
 #ifndef MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS
 
 /*static*/ bool
 LayerManagerComposite::SupportsDirectTexturing()
 {
--- a/gfx/layers/composite/LayerManagerComposite.h
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -164,21 +164,30 @@ public:
   virtual bool AreComponentAlphaLayersEnabled() override;
 
   virtual already_AddRefed<DrawTarget>
     CreateOptimalMaskDrawTarget(const IntSize &aSize) override;
 
   virtual const char* Name() const override { return ""; }
 
   /**
-   * Restricts the shadow visible region of layers that are covered with
-   * opaque content. aOpaqueRegion is the region already known to be covered
-   * with opaque content, in the post-transform coordinate space of aLayer.
+   * Post-processes layers before composition. This performs the following:
+   *
+   *   - Applies occlusion culling. This restricts the shadow visible region
+   *     of layers that are covered with opaque content.
+   *     |aOpaqueRegion| is the region already known to be covered with opaque
+   *     content, in the post-transform coordinate space of aLayer.
+   *
+   *   - Recomputes visible regions to account for async transforms.
+   *     Each layer accumulates into |aVisibleRegion| its post-transform
+   *     (including async transforms) visible region.
    */
-  void ApplyOcclusionCulling(Layer* aLayer, nsIntRegion& aOpaqueRegion);
+  void PostProcessLayers(Layer* aLayer,
+                         nsIntRegion& aOpaqueRegion,
+                         LayerIntRegion& aVisibleRegion);
 
   /**
    * RAII helper class to add a mask effect with the compositable from aMaskLayer
    * to the EffectChain aEffect and notify the compositable when we are done.
    */
   class AutoAddMaskEffect
   {
   public:
@@ -435,17 +444,17 @@ public:
 
   /**
    * The following methods are
    *
    * CONSTRUCTION PHASE ONLY
    *
    * They are analogous to the Layer interface.
    */
-  void SetShadowVisibleRegion(const nsIntRegion& aRegion)
+  void SetShadowVisibleRegion(const LayerIntRegion& aRegion)
   {
     mShadowVisibleRegion = aRegion;
   }
 
   void SetShadowOpacity(float aOpacity)
   {
     mShadowOpacity = aOpacity;
   }
@@ -472,32 +481,32 @@ public:
   void SetClearRect(const gfx::IntRect& aRect)
   {
     mClearRect = aRect;
   }
 
   // These getters can be used anytime.
   float GetShadowOpacity() { return mShadowOpacity; }
   const Maybe<ParentLayerIntRect>& GetShadowClipRect() { return mShadowClipRect; }
-  const nsIntRegion& GetShadowVisibleRegion() { return mShadowVisibleRegion; }
+  const LayerIntRegion& GetShadowVisibleRegion() { return mShadowVisibleRegion; }
   const gfx::Matrix4x4& GetShadowTransform() { return mShadowTransform; }
   bool GetShadowTransformSetByAnimation() { return mShadowTransformSetByAnimation; }
   bool HasLayerBeenComposited() { return mLayerComposited; }
   gfx::IntRect GetClearRect() { return mClearRect; }
 
   /**
    * Return the part of the visible region that has been fully rendered.
    * While progressive drawing is in progress this region will be
    * a subset of the shadow visible region.
    */
   nsIntRegion GetFullyRenderedRegion();
 
 protected:
   gfx::Matrix4x4 mShadowTransform;
-  nsIntRegion mShadowVisibleRegion;
+  LayerIntRegion mShadowVisibleRegion;
   Maybe<ParentLayerIntRect> mShadowClipRect;
   LayerManagerComposite* mCompositeManager;
   RefPtr<Compositor> mCompositor;
   float mShadowOpacity;
   bool mShadowTransformSetByAnimation;
   bool mDestroyed;
   bool mLayerComposited;
   gfx::IntRect mClearRect;
@@ -562,17 +571,17 @@ RenderWithAllMasks(Layer* aLayer, Compos
   //  (1) The first mask
   //  (2) The list of intermediate masks (every mask except first and last)
   //  (3) The final mask.
   // Part (2) can be empty.
   // For parts (1) and (2) we need to allocate intermediate surfaces to render
   // into. The final mask gets rendered into the original render target.
 
   // Calculate the size of the intermediate surfaces.
-  gfx::Rect visibleRect(aLayer->GetEffectiveVisibleRegion().GetBounds());
+  gfx::Rect visibleRect(aLayer->GetEffectiveVisibleRegion().ToUnknownRegion().GetBounds());
   gfx::Matrix4x4 transform = aLayer->GetEffectiveTransform();
   // TODO: Use RenderTargetIntRect and TransformTo<...> here
   gfx::IntRect surfaceRect =
     RoundedOut(transform.TransformAndClipBounds(visibleRect, gfx::Rect(aClipRect)));
   if (surfaceRect.IsEmpty()) {
     return;
   }
 
--- a/gfx/layers/composite/PaintedLayerComposite.cpp
+++ b/gfx/layers/composite/PaintedLayerComposite.cpp
@@ -107,17 +107,17 @@ PaintedLayerComposite::RenderLayer(const
     js::ProfileEntry::Category::GRAPHICS);
 
   Compositor* compositor = mCompositeManager->GetCompositor();
 
   MOZ_ASSERT(mBuffer->GetCompositor() == compositor &&
              mBuffer->GetLayer() == this,
              "buffer is corrupted");
 
-  const nsIntRegion& visibleRegion = GetEffectiveVisibleRegion();
+  const nsIntRegion visibleRegion = GetEffectiveVisibleRegion().ToUnknownRegion();
 
 #ifdef MOZ_DUMP_PAINTING
   if (gfxEnv::DumpCompositorTextures()) {
     RefPtr<gfx::DataSourceSurface> surf = mBuffer->GetAsSurface();
     if (surf) {
       WriteSnapshotToDumpFile(this, surf);
     }
   }
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -768,17 +768,17 @@ CompositorParent::RecvNotifyRegionInvali
   return true;
 }
 
 void
 CompositorParent::Invalidate()
 {
   if (mLayerManager && mLayerManager->GetRoot()) {
     mLayerManager->AddInvalidRegion(
-        mLayerManager->GetRoot()->GetVisibleRegion().GetBounds());
+                                    mLayerManager->GetRoot()->GetVisibleRegion().ToUnknownRegion().GetBounds());
   }
 }
 
 bool
 CompositorParent::RecvStartFrameTimeRecording(const int32_t& aBufferSize, uint32_t* aOutStartIndex)
 {
   if (mLayerManager) {
     *aOutStartIndex = mLayerManager->StartFrameTimeRecording(aBufferSize);
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -29,16 +29,17 @@ using class mozilla::TimeDuration from "
 using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
 using mozilla::ScreenRotation from "mozilla/WidgetUtils.h";
 using nsCSSProperty from "nsCSSProperty.h";
 using mozilla::dom::ScreenOrientationInternal from "mozilla/dom/ScreenOrientation.h";
 using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h";
 using mozilla::LayerMargin from "Units.h";
 using mozilla::LayerPoint from "Units.h";
 using mozilla::LayerRect from "Units.h";
+using mozilla::LayerIntRegion from "Units.h";
 using mozilla::ParentLayerIntRect from "Units.h";
 using mozilla::layers::ScaleMode from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::EventRegions from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::EventRegionsOverride from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::DiagnosticTypes from "mozilla/layers/CompositorTypes.h";
 using struct mozilla::layers::FrameMetrics from "FrameMetrics.h";
 using mozilla::layers::FrameMetrics::ViewID from "FrameMetrics.h";
 using struct mozilla::layers::FenceHandle from "mozilla/layers/FenceUtils.h";
@@ -197,17 +198,17 @@ struct Animation {
   nsCSSProperty property;
   AnimationData data;
   float playbackRate;
 };
 
 // Change a layer's attributes
 struct CommonLayerAttributes {
   IntRect layerBounds;
-  nsIntRegion visibleRegion;
+  LayerIntRegion visibleRegion;
   EventRegions eventRegions;
   TransformMatrix transform;
   bool transformIsPerspective;
   float postXScale;
   float postYScale;
   uint32_t contentFlags;
   float opacity;
   bool useClipRect;
--- a/gfx/src/nsCoord.h
+++ b/gfx/src/nsCoord.h
@@ -105,16 +105,18 @@ inline nscoord NSToCoordRoundWithClamp(f
 #ifndef NS_COORD_IS_FLOAT
   // Bounds-check before converting out of float, to avoid overflow
   if (aValue >= nscoord_MAX) {
     return nscoord_MAX;
   }
   if (aValue <= nscoord_MIN) {
     return nscoord_MIN;
   }
+  // NOTE: we can't replace the early returns above with fminf/fmaxf because
+  // NSToCoordRound(float(nscoord_MAX)) is negative on win32 (bug 1226627).
 #endif
   return NSToCoordRound(aValue);
 }
 
 /**
  * Returns aCoord * aScale, capping the product to nscoord_MAX or nscoord_MIN as
  * appropriate for the signs of aCoord and aScale.  If requireNotNegative is
  * true, this method will enforce that aScale is not negative; use that
@@ -254,22 +256,18 @@ inline nscoord NSToCoordFloor(double aVa
 {
   return nscoord(floor(aValue));
 }
 
 inline nscoord NSToCoordFloorClamped(float aValue)
 {
 #ifndef NS_COORD_IS_FLOAT
   // Bounds-check before converting out of float, to avoid overflow
-  if (aValue >= nscoord_MAX) {
-    return nscoord_MAX;
-  }
-  if (aValue <= nscoord_MIN) {
-    return nscoord_MIN;
-  }
+  aValue = fminf(aValue, nscoord_MAX);
+  aValue = fmaxf(aValue, nscoord_MIN);
 #endif
   return NSToCoordFloor(aValue);
 }
 
 inline nscoord NSToCoordCeil(float aValue)
 {
   return nscoord(ceilf(aValue));
 }
@@ -278,22 +276,18 @@ inline nscoord NSToCoordCeil(double aVal
 {
   return nscoord(ceil(aValue));
 }
 
 inline nscoord NSToCoordCeilClamped(double aValue)
 {
 #ifndef NS_COORD_IS_FLOAT
   // Bounds-check before converting out of double, to avoid overflow
-  if (aValue >= nscoord_MAX) {
-    return nscoord_MAX;
-  }
-  if (aValue <= nscoord_MIN) {
-    return nscoord_MIN;
-  }
+  aValue = fmin(aValue, nscoord_MAX);
+  aValue = fmax(aValue, nscoord_MIN);
 #endif
   return NSToCoordCeil(aValue);
 }
 
 // The NSToCoordTrunc* functions remove the fractional component of
 // aValue, and are thus equivalent to NSToCoordFloor* for positive
 // values and NSToCoordCeil* for negative values.
 
@@ -310,36 +304,28 @@ inline nscoord NSToCoordTrunc(double aVa
   // rules for float to integer conversion.
   return nscoord(aValue);
 }
 
 inline nscoord NSToCoordTruncClamped(float aValue)
 {
 #ifndef NS_COORD_IS_FLOAT
   // Bounds-check before converting out of float, to avoid overflow
-  if (aValue >= nscoord_MAX) {
-    return nscoord_MAX;
-  }
-  if (aValue <= nscoord_MIN) {
-    return nscoord_MIN;
-  }
+  aValue = fminf(aValue, nscoord_MAX);
+  aValue = fmaxf(aValue, nscoord_MIN);
 #endif
   return NSToCoordTrunc(aValue);
 }
 
 inline nscoord NSToCoordTruncClamped(double aValue)
 {
 #ifndef NS_COORD_IS_FLOAT
   // Bounds-check before converting out of double, to avoid overflow
-  if (aValue >= nscoord_MAX) {
-    return nscoord_MAX;
-  }
-  if (aValue <= nscoord_MIN) {
-    return nscoord_MIN;
-  }
+  aValue = fmin(aValue, nscoord_MAX);
+  aValue = fmax(aValue, nscoord_MIN);
 #endif
   return NSToCoordTrunc(aValue);
 }
 
 /*
  * Int Rounding Functions
  */
 inline int32_t NSToIntFloor(float aValue)
--- a/gfx/src/nsRegion.h
+++ b/gfx/src/nsRegion.h
@@ -724,16 +724,20 @@ public:
   }
 
   Derived& ScaleInverseRoundOut (float aXScale, float aYScale)
   {
     mImpl.ScaleInverseRoundOut(aXScale, aYScale);
     return This();
   }
 
+  // Prefer using TransformTo<TargetUnits>(region) from UnitTransforms.h,
+  // as applying the transform should typically change the unit system.
+  // TODO(botond): Move this to IntRegionTyped and disable it for
+  //               unit != UnknownUnits.
   Derived& Transform (const mozilla::gfx::Matrix4x4 &aTransform)
   {
     mImpl.Transform(aTransform);
     return This();
   }
 
   /**
    * Make sure the region has at most aMaxRects by adding area to it
@@ -831,16 +835,18 @@ class IntRegionTyped :
     public BaseIntRegion<IntRegionTyped<units>, IntRectTyped<units>, IntPointTyped<units>, IntMarginTyped<units>>
 {
   typedef BaseIntRegion<IntRegionTyped<units>, IntRectTyped<units>, IntPointTyped<units>, IntMarginTyped<units>> Super;
 
   // Make other specializations of IntRegionTyped friends.
   template <typename OtherUnits>
   friend class IntRegionTyped;
 
+  static_assert(IsPixel<units>::value, "'units' must be a coordinate system tag");
+
 public:
   // Forward constructors.
   IntRegionTyped() {}
   MOZ_IMPLICIT IntRegionTyped(const IntRectTyped<units>& aRect) : Super(aRect) {}
   IntRegionTyped(const IntRegionTyped& aRegion) : Super(aRegion) {}
   IntRegionTyped(IntRegionTyped&& aRegion) : Super(mozilla::Move(aRegion)) {}
 
   // Assignment operators need to be forwarded as well, otherwise the compiler
--- a/gfx/tests/crashtests/crashtests.list
+++ b/gfx/tests/crashtests/crashtests.list
@@ -49,17 +49,17 @@ load 398042-1.xhtml
 load 398042-2.xhtml
 load 402307-1.html
 load 403464-1.html
 load 404112-1.html
 load 404112-2.html
 load 405268-1.xhtml
 load 407761-1.html
 load 407842.html
-load 408754-1.html
+asserts-if(Android,1) load 408754-1.html
 load 410728-1.xml
 load 416637-1.html
 load 419095-1.html
 load 419255-1.html
 load 420945-1.html
 load 420962-1.html
 load 421393-1.html
 load 421813-1.html
--- a/gfx/tests/gtest/TestCompositor.cpp
+++ b/gfx/tests/gtest/TestCompositor.cpp
@@ -238,29 +238,29 @@ TEST(Gfx, CompositorSimpleTree)
       nsIntRegion(IntRect(0, 0, 100, 100)),
       nsIntRegion(IntRect(0, 50, 100, 100)),
     };
     RefPtr<Layer> root = CreateLayerTree("c(ooo)", layerVisibleRegion, nullptr, lmBase, layers);
 
     { // background
       ColorLayer* colorLayer = layers[1]->AsColorLayer();
       colorLayer->SetColor(Color(1.f, 0.f, 1.f, 1.f));
-      colorLayer->SetBounds(colorLayer->GetVisibleRegion().GetBounds());
+      colorLayer->SetBounds(colorLayer->GetVisibleRegion().ToUnknownRegion().GetBounds());
     }
 
     {
       ColorLayer* colorLayer = layers[2]->AsColorLayer();
       colorLayer->SetColor(Color(1.f, 0.f, 0.f, 1.f));
-      colorLayer->SetBounds(colorLayer->GetVisibleRegion().GetBounds());
+      colorLayer->SetBounds(colorLayer->GetVisibleRegion().ToUnknownRegion().GetBounds());
     }
 
     {
       ColorLayer* colorLayer = layers[3]->AsColorLayer();
       colorLayer->SetColor(Color(0.f, 0.f, 1.f, 1.f));
-      colorLayer->SetBounds(colorLayer->GetVisibleRegion().GetBounds());
+      colorLayer->SetBounds(colorLayer->GetVisibleRegion().ToUnknownRegion().GetBounds());
     }
 
     RefPtr<DrawTarget> refDT = CreateDT();
     refDT->FillRect(Rect(0, 0, gCompWidth, gCompHeight), ColorPattern(Color(1.f, 0.f, 1.f, 1.f)));
     refDT->FillRect(Rect(0, 0, 100, 100), ColorPattern(Color(1.f, 0.f, 0.f, 1.f)));
     refDT->FillRect(Rect(0, 50, 100, 100), ColorPattern(Color(0.f, 0.f, 1.f, 1.f)));
     EXPECT_TRUE(CompositeAndCompare(layerManager, refDT));
   }
--- a/gfx/tests/gtest/TestLayers.cpp
+++ b/gfx/tests/gtest/TestLayers.cpp
@@ -209,17 +209,17 @@ already_AddRefed<Layer> CreateLayerTree(
         MOZ_CRASH();
       }
     } else if (aLayerTreeDescription[i] == ')') {
       parentContainerLayer = parentContainerLayer->GetParent();
       lastLayer = nullptr;
     } else {
       RefPtr<Layer> layer = CreateLayer(aLayerTreeDescription[i], manager.get());
       if (aVisibleRegions) {
-        layer->SetVisibleRegion(aVisibleRegions[layerNumber]);
+        layer->SetVisibleRegion(LayerIntRegion::FromUnknownRegion(aVisibleRegions[layerNumber]));
         layer->SetEventRegions(EventRegions(aVisibleRegions[layerNumber]));
       }
       if (aTransforms) {
         layer->SetBaseTransform(aTransforms[layerNumber]);
       }
       aLayersOut.AppendElement(layer);
       layerNumber++;
       if (rootLayer && !parentContainerLayer) {
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -43,16 +43,17 @@
 
 #include "cairo.h"
 
 #include "harfbuzz/hb.h"
 #include "harfbuzz/hb-ot.h"
 #include "graphite2/Font.h"
 
 #include <algorithm>
+#include <cmath>
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::unicode;
 using mozilla::services::GetObserverService;
 
 gfxFontCache *gfxFontCache::gGlobalCache = nullptr;
 
@@ -1844,16 +1845,56 @@ gfxFont::DrawGlyphs(gfxShapedText       
             }
             inlineCoord += aRunParams.isRTL ? -space : space;
         }
     }
 
     return emittedGlyphs;
 }
 
+// This method is mostly parallel to DrawGlyphs.
+void
+gfxFont::DrawEmphasisMarks(gfxTextRun* aShapedText, gfxPoint* aPt,
+                           uint32_t aOffset, uint32_t aCount,
+                           const EmphasisMarkDrawParams& aParams)
+{
+    gfxFloat& inlineCoord = aParams.isVertical ? aPt->y : aPt->x;
+    uint32_t markLength = aParams.mark->GetLength();
+
+    gfxFloat clusterStart = NAN;
+    bool shouldDrawEmphasisMark = false;
+    for (uint32_t i = 0, idx = aOffset; i < aCount; ++i, ++idx) {
+        if (aParams.spacing) {
+            inlineCoord += aParams.direction * aParams.spacing[i].mBefore;
+        }
+        if (aShapedText->IsClusterStart(idx)) {
+            clusterStart = inlineCoord;
+        }
+        if (aShapedText->CharMayHaveEmphasisMark(idx)) {
+            shouldDrawEmphasisMark = true;
+        }
+        inlineCoord += aParams.direction * aShapedText->GetAdvanceForGlyph(idx);
+        if (shouldDrawEmphasisMark &&
+            (i + 1 == aCount || aShapedText->IsClusterStart(idx + 1))) {
+            MOZ_ASSERT(!std::isnan(clusterStart), "Should have cluster start");
+            gfxFloat clusterAdvance = inlineCoord - clusterStart;
+            // Move the coord backward to get the needed start point.
+            gfxFloat delta = (clusterAdvance + aParams.advance) / 2;
+            inlineCoord -= delta;
+            aParams.mark->Draw(aParams.context, *aPt, DrawMode::GLYPH_FILL,
+                               0, markLength, nullptr, nullptr, nullptr);
+            inlineCoord += delta;
+            shouldDrawEmphasisMark = false;
+        }
+        if (aParams.spacing) {
+            inlineCoord += aParams.direction * aParams.spacing[i].mAfter;
+        }
+    }
+}
+
 void
 gfxFont::Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd,
               gfxPoint *aPt, const TextRunDrawParams& aRunParams,
               uint16_t aOrientation)
 {
     NS_ASSERTION(aRunParams.drawMode == DrawMode::GLYPH_PATH ||
                  !(int(aRunParams.drawMode) & int(DrawMode::GLYPH_PATH)),
                  "GLYPH_PATH cannot be used with GLYPH_FILL, GLYPH_STROKE or GLYPH_STROKE_UNDERNEATH");
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -1,9 +1,10 @@
-/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=4 et sw=4 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/. */
 
 #ifndef GFX_FONT_H
 #define GFX_FONT_H
 
 #include "gfxTypes.h"
@@ -736,17 +737,22 @@ public:
             // the mGlyphID is actually the UTF16 character code. The bit is
             // inverted so we can memset the array to zero to indicate all missing.
             FLAG_NOT_MISSING              = 0x01,
             FLAG_NOT_CLUSTER_START        = 0x02,
             FLAG_NOT_LIGATURE_GROUP_START = 0x04,
 
             FLAG_CHAR_IS_TAB              = 0x08,
             FLAG_CHAR_IS_NEWLINE          = 0x10,
-            CHAR_IDENTITY_FLAGS_MASK      = 0x18,
+            // Per CSS Text Decoration Module Level 3, emphasis marks are not
+            // drawn for any character in Unicode categories Z*, Cc, Cf, and Cn
+            // which is not combined with any combining characters. This flag is
+            // set for all those characters except 0x20 whitespace.
+            FLAG_CHAR_NO_EMPHASIS_MARK    = 0x20,
+            CHAR_TYPE_FLAGS_MASK          = 0x38,
 
             GLYPH_COUNT_MASK = 0x00FFFF00U,
             GLYPH_COUNT_SHIFT = 8
         };
 
         // "Simple glyphs" have a simple glyph ID, simple advance and their
         // x and y offsets are zero. Also the glyph extents do not overflow
         // the font-box defined by the font ascent, descent and glyph advance width.
@@ -787,19 +793,23 @@ public:
         }
 
         bool CharIsTab() const {
             return !IsSimpleGlyph() && (mValue & FLAG_CHAR_IS_TAB) != 0;
         }
         bool CharIsNewline() const {
             return !IsSimpleGlyph() && (mValue & FLAG_CHAR_IS_NEWLINE) != 0;
         }
+        bool CharMayHaveEmphasisMark() const {
+            return !CharIsSpace() &&
+                (IsSimpleGlyph() || !(mValue & FLAG_CHAR_NO_EMPHASIS_MARK));
+        }
 
-        uint32_t CharIdentityFlags() const {
-            return IsSimpleGlyph() ? 0 : (mValue & CHAR_IDENTITY_FLAGS_MASK);
+        uint32_t CharTypeFlags() const {
+            return IsSimpleGlyph() ? 0 : (mValue & CHAR_TYPE_FLAGS_MASK);
         }
 
         void SetClusterStart(bool aIsClusterStart) {
             NS_ASSERTION(!IsSimpleGlyph(),
                          "can't call SetClusterStart on simple glyphs");
             if (aIsClusterStart) {
                 mValue &= ~FLAG_NOT_CLUSTER_START;
             } else {
@@ -818,40 +828,40 @@ public:
             uint32_t toggle = breakMask ^ (mValue & FLAGS_CAN_BREAK_BEFORE);
             mValue ^= toggle;
             return toggle;
         }
 
         CompressedGlyph& SetSimpleGlyph(uint32_t aAdvanceAppUnits, uint32_t aGlyph) {
             NS_ASSERTION(IsSimpleAdvance(aAdvanceAppUnits), "Advance overflow");
             NS_ASSERTION(IsSimpleGlyphID(aGlyph), "Glyph overflow");
-            NS_ASSERTION(!CharIdentityFlags(), "Char identity flags lost");
+            NS_ASSERTION(!CharTypeFlags(), "Char type flags lost");
             mValue = (mValue & (FLAGS_CAN_BREAK_BEFORE | FLAG_CHAR_IS_SPACE)) |
                 FLAG_IS_SIMPLE_GLYPH |
                 (aAdvanceAppUnits << ADVANCE_SHIFT) | aGlyph;
             return *this;
         }
         CompressedGlyph& SetComplex(bool aClusterStart, bool aLigatureStart,
                 uint32_t aGlyphCount) {
             mValue = (mValue & (FLAGS_CAN_BREAK_BEFORE | FLAG_CHAR_IS_SPACE)) |
                 FLAG_NOT_MISSING |
-                CharIdentityFlags() |
+                CharTypeFlags() |
                 (aClusterStart ? 0 : FLAG_NOT_CLUSTER_START) |
                 (aLigatureStart ? 0 : FLAG_NOT_LIGATURE_GROUP_START) |
                 (aGlyphCount << GLYPH_COUNT_SHIFT);
             return *this;
         }
         /**
          * Missing glyphs are treated as ligature group starts; don't mess with
          * the cluster-start flag (see bugs 618870 and 619286).
          */
         CompressedGlyph& SetMissing(uint32_t aGlyphCount) {
             mValue = (mValue & (FLAGS_CAN_BREAK_BEFORE | FLAG_NOT_CLUSTER_START |
                                 FLAG_CHAR_IS_SPACE)) |
-                CharIdentityFlags() |
+                CharTypeFlags() |
                 (aGlyphCount << GLYPH_COUNT_SHIFT);
             return *this;
         }
         uint32_t GetGlyphCount() const {
             NS_ASSERTION(!IsSimpleGlyph(), "Expected non-simple-glyph");
             return (mValue & GLYPH_COUNT_MASK) >> GLYPH_COUNT_SHIFT;
         }
 
@@ -861,16 +871,20 @@ public:
         void SetIsTab() {
             NS_ASSERTION(!IsSimpleGlyph(), "Expected non-simple-glyph");
             mValue |= FLAG_CHAR_IS_TAB;
         }
         void SetIsNewline() {
             NS_ASSERTION(!IsSimpleGlyph(), "Expected non-simple-glyph");
             mValue |= FLAG_CHAR_IS_NEWLINE;
         }
+        void SetNoEmphasisMark() {
+            NS_ASSERTION(!IsSimpleGlyph(), "Expected non-simple-glyph");
+            mValue |= FLAG_CHAR_NO_EMPHASIS_MARK;
+        }
 
     private:
         uint32_t mValue;
     };
 
     // Accessor for the array of CompressedGlyph records, which will be in
     // a different place in gfxShapedWord vs gfxTextRun
     virtual CompressedGlyph *GetCharacterGlyphs() = 0;
@@ -988,16 +1002,32 @@ public:
 
     bool FilterIfIgnorable(uint32_t aIndex, uint32_t aCh);
 
 protected:
     // Allocate aCount DetailedGlyphs for the given index
     DetailedGlyph *AllocateDetailedGlyphs(uint32_t aCharIndex,
                                           uint32_t aCount);
 
+    // Ensure the glyph on the given index is complex glyph so that we can use
+    // it to record specific characters that layout may need to detect.
+    void EnsureComplexGlyph(uint32_t aIndex, CompressedGlyph& aGlyph)
+    {
+        MOZ_ASSERT(GetCharacterGlyphs() + aIndex == &aGlyph);
+        if (aGlyph.IsSimpleGlyph()) {
+            DetailedGlyph details = {
+                aGlyph.GetSimpleGlyph(),
+                (int32_t) aGlyph.GetSimpleAdvance(),
+                0, 0
+            };
+            SetGlyphs(aIndex, CompressedGlyph().SetComplex(true, true, 1),
+                      &details);
+        }
+    }
+
     // For characters whose glyph data does not fit the "simple" glyph criteria
     // in CompressedGlyph, we use a sorted array to store the association
     // between the source character offset and an index into an array 
     // DetailedGlyphs. The CompressedGlyph record includes a count of
     // the number of DetailedGlyph records that belong to the character,
     // starting at the given index.
     class DetailedGlyphStore {
     public:
@@ -1119,17 +1149,17 @@ protected:
 
 /*
  * gfxShapedWord: an individual (space-delimited) run of text shaped with a
  * particular font, without regard to external context.
  *
  * The glyph data is copied into gfxTextRuns as needed from the cache of
  * ShapedWords associated with each gfxFont instance.
  */
-class gfxShapedWord : public gfxShapedText
+class gfxShapedWord final : public gfxShapedText
 {
 public:
     // Create a ShapedWord that can hold glyphs for aLength characters,
     // with mCharacterGlyphs sized appropriately.
     //
     // Returns null on allocation failure (does NOT use infallible alloc)
     // so caller must check for success.
     //
@@ -1272,16 +1302,17 @@ private:
     // The original text, in either 8-bit or 16-bit form, will be stored
     // immediately following the CompressedGlyphs.
     CompressedGlyph  mCharGlyphsStorage[1];
 };
 
 class GlyphBufferAzure;
 struct TextRunDrawParams;
 struct FontDrawParams;
+struct EmphasisMarkDrawParams;
 
 class gfxFont {
 
     friend class gfxHarfBuzzShaper;
     friend class gfxGraphiteShaper;
 
 protected:
     typedef mozilla::gfx::DrawTarget DrawTarget;
@@ -1567,16 +1598,26 @@ public:
      * -- aStart and aEnd are aligned to cluster and ligature boundaries
      * -- all glyphs use this font
      */
     void Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd,
               gfxPoint *aPt, const TextRunDrawParams& aRunParams,
               uint16_t aOrientation);
 
     /**
+     * Draw the emphasis marks for the given text run. Its prerequisite
+     * and output are similiar to the method Draw().
+     * @param aPt the baseline origin of the emphasis marks.
+     * @param aParams some drawing parameters, see EmphasisMarkDrawParams.
+     */
+    void DrawEmphasisMarks(gfxTextRun* aShapedText, gfxPoint* aPt,
+                           uint32_t aOffset, uint32_t aCount,
+                           const EmphasisMarkDrawParams& aParams);
+
+    /**
      * Measure a run of characters. See gfxTextRun::Metrics.
      * @param aTight if false, then return the union of the glyph extents
      * with the font-box for the characters (the rectangle with x=0,width=
      * the advance width for the character run,y=-(font ascent), and height=
      * font ascent + font descent). Otherwise, we must return as tight as possible
      * an approximation to the area actually painted by glyphs.
      * @param aContextForTightBoundingBox when aTight is true, this must
      * be non-null.
@@ -2132,9 +2173,18 @@ struct FontDrawParams {
     double                    synBoldOnePixelOffset;
     int32_t                   extraStrikes;
     mozilla::gfx::DrawOptions drawOptions;
     bool                      isVerticalFont;
     bool                      haveSVGGlyphs;
     bool                      haveColorGlyphs;
 };
 
+struct EmphasisMarkDrawParams {
+    gfxContext* context;
+    gfxFont::Spacing* spacing;
+    gfxTextRun* mark;
+    gfxFloat advance;
+    gfxFloat direction;
+    bool isVertical;
+};
+
 #endif
--- a/gfx/thebes/gfxTextRun.cpp
+++ b/gfx/thebes/gfxTextRun.cpp
@@ -1,9 +1,10 @@
-/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=4 et sw=4 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 "gfxTextRun.h"
 #include "gfxGlyphExtents.h"
 #include "gfxPlatformFontList.h"
 #include "gfxUserFontSet.h"
@@ -317,35 +318,19 @@ gfxTextRun::ComputePartialLigatureWidth(
         return 0;
     LigatureData data = ComputeLigatureData(aPartStart, aPartEnd, aProvider);
     return data.mPartWidth;
 }
 
 int32_t
 gfxTextRun::GetAdvanceForGlyphs(uint32_t aStart, uint32_t aEnd)
 {
-    const CompressedGlyph *glyphData = mCharacterGlyphs + aStart;
     int32_t advance = 0;
-    uint32_t i;
-    for (i = aStart; i < aEnd; ++i, ++glyphData) {
-        if (glyphData->IsSimpleGlyph()) {
-            advance += glyphData->GetSimpleAdvance();   
-        } else {
-            uint32_t glyphCount = glyphData->GetGlyphCount();
-            if (glyphCount == 0) {
-                continue;
-            }
-            const DetailedGlyph *details = GetDetailedGlyphs(i);
-            if (details) {
-                uint32_t j;
-                for (j = 0; j < glyphCount; ++j, ++details) {
-                    advance += details->mAdvance;
-                }
-            }
-        }
+    for (auto i = aStart; i < aEnd; ++i) {
+        advance += GetAdvanceForGlyph(i);
     }
     return advance;
 }
 
 static void
 GetAdjustedSpacing(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd,
                    gfxTextRun::PropertyProvider *aProvider,
                    gfxTextRun::PropertyProvider::Spacing *aSpacing)
@@ -676,16 +661,60 @@ gfxTextRun::Draw(gfxContext *aContext, g
         syntheticBoldBuffer.PopAlpha();
     }
 
     if (aAdvanceWidth) {
         *aAdvanceWidth = advance;
     }
 }
 
+// This method is mostly parallel to Draw().
+void
+gfxTextRun::DrawEmphasisMarks(gfxContext *aContext, gfxTextRun* aMark,
+                              gfxFloat aMarkAdvance, gfxPoint aPt,
+                              uint32_t aStart, uint32_t aLength,
+                              PropertyProvider* aProvider)
+{
+    MOZ_ASSERT(aStart + aLength <= GetLength());
+
+    EmphasisMarkDrawParams params;
+    params.context = aContext;
+    params.mark = aMark;
+    params.advance = aMarkAdvance;
+    params.direction = GetDirection();
+    params.isVertical = IsVertical();
+
+    gfxFloat& inlineCoord = params.isVertical ? aPt.y : aPt.x;
+    gfxFloat direction = params.direction;
+
+    GlyphRunIterator iter(this, aStart, aLength);
+    while (iter.NextRun()) {
+        gfxFont* font = iter.GetGlyphRun()->mFont;
+        uint32_t start = iter.GetStringStart();
+        uint32_t end = iter.GetStringEnd();
+        uint32_t ligatureRunStart = start;
+        uint32_t ligatureRunEnd = end;
+        ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
+
+        inlineCoord += direction *
+            ComputePartialLigatureWidth(start, ligatureRunStart, aProvider);
+
+        nsAutoTArray<PropertyProvider::Spacing, 200> spacingBuffer;
+        bool haveSpacing = GetAdjustedSpacingArray(
+            ligatureRunStart, ligatureRunEnd, aProvider,
+            ligatureRunStart, ligatureRunEnd, &spacingBuffer);
+        params.spacing = haveSpacing ? spacingBuffer.Elements() : nullptr;
+        font->DrawEmphasisMarks(this, &aPt, ligatureRunStart,
+                                ligatureRunEnd - ligatureRunStart, params);
+
+        inlineCoord += direction *
+            ComputePartialLigatureWidth(ligatureRunEnd, end, aProvider);
+    }
+}
+
 void
 gfxTextRun::AccumulateMetricsForRun(gfxFont *aFont,
                                     uint32_t aStart, uint32_t aEnd,
                                     gfxFont::BoundingBoxType aBoundingBoxType,
                                     gfxContext *aRefContext,
                                     PropertyProvider *aProvider,
                                     uint32_t aSpacingStart, uint32_t aSpacingEnd,
                                     uint16_t aOrientation,
--- a/gfx/thebes/gfxTextRun.h
+++ b/gfx/thebes/gfxTextRun.h
@@ -1,9 +1,10 @@
-/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=4 et sw=4 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/. */
 
 #ifndef GFX_TEXTRUN_H
 #define GFX_TEXTRUN_H
 
 #include "gfxTypes.h"
@@ -123,16 +124,20 @@ public:
     bool CharIsTab(uint32_t aPos) const {
         NS_ASSERTION(aPos < GetLength(), "aPos out of range");
         return mCharacterGlyphs[aPos].CharIsTab();
     }
     bool CharIsNewline(uint32_t aPos) const {
         NS_ASSERTION(aPos < GetLength(), "aPos out of range");
         return mCharacterGlyphs[aPos].CharIsNewline();
     }
+    bool CharMayHaveEmphasisMark(uint32_t aPos) const {
+        NS_ASSERTION(aPos < GetLength(), "aPos out of range");
+        return mCharacterGlyphs[aPos].CharMayHaveEmphasisMark();
+    }
 
     // All uint32_t aStart, uint32_t aLength ranges below are restricted to
     // grapheme cluster boundaries! All offsets are in terms of the string
     // passed into MakeTextRun.
     
     // All coordinates are in layout/app units
 
     /**
@@ -242,16 +247,26 @@ public:
     void Draw(gfxContext *aContext, gfxPoint aPt,
               DrawMode aDrawMode,
               uint32_t aStart, uint32_t aLength,
               PropertyProvider *aProvider,
               gfxFloat *aAdvanceWidth, gfxTextContextPaint *aContextPaint,
               gfxTextRunDrawCallbacks *aCallbacks = nullptr);
 
     /**
+     * Draws the emphasis marks for this text run. Uses only GetSpacing
+     * from aProvider. The provided point is the baseline origin of the
+     * line of emphasis marks.
+     */
+    void DrawEmphasisMarks(gfxContext* aContext, gfxTextRun* aMark,
+                           gfxFloat aMarkAdvance, gfxPoint aPt,
+                           uint32_t aStart, uint32_t aLength,
+                           PropertyProvider* aProvider);
+
+    /**
      * Computes the ReflowMetrics for a substring.
      * Uses GetSpacing from aBreakProvider.
      * @param aBoundingBoxType which kind of bounding box (loose/tight)
      */
     Metrics MeasureText(uint32_t aStart, uint32_t aLength,
                         gfxFont::BoundingBoxType aBoundingBoxType,
                         gfxContext *aRefContextForTightBoundingBox,
                         PropertyProvider *aProvider);
@@ -470,17 +485,17 @@ public:
      */
     nsresult AddGlyphRun(gfxFont *aFont, uint8_t aMatchType,
                          uint32_t aStartCharIndex, bool aForceNewRun,
                          uint16_t aOrientation);
     void ResetGlyphRuns() { mGlyphRuns.Clear(); }
     void SortGlyphRuns();
     void SanitizeGlyphRuns();
 
-    CompressedGlyph* GetCharacterGlyphs() {
+    CompressedGlyph* GetCharacterGlyphs() final {
         NS_ASSERTION(mCharacterGlyphs, "failed to initialize mCharacterGlyphs");
         return mCharacterGlyphs;
     }
 
     // clean out results from shaping in progress, used for fallback scenarios
     void ClearGlyphsAndCharacters();
 
     void SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext, uint32_t aCharIndex,
@@ -505,36 +520,23 @@ public:
 
     // Record the positions of specific characters that layout may need to
     // detect in the textrun, even though it doesn't have an explicit copy
     // of the original text. These are recorded using flag bits in the
     // CompressedGlyph record; if necessary, we convert "simple" glyph records
     // to "complex" ones as the Tab and Newline flags are not present in
     // simple CompressedGlyph records.
     void SetIsTab(uint32_t aIndex) {
-        CompressedGlyph *g = &mCharacterGlyphs[aIndex];
-        if (g->IsSimpleGlyph()) {
-            DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
-            details->mGlyphID = g->GetSimpleGlyph();
-            details->mAdvance = g->GetSimpleAdvance();
-            details->mXOffset = details->mYOffset = 0;
-            SetGlyphs(aIndex, CompressedGlyph().SetComplex(true, true, 1), details);
-        }
-        g->SetIsTab();
+        EnsureComplexGlyph(aIndex).SetIsTab();
     }
     void SetIsNewline(uint32_t aIndex) {
-        CompressedGlyph *g = &mCharacterGlyphs[aIndex];
-        if (g->IsSimpleGlyph()) {
-            DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
-            details->mGlyphID = g->GetSimpleGlyph();
-            details->mAdvance = g->GetSimpleAdvance();
-            details->mXOffset = details->mYOffset = 0;
-            SetGlyphs(aIndex, CompressedGlyph().SetComplex(true, true, 1), details);
-        }
-        g->SetIsNewline();
+        EnsureComplexGlyph(aIndex).SetIsNewline();
+    }
+    void SetNoEmphasisMark(uint32_t aIndex) {
+        EnsureComplexGlyph(aIndex).SetNoEmphasisMark();
     }
 
     /**
      * Prefetch all the glyph extents needed to ensure that Measure calls
      * on this textrun not requesting tight boundingBoxes will succeed. Note
      * that some glyph extents might not be fetched due to OOM or other
      * errors.
      */
@@ -614,16 +616,34 @@ public:
         eShapingState_ForceFallbackFeature    // redo with fallback forced on
     };
 
     ShapingState GetShapingState() const { return mShapingState; }
     void SetShapingState(ShapingState aShapingState) {
         mShapingState = aShapingState;
     }
 
+    int32_t GetAdvanceForGlyph(uint32_t aIndex)
+    {
+        const CompressedGlyph& glyphData = mCharacterGlyphs[aIndex];
+        if (glyphData.IsSimpleGlyph()) {
+            return glyphData.GetSimpleAdvance();
+        }
+        uint32_t glyphCount = glyphData.GetGlyphCount();
+        if (!glyphCount) {
+            return 0;
+        }
+        const DetailedGlyph* details = GetDetailedGlyphs(aIndex);
+        int32_t advance = 0;
+        for (uint32_t j = 0; j < glyphCount; ++j, ++details) {
+            advance += details->mAdvance;
+        }
+        return advance;
+    }
+
 #ifdef DEBUG
     void Dump(FILE* aOutput);
 #endif
 
 protected:
     /**
      * Create a textrun, and set its mCharacterGlyphs to point immediately
      * after the base object; this is ONLY used in conjunction with placement
@@ -654,16 +674,22 @@ private:
     // is assumed to be zero; such characters are not passed to aProvider.
     // This is useful to protect aProvider from being passed character indices
     // it is not currently able to handle.
     bool GetAdjustedSpacingArray(uint32_t aStart, uint32_t aEnd,
                                    PropertyProvider *aProvider,
                                    uint32_t aSpacingStart, uint32_t aSpacingEnd,
                                    nsTArray<PropertyProvider::Spacing> *aSpacing);
 
+    CompressedGlyph& EnsureComplexGlyph(uint32_t aIndex)
+    {
+        gfxShapedText::EnsureComplexGlyph(aIndex, mCharacterGlyphs[aIndex]);
+        return mCharacterGlyphs[aIndex];
+    }
+
     //  **** ligature helpers ****
     // (Platforms do the actual ligaturization, but we need to do a bunch of stuff
     // to handle requests that begin or end inside a ligature)
 
     // if aProvider is null then mBeforeSpacing and mAfterSpacing are set to zero
     LigatureData ComputeLigatureData(uint32_t aPartStart, uint32_t aPartEnd,
                                      PropertyProvider *aProvider);
     gfxFloat ComputePartialLigatureWidth(uint32_t aPartStart, uint32_t aPartEnd,
--- a/intl/locale/unix/nsPosixLocale.cpp
+++ b/intl/locale/unix/nsPosixLocale.cpp
@@ -61,16 +61,20 @@ nsPosixLocale::GetXPLocale(const char* p
   char  extra[MAX_EXTRA_LEN+1];
   char  posix_locale[MAX_LOCALE_LEN+1];
 
   if (posixLocale!=nullptr) {
     if (strcmp(posixLocale,"C")==0 || strcmp(posixLocale,"POSIX")==0) {
       locale.AssignLiteral("en-US");
       return NS_OK;
     }
+    if (strcmp(posixLocale,"C.UTF-8")==0) {
+      locale.AssignLiteral("en-US.UTF-8");
+      return NS_OK;
+    }
     if (!ParseLocaleString(posixLocale,lang_code,country_code,extra,'_')) {
 //      * locale = "x-user-defined";
       // use posix if parse failed
       CopyASCIItoUTF16(nsDependentCString(posixLocale), locale);
       return NS_OK;
     }
 
     // Special case: substitute "nb" (Norwegian Bokmal) for macrolanguage
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -8,16 +8,17 @@
 #include "FileDescriptorSetParent.h"
 #ifdef MOZ_WEBRTC
 #include "CamerasParent.h"
 #endif
 #include "mozilla/media/MediaParent.h"
 #include "mozilla/AppProcessChecker.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/DOMTypes.h"
 #include "mozilla/dom/NuwaParent.h"
 #include "mozilla/dom/PBlobParent.h"
 #include "mozilla/dom/MessagePortParent.h"
 #include "mozilla/dom/ServiceWorkerRegistrar.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/indexedDB/ActorsParent.h"
 #include "mozilla/dom/ipc/BlobParent.h"
@@ -238,16 +239,28 @@ BackgroundParentImpl::DeallocPBlobParent
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   mozilla::dom::BlobParent::Destroy(aActor);
   return true;
 }
 
+bool
+BackgroundParentImpl::RecvPBlobConstructor(PBlobParent* aActor,
+                                           const BlobConstructorParams& aParams)
+{
+  const ParentBlobConstructorParams& params = aParams;
+  if (params.blobParams().type() == AnyBlobConstructorParams::TKnownBlobConstructorParams) {
+    return aActor->SendCreatedFromKnownBlob();
+  }
+
+  return true;
+}
+
 PFileDescriptorSetParent*
 BackgroundParentImpl::AllocPFileDescriptorSetParent(
                                           const FileDescriptor& aFileDescriptor)
 {
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
 
   return new FileDescriptorSetParent(aFileDescriptor);
--- a/ipc/glue/BackgroundParentImpl.h
+++ b/ipc/glue/BackgroundParentImpl.h
@@ -59,16 +59,20 @@ protected:
                                         override;
 
   virtual PBlobParent*
   AllocPBlobParent(const BlobConstructorParams& aParams) override;
 
   virtual bool
   DeallocPBlobParent(PBlobParent* aActor) override;
 
+  virtual bool
+  RecvPBlobConstructor(PBlobParent* aActor,
+                       const BlobConstructorParams& params) override;
+
   virtual PFileDescriptorSetParent*
   AllocPFileDescriptorSetParent(const FileDescriptor& aFileDescriptor)
                                 override;
 
   virtual bool
   DeallocPFileDescriptorSetParent(PFileDescriptorSetParent* aActor)
                                   override;
 
--- a/js/src/jit/Safepoints.cpp
+++ b/js/src/jit/Safepoints.cpp
@@ -196,17 +196,17 @@ SafepointWriter::writeValueSlots(LSafepo
 #ifdef JS_JITSPEW
     for (uint32_t i = 0; i < slots.length(); i++)
         JitSpew(JitSpew_Safepoints, "    gc value: %d", slots[i]);
 #endif
 
     MapSlotsToBitset(frameSlots_, argumentSlots_, stream_, slots);
 }
 
-#if defined(DEBUG) && defined(JS_NUNBOX32)
+#if defined(JS_JITSPEW) && defined(JS_NUNBOX32)
 static void
 DumpNunboxPart(const LAllocation& a)
 {
     Fprinter& out = JitSpewPrinter();
     if (a.isStackSlot()) {
         out.printf("stack %d", a.toStackSlot()->slot());
     } else if (a.isArgument()) {
         out.printf("arg %d", a.toArgument()->index());
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -2279,17 +2279,17 @@ ComputeAndSetIgnoreInvalidationRect(Pain
   // parentFrame is a scrollable frame, and aLayer contains the scrolled
   // contents of that frame.
 
   // maxNewVisibleBounds is a conservative approximation of the new visible
   // region of aLayer.
   nsIntRect maxNewVisibleBounds =
     dirtyRect.ScaleToOutsidePixels(aData->mXScale, aData->mYScale,
                                    aData->mAppUnitsPerDevPixel) - aLayerTranslation;
-  aData->mOldVisibleBounds = aLayer->GetVisibleRegion().GetBounds();
+  aData->mOldVisibleBounds = aLayer->GetVisibleRegion().ToUnknownRegion().GetBounds();
 
   // When the visible region of aLayer changes (e.g. due to scrolling),
   // three distinct types of invalidations need to be triggered:
   //  (1) Items (or parts of items) that have left the visible region need
   //      to be invalidated so that the pixels they painted are no longer
   //      part of the layer's valid region.
   //  (2) Items (or parts of items) that weren't in the old visible region
   //      but are in the new visible region need to be invalidated. This
@@ -2457,17 +2457,17 @@ SetOuterVisibleRegion(Layer* aLayer, nsI
     nsIntRect visRect;
     if (gfxUtils::GfxRectToIntRect(layerVisible, &visRect)) {
       *aOuterVisibleRegion = visRect;
     } else  {
       aOuterVisibleRegion->SetEmpty();
     }
   }
 
-  aLayer->SetVisibleRegion(*aOuterVisibleRegion);
+  aLayer->SetVisibleRegion(LayerIntRegion::FromUnknownRegion(*aOuterVisibleRegion));
 }
 
 void
 ContainerState::SetOuterVisibleRegionForLayer(Layer* aLayer,
                                               const nsIntRegion& aOuterVisibleRegion,
                                               const nsIntRect* aLayerContentsVisibleRect,
                                               bool aOuterUntransformed) const
 {
@@ -3176,17 +3176,17 @@ void ContainerState::FinishPaintedLayerD
       newLayerEntry->mAnimatedGeometryRootForScrollMetadata = data->mAnimatedGeometryRootForScrollMetadata;
       newLayerEntry->mFixedPosFrameForLayerData = data->mFixedPosFrameForLayerData;
       newLayerEntry->mIsCaret = data->mIsCaret;
 
       // Hide the PaintedLayer. We leave it in the layer tree so that we
       // can find and recycle it later.
       ParentLayerIntRect emptyRect;
       data->mLayer->SetClipRect(Some(emptyRect));
-      data->mLayer->SetVisibleRegion(nsIntRegion());
+      data->mLayer->SetVisibleRegion(LayerIntRegion());
       data->mLayer->InvalidateRegion(data->mLayer->GetValidRegion().GetBounds());
       data->mLayer->SetEventRegions(EventRegions());
     }
   }
 
   if (!layer) {
     // We couldn't optimize to an image layer or a color layer above.
     layer = data->mLayer;
@@ -4803,17 +4803,17 @@ InvalidateVisibleBoundsChangesForScrolle
     // during DLBI. Now is the right time to do that, because at this point aLayer
     // knows its new visible region.
     // We use the visible regions' bounds here (as opposed to the true region)
     // in order to limit rgn's complexity. The only possible disadvantage of
     // this is that it might cause us to unnecessarily recomposite parts of the
     // window that are in the visible region's bounds but not in the visible
     // region itself, but that is acceptable for scrolled layers.
     nsIntRegion rgn;
-    rgn.Or(data->mOldVisibleBounds, aLayer->GetVisibleRegion().GetBounds());
+    rgn.Or(data->mOldVisibleBounds, aLayer->GetVisibleRegion().ToUnknownRegion().GetBounds());
     rgn.Sub(rgn, *data->mIgnoreInvalidationsOutsideRect);
     if (!rgn.IsEmpty()) {
       aLayer->InvalidateRegion(rgn);
 #ifdef MOZ_DUMP_PAINTING
       if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
         printf_stderr("Invalidating changes of the visible region bounds of the scrolled contents\n");
         nsAutoCString str;
         AppendToString(str, rgn);
@@ -5403,17 +5403,17 @@ FrameLayerBuilder::BuildContainerLayerFo
       flags &= ~Layer::CONTENT_COMPONENT_ALPHA;
       flags |= Layer::CONTENT_OPAQUE;
     }
   }
   containerLayer->SetContentFlags(flags);
   // If aContainerItem is non-null some BuildContainerLayer further up the
   // call stack is responsible for setting containerLayer's visible region.
   if (!aContainerItem) {
-    containerLayer->SetVisibleRegion(pixBounds);
+    containerLayer->SetVisibleRegion(LayerIntRegion::FromUnknownRegion(pixBounds));
   }
   if (aParameters.mLayerContentsVisibleRect) {
     *aParameters.mLayerContentsVisibleRect = pixBounds + scaleParameters.mOffset;
   }
 
   mContainerLayerGeneration = oldGeneration;
   nsPresContext::ClearNotifySubDocInvalidationData(containerLayer);
 
@@ -5738,17 +5738,17 @@ static bool ShouldDrawRectsSeparately(gf
   return !dt->SupportsRegionClipping();
 }
 
 static void DrawForcedBackgroundColor(DrawTarget& aDrawTarget,
                                       Layer* aLayer, nscolor
                                       aBackgroundColor)
 {
   if (NS_GET_A(aBackgroundColor) > 0) {
-    nsIntRect r = aLayer->GetVisibleRegion().GetBounds();
+    LayerIntRect r = aLayer->GetVisibleRegion().GetBounds();
     ColorPattern color(ToDeviceColor(aBackgroundColor));
     aDrawTarget.FillRect(Rect(r.x, r.y, r.width, r.height), color);
   }
 }
 
 /*
  * A note on residual transforms:
  *
--- a/layout/base/MobileViewportManager.cpp
+++ b/layout/base/MobileViewportManager.cpp
@@ -217,18 +217,23 @@ MobileViewportManager::UpdateSPCSPS(cons
   MVM_LOG("%p: Setting SPCSPS %s\n", this, Stringify(compSize).c_str());
   nsLayoutUtils::SetScrollPositionClampingScrollPortSize(mPresShell, compSize);
 }
 
 void
 MobileViewportManager::UpdateDisplayPortMargins()
 {
   if (nsIFrame* root = mPresShell->GetRootScrollFrame()) {
-    if (!nsLayoutUtils::GetDisplayPort(root->GetContent(), nullptr)) {
-      // There isn't already a displayport, so we don't want to add one.
+    bool hasDisplayPort = nsLayoutUtils::GetDisplayPort(root->GetContent(), nullptr);
+    bool hasResolution = mPresShell->ScaleToResolution() &&
+        mPresShell->GetResolution() != 1.0f;
+    if (!hasDisplayPort && !hasResolution) {
+      // We only want to update the displayport if there is one already, or
+      // add one if there's a resolution on the document (see bug 1225508
+      // comment 1).
       return;
     }
     nsIScrollableFrame* scrollable = do_QueryFrame(root);
     nsLayoutUtils::CalculateAndSetDisplayPortMargins(scrollable,
       nsLayoutUtils::RepaintMode::DoNotRepaint);
   }
 }
 
--- a/layout/base/UnitTransforms.h
+++ b/layout/base/UnitTransforms.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZ_UNIT_TRANSFORMS_H_
 #define MOZ_UNIT_TRANSFORMS_H_
 
 #include "Units.h"
 #include "mozilla/gfx/Matrix.h"
+#include "nsRegion.h"
 
 namespace mozilla {
 
 // Convenience functions for converting an entity from one strongly-typed
 // coordinate system to another without changing the values it stores (this
 // can be thought of as a cast).
 // To use these functions, you must provide a justification for each use!
 // Feel free to add more justifications to PixelCastJustification, along with
@@ -73,16 +74,20 @@ gfx::IntRectTyped<TargetUnits> ViewAs(co
 template <class TargetUnits, class SourceUnits>
 gfx::MarginTyped<TargetUnits> ViewAs(const gfx::MarginTyped<SourceUnits>& aMargin, PixelCastJustification) {
   return gfx::MarginTyped<TargetUnits>(aMargin.top, aMargin.right, aMargin.bottom, aMargin.left);
 }
 template <class TargetUnits, class SourceUnits>
 gfx::IntMarginTyped<TargetUnits> ViewAs(const gfx::IntMarginTyped<SourceUnits>& aMargin, PixelCastJustification) {
   return gfx::IntMarginTyped<TargetUnits>(aMargin.top, aMargin.right, aMargin.bottom, aMargin.left);
 }
+template <class TargetUnits, class SourceUnits>
+gfx::IntRegionTyped<TargetUnits> ViewAs(const gfx::IntRegionTyped<SourceUnits>& aRegion, PixelCastJustification) {
+  return gfx::IntRegionTyped<TargetUnits>::FromUnknownRegion(aRegion.ToUnknownRegion());
+}
 template <class NewTargetUnits, class OldTargetUnits, class SourceUnits>
 gfx::ScaleFactor<SourceUnits, NewTargetUnits> ViewTargetAs(
     const gfx::ScaleFactor<SourceUnits, OldTargetUnits>& aScaleFactor,
     PixelCastJustification) {
   return gfx::ScaleFactor<SourceUnits, NewTargetUnits>(aScaleFactor.scale);
 }
 
 // Convenience functions for casting untyped entities to typed entities.
@@ -107,16 +112,20 @@ gfx::IntSizeTyped<TargetUnits> ViewAs(co
 template <class TargetUnits>
 gfx::IntPointTyped<TargetUnits> ViewAs(const nsIntPoint& aPoint) {
   return gfx::IntPointTyped<TargetUnits>(aPoint.x, aPoint.y);
 }
 template <class TargetUnits>
 gfx::IntRectTyped<TargetUnits> ViewAs(const nsIntRect& aRect) {
   return gfx::IntRectTyped<TargetUnits>(aRect.x, aRect.y, aRect.width, aRect.height);
 }
+template <class TargetUnits>
+gfx::IntRegionTyped<TargetUnits> ViewAs(const nsIntRegion& aRegion) {
+  return gfx::IntRegionTyped<TargetUnits>::FromUnknownRegion(aRegion);
+}
 
 // Convenience functions for transforming an entity from one strongly-typed
 // coordinate system to another using the provided transformation matrix.
 template <typename TargetUnits, typename SourceUnits>
 static gfx::PointTyped<TargetUnits> TransformTo(const gfx::Matrix4x4& aTransform,
                                                 const gfx::PointTyped<SourceUnits>& aPoint)
 {
   return ViewAs<TargetUnits>(aTransform * aPoint.ToUnknownPoint());
@@ -135,16 +144,22 @@ static gfx::RectTyped<TargetUnits> Trans
 }
 template <typename TargetUnits, typename SourceUnits>
 static gfx::IntRectTyped<TargetUnits> TransformTo(const gfx::Matrix4x4& aTransform,
                                                   const gfx::IntRectTyped<SourceUnits>& aRect)
 {
   gfx::Rect rect(aRect.ToUnknownRect());
   return RoundedToInt(ViewAs<TargetUnits>(aTransform.TransformBounds(rect)));
 }
+template <typename TargetUnits, typename SourceUnits>
+static gfx::IntRegionTyped<TargetUnits> TransformTo(const gfx::Matrix4x4& aTransform,
+                                                    const gfx::IntRegionTyped<SourceUnits>& aRegion)
+{
+  return ViewAs<TargetUnits>(aRegion.ToUnknownRegion().Transform(aTransform));
+}
 
 // Transform |aVector|, which is anchored at |aAnchor|, by the given transform
 // matrix, yielding a point in |TargetUnits|.
 // The anchor is necessary because with 3D tranforms, the location of the
 // vector can affect the result of the transform.
 template <typename TargetUnits, typename SourceUnits>
 static gfx::PointTyped<TargetUnits> TransformVector(const gfx::Matrix4x4& aTransform,
                                                     const gfx::PointTyped<SourceUnits>& aVector,
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -1516,16 +1516,34 @@ nsPresContext::GetDefaultFont(uint8_t aF
     default:
       font = nullptr;
       NS_ERROR("invalid arg");
       break;
   }
   return font;
 }
 
+already_AddRefed<nsIAtom>
+nsPresContext::GetContentLanguage() const
+{
+  nsAutoString language;
+  Document()->GetContentLanguage(language);
+  language.StripWhitespace();
+
+  // Content-Language may be a comma-separated list of language codes,
+  // in which case the HTML5 spec says to treat it as unknown
+  if (!language.IsEmpty() &&
+      !language.Contains(char16_t(','))) {
+    return do_GetAtom(language);
+    // NOTE:  This does *not* count as an explicit language; in other
+    // words, it doesn't trigger language-specific hyphenation.
+  }
+  return nullptr;
+}
+
 void
 nsPresContext::SetFullZoom(float aZoom)
 {
   if (!mShell || mFullZoom == aZoom) {
     return;
   }
 
   // Re-fetch the view manager's window dimensions in case there's a deferred
@@ -2520,17 +2538,17 @@ nsPresContext::NotifySubDocInvalidation(
 {
   ContainerLayerPresContext *data =
     static_cast<ContainerLayerPresContext*>(
       aContainer->GetUserData(&gNotifySubDocInvalidationData));
   if (!data) {
     return;
   }
 
-  nsIntPoint topLeft = aContainer->GetVisibleRegion().GetBounds().TopLeft();
+  nsIntPoint topLeft = aContainer->GetVisibleRegion().ToUnknownRegion().GetBounds().TopLeft();
 
   nsIntRegionRectIterator iter(aRegion);
   while (const nsIntRect* r = iter.Next()) {
     nsIntRect rect = *r;
     //PresContext coordinate space is relative to the start of our visible
     // region. Is this really true? This feels like the wrong way to get the right
     // answer.
     rect.MoveBy(-topLeft);
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -520,17 +520,18 @@ public:
   * in print preview.
   * XXX Temporary: see http://wiki.mozilla.org/Gecko:PrintPreview
   */
   float GetPrintPreviewScale() { return mPPScale; }
   void SetPrintPreviewScale(float aScale) { mPPScale = aScale; }
 
   nsDeviceContext* DeviceContext() { return mDeviceContext; }
   mozilla::EventStateManager* EventStateManager() { return mEventManager; }
-  nsIAtom* GetLanguageFromCharset() { return mLanguage; }
+  nsIAtom* GetLanguageFromCharset() const { return mLanguage; }
+  already_AddRefed<nsIAtom> GetContentLanguage() const;
 
   float TextZoom() { return mTextZoom; }
   void SetTextZoom(float aZoom) {
     MOZ_ASSERT(aZoom > 0.0f, "invalid zoom factor");
     if (aZoom == mTextZoom)
       return;
 
     mTextZoom = aZoom;
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -6125,17 +6125,17 @@ PresShell::Paint(nsView*        aViewToP
 
   RefPtr<ColorLayer> root = layerManager->CreateColorLayer();
   if (root) {
     nsPresContext* pc = GetPresContext();
     nsIntRect bounds =
       pc->GetVisibleArea().ToOutsidePixels(pc->AppUnitsPerDevPixel());
     bgcolor = NS_ComposeColors(bgcolor, mCanvasBackgroundColor);
     root->SetColor(Color::FromABGR(bgcolor));
-    root->SetVisibleRegion(bounds);
+    root->SetVisibleRegion(LayerIntRegion::FromUnknownRegion(bounds));
     layerManager->SetRoot(root);
   }
   MaybeSetupTransactionIdAllocator(layerManager, aViewToPaint);
   layerManager->EndTransaction(nullptr, nullptr, (aFlags & PAINT_COMPOSITE) ?
     LayerManager::END_DEFAULT : LayerManager::END_NO_COMPOSITE);
 }
 
 // static
new file mode 100644
--- /dev/null
+++ b/layout/generic/crashtests/1224230-1.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<script>
+function boom() {
+  var div = document.querySelector("div");
+  div.offsetHeight;
+  div.style.fontSize = "120%";
+}
+</script>
+<body onload="boom()">
+<div style="float:left; position:relative;">
+  <div style="display:inline;">
+    <img src="foo.jpg" style="float:left;">
+    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam tortor nulla,
+eleifend eu eleifend eu, scelerisque sit amet sapien. Sed iaculis tellus ut quam
+pharetra consequat. Donec vitae nulla eu mi porta vulputate. In vestibulum, erat
+quis aliquam tempor, lectus augue viverra justo, vitae semper nibh neque tempor
+orci. Etiam luctus aliquet magna id pellentesque. Interdum et malesuada fames ac
+ante ipsum primis in faucibus. Suspendisse sit amet eros volutpat, convallis
+purus non, porta sapien. Duis fermentum at tortor nec ultricies. Morbi et lacus
+vitae risus elementum condimentum quis vitae justo. Cum sociis natoque penatibus
+et magnis dis parturient montes, nascetur ridiculus mus.<br><br>
new file mode 100644
--- /dev/null
+++ b/layout/generic/crashtests/1225376.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<body>
+
+<div style="display: inline-grid;">
+    <div style="display: flex; align-self: right safe;"></div>
+</div>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/generic/crashtests/1225592.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<body>
+<div style="grid-template-columns: repeat(16384, auto); display: grid;"></div>
+<div style="grid-template-columns: repeat(9000, auto) repeat(9000, auto); display: grid;"></div>
+
+<div style="grid-template-columns: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ; display: grid;"></div>
+
+
+<div style='grid-template-areas: ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . "; display: grid;'></div>
+
+</body>
+</html>
--- a/layout/generic/crashtests/crashtests.list
+++ b/layout/generic/crashtests/crashtests.list
@@ -591,17 +591,20 @@ load 1169420-1.html
 load 1169420-2.html
 load 1183431.html
 load 1221112-1.html
 load 1221112-2.html
 load 1221874-1.html
 load 1222783.xhtml
 load 1223568-1.html
 load 1223568-2.html
+load 1224230-1.html
 pref(layout.css.grid.enabled,true) load 1225118.html
+pref(layout.css.grid.enabled,true) load 1225376.html
+pref(layout.css.grid.enabled,true) load 1225592.html
 load first-letter-638937-1.html
 load first-letter-638937-2.html
 pref(dom.meta-viewport.enabled,true) test-pref(font.size.inflation.emPerLine,15) asserts(1-100) load font-inflation-762332.html # bug 762332
 load outline-on-frameset.xhtml
 load text-overflow-bug666751-1.html
 load text-overflow-bug666751-2.html
 load text-overflow-bug670564.xhtml
 load text-overflow-bug671796.xhtml
--- a/layout/generic/nsFlexContainerFrame.cpp
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -3792,18 +3792,17 @@ nsFlexContainerFrame::DoFlexLayout(nsPre
   const nscoord contentBoxCrossSize =
     ComputeCrossSize(aReflowState, aAxisTracker, sumLineCrossSizes,
                      aAvailableBSizeForContent, &isCrossSizeDefinite, aStatus);
 
   // Set up state for cross-axis alignment, at a high level (outside the
   // scope of a particular flex line)
   CrossAxisPositionTracker
     crossAxisPosnTracker(lines.getFirst(),
-                         aReflowState.mStylePosition->ComputedAlignContent(
-                           aReflowState.mStyleDisplay),
+                         aReflowState.mStylePosition->ComputedAlignContent(),
                          contentBoxCrossSize, isCrossSizeDefinite,
                          aAxisTracker);
 
   // Now that we know the cross size of each line (including
   // "align-content:stretch" adjustments, from the CrossAxisPositionTracker
   // constructor), we can create struts for any flex items with
   // "visibility: collapse" (and restart flex layout).
   if (aStruts.IsEmpty()) { // (Don't make struts if we already did)
--- a/layout/generic/nsGridContainerFrame.cpp
+++ b/layout/generic/nsGridContainerFrame.cpp
@@ -266,43 +266,44 @@ struct MOZ_STACK_CLASS nsGridContainerFr
    */
   bool ResolveIntrinsicSizeStep1(GridReflowState&            aState,
                                  const TrackSizingFunctions& aFunctions,
                                  nscoord                     aPercentageBasis,
                                  IntrinsicISizeType          aConstraint,
                                  const LineRange&            aRange,
                                  nsIFrame*                   aGridItem);
   /**
-   * Collect the tracks which are growable (matching aSelector) and return
-   * aAvailableSpace minus the sum of mBase's in aPlan for the tracks
-   * in aRange, or 0 if this subtraction goes below 0.
+   * Collect the tracks which are growable (matching aSelector) into
+   * aGrowableTracks, and return the amount of space that can be used
+   * to grow those tracks.  Specifically, we return aAvailableSpace minus
+   * the sum of mBase's in aPlan (clamped to 0) for the tracks in aRange,
+   * or zero when there are no growable tracks.
    * @note aPlan[*].mBase represents a planned new base or limit.
    */
   static nscoord CollectGrowable(nscoord                    aAvailableSpace,
                                  const nsTArray<TrackSize>& aPlan,
                                  const LineRange&           aRange,
                                  TrackSize::StateBits       aSelector,
                                  nsTArray<uint32_t>&        aGrowableTracks)
   {
     MOZ_ASSERT(aAvailableSpace > 0, "why call me?");
     nscoord space = aAvailableSpace;
     const uint32_t start = aRange.mStart;
     const uint32_t end = aRange.mEnd;
     for (uint32_t i = start; i < end; ++i) {
       const TrackSize& sz = aPlan[i];
-      MOZ_ASSERT(!sz.IsFrozen());
       space -= sz.mBase;
       if (space <= 0) {
         return 0;
       }
-      if (sz.mState & aSelector) {
+      if ((sz.mState & aSelector) && !sz.IsFrozen()) {
         aGrowableTracks.AppendElement(i);
       }
     }
-    return space;
+    return aGrowableTracks.IsEmpty() ? 0 : space;
   }
 
   void SetupGrowthPlan(nsTArray<TrackSize>&      aPlan,
                        const nsTArray<uint32_t>& aTracks) const
   {
     for (uint32_t track : aTracks) {
       aPlan[track] = mSizes[track];
     }
@@ -1015,20 +1016,20 @@ static Maybe<LogicalAxis>
 AlignSelf(uint8_t aAlignSelf, const LogicalRect& aCB, const WritingMode aCBWM,
           const nsHTMLReflowState& aRS, const LogicalSize& aSize,
           LogicalSize* aContentSize, LogicalPoint* aPos)
 {
   Maybe<LogicalAxis> resizedAxis;
   auto alignSelf = aAlignSelf;
   bool overflowSafe = alignSelf & NS_STYLE_ALIGN_SAFE;
   alignSelf &= ~NS_STYLE_ALIGN_FLAG_BITS;
-  MOZ_ASSERT(alignSelf != NS_STYLE_ALIGN_LEFT &&
-             alignSelf != NS_STYLE_ALIGN_RIGHT,
-             "Grid's 'align-self' axis is never parallel to the container's "
-             "inline axis, so these should've computed to 'start' already");
+  // Grid's 'align-self' axis is never parallel to the container's inline axis.
+  if (alignSelf == NS_STYLE_ALIGN_LEFT || alignSelf == NS_STYLE_ALIGN_RIGHT) {
+    alignSelf = NS_STYLE_ALIGN_START;
+  }
   if (MOZ_UNLIKELY(alignSelf == NS_STYLE_ALIGN_AUTO)) {
     // Happens in rare edge cases when 'position' was ignored by the frame
     // constructor (and the style system computed 'auto' based on 'position').
     alignSelf = NS_STYLE_ALIGN_STRETCH;
   }
   WritingMode childWM = aRS.GetWritingMode();
   bool isOrthogonal = aCBWM.IsOrthogonalTo(childWM);
   // |sameSide| is true if the container's start side in this axis is the same
@@ -1092,19 +1093,20 @@ GetAlignJustifyValue(uint16_t aAlignment
 {
   *aOverflowSafe = aAlignment & NS_STYLE_ALIGN_SAFE;
   aAlignment &= (NS_STYLE_ALIGN_ALL_BITS & ~NS_STYLE_ALIGN_FLAG_BITS);
 
   // Map some alignment values to 'start' / 'end'.
   switch (aAlignment) {
     case NS_STYLE_ALIGN_LEFT:
     case NS_STYLE_ALIGN_RIGHT: {
-      MOZ_ASSERT(!aIsAlign, "Grid container's 'align-contents' axis is never "
-                 "parallel to its inline axis, so these should've computed to "
-                 "'start' already");
+      if (aIsAlign) {
+        // Grid's 'align-content' axis is never parallel to the inline axis.
+        return NS_STYLE_ALIGN_START;
+      }
       bool isStart = aWM.IsBidiLTR() == (aAlignment == NS_STYLE_ALIGN_LEFT);
       return isStart ? NS_STYLE_ALIGN_START : NS_STYLE_ALIGN_END;
     }
     case NS_STYLE_ALIGN_FLEX_START: // same as 'start' for Grid
       return NS_STYLE_ALIGN_START;
     case NS_STYLE_ALIGN_FLEX_END: // same as 'end' for Grid
       return NS_STYLE_ALIGN_END;
   }
@@ -1921,38 +1923,41 @@ nsGridContainerFrame::Tracks::Initialize
     percentageBasis = 0;
   }
 
   const uint32_t explicitGridOffset = aFunctions.mExplicitGridOffset;
   MOZ_ASSERT(mSizes.Length() >=
                explicitGridOffset + aFunctions.mMinSizingFunctions.Length());
   MOZ_ASSERT(aFunctions.mMinSizingFunctions.Length() ==
                aFunctions.mMaxSizingFunctions.Length());
+  // First we initialize the implicit tracks before the explicit grid starts.
   uint32_t i = 0;
-  for (; i < explicitGridOffset; ++i) {
+  uint32_t sentinel = std::min<uint32_t>(explicitGridOffset, mSizes.Length());
+  for (; i < sentinel; ++i) {
     mSizes[i].Initialize(percentageBasis,
                          aFunctions.mAutoMinSizing,
                          aFunctions.mAutoMaxSizing);
   }
-  uint32_t j = 0;
-  for (uint32_t len = aFunctions.mMinSizingFunctions.Length(); j < len; ++j) {
-    mSizes[i + j].Initialize(percentageBasis,
-                             aFunctions.mMinSizingFunctions[j],
-                             aFunctions.mMaxSizingFunctions[j]);
+  // Now initialize the explicit grid tracks.
+  sentinel = std::min<uint32_t>(i + aFunctions.mMinSizingFunctions.Length(),
+                                mSizes.Length());
+  for (uint32_t j = 0; i < sentinel; ++i, ++j) {
+    mSizes[i].Initialize(percentageBasis,
+                         aFunctions.mMinSizingFunctions[j],
+                         aFunctions.mMaxSizingFunctions[j]);
   }
-  i += j;
-  for (; i < mSizes.Length(); ++i) {
+  // Finally, initialize the implicit tracks that comes after the explicit grid.
+  sentinel = mSizes.Length();
+  for (; i < sentinel; ++i) {
     mSizes[i].Initialize(percentageBasis,
                          aFunctions.mAutoMinSizing,
                          aFunctions.mAutoMaxSizing);
   }
 
   mGridGap = aGridGap;
-  // XXX allow negative values? pending outcome of www-style thread:
-  // https://lists.w3.org/Archives/Public/www-style/2015Oct/0028.html
   MOZ_ASSERT(mGridGap >= nscoord(0), "negative grid gap");
 }
 
 /**
  * Return the [min|max]-content contribution of aChild to its parent (i.e.
  * the child's margin-box) in aAxis.
  */
 static nscoord
@@ -2595,17 +2600,17 @@ nsGridContainerFrame::Tracks::AlignJusti
 {
   if (mSizes.IsEmpty()) {
     return;
   }
 
   const bool isAlign = mAxis == eLogicalAxisBlock;
   auto stylePos = aReflowState.mStylePosition;
   const auto valueAndFallback = isAlign ?
-    stylePos->ComputedAlignContent(aReflowState.mStyleDisplay) :
+    stylePos->ComputedAlignContent() :
     stylePos->ComputedJustifyContent(aReflowState.mStyleDisplay);
   WritingMode wm = aReflowState.GetWritingMode();
   bool overflowSafe;
   auto alignment = ::GetAlignJustifyValue(valueAndFallback, wm, isAlign,
                                           &overflowSafe);
   if (alignment == NS_STYLE_ALIGN_AUTO) {
     alignment = NS_STYLE_ALIGN_START;
   }
@@ -2975,16 +2980,17 @@ nsGridContainerFrame::Reflow(nsPresConte
                       LogicalSize(wm, computedISize, computedBSize),
                       nsLayoutUtils::PREF_ISIZE);
 
   nscoord bSize = 0;
   if (computedBSize == NS_AUTOHEIGHT) {
     for (uint32_t i = 0; i < mGridRowEnd; ++i) {
       bSize += gridReflowState.mRows.mSizes[i].mBase;
     }
+    bSize += gridReflowState.mRows.SumOfGridGaps();
   } else {
     bSize = computedBSize;
   }
   bSize = std::max(bSize - GetConsumedBSize(), 0);
   LogicalSize desiredSize(wm, computedISize + bp.IStartEnd(wm),
                           bSize + bp.BStartEnd(wm));
   aDesiredSize.SetSize(wm, desiredSize);
   aDesiredSize.SetOverflowAreasToDesiredBounds();
--- a/layout/generic/nsLineLayout.cpp
+++ b/layout/generic/nsLineLayout.cpp
@@ -1671,16 +1671,78 @@ nsLineLayout::PlaceTopBottomFrames(PerSp
     }
     if (span) {
       nscoord fromStart = aDistanceFromStart + pfd->mBounds.BStart(lineWM);
       PlaceTopBottomFrames(span, fromStart, aLineBSize);
     }
   }
 }
 
+void
+nsLineLayout::AdjustLeadings(nsIFrame* spanFrame, PerSpanData* psd,
+                             const nsStyleText* aStyleText,
+                             nsFontMetrics* aFontMetrics,
+                             bool* aZeroEffectiveSpanBox)
+{
+  MOZ_ASSERT(spanFrame == psd->mFrame->mFrame);
+  nscoord requiredStartLeading = 0;
+  nscoord requiredEndLeading = 0;
+  if (spanFrame->GetType() == nsGkAtoms::rubyFrame) {
+    // We may need to extend leadings here for ruby annotations as
+    // required by section Line Spacing in the CSS Ruby spec.
+    // See http://dev.w3.org/csswg/css-ruby/#line-height
+    auto rubyFrame = static_cast<nsRubyFrame*>(spanFrame);
+    nscoord startLeading, endLeading;
+    rubyFrame->GetBlockLeadings(startLeading, endLeading);
+    requiredStartLeading += startLeading;
+    requiredEndLeading += endLeading;
+  }
+  if (aStyleText->HasTextEmphasis()) {
+    // Emphasis marks are symbols rendered using the same font settings
+    // as the element with its size scaled down to 50%, so we add half
+    // height of the font metrics to the specified side as leading.
+    nscoord halfHeight = aFontMetrics->MaxHeight() / 2;
+    LogicalSide side = aStyleText->TextEmphasisSide(mRootSpan->mWritingMode);
+    if (side == eLogicalSideBStart) {
+      requiredStartLeading += halfHeight;
+    } else {
+      MOZ_ASSERT(side == eLogicalSideBEnd,
+                 "emphasis marks must be in block axis");
+      requiredEndLeading += halfHeight;
+    }
+  }
+
+  nscoord requiredLeading = requiredStartLeading + requiredEndLeading;
+  // If we do not require any additional leadings, don't touch anything
+  // here even if it is greater than the original leading, because the
+  // latter could be negative.
+  if (requiredLeading != 0) {
+    nscoord leading = psd->mBStartLeading + psd->mBEndLeading;
+    nscoord deltaLeading = requiredLeading - leading;
+    if (deltaLeading > 0) {
+      // If the total leading is not wide enough for ruby annotations
+      // and/or emphasis marks, extend the side which is not enough. If
+      // both sides are not wide enough, replace the leadings with the
+      // requested values.
+      if (requiredStartLeading < psd->mBStartLeading) {
+        psd->mBEndLeading += deltaLeading;
+      } else if (requiredEndLeading < psd->mBEndLeading) {
+        psd->mBStartLeading += deltaLeading;
+      } else {
+        psd->mBStartLeading = requiredStartLeading;
+        psd->mBEndLeading = requiredEndLeading;
+      }
+      psd->mLogicalBSize += deltaLeading;
+      // We have adjusted the leadings, it is no longer a zero
+      // effective span box.
+      *aZeroEffectiveSpanBox = false;
+    }
+  }
+}
+
 static float
 GetInflationForBlockDirAlignment(nsIFrame* aFrame,
                                  nscoord aInflationMinFontSize)
 {
   if (aFrame->IsSVGText()) {
     const nsIFrame* container =
       nsLayoutUtils::GetClosestFrameOfType(aFrame, nsGkAtoms::svgTextFrame);
     NS_ASSERTION(container, "expected to find an ancestor SVGTextFrame");
@@ -1842,52 +1904,28 @@ nsLineLayout::VerticalAlignFrames(PerSpa
       CalcLineHeight(spanFrame->GetContent(), spanFrame->StyleContext(),
                      mBlockReflowState->ComputedHeight(),
                      inflation);
     nscoord contentBSize = spanFramePFD->mBounds.BSize(lineWM) -
       spanFramePFD->mBorderPadding.BStartEnd(lineWM);
 
     // Special-case for a ::first-letter frame, set the line height to
     // the frame block size if the user has left line-height == normal
+    const nsStyleText* styleText = spanFrame->StyleText();
     if (spanFramePFD->mIsLetterFrame &&
         !spanFrame->GetPrevInFlow() &&
-        spanFrame->StyleText()->mLineHeight.GetUnit() == eStyleUnit_Normal) {
+        styleText->mLineHeight.GetUnit() == eStyleUnit_Normal) {
       logicalBSize = spanFramePFD->mBounds.BSize(lineWM);
     }
 
     nscoord leading = logicalBSize - contentBSize;
     psd->mBStartLeading = leading / 2;
     psd->mBEndLeading = leading - psd->mBStartLeading;
     psd->mLogicalBSize = logicalBSize;
-    if (spanFrame->GetType() == nsGkAtoms::rubyFrame) {
-      // We may need to extend leadings here for ruby annotations as
-      // required by section Line Spacing in the CSS Ruby spec.
-      // See http://dev.w3.org/csswg/css-ruby/#line-height
-      auto rubyFrame = static_cast<nsRubyFrame*>(spanFrame);
-      nscoord startLeading, endLeading;
-      rubyFrame->GetBlockLeadings(startLeading, endLeading);
-      nscoord deltaLeading = startLeading + endLeading - leading;
-      if (deltaLeading > 0) {
-        // If the total leading is not wide enough for ruby annotations,
-        // extend the side which is not enough. If both sides are not
-        // wide enough, replace the leadings with the requested values.
-        if (startLeading < psd->mBStartLeading) {
-          psd->mBEndLeading += deltaLeading;
-        } else if (endLeading < psd->mBEndLeading) {
-          psd->mBStartLeading += deltaLeading;
-        } else {
-          psd->mBStartLeading = startLeading;
-          psd->mBEndLeading = endLeading;
-        }
-        psd->mLogicalBSize += deltaLeading;
-        // We have adjusted the leadings, it is no longer a zero
-        // effective span box.
-        zeroEffectiveSpanBox = false;
-      }
-    }
+    AdjustLeadings(spanFrame, psd, styleText, fm, &zeroEffectiveSpanBox);
 
     if (zeroEffectiveSpanBox) {
       // When the span-box is to be ignored, zero out the initial
       // values so that the span doesn't impact the final line
       // height. The contents of the span can impact the final line
       // height.
 
       // Note that things are readjusted for this span after its children
@@ -3280,18 +3318,21 @@ nsLineLayout::RelativePositionFrames(Per
       RelativePositionFrames(pfd->mSpan, r);
     } else {
       r = pfd->mOverflowAreas;
       if (pfd->mIsTextFrame) {
         // We need to recompute overflow areas in two cases:
         // (1) When PFD_RECOMPUTEOVERFLOW is set due to trimming
         // (2) When there are text decorations, since we can't recompute the
         //     overflow area until Reflow and VerticalAlignLine have finished
+        // (3) When there are text emphasis marks, since the marks may be
+        //     put further away if the text is inside ruby.
         if (pfd->mRecomputeOverflow ||
-            frame->StyleContext()->HasTextDecorationLines()) {
+            frame->StyleContext()->HasTextDecorationLines() ||
+            frame->StyleText()->HasTextEmphasis()) {
           nsTextFrame* f = static_cast<nsTextFrame*>(frame);
           r = f->RecomputeOverflow(mBlockReflowState->frame);
         }
         frame->FinishAndStoreOverflow(r, frame->GetSize());
       }
 
       // If we have something that's not an inline but with a complex frame
       // hierarchy inside that contains views, they need to be
--- a/layout/generic/nsLineLayout.h
+++ b/layout/generic/nsLineLayout.h
@@ -657,16 +657,21 @@ protected:
                        bool aCanRollBackBeforeFrame,
                        nsHTMLReflowMetrics& aMetrics,
                        nsReflowStatus& aStatus,
                        bool* aOptionalBreakAfterFits);
 
   void PlaceFrame(PerFrameData* pfd,
                   nsHTMLReflowMetrics& aMetrics);
 
+  void AdjustLeadings(nsIFrame* spanFrame, PerSpanData* psd,
+                      const nsStyleText* aStyleText,
+                      nsFontMetrics* aFontMetrics,
+                      bool* aZeroEffectiveSpanBox);
+
   void VerticalAlignFrames(PerSpanData* psd);
 
   void PlaceTopBottomFrames(PerSpanData* psd,
                             nscoord aDistanceFromStart,
                             nscoord aLineBSize);
 
   void ApplyRelativePositioning(PerFrameData* aPFD);
 
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -12,20 +12,20 @@
 #include "gfxUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/BinarySearch.h"
+#include "mozilla/IntegerRange.h"
 
 #include "nsCOMPtr.h"
 #include "nsBlockFrame.h"
-#include "nsCRT.h"
 #include "nsFontMetrics.h"
 #include "nsSplittableFrame.h"
 #include "nsLineLayout.h"
 #include "nsString.h"
 #include "nsUnicharUtils.h"
 #include "nsPresContext.h"
 #include "nsIContent.h"
 #include "nsStyleConsts.h"
@@ -46,16 +46,17 @@
 #include "nsFrame.h"
 #include "nsIMathMLFrame.h"
 #include "nsPlaceholderFrame.h"
 #include "nsTextFrameUtils.h"
 #include "nsTextRunTransformations.h"
 #include "MathMLTextRunFactory.h"
 #include "nsExpirationTracker.h"
 #include "nsUnicodeProperties.h"
+#include "nsStyleUtil.h"
 
 #include "nsTextFragment.h"
 #include "nsGkAtoms.h"
 #include "nsFrameSelection.h"
 #include "nsRange.h"
 #include "nsCSSRendering.h"
 #include "nsContentUtils.h"
 #include "nsLineBreaker.h"
@@ -921,16 +922,17 @@ public:
    * we constructed just a partial textrun to set up linebreaker and other
    * state for following textruns.
    */
   gfxTextRun* BuildTextRunForFrames(void* aTextBuffer);
   bool SetupLineBreakerContext(gfxTextRun *aTextRun);
   void AssignTextRun(gfxTextRun* aTextRun, float aInflation);
   nsTextFrame* GetNextBreakBeforeFrame(uint32_t* aIndex);
   void SetupBreakSinksForTextRun(gfxTextRun* aTextRun, const void* aTextPtr);
+  void SetupTextEmphasisForTextRun(gfxTextRun* aTextRun, const void* aTextPtr);
   struct FindBoundaryState {
     nsIFrame*    mStopAtFrame;
     nsTextFrame* mFirstTextFrame;
     nsTextFrame* mLastTextFrame;
     bool mSeenTextRunBoundaryOnLaterLine;
     bool mSeenTextRunBoundaryOnThisLine;
     bool mSeenSpaceForLineBreakingOnThisLine;
   };
@@ -1895,16 +1897,17 @@ GetCSSWhitespaceToCompressionMode(nsText
 gfxTextRun*
 BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
 {
   gfxSkipChars skipChars;
 
   const void* textPtr = aTextBuffer;
   bool anyTextTransformStyle = false;
   bool anyMathMLStyling = false;
+  bool anyTextEmphasis = false;
   uint8_t sstyScriptLevel = 0;
   uint32_t mathFlags = 0;
   uint32_t textFlags = nsTextFrameUtils::TEXT_NO_BREAKS;
 
   if (mCurrentRunContextInfo & nsTextFrameUtils::INCOMING_WHITESPACE) {
     textFlags |= nsTextFrameUtils::TEXT_INCOMING_WHITESPACE;
   }
   if (mCurrentRunContextInfo & nsTextFrameUtils::INCOMING_ARABICCHAR) {
@@ -1963,16 +1966,19 @@ BuildTextRunsScanner::BuildTextRunForFra
     nsTextFrame* f = mappedFlow->mStartFrame;
 
     lastStyleContext = f->StyleContext();
     // Detect use of text-transform or font-variant anywhere in the run
     textStyle = f->StyleText();
     if (NS_STYLE_TEXT_TRANSFORM_NONE != textStyle->mTextTransform) {
       anyTextTransformStyle = true;
     }
+    if (textStyle->HasTextEmphasis()) {
+      anyTextEmphasis = true;
+    }
     textFlags |= GetSpacingFlags(f);
     nsTextFrameUtils::CompressionMode compression =
       GetCSSWhitespaceToCompressionMode(f, textStyle);
     if ((enabledJustification || f->ShouldSuppressLineBreak()) &&
         !textStyle->WhiteSpaceIsSignificant() && !isSVG) {
       textFlags |= gfxTextRunFactory::TEXT_ENABLE_SPACING;
     }
     fontStyle = f->StyleFont();
@@ -2251,16 +2257,20 @@ BuildTextRunsScanner::BuildTextRunForFra
   }
 
   // We have to set these up after we've created the textrun, because
   // the breaks may be stored in the textrun during this very call.
   // This is a bit annoying because it requires another loop over the frames
   // making up the textrun, but I don't see a way to avoid this.
   SetupBreakSinksForTextRun(textRun, textPtr);
 
+  if (anyTextEmphasis) {
+    SetupTextEmphasisForTextRun(textRun, textPtr);
+  }
+
   if (mSkipIncompleteTextRuns) {
     mSkipIncompleteTextRuns = !TextContainsLineBreakerWhiteSpace(textPtr,
         transformedLength, mDoubleByteText);
     // Arrange for this textrun to be deleted the next time the linebreaker
     // is flushed out
     mTextRunsToDelete.AppendElement(textRun);
     // Since we're doing to destroy the user data now, avoid a dangling
     // pointer. Strictly speaking we don't need to do this since it should
@@ -2484,16 +2494,75 @@ BuildTextRunsScanner::SetupBreakSinksFor
                                 length, flags, sink);
       }
     }
     
     iter = iterNext;
   }
 }
 
+static bool
+MayCharacterHaveEmphasisMark(uint32_t aCh)
+{
+  auto category = unicode::GetGeneralCategory(aCh);
+  // Comparing an unsigned variable against zero is a compile error,
+  // so we use static assert here to ensure we really don't need to
+  // compare it with the given constant.
+  static_assert(IsUnsigned<decltype(category)>::value &&
+                HB_UNICODE_GENERAL_CATEGORY_CONTROL == 0,
+                "if this constant is not zero, or category is signed, "
+                "we need to explicitly do the comparison below");
+  return !(category <= HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED ||
+           (category >= HB_UNICODE_GENERAL_CATEGORY_LINE_SEPARATOR &&
+            category <= HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR));
+}
+
+static bool
+MayCharacterHaveEmphasisMark(uint8_t aCh)
+{
+  // 0x00~0x1f and 0x7f~0x9f are in category Cc
+  // 0x20 and 0xa0 are in category Zs
+  bool result = !(aCh <= 0x20 || (aCh >= 0x7f && aCh <= 0xa0));
+  MOZ_ASSERT(result == MayCharacterHaveEmphasisMark(uint32_t(aCh)),
+             "result for uint8_t should match result for uint32_t");
+  return result;
+}
+
+void
+BuildTextRunsScanner::SetupTextEmphasisForTextRun(gfxTextRun* aTextRun,
+                                                  const void* aTextPtr)
+{
+  if (!mDoubleByteText) {
+    auto text = reinterpret_cast<const uint8_t*>(aTextPtr);
+    for (auto i : MakeRange(aTextRun->GetLength())) {
+      if (!MayCharacterHaveEmphasisMark(text[i])) {
+        aTextRun->SetNoEmphasisMark(i);
+      }
+    }
+  } else {
+    auto text = reinterpret_cast<const char16_t*>(aTextPtr);
+    auto length = aTextRun->GetLength();
+    for (size_t i = 0; i < length; ++i) {
+      if (NS_IS_HIGH_SURROGATE(text[i]) && i + 1 < length &&
+          NS_IS_LOW_SURROGATE(text[i + 1])) {
+        uint32_t ch = SURROGATE_TO_UCS4(text[i], text[i + 1]);
+        if (!MayCharacterHaveEmphasisMark(ch)) {
+          aTextRun->SetNoEmphasisMark(i);
+          aTextRun->SetNoEmphasisMark(i + 1);
+        }
+        ++i;
+      } else {
+        if (!MayCharacterHaveEmphasisMark(uint32_t(text[i]))) {
+          aTextRun->SetNoEmphasisMark(i);
+        }
+      }
+    }
+  }
+}
+
 // Find the flow corresponding to aContent in aUserData
 static inline TextRunMappedFlow*
 FindFlowForContent(TextRunUserData* aUserData, nsIContent* aContent)
 {
   // Find the flow that contains us
   int32_t i = aUserData->mLastFlowIndex;
   int32_t delta = 1;
   int32_t sign = 1;
@@ -2836,20 +2905,18 @@ static bool IsChineseOrJapanese(nsTextFr
     // be expanded properly even when surrounded by other language.
     return true;
   }
 
   nsIAtom* language = aFrame->StyleFont()->mLanguage;
   if (!language) {
     return false;
   }
-  const char16_t *lang = language->GetUTF16String();
-  return (!nsCRT::strncmp(lang, MOZ_UTF16("ja"), 2) ||
-          !nsCRT::strncmp(lang, MOZ_UTF16("zh"), 2)) &&
-         (language->GetLength() == 2 || lang[2] == '-');
+  return nsStyleUtil::MatchesLanguagePrefix(language, MOZ_UTF16("ja")) ||
+         nsStyleUtil::MatchesLanguagePrefix(language, MOZ_UTF16("zh"));
 }
 
 #ifdef DEBUG
 static bool IsInBounds(const gfxSkipCharsIterator& aStart, int32_t aContentLength,
                          uint32_t aOffset, uint32_t aLength) {
   if (aStart.GetSkippedOffset() > aOffset)
     return false;
   if (aContentLength == INT32_MAX)
@@ -5042,30 +5109,113 @@ GetInflationForTextDecorations(nsIFrame*
     }
     NS_ASSERTION(container, "expected to find an ancestor SVGTextFrame");
     return
       static_cast<const SVGTextFrame*>(container)->GetFontSizeScaleFactor();
   }
   return nsLayoutUtils::FontSizeInflationInner(aFrame, aInflationMinFontSize);
 }
 
+static already_AddRefed<nsFontMetrics>
+GetFontMetricsOfEmphasisMarks(nsStyleContext* aStyleContext, float aInflation)
+{
+  nsPresContext* pc = aStyleContext->PresContext();
+  WritingMode wm(aStyleContext);
+  gfxFont::Orientation orientation = wm.IsVertical() && !wm.IsSideways() ?
+                                     gfxFont::eVertical : gfxFont::eHorizontal;
+
+  const nsStyleFont* styleFont = aStyleContext->StyleFont();
+  nsFont font = styleFont->mFont;
+  font.size = NSToCoordRound(font.size * aInflation * 0.5f);
+
+  RefPtr<nsFontMetrics> fm;
+  pc->DeviceContext()->GetMetricsFor(font, styleFont->mLanguage,
+                                     styleFont->mExplicitLanguage,
+                                     orientation, pc->GetUserFontSet(),
+                                     pc->GetTextPerfMetrics(),
+                                     *getter_AddRefs(fm));
+  return fm.forget();
+}
+
+static gfxTextRun*
+GenerateTextRunForEmphasisMarks(nsTextFrame* aFrame, nsFontMetrics* aFontMetrics,
+                                WritingMode aWM, const nsStyleText* aStyleText)
+{
+  const nsString& emphasisString = aStyleText->mTextEmphasisStyleString;
+  RefPtr<gfxContext> ctx = CreateReferenceThebesContext(aFrame);
+  auto appUnitsPerDevUnit = aFrame->PresContext()->AppUnitsPerDevPixel();
+  uint32_t flags = nsLayoutUtils::
+    GetTextRunOrientFlagsForStyle(aFrame->StyleContext());
+  if (flags == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_MIXED) {
+    // The emphasis marks should always be rendered upright per spec.
+    flags = gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT;
+  }
+  return aFontMetrics->GetThebesFontGroup()->
+    MakeTextRun<char16_t>(emphasisString.get(), emphasisString.Length(),
+                          ctx, appUnitsPerDevUnit, flags, nullptr);
+}
+
+nsRect
+nsTextFrame::UpdateTextEmphasis(WritingMode aWM, PropertyProvider& aProvider)
+{
+  const nsStyleText* styleText = StyleText();
+  if (!styleText->HasTextEmphasis()) {
+    Properties().Delete(EmphasisMarkProperty());
+    return nsRect();
+  }
+
+  RefPtr<nsFontMetrics> fm =
+    GetFontMetricsOfEmphasisMarks(StyleContext(), GetFontSizeInflation());
+  EmphasisMarkInfo* info = new EmphasisMarkInfo;
+  info->textRun =
+    GenerateTextRunForEmphasisMarks(this, fm, aWM, styleText);
+  info->advance =
+    info->textRun->GetAdvanceWidth(0, info->textRun->GetLength(), nullptr);
+
+  // Calculate the baseline offset
+  LogicalSide side = styleText->TextEmphasisSide(aWM);
+  nsFontMetrics* baseFontMetrics = aProvider.GetFontMetrics();
+  LogicalSize frameSize = GetLogicalSize();
+  // The overflow rect is inflated in the inline direction by half
+  // advance of the emphasis mark on each side, so that even if a mark
+  // is drawn for a zero-width character, it won't be clipped.
+  LogicalRect overflowRect(aWM, -info->advance / 2,
+                           /* BStart to be computed below */0,
+                           frameSize.ISize(aWM) + info->advance,
+                           fm->MaxAscent() + fm->MaxDescent());
+  // When the writing mode is vertical-lr the line is inverted, and thus
+  // the ascent and descent are swapped.
+  nscoord absOffset = (side == eLogicalSideBStart) != aWM.IsLineInverted() ?
+    baseFontMetrics->MaxAscent() + fm->MaxDescent() :
+    baseFontMetrics->MaxDescent() + fm->MaxAscent();
+  // XXX emphasis marks should be drawn outside ruby, see bug 1224013.
+  if (side == eLogicalSideBStart) {
+    info->baselineOffset = -absOffset;
+    overflowRect.BStart(aWM) = -overflowRect.BSize(aWM);
+  } else {
+    MOZ_ASSERT(side == eLogicalSideBEnd);
+    info->baselineOffset = absOffset;
+    overflowRect.BStart(aWM) = frameSize.BSize(aWM);
+  }
+
+  Properties().Set(EmphasisMarkProperty(), info);
+  return overflowRect.GetPhysicalRect(aWM, frameSize.GetPhysicalSize(aWM));
+}
+
 void
 nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext,
                                      nsIFrame* aBlock,
                                      PropertyProvider& aProvider,
                                      nsRect* aVisualOverflowRect,
                                      bool aIncludeTextDecorations)
 {
-  // Text-shadow overflows
-  nsRect shadowRect =
-    nsLayoutUtils::GetTextShadowRectsUnion(*aVisualOverflowRect, this);
-  aVisualOverflowRect->UnionRect(*aVisualOverflowRect, shadowRect);
+  const WritingMode wm = GetWritingMode();
   bool verticalRun = mTextRun->IsVertical();
   bool useVerticalMetrics = verticalRun && mTextRun->UseCenterBaseline();
-  bool inverted = GetWritingMode().IsLineInverted();
+  bool inverted = wm.IsLineInverted();
 
   if (IsFloatingFirstLetterChild()) {
     // The underline/overline drawable area must be contained in the overflow
     // rect when this is in floating first letter frame at *both* modes.
     // In this case, aBlock is the ::first-letter frame.
     uint8_t decorationStyle = aBlock->StyleContext()->
                                 StyleTextReset()->GetDecorationStyle();
     // If the style is none, let's include decoration line rect as solid style
@@ -5115,17 +5265,16 @@ nsTextFrame::UnionAdditionalOverflow(nsP
     if (textDecs.HasDecorationLines()) {
       nscoord inflationMinFontSize =
         nsLayoutUtils::InflationMinFontSizeFor(aBlock);
 
       const nscoord measure = verticalRun ? GetSize().height : GetSize().width;
       const gfxFloat appUnitsPerDevUnit = aPresContext->AppUnitsPerDevPixel(),
                      gfxWidth = measure / appUnitsPerDevUnit;
       gfxFloat ascent = gfxFloat(mAscent) / appUnitsPerDevUnit;
-      const WritingMode wm = GetWritingMode();
       if (wm.IsVerticalRL()) {
         ascent = -ascent;
       }
 
       nscoord topOrLeft(nscoord_MAX), bottomOrRight(nscoord_MIN);
       // Below we loop through all text decorations and compute the rectangle
       // containing all of them, in this frame's coordinate space
       for (uint32_t i = 0; i < textDecs.mUnderlines.Length(); ++i) {
@@ -5225,17 +5374,26 @@ nsTextFrame::UnionAdditionalOverflow(nsP
         }
       }
 
       aVisualOverflowRect->UnionRect(
         *aVisualOverflowRect,
         verticalRun ? nsRect(topOrLeft, 0, bottomOrRight - topOrLeft, measure)
                     : nsRect(0, topOrLeft, measure, bottomOrRight - topOrLeft));
     }
-  }
+
+    aVisualOverflowRect->UnionRect(*aVisualOverflowRect,
+                                   UpdateTextEmphasis(wm, aProvider));
+  }
+
+  // Text-shadow overflows
+  nsRect shadowRect =
+    nsLayoutUtils::GetTextShadowRectsUnion(*aVisualOverflowRect, this);
+  aVisualOverflowRect->UnionRect(*aVisualOverflowRect, shadowRect);
+
   // When this frame is not selected, the text-decoration area must be in
   // frame bounds.
   if (!IsSelected() ||
       !CombineSelectionUnderlineRect(aPresContext, *aVisualOverflowRect))
     return;
   AddStateBits(TEXT_SELECTION_UNDERLINE_OVERFLOWED);
 }
 
@@ -6031,16 +6189,46 @@ nsTextFrame::PaintTextWithSelection(gfxC
                                     aCallbacks);
     }
   }
 
   DestroySelectionDetails(details);
   return true;
 }
 
+void
+nsTextFrame::DrawEmphasisMarks(gfxContext* aContext, WritingMode aWM,
+                               const gfxPoint& aTextBaselinePt,
+                               uint32_t aOffset, uint32_t aLength,
+                               PropertyProvider& aProvider)
+{
+  auto info = static_cast<const EmphasisMarkInfo*>(
+    Properties().Get(EmphasisMarkProperty()));
+  if (!info) {
+    MOZ_ASSERT(!StyleText()->HasTextEmphasis());
+    return;
+  }
+
+  nscolor color = nsLayoutUtils::
+    GetColor(this, eCSSProperty_text_emphasis_color);
+  aContext->SetColor(Color::FromABGR(color));
+  gfxPoint pt(aTextBaselinePt);
+  if (!aWM.IsVertical()) {
+    pt.y += info->baselineOffset;
+  } else {
+    if (aWM.IsVerticalRL()) {
+      pt.x -= info->baselineOffset;
+    } else {
+      pt.x += info->baselineOffset;
+    }
+  }
+  mTextRun->DrawEmphasisMarks(aContext, info->textRun, info->advance,
+                              pt, aOffset, aLength, &aProvider);
+}
+
 nscolor
 nsTextFrame::GetCaretColorAt(int32_t aOffset)
 {
   NS_PRECONDITION(aOffset >= 0, "aOffset must be positive");
 
   nscolor result = nsFrame::GetCaretColorAt(aOffset);
   gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
   PropertyProvider provider(this, iter, nsTextFrame::eInflated);
@@ -6533,16 +6721,19 @@ nsTextFrame::DrawTextRunAndDecorations(
         eNormalDecoration, aCallbacks, verticalRun);
     }
 
     // CSS 2.1 mandates that text be painted after over/underlines, and *then*
     // line-throughs
     DrawTextRun(aCtx, aTextBaselinePt, aOffset, aLength, aProvider, aTextColor,
                 aAdvanceWidth, aDrawSoftHyphen, aContextPaint, aCallbacks);
 
+    // Emphasis marks
+    DrawEmphasisMarks(aCtx, wm, aTextBaselinePt, aOffset, aLength, aProvider);
+
     // Line-throughs
     for (uint32_t i = aDecorations.mStrikes.Length(); i-- > 0; ) {
       const LineDecoration& dec = aDecorations.mStrikes[i];
       if (dec.mStyle == NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
         continue;
       }
 
       float inflation =
@@ -6579,17 +6770,18 @@ nsTextFrame::DrawText(
 {
   TextDecorations decorations;
   GetTextDecorations(aTextStyle.PresContext(),
                      aCallbacks ? eUnresolvedColors : eResolvedColors,
                      decorations);
 
   // Hide text decorations if we're currently hiding @font-face fallback text
   const bool drawDecorations = !aProvider.GetFontGroup()->ShouldSkipDrawing() &&
-                               decorations.HasDecorationLines();
+                               (decorations.HasDecorationLines() ||
+                                StyleText()->HasTextEmphasis());
   if (drawDecorations) {
     DrawTextRunAndDecorations(aCtx, aDirtyRect, aFramePt, aTextBaselinePt, aOffset, aLength,
                               aProvider, aTextStyle, aTextColor, aClipEdges, aAdvanceWidth,
                               aDrawSoftHyphen, decorations,
                               aDecorationOverrideColor, aContextPaint, aCallbacks);
   } else {
     DrawTextRun(aCtx, aTextBaselinePt, aOffset, aLength, aProvider,
                 aTextColor, aAdvanceWidth, aDrawSoftHyphen, aContextPaint, aCallbacks);
--- a/layout/generic/nsTextFrame.h
+++ b/layout/generic/nsTextFrame.h
@@ -435,16 +435,22 @@ public:
                                      PropertyProvider& aProvider,
                                      uint32_t aContentOffset,
                                      uint32_t aContentLength,
                                      nsTextPaintStyle& aTextPaintStyle,
                                      SelectionDetails* aDetails,
                                      SelectionType aSelectionType,
                                      DrawPathCallbacks* aCallbacks);
 
+  void DrawEmphasisMarks(gfxContext* aContext,
+                         mozilla::WritingMode aWM,
+                         const gfxPoint& aTextBaselinePt,
+                         uint32_t aOffset, uint32_t aLength,
+                         PropertyProvider& aProvider);
+
   virtual nscolor GetCaretColorAt(int32_t aOffset) override;
 
   int16_t GetSelectionStatus(int16_t* aSelectionFlags);
 
   int32_t GetContentOffset() const { return mContentOffset; }
   int32_t GetContentLength() const
   {
     NS_ASSERTION(GetContentEnd() - mContentOffset >= 0, "negative length");
@@ -580,16 +586,21 @@ protected:
   SelectionDetails* GetSelectionDetails();
 
   void UnionAdditionalOverflow(nsPresContext* aPresContext,
                                nsIFrame* aBlock,
                                PropertyProvider& aProvider,
                                nsRect* aVisualOverflowRect,
                                bool aIncludeTextDecorations);
 
+  // Update information of emphasis marks, and return the visial
+  // overflow rect of the emphasis marks.
+  nsRect UpdateTextEmphasis(mozilla::WritingMode aWM,
+                            PropertyProvider& aProvider);
+
   void PaintOneShadow(uint32_t aOffset,
                       uint32_t aLength,
                       nsCSSShadowItem* aShadowDetails,
                       PropertyProvider* aProvider,
                       const nsRect& aDirtyRect,
                       const gfxPoint& aFramePt,
                       const gfxPoint& aTextBaselinePt,
                       gfxContext* aCtx,
@@ -807,11 +818,19 @@ protected:
 
   void ClearFrameOffsetCache();
 
   virtual bool HasAnyNoncollapsedCharacters() override;
 
   void ClearMetrics(nsHTMLReflowMetrics& aMetrics);
 
   NS_DECLARE_FRAME_PROPERTY(JustificationAssignment, nullptr)
+
+  struct EmphasisMarkInfo
+  {
+    nsAutoPtr<gfxTextRun> textRun;
+    gfxFloat advance;
+    gfxFloat baselineOffset;
+  };
+  NS_DECLARE_FRAME_PROPERTY(EmphasisMarkProperty, DeleteValue<EmphasisMarkInfo>)
 };
 
 #endif
new file mode 100644
--- /dev/null
+++ b/layout/reftests/async-scrolling/bg-fixed-in-opacity-ref.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html reftest-async-scroll >
+<style>
+
+body {
+  margin: 0px;
+  position: relative;
+  height: 1200.23px;
+  overflow: hidden;
+}
+
+.fixed-background {
+  position: absolute;
+  top: 0px;
+  bottom: 0px;
+  width: 100px;
+  background: linear-gradient(blue, blue) no-repeat fixed 0px 200px;
+  background-size: 100px 100px;
+  opacity: 0.5;
+}
+
+</style>
+<div style="overflow: hidden;" class="fixed-background"></div>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/async-scrolling/bg-fixed-in-opacity.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html reftest-async-scroll
+      reftest-displayport-x="0" reftest-displayport-y="0"
+      reftest-displayport-w="800" reftest-displayport-h="2000"
+      reftest-async-scroll-x="0" reftest-async-scroll-y="50.23">
+<style>
+
+body {
+  margin: 0px;
+  position: relative;
+  height: 1200.23px;
+  overflow: hidden;
+}
+
+.fixed-background {
+  position: absolute;
+  top: 0px;
+  bottom: 0px;
+  width: 100px;
+  background: linear-gradient(blue, blue) no-repeat fixed 0px 200px;
+  background-size: 100px 100px;
+  opacity: 0.5;
+}
+
+</style>
+<div style="overflow: hidden;" class="fixed-background"></div>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/async-scrolling/group-opacity-surface-size-1-ref.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<style>
+
+body {
+  margin: 0;
+  overflow: hidden;
+}
+
+.opacity {
+  opacity: 0.8;
+}
+
+.box {
+  left: 0;
+  top: 200px;
+  width: 200px;
+  height: 200px;
+}
+
+.absolute {
+  position: absolute;
+  background: green;
+  top: 100px;
+}
+
+.fixed {
+  position: absolute;
+  background: blue;
+}
+
+</style>
+
+<div class="opacity">
+  <div class="box absolute"></div>
+  <div class="box fixed"></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/async-scrolling/group-opacity-surface-size-1.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html reftest-async-scroll
+      reftest-displayport-x="0" reftest-displayport-y="0"
+      reftest-displayport-w="800" reftest-displayport-h="2000"
+      reftest-async-scroll-x="0" reftest-async-scroll-y="100">
+<style>
+
+body {
+  margin: 0;
+  overflow: hidden;
+  height: 4000px;
+}
+
+.opacity {
+  opacity: 0.8;
+}
+
+.box {
+  left: 0;
+  top: 200px;
+  width: 200px;
+  height: 200px;
+}
+
+.absolute {
+  position: absolute;
+  background: green;
+}
+
+.fixed {
+  position: fixed;
+  background: blue;
+}
+
+</style>
+
+<div class="opacity">
+  <div class="box absolute"></div>
+  <div class="box fixed"></div>
+</div>
--- a/layout/reftests/async-scrolling/reftest.list
+++ b/layout/reftests/async-scrolling/reftest.list
@@ -1,16 +1,17 @@
 skip-if(!asyncPan) == bg-fixed-1.html bg-fixed-1-ref.html
 skip-if(!asyncPan) == bg-fixed-cover-1.html bg-fixed-cover-1-ref.html
 skip-if(!asyncPan) == bg-fixed-cover-2.html bg-fixed-cover-2-ref.html
 skip-if(!asyncPan) == bg-fixed-cover-3.html bg-fixed-cover-3-ref.html
 skip-if(!asyncPan) == bg-fixed-child.html bg-fixed-child-ref.html
 skip-if(!asyncPan) == bg-fixed-child-clip-1.html bg-fixed-child-clip-ref.html
 skip-if(!asyncPan) == bg-fixed-child-clip-2.html bg-fixed-child-clip-ref.html
 fuzzy(1,246) skip-if(!asyncPan) == bg-fixed-child-mask.html bg-fixed-child-mask-ref.html