merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 30 Nov 2015 13:19:02 +0100
changeset 274677 a18630f9ab42ddfde03ba8c7757a42069c48c7ed
parent 274676 2d385f1302a2a86dc22f6d10dad2ac15b03d5f3d (current diff)
parent 274549 b181b44bfeeba4e1115d8eedebbfe267226d9b65 (diff)
child 274678 5a8a532f97cd82b06b2f45515b633958bda50c8e
child 274774 21f92638bb1464a42955ed0a9278685fd35e2ebc
child 274786 620f5d248dabda25f0c118451e304a077cbb3311
push id68637
push usercbook@mozilla.com
push dateMon, 30 Nov 2015 12:45:36 +0000
treeherdermozilla-inbound@5a8a532f97cd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone45.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge 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>