merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 03 Jun 2016 12:00:06 +0200
changeset 341335 e27fe24a746fa839f1cabe198faf1bad42c7dc4b
parent 341121 718e392bad427b53b782741e1e7999c29db45746 (current diff)
parent 341334 8b89d98ce322f0c68538db7837cd7eb749d1ed1c (diff)
child 341336 f3740cfc4b04fb61a1da306df4b1c2437f6bfb65
child 341353 0ef7cc6b42c72be6ef8d0786fb1f5371c11e9e32
child 341461 ae8e12ba7142c85d035a577fb0cfc9463a390097
push id1183
push userraliiev@mozilla.com
push dateMon, 05 Sep 2016 20:01:49 +0000
treeherdermozilla-release@3148731bed45 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone49.0a1
first release with
nightly linux32
e27fe24a746f / 49.0a1 / 20160603030242 / files
nightly linux64
e27fe24a746f / 49.0a1 / 20160603030242 / files
nightly mac
e27fe24a746f / 49.0a1 / 20160603030242 / files
nightly win32
e27fe24a746f / 49.0a1 / 20160603030242 / files
nightly win64
e27fe24a746f / 49.0a1 / 20160603030242 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
docshell/base/LoadContext.cpp
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
dom/base/Navigator.cpp
dom/ipc/TabParent.cpp
mobile/android/base/java/org/mozilla/gecko/NetworkInfoService.java
mobile/android/base/moz.build
netwerk/base/NetworkInfoServiceAndroid.jsm
netwerk/base/moz.build
netwerk/base/nsNetworkInfoService.js
netwerk/base/nsNetworkInfoService.manifest
security/nss/external_tests/der_gtest/der_gtest.cc
security/nss/external_tests/pk11_gtest/pk11_gtest.cc
security/nss/external_tests/util_gtest/util_gtest.cc
security/nss/tests/der_gtests/der_gtests.sh
security/nss/tests/pk11_gtests/pk11_gtests.sh
security/nss/tests/util_gtests/util_gtests.sh
testing/talos/treeherder-schemas/performance-artifact.json
testing/taskcluster/tasks/branches/base_job_flags.yml.orig
--- a/.gdbinit
+++ b/.gdbinit
@@ -174,8 +174,10 @@ end
 
 def js
   call DumpJSStack()
 end
 
 def ft
   call $arg0->DumpFrameTree()
 end
+
+source .gdbinit_python
new file mode 100644
--- /dev/null
+++ b/.gdbinit_python
@@ -0,0 +1,5 @@
+python
+import sys
+sys.path.append('python/gdbpp/')
+import gdbpp
+end
--- a/.gitignore
+++ b/.gitignore
@@ -111,8 +111,11 @@ testing/eslint/node_modules/
 # the status command, so we ignore them.
 testing/talos/.Python
 testing/talos/bin/
 testing/talos/include/
 testing/talos/lib/
 testing/talos/talos/tests/tp5n.zip
 testing/talos/talos/tests/tp5n
 testing/talos/talos/tests/devtools/damp.manifest.develop
+
+# Ignore files created when running a reftest.
+lextab.py
--- a/.hgignore
+++ b/.hgignore
@@ -128,8 +128,11 @@ GPATH
 # the status command, so we ignore them.
 ^testing/talos/.Python
 ^testing/talos/bin/
 ^testing/talos/include/
 ^testing/talos/lib/
 ^testing/talos/talos/tests/tp5n.zip
 ^testing/talos/talos/tests/tp5n
 ^testing/talos/talos/tests/devtools/damp.manifest.develop
+
+# Ignore files created when running a reftest.
+^lextab.py$
--- a/browser/components/contextualidentity/test/browser/browser.ini
+++ b/browser/components/contextualidentity/test/browser/browser.ini
@@ -6,12 +6,14 @@ support-files =
   serviceworker.html
   worker.js
 
 [browser_aboutURLs.js]
 [browser_usercontext.js]
 [browser_usercontextid_tabdrop.js]
 skip-if = os == "mac" || os == "win" # Intermittent failure - bug 1268276
 [browser_windowName.js]
+tags = openwindow
 [browser_windowOpen.js]
+tags = openwindow
 [browser_serviceworkers.js]
 [browser_broadcastchannel.js]
 [browser_blobUrl.js]
--- a/browser/components/extensions/ext-history.js
+++ b/browser/components/extensions/ext-history.js
@@ -9,27 +9,46 @@ XPCOMUtils.defineLazyGetter(this, "Histo
   return PlacesUtils.history;
 });
 
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 const {
   normalizeTime,
 } = ExtensionUtils;
 
+let historySvc = Ci.nsINavHistoryService;
+const TRANSITION_TO_TRANSITION_TYPES_MAP = new Map([
+  ["link", historySvc.TRANSITION_LINK],
+  ["typed", historySvc.TRANSITION_TYPED],
+  ["auto_bookmark", historySvc.TRANSITION_BOOKMARK],
+  ["auto_subframe", historySvc.TRANSITION_EMBED],
+  ["manual_subframe", historySvc.TRANSITION_FRAMED_LINK],
+]);
+
+function getTransitionType(transition) {
+  // cannot set a default value for the transition argument as the framework sets it to null
+  transition = transition || "link";
+  let transitionType = TRANSITION_TO_TRANSITION_TYPES_MAP.get(transition);
+  if (!transitionType) {
+    throw new Error(`|${transition}| is not a supported transition for history`);
+  }
+  return transitionType;
+}
+
 /*
  * Converts a nsINavHistoryResultNode into a plain object
  *
  * https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsINavHistoryResultNode
  */
 function convertNavHistoryResultNode(node) {
   return {
     id: node.pageGuid,
     url: node.uri,
     title: node.title,
-    lastVisitTime: PlacesUtils.toTime(node.time),
+    lastVisitTime: PlacesUtils.toDate(node.time).getTime(),
     visitCount: node.accessCount,
   };
 }
 
 /*
  * Converts a nsINavHistoryContainerResultNode into an array of objects
  *
  * https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsINavHistoryContainerResultNode
@@ -43,23 +62,49 @@ function convertNavHistoryContainerResul
   }
   container.containerOpen = false;
   return results;
 }
 
 extensions.registerSchemaAPI("history", "history", (extension, context) => {
   return {
     history: {
+      addUrl: function(details) {
+        let transition, date;
+        try {
+          transition = getTransitionType(details.transition);
+        } catch (error) {
+          return Promise.reject({message: error.message});
+        }
+        if (details.visitTime) {
+          date = normalizeTime(details.visitTime);
+        }
+        let pageInfo = {
+          title: details.title,
+          url: details.url,
+          visits: [
+            {
+              transition,
+              date,
+            },
+          ],
+        };
+        try {
+          return History.insert(pageInfo).then(() => undefined);
+        } catch (error) {
+          return Promise.reject({message: error.message});
+        }
+      },
       deleteAll: function() {
         return History.clear();
       },
       deleteRange: function(filter) {
         let newFilter = {
-          beginDate: new Date(filter.startTime),
-          endDate: new Date(filter.endTime),
+          beginDate: normalizeTime(filter.startTime),
+          endDate: normalizeTime(filter.endTime),
         };
         // History.removeVisitsByFilter returns a boolean, but our API should return nothing
         return History.removeVisitsByFilter(newFilter).then(() => undefined);
       },
       deleteUrl: function(details) {
         let url = details.url;
         // History.remove returns a boolean, but our API should return nothing
         return History.remove(url).then(() => undefined);
--- a/browser/components/extensions/schemas/history.json
+++ b/browser/components/extensions/schemas/history.json
@@ -182,28 +182,42 @@
                 }
               }
             ]
           }
         ]
       },
       {
         "name": "addUrl",
-        "unsupported": true,
         "type": "function",
-        "description": "Adds a URL to the history at the current time with a $(topic:transition-types)[transition type] of \"link\".",
+        "description": "Adds a URL to the history with a default visitTime of the current time and a default $(topic:transition-types)[transition type] of \"link\".",
         "async": "callback",
         "parameters": [
           {
             "name": "details",
             "type": "object",
             "properties": {
               "url": {
                 "type": "string",
-                "description": "The URL to add."
+                "description": "The URL to add. Must be a valid URL that can be added to history."
+              },
+              "title": {
+                "type": "string",
+                "optional": true,
+                "description": "The title of the page."
+              },
+              "transition": {
+                "$ref": "TransitionType",
+                "optional": true,
+                "description": "The $(topic:transition-types)[transition type] for this visit from its referrer."
+              },
+              "visitTime": {
+                "$ref": "HistoryTime",
+                "optional": true,
+                "description": "The date when this visit occurred."
               }
             }
           },
           {
             "name": "callback",
             "type": "function",
             "optional": true,
             "parameters": []
@@ -240,22 +254,22 @@
         "description": "Removes all items within the specified date range from the history.  Pages will not be removed from the history unless all visits fall within the range.",
         "async": "callback",
         "parameters": [
           {
             "name": "range",
             "type": "object",
             "properties": {
               "startTime": {
-                "type": "number",
-                "description": "Items added to history after this date, represented in milliseconds since the epoch."
+                "$ref": "HistoryTime",
+                "description": "Items added to history after this date."
               },
               "endTime": {
-                "type": "number",
-                "description": "Items added to history before this date, represented in milliseconds since the epoch."
+                "$ref": "HistoryTime",
+                "description": "Items added to history before this date."
               }
             }
           },
           {
             "name": "callback",
             "type": "function",
             "parameters": []
           }
--- a/browser/components/extensions/test/browser/browser_ext_history.js
+++ b/browser/components/extensions/test/browser/browser_ext_history.js
@@ -1,14 +1,18 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils",
                                   "resource://testing-common/PlacesTestUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
+                                  "resource://gre/modules/PlacesUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "ExtensionUtils",
+                                  "resource://gre/modules/ExtensionUtils.jsm");
 
 add_task(function* test_delete() {
   function background() {
     browser.test.onMessage.addListener((msg, arg) => {
       if (msg === "delete-url") {
         browser.history.deleteUrl({url: arg}).then(result => {
           browser.test.assertEq(undefined, result, "browser.history.deleteUrl returns nothing");
           browser.test.sendMessage("url-deleted");
@@ -25,17 +29,16 @@ add_task(function* test_delete() {
         });
       }
     });
 
     browser.test.sendMessage("ready");
   }
 
   const REFERENCE_DATE = new Date(1999, 9, 9, 9, 9);
-  let {PlacesUtils} = Cu.import("resource://gre/modules/PlacesUtils.jsm", {});
 
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       permissions: ["history"],
     },
     background: `(${background})()`,
   });
 
@@ -66,30 +69,30 @@ add_task(function* test_delete() {
   let testUrl = visits[6].uri.spec;
   ok(yield PlacesTestUtils.isPageInDB(testUrl), "expected url found in history database");
 
   extension.sendMessage("delete-url", testUrl);
   yield extension.awaitMessage("url-deleted");
   is(yield PlacesTestUtils.isPageInDB(testUrl), false, "expected url not found in history database");
 
   let filter = {
-    startTime: PlacesUtils.toTime(visits[1].visitDate),
-    endTime: PlacesUtils.toTime(visits[3].visitDate),
+    startTime: PlacesUtils.toDate(visits[1].visitDate),
+    endTime: PlacesUtils.toDate(visits[3].visitDate),
   };
 
   extension.sendMessage("delete-range", filter);
   yield extension.awaitMessage("range-deleted");
 
   ok(yield PlacesTestUtils.isPageInDB(visits[0].uri), "expected uri found in history database");
   is(yield PlacesTestUtils.visitsInDB(visits[0].uri), 2, "2 visits for uri found in history database");
   ok(yield PlacesTestUtils.isPageInDB(visits[5].uri), "expected uri found in history database");
   is(yield PlacesTestUtils.visitsInDB(visits[5].uri), 1, "1 visit for uri found in history database");
 
-  filter.startTime = PlacesUtils.toTime(visits[0].visitDate);
-  filter.endTime = PlacesUtils.toTime(visits[5].visitDate);
+  filter.startTime = PlacesUtils.toDate(visits[0].visitDate);
+  filter.endTime = PlacesUtils.toDate(visits[5].visitDate);
 
   extension.sendMessage("delete-range", filter);
   yield extension.awaitMessage("range-deleted");
 
   is(yield PlacesTestUtils.isPageInDB(visits[0].uri), false, "expected uri not found in history database");
   is(yield PlacesTestUtils.visitsInDB(visits[0].uri), 0, "0 visits for uri found in history database");
   is(yield PlacesTestUtils.isPageInDB(visits[5].uri), false, "expected uri not found in history database");
   is(yield PlacesTestUtils.visitsInDB(visits[5].uri), 0, "0 visits for uri found in history database");
@@ -186,8 +189,91 @@ add_task(function* test_search() {
   results = yield extension.awaitMessage("max-results-search");
   is(results.length, 1, "history.search returned 1 result");
   checkResult(results, DOUBLE_VISIT_URL, 2);
 
   yield extension.awaitFinish("search");
   yield extension.unload();
   yield PlacesTestUtils.clearHistory();
 });
+
+add_task(function* test_add_url() {
+  function background() {
+    const TEST_DOMAIN = "http://example.com/";
+
+    browser.test.onMessage.addListener((msg, testData) => {
+      let [details, type] = testData;
+      details.url = details.url || `${TEST_DOMAIN}${type}`;
+      if (msg === "add-url") {
+        details.title = `Title for ${type}`;
+        browser.history.addUrl(details).then(() => {
+          return browser.history.search({text: details.url});
+        }).then(results => {
+          browser.test.assertEq(1, results.length, "1 result found when searching for added URL");
+          browser.test.sendMessage("url-added", {details, result: results[0]});
+        });
+      } else if (msg === "expect-failure") {
+        let expectedMsg = testData[2];
+        browser.history.addUrl(details).then(() => {
+          browser.test.fail(`Expected error thrown for ${type}`);
+        }, error => {
+          browser.test.assertTrue(
+            error.message.includes(expectedMsg),
+            `"Expected error thrown when trying to add a URL with  ${type}`
+          );
+          browser.test.sendMessage("add-failed");
+        });
+      }
+    });
+
+    browser.test.sendMessage("ready");
+  }
+
+  let addTestData = [
+    [{}, "default"],
+    [{visitTime: new Date()}, "with_date"],
+    [{visitTime: Date.now()}, "with_ms_number"],
+    [{visitTime: Date.now().toString()}, "with_ms_string"],
+    [{visitTime: new Date().toISOString()}, "with_iso_string"],
+    [{transition: "typed"}, "valid_transition"],
+  ];
+
+  let failTestData = [
+    [{transition: "generated"}, "an invalid transition", "|generated| is not a supported transition for history"],
+    [{visitTime: Date.now() + 1000000}, "a future date", "cannot be a future date"],
+    [{url: "about.config"}, "an invalid url", "about.config is not a valid URL"],
+  ];
+
+  function* checkUrl(results) {
+    ok(yield PlacesTestUtils.isPageInDB(results.details.url), `${results.details.url} found in history database`);
+    ok(PlacesUtils.isValidGuid(results.result.id), "URL was added with a valid id");
+    is(results.result.title, results.details.title, "URL was added with the correct title");
+    if (results.details.visitTime) {
+      is(results.result.lastVisitTime,
+         Number(ExtensionUtils.normalizeTime(results.details.visitTime)),
+         "URL was added with the correct date");
+    }
+  }
+
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      permissions: ["history"],
+    },
+    background: `(${background})()`,
+  });
+
+  yield PlacesTestUtils.clearHistory();
+  yield extension.startup();
+  yield extension.awaitMessage("ready");
+
+  for (let data of addTestData) {
+    extension.sendMessage("add-url", data);
+    let results = yield extension.awaitMessage("url-added");
+    yield checkUrl(results);
+  }
+
+  for (let data of failTestData) {
+    extension.sendMessage("expect-failure", data);
+    yield extension.awaitMessage("add-failed");
+  }
+
+  yield extension.unload();
+});
--- a/browser/components/preferences/in-content/security.js
+++ b/browser/components/preferences/in-content/security.js
@@ -184,20 +184,20 @@ var gSecurityPane = {
     });
 
     blockUncommonUnwanted.addEventListener("command", function() {
       blockUnwantedPref.value = blockUncommonUnwanted.checked;
       blockUncommonPref.value = blockUncommonUnwanted.checked;
 
       let malware = malwareTable.value
         .split(",")
-        .filter(x => x !== "goog-unwanted-simple" && x !== "test-unwanted-simple");
+        .filter(x => x !== "goog-unwanted-shavar" && x !== "test-unwanted-simple");
 
       if (blockUncommonUnwanted.checked) {
-        malware.push("goog-unwanted-simple");
+        malware.push("goog-unwanted-shavar");
         malware.push("test-unwanted-simple");
       }
 
       // sort alphabetically to keep the pref consistent
       malware.sort();
 
       malwareTable.value = malware.join(",");
     });
--- a/browser/components/preferences/in-content/tests/browser_security.js
+++ b/browser/components/preferences/in-content/tests/browser_security.js
@@ -107,18 +107,18 @@ add_task(function*() {
     // check that both settings are now turned on or off
     is(Services.prefs.getBoolPref("browser.safebrowsing.downloads.remote.block_potentially_unwanted"), !checked,
        "block_potentially_unwanted is set correctly");
     is(Services.prefs.getBoolPref("browser.safebrowsing.downloads.remote.block_uncommon"), !checked,
        "block_uncommon is set correctly");
 
     // when the preference is on, the malware table should include these ids
     let malwareTable = Services.prefs.getCharPref("urlclassifier.malwareTable").split(",");
-    is(malwareTable.includes("goog-unwanted-simple"), !checked,
-       "malware table doesn't include goog-unwanted-simple");
+    is(malwareTable.includes("goog-unwanted-shavar"), !checked,
+       "malware table doesn't include goog-unwanted-shavar");
     is(malwareTable.includes("test-unwanted-simple"), !checked,
        "malware table doesn't include test-unwanted-simple");
     let sortedMalware = malwareTable.slice(0);
     sortedMalware.sort();
     Assert.deepEqual(malwareTable, sortedMalware, "malware table has been sorted");
 
     yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
   }
--- a/browser/components/privatebrowsing/test/browser/browser.ini
+++ b/browser/components/privatebrowsing/test/browser/browser.ini
@@ -1,9 +1,10 @@
 [DEFAULT]
+tags = openwindow
 skip-if = buildapp == "mulet"
 support-files =
   browser_privatebrowsing_concurrent_page.html
   browser_privatebrowsing_geoprompt_page.html
   browser_privatebrowsing_localStorage_before_after_page.html
   browser_privatebrowsing_localStorage_before_after_page2.html
   browser_privatebrowsing_localStorage_page1.html
   browser_privatebrowsing_localStorage_page2.html
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -60,17 +60,17 @@
   display: -moz-box;
   height: 2px;
   margin-top: -2px;
   position: relative;
   z-index: 2; /* navbar is at 1 */
 }
 
 @media (-moz-mac-yosemite-theme) {
-  #navigator-toolbox::after {
+  #navigator-toolbox:-moz-window-inactive::after {
     background-image: linear-gradient(to top, hsla(0,0%,0%,.1), hsla(0,0%,0%,.1) 1px, hsla(0,0%,100%,0) 1px, hsla(0,0%,100%,0) 2px, transparent 3px);
   }
 }
 
 #navigator-toolbox toolbarbutton:-moz-lwtheme {
   color: inherit;
   text-shadow: inherit;
 }
new file mode 100644
--- /dev/null
+++ b/build/.gdbinit_python.in
@@ -0,0 +1,6 @@
+#filter substitution
+python
+import sys
+sys.path.append('@topsrcdir@/python/gdbpp')
+import gdbpp
+end
--- a/build/moz.build
+++ b/build/moz.build
@@ -55,16 +55,18 @@ if CONFIG['ENABLE_TESTS'] or CONFIG['MOZ
         FINAL_TARGET_FILES += ['/tools/rb/fix_linux_stack.py']
 
 if CONFIG['MOZ_DMD']:
     FINAL_TARGET_FILES += ['/memory/replace/dmd/dmd.py']
 
 # Put a useful .gdbinit in the bin directory, to be picked up automatically
 # by GDB when we debug executables there.
 FINAL_TARGET_FILES += ['/.gdbinit']
+FINAL_TARGET_PP_FILES += ['.gdbinit_python.in']
+OBJDIR_FILES += ['!/dist/bin/.gdbinit_python']
 
 # Install the clang-cl runtime library for ASAN next to the binaries we produce.
 if CONFIG['MOZ_ASAN'] and CONFIG['CLANG_CL']:
     FINAL_TARGET_FILES += ['%' + CONFIG['MOZ_CLANG_RT_ASAN_LIB_PATH']]
 
 if CONFIG['MOZ_APP_BASENAME']:
     FINAL_TARGET_PP_FILES += ['application.ini']
     if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android' and CONFIG['MOZ_UPDATER']:
--- a/caps/BasePrincipal.cpp
+++ b/caps/BasePrincipal.cpp
@@ -40,70 +40,80 @@ PrincipalOriginAttributes::InheritFromDo
 
   // addonId is computed from the principal URI and never propagated
   mUserContextId = aAttrs.mUserContextId;
 
   // TODO:
   // Bug 1225349 - PrincipalOriginAttributes should inherit mSignedPkg
   // accordingly by URI
   mSignedPkg = aAttrs.mSignedPkg;
+
+  mPrivateBrowsingId = aAttrs.mPrivateBrowsingId;
 }
 
 void
 PrincipalOriginAttributes::InheritFromNecko(const NeckoOriginAttributes& aAttrs)
 {
   mAppId = aAttrs.mAppId;
   mInIsolatedMozBrowser = aAttrs.mInIsolatedMozBrowser;
 
   // addonId is computed from the principal URI and never propagated
   mUserContextId = aAttrs.mUserContextId;
   mSignedPkg = aAttrs.mSignedPkg;
+
+  mPrivateBrowsingId = aAttrs.mPrivateBrowsingId;
 }
 
 void
 DocShellOriginAttributes::InheritFromDocToChildDocShell(const PrincipalOriginAttributes& aAttrs)
 {
   mAppId = aAttrs.mAppId;
   mInIsolatedMozBrowser = aAttrs.mInIsolatedMozBrowser;
 
   // addonId is computed from the principal URI and never propagated
   mUserContextId = aAttrs.mUserContextId;
 
   // TODO:
   // Bug 1225353 - DocShell/NeckoOriginAttributes should inherit
   // mSignedPkg accordingly by mSignedPkgInBrowser
   mSignedPkg = aAttrs.mSignedPkg;
+
+  mPrivateBrowsingId = aAttrs.mPrivateBrowsingId;
 }
 
 void
 NeckoOriginAttributes::InheritFromDocToNecko(const PrincipalOriginAttributes& aAttrs)
 {
   mAppId = aAttrs.mAppId;
   mInIsolatedMozBrowser = aAttrs.mInIsolatedMozBrowser;
 
   // addonId is computed from the principal URI and never propagated
   mUserContextId = aAttrs.mUserContextId;
 
   // TODO:
   // Bug 1225353 - DocShell/NeckoOriginAttributes should inherit
   // mSignedPkg accordingly by mSignedPkgInBrowser
+
+  mPrivateBrowsingId = aAttrs.mPrivateBrowsingId;
 }
 
 void
 NeckoOriginAttributes::InheritFromDocShellToNecko(const DocShellOriginAttributes& aAttrs)
 {
   mAppId = aAttrs.mAppId;
   mInIsolatedMozBrowser = aAttrs.mInIsolatedMozBrowser;
 
   // addonId is computed from the principal URI and never propagated
   mUserContextId = aAttrs.mUserContextId;
 
   // TODO:
   // Bug 1225353 - DocShell/NeckoOriginAttributes should inherit
   // mSignedPkg accordingly by mSignedPkgInBrowser
+
+  mPrivateBrowsingId = aAttrs.mPrivateBrowsingId;
 }
 
 void
 OriginAttributes::CreateSuffix(nsACString& aStr) const
 {
   UniquePtr<URLParams> params(new URLParams());
   nsAutoString value;
 
@@ -140,16 +150,22 @@ OriginAttributes::CreateSuffix(nsACStrin
     params->Set(NS_LITERAL_STRING("userContextId"), value);
   }
 
   if (!mSignedPkg.IsEmpty()) {
     MOZ_RELEASE_ASSERT(mSignedPkg.FindCharInSet(dom::quota::QuotaManager::kReplaceChars) == kNotFound);
     params->Set(NS_LITERAL_STRING("signedPkg"), mSignedPkg);
   }
 
+  if (mPrivateBrowsingId) {
+    value.Truncate();
+    value.AppendInt(mPrivateBrowsingId);
+    params->Set(NS_LITERAL_STRING("privateBrowsingId"), value);
+  }
+
   aStr.Truncate();
 
   params->Serialize(value);
   if (!value.IsEmpty()) {
     aStr.AppendLiteral("^");
     aStr.Append(NS_ConvertUTF16toUTF8(value));
   }
 
@@ -166,16 +182,20 @@ namespace {
 class MOZ_STACK_CLASS PopulateFromSuffixIterator final
   : public URLParams::ForEachIterator
 {
 public:
   explicit PopulateFromSuffixIterator(OriginAttributes* aOriginAttributes)
     : mOriginAttributes(aOriginAttributes)
   {
     MOZ_ASSERT(aOriginAttributes);
+    // If mPrivateBrowsingId is passed in as >0 and is not present in the suffix,
+    // then it will remain >0 when it should be 0 according to the suffix. Set to 0 before
+    // iterating to fix this.
+    mOriginAttributes->mPrivateBrowsingId = 0;
   }
 
   bool URLParamsIterator(const nsString& aName,
                          const nsString& aValue) override
   {
     if (aName.EqualsLiteral("appId")) {
       nsresult rv;
       int64_t val  = aValue.ToInteger64(&rv);
@@ -212,16 +232,26 @@ public:
     }
 
     if (aName.EqualsLiteral("signedPkg")) {
       MOZ_RELEASE_ASSERT(mOriginAttributes->mSignedPkg.IsEmpty());
       mOriginAttributes->mSignedPkg.Assign(aValue);
       return true;
     }
 
+    if (aName.EqualsLiteral("privateBrowsingId")) {
+      nsresult rv;
+      int64_t val = aValue.ToInteger64(&rv);
+      NS_ENSURE_SUCCESS(rv, false);
+      NS_ENSURE_TRUE(val >= 0 && val <= UINT32_MAX, false);
+      mOriginAttributes->mPrivateBrowsingId = static_cast<uint32_t>(val);
+
+      return true;
+    }
+
     // No other attributes are supported.
     return false;
   }
 
 private:
   OriginAttributes* mOriginAttributes;
 };
 
@@ -257,16 +287,22 @@ OriginAttributes::PopulateFromOrigin(con
     aOriginNoSuffix = origin;
     return true;
   }
 
   aOriginNoSuffix = Substring(origin, 0, pos);
   return PopulateFromSuffix(Substring(origin, pos));
 }
 
+void
+OriginAttributes::SyncAttributesWithPrivateBrowsing(bool aInPrivateBrowsing)
+{
+  mPrivateBrowsingId = aInPrivateBrowsing ? 1 : 0;
+}
+
 BasePrincipal::BasePrincipal()
 {}
 
 BasePrincipal::~BasePrincipal()
 {}
 
 NS_IMETHODIMP
 BasePrincipal::GetOrigin(nsACString& aOrigin)
--- a/caps/BasePrincipal.h
+++ b/caps/BasePrincipal.h
@@ -28,17 +28,18 @@ class OriginAttributes : public dom::Ori
 {
 public:
   bool operator==(const OriginAttributes& aOther) const
   {
     return mAppId == aOther.mAppId &&
            mInIsolatedMozBrowser == aOther.mInIsolatedMozBrowser &&
            mAddonId == aOther.mAddonId &&
            mUserContextId == aOther.mUserContextId &&
-           mSignedPkg == aOther.mSignedPkg;
+           mSignedPkg == aOther.mSignedPkg &&
+           mPrivateBrowsingId == aOther.mPrivateBrowsingId;
   }
   bool operator!=(const OriginAttributes& aOther) const
   {
     return !(*this == aOther);
   }
 
   // Serializes/Deserializes non-default values into the suffix format, i.e.
   // |!key1=value1&key2=value2|. If there are no non-default attributes, this
@@ -46,16 +47,20 @@ public:
   void CreateSuffix(nsACString& aStr) const;
   bool PopulateFromSuffix(const nsACString& aStr);
 
   // Populates the attributes from a string like
   // |uri!key1=value1&key2=value2| and returns the uri without the suffix.
   bool PopulateFromOrigin(const nsACString& aOrigin,
                           nsACString& aOriginNoSuffix);
 
+  // Helper function to match mIsPrivateBrowsing to existing private browsing
+  // flags. Once all other flags are removed, this can be removed too.
+  void SyncAttributesWithPrivateBrowsing(bool aInPrivateBrowsing);
+
 protected:
   OriginAttributes() {}
   explicit OriginAttributes(const OriginAttributesDictionary& aOther)
     : OriginAttributesDictionary(aOther) {}
 };
 
 class PrincipalOriginAttributes;
 class DocShellOriginAttributes;
@@ -170,16 +175,20 @@ public:
     if (mUserContextId.WasPassed() && mUserContextId.Value() != aAttrs.mUserContextId) {
       return false;
     }
 
     if (mSignedPkg.WasPassed() && mSignedPkg.Value() != aAttrs.mSignedPkg) {
       return false;
     }
 
+    if (mPrivateBrowsingId.WasPassed() && mPrivateBrowsingId.Value() != aAttrs.mPrivateBrowsingId) {
+      return false;
+    }
+
     return true;
   }
 };
 
 /*
  * Base class from which all nsIPrincipal implementations inherit. Use this for
  * default implementations and other commonalities between principal
  * implementations.
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -669,47 +669,42 @@ EqualOrSubdomain(nsIURI* aProbeArg, nsIU
         rv = probe->SetHost(newHost);
         NS_ENSURE_SUCCESS(rv, false);
     }
 }
 
 static bool
 AllSchemesMatch(nsIURI* aURI, nsIURI* aOtherURI)
 {
-    nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(aURI);
-    nsCOMPtr<nsINestedURI> nestedOtherURI = do_QueryInterface(aOtherURI);
     auto stringComparator = nsCaseInsensitiveCStringComparator();
-    if (!nestedURI && !nestedOtherURI) {
-        // Neither of the URIs is nested, compare their schemes directly:
-        nsAutoCString scheme, otherScheme;
-        aURI->GetScheme(scheme);
-        aOtherURI->GetScheme(otherScheme);
-        return scheme.Equals(otherScheme, stringComparator);
-    }
-    while (nestedURI && nestedOtherURI) {
-        nsCOMPtr<nsIURI> currentURI = do_QueryInterface(nestedURI);
-        nsCOMPtr<nsIURI> currentOtherURI = do_QueryInterface(nestedOtherURI);
+    nsCOMPtr<nsIURI> currentURI = aURI;
+    nsCOMPtr<nsIURI> currentOtherURI = aOtherURI;
+    while (currentURI && currentOtherURI) {
         nsAutoCString scheme, otherScheme;
         currentURI->GetScheme(scheme);
         currentOtherURI->GetScheme(otherScheme);
         if (!scheme.Equals(otherScheme, stringComparator)) {
             return false;
         }
-
+        nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(currentURI);
+        nsCOMPtr<nsINestedURI> nestedOtherURI = do_QueryInterface(currentOtherURI);
+        // If neither are nested and all schemes have matched so far
+        // (or we would have bailed already), we're the same:
+        if (!nestedURI && !nestedOtherURI) {
+            return true;
+        }
+        // If one is nested and the other not, they're not equal:
+        if (!nestedURI != !nestedOtherURI) {
+            return false;
+        }
+        // At this stage, both are still nested URIs, so let's play again:
         nestedURI->GetInnerURI(getter_AddRefs(currentURI));
         nestedOtherURI->GetInnerURI(getter_AddRefs(currentOtherURI));
-        nestedURI = do_QueryInterface(currentURI);
-        nestedOtherURI = do_QueryInterface(currentOtherURI);
     }
-    if (!!nestedURI != !!nestedOtherURI) {
-        // If only one of the scheme chains runs out at one point, clearly the chains
-        // aren't of the same length, so we bail:
-        return false;
-    }
-    return true;
+    return false;
 }
 
 NS_IMETHODIMP
 nsScriptSecurityManager::CheckLoadURIWithPrincipal(nsIPrincipal* aPrincipal,
                                                    nsIURI *aTargetURI,
                                                    uint32_t aFlags)
 {
     NS_PRECONDITION(aPrincipal, "CheckLoadURIWithPrincipal must have a principal");
--- a/caps/tests/mochitest/test_bug995943.xul
+++ b/caps/tests/mochitest/test_bug995943.xul
@@ -22,17 +22,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   const Ci = Components.interfaces;
   Cu.import("resource://gre/modules/Services.jsm");
   function debug(msg) { info(msg); }
 
   /** Test for CAPS file:// URI prefs. **/
   SimpleTest.waitForExplicitFinish();
   SimpleTest.requestCompleteLog();
   if (navigator.userAgent.indexOf("Mac OS X 10.10") != -1)
-    SimpleTest.expectAssertions(5); // See bug 1067022
+    SimpleTest.expectAssertions(6, 9); // See bug 1067022
   else if (Services.appinfo.OS == "WINNT")
     SimpleTest.expectAssertions(0, 1); // See bug 1067022
 
   var rootdir = Services.appinfo.OS == "WINNT" ? "file:///C:" : "file:///";
 
   function checkLoadFileURI(domain, shouldLoad) {
     debug("Invoking checkLoadFileURI with domain: " + domain + ", shouldLoad: " + shouldLoad);
     return new Promise(function(resolve, reject) {
--- a/caps/tests/mochitest/test_principal_jarprefix_origin_appid_appstatus.html
+++ b/caps/tests/mochitest/test_principal_jarprefix_origin_appid_appstatus.html
@@ -17,16 +17,19 @@ https://bugzilla.mozilla.org/show_bug.cg
 </div>
 <pre id="test">
 <script type="application/javascript;version=1.7">
 
 /** Test for Bug 758258 **/
 
 var Ci = Components.interfaces;
 SimpleTest.waitForExplicitFinish();
+if (navigator.userAgent.indexOf("Mac OS X 10.10") != -1) {
+  SimpleTest.expectAssertions(0, 4);
+}
 
 /*
  * gData is an array of objects. Each object represents a test case.
  * - app: gives the app manifest URL, will set mozapp to it on the iframe;
  * - origin: gives the origin of the iframe. This is the URL thas is going to
  *           to be passed as iframe.src but also the expected principal's
  *           origin.
  * - isapp: tells if the iframe is really a mozapp. If the manifest url isn't
--- a/docshell/base/LoadContext.cpp
+++ b/docshell/base/LoadContext.cpp
@@ -48,17 +48,17 @@ LoadContext::LoadContext(nsIPrincipal* a
   , mUsePrivateBrowsing(false)
   , mUseRemoteTabs(false)
 #ifdef DEBUG
   , mIsNotNull(true)
 #endif
 {
   PrincipalOriginAttributes poa = BasePrincipal::Cast(aPrincipal)->OriginAttributesRef();
   mOriginAttributes.InheritFromDocToChildDocShell(poa);
-
+  mOriginAttributes.SyncAttributesWithPrivateBrowsing(mUsePrivateBrowsing);
   if (!aOptionalBase) {
     return;
   }
 
   MOZ_ALWAYS_SUCCEEDS(aOptionalBase->GetIsContent(&mIsContent));
   MOZ_ALWAYS_SUCCEEDS(aOptionalBase->GetUsePrivateBrowsing(&mUsePrivateBrowsing));
   MOZ_ALWAYS_SUCCEEDS(aOptionalBase->GetUseRemoteTabs(&mUseRemoteTabs));
 }
--- a/docshell/base/LoadContext.h
+++ b/docshell/base/LoadContext.h
@@ -49,16 +49,17 @@ public:
     , mIsContent(aToCopy.mIsContent)
     , mUsePrivateBrowsing(aToCopy.mUsePrivateBrowsing)
     , mUseRemoteTabs(aToCopy.mUseRemoteTabs)
     , mOriginAttributes(aAttrs)
 #ifdef DEBUG
     , mIsNotNull(aToCopy.mIsNotNull)
 #endif
   {
+    MOZ_ASSERT(aToCopy.mUsePrivateBrowsing == (aAttrs.mPrivateBrowsingId != 0));
   }
 
   // appId/inIsolatedMozBrowser arguments override those in SerializedLoadContext
   // provided by child process.
   LoadContext(const IPC::SerializedLoadContext& aToCopy,
               uint64_t aNestedFrameId,
               DocShellOriginAttributes& aAttrs)
     : mTopFrameElement(nullptr)
@@ -66,16 +67,17 @@ public:
     , mIsContent(aToCopy.mIsContent)
     , mUsePrivateBrowsing(aToCopy.mUsePrivateBrowsing)
     , mUseRemoteTabs(aToCopy.mUseRemoteTabs)
     , mOriginAttributes(aAttrs)
 #ifdef DEBUG
     , mIsNotNull(aToCopy.mIsNotNull)
 #endif
   {
+    MOZ_ASSERT(aToCopy.mUsePrivateBrowsing == (aAttrs.mPrivateBrowsingId != 0));
   }
 
   LoadContext(dom::Element* aTopFrameElement,
               bool aIsContent,
               bool aUsePrivateBrowsing,
               bool aUseRemoteTabs,
               const DocShellOriginAttributes& aAttrs)
     : mTopFrameElement(do_GetWeakReference(aTopFrameElement))
@@ -83,16 +85,17 @@ public:
     , mIsContent(aIsContent)
     , mUsePrivateBrowsing(aUsePrivateBrowsing)
     , mUseRemoteTabs(aUseRemoteTabs)
     , mOriginAttributes(aAttrs)
 #ifdef DEBUG
     , mIsNotNull(true)
 #endif
   {
+    MOZ_ASSERT(aUsePrivateBrowsing == (aAttrs.mPrivateBrowsingId != 0));
   }
 
   // Constructor taking reserved appId for the safebrowsing cookie.
   explicit LoadContext(uint32_t aAppId)
     : mTopFrameElement(nullptr)
     , mNestedFrameId(0)
     , mIsContent(false)
     , mUsePrivateBrowsing(false)
--- a/docshell/base/SerializedLoadContext.cpp
+++ b/docshell/base/SerializedLoadContext.cpp
@@ -36,16 +36,17 @@ SerializedLoadContext::SerializedLoadCon
     nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryInterface(aChannel);
     if (pbChannel &&
         NS_SUCCEEDED(pbChannel->IsPrivateModeOverriden(&isPrivate,
                                                        &isOverriden)) &&
         isOverriden) {
       mUsePrivateBrowsing = isPrivate;
       mIsPrivateBitValid = true;
     }
+    mOriginAttributes.SyncAttributesWithPrivateBrowsing(mUsePrivateBrowsing);
   }
 }
 
 SerializedLoadContext::SerializedLoadContext(nsIWebSocketChannel* aChannel)
 {
   nsCOMPtr<nsILoadContext> loadContext;
   if (aChannel) {
     NS_QueryNotificationCallbacks(aChannel, loadContext);
@@ -56,16 +57,17 @@ SerializedLoadContext::SerializedLoadCon
 void
 SerializedLoadContext::Init(nsILoadContext* aLoadContext)
 {
   if (aLoadContext) {
     mIsNotNull = true;
     mIsPrivateBitValid = true;
     aLoadContext->GetIsContent(&mIsContent);
     aLoadContext->GetUsePrivateBrowsing(&mUsePrivateBrowsing);
+    mOriginAttributes.SyncAttributesWithPrivateBrowsing(mUsePrivateBrowsing);
     aLoadContext->GetUseRemoteTabs(&mUseRemoteTabs);
     if (!aLoadContext->GetOriginAttributes(mOriginAttributes)) {
       NS_WARNING("GetOriginAttributes failed");
     }
   } else {
     mIsNotNull = false;
     mIsPrivateBitValid = false;
     // none of below values really matter when mIsNotNull == false:
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -809,16 +809,17 @@ nsDocShell::nsDocShell()
   , mInvisible(false)
   , mHasLoadedNonBlankURI(false)
   , mDefaultLoadFlags(nsIRequest::LOAD_NORMAL)
   , mBlankTiming(false)
   , mFrameType(FRAME_TYPE_REGULAR)
   , mParentCharsetSource(0)
   , mJSRunToCompletionDepth(0)
 {
+  AssertOriginAttributesMatchPrivateBrowsing();
   mHistoryID = ++gDocshellIDCounter;
   if (gDocShellCount++ == 0) {
     NS_ASSERTION(sURIFixup == nullptr,
                  "Huh, sURIFixup not null in first nsDocShell ctor!");
 
     CallGetService(NS_URIFIXUP_CONTRACTID, &sURIFixup);
   }
 
@@ -2177,17 +2178,17 @@ nsDocShell::SetAllowJavascript(bool aAll
   RecomputeCanExecuteScripts();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::GetUsePrivateBrowsing(bool* aUsePrivateBrowsing)
 {
   NS_ENSURE_ARG_POINTER(aUsePrivateBrowsing);
-
+  AssertOriginAttributesMatchPrivateBrowsing();
   *aUsePrivateBrowsing = mInPrivateBrowsing;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::SetUsePrivateBrowsing(bool aUsePrivateBrowsing)
 {
   nsContentUtils::ReportToConsoleNonLocalized(
@@ -2200,16 +2201,19 @@ nsDocShell::SetUsePrivateBrowsing(bool a
 }
 
 NS_IMETHODIMP
 nsDocShell::SetPrivateBrowsing(bool aUsePrivateBrowsing)
 {
   bool changed = aUsePrivateBrowsing != mInPrivateBrowsing;
   if (changed) {
     mInPrivateBrowsing = aUsePrivateBrowsing;
+
+    mOriginAttributes.SyncAttributesWithPrivateBrowsing(mInPrivateBrowsing);
+
     if (mAffectPrivateSessionLifetime) {
       if (aUsePrivateBrowsing) {
         IncreasePrivateDocShellCount();
       } else {
         DecreasePrivateDocShellCount();
       }
     }
   }
@@ -2269,16 +2273,17 @@ nsDocShell::SetRemoteTabs(bool aUseRemot
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::SetAffectPrivateSessionLifetime(bool aAffectLifetime)
 {
   bool change = aAffectLifetime != mAffectPrivateSessionLifetime;
   if (change && mInPrivateBrowsing) {
+    AssertOriginAttributesMatchPrivateBrowsing();
     if (aAffectLifetime) {
       IncreasePrivateDocShellCount();
     } else {
       DecreasePrivateDocShellCount();
     }
   }
   mAffectPrivateSessionLifetime = aAffectLifetime;
 
@@ -2961,16 +2966,17 @@ nsDocShell::GetSessionStorageForPrincipa
 {
   nsCOMPtr<nsIDOMStorageManager> manager = TopSessionStorageManager();
   if (!manager) {
     return NS_ERROR_UNEXPECTED;
   }
 
   nsCOMPtr<nsPIDOMWindowOuter> domWin = GetWindow();
 
+  AssertOriginAttributesMatchPrivateBrowsing();
   if (aCreate) {
     return manager->CreateStorage(domWin->GetCurrentInnerWindow(), aPrincipal,
                                   aDocumentURI, mInPrivateBrowsing, aStorage);
   }
 
   return manager->GetStorage(domWin->GetCurrentInnerWindow(), aPrincipal,
                              mInPrivateBrowsing, aStorage);
 }
@@ -3655,16 +3661,21 @@ nsDocShell::FindItemWithName(const char1
     // the item is active for the special cases.
     if (foundItem) {
       foundItem.swap(*aResult);
     }
     return NS_OK;
   }
 }
 
+void
+nsDocShell::AssertOriginAttributesMatchPrivateBrowsing(){
+  MOZ_ASSERT((mOriginAttributes.mPrivateBrowsingId != 0) == mInPrivateBrowsing);
+}
+
 nsresult
 nsDocShell::DoFindItemWithName(const char16_t* aName,
                                nsISupports* aRequestor,
                                nsIDocShellTreeItem* aOriginalRequestor,
                                nsIDocShellTreeItem** aResult)
 {
   // First we check our name.
   if (mName.Equals(aName) && ItemIsActive(this) &&
@@ -5758,16 +5769,17 @@ nsDocShell::Destroy()
   mSecurityUI = nullptr;
 
   // Cancel any timers that were set for this docshell; this is needed
   // to break the cycle between us and the timers.
   CancelRefreshURITimers();
 
   if (mInPrivateBrowsing) {
     mInPrivateBrowsing = false;
+    mOriginAttributes.SyncAttributesWithPrivateBrowsing(mInPrivateBrowsing);
     if (mAffectPrivateSessionLifetime) {
       DecreasePrivateDocShellCount();
     }
   }
 
   return NS_OK;
 }
 
@@ -6415,16 +6427,17 @@ nsDocShell::SetTitle(const char16_t* aTi
   // tree owner.
   if (!parent) {
     nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
     if (treeOwnerAsWin) {
       treeOwnerAsWin->SetTitle(aTitle);
     }
   }
 
+  AssertOriginAttributesMatchPrivateBrowsing();
   if (mCurrentURI && mLoadType != LOAD_ERROR_PAGE && mUseGlobalHistory &&
       !mInPrivateBrowsing) {
     nsCOMPtr<IHistory> history = services::GetHistoryService();
     if (history) {
       history->SetURITitle(mCurrentURI, mTitle);
     } else if (mGlobalHistory) {
       mGlobalHistory->SetPageTitle(mCurrentURI, nsString(mTitle));
     }
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -1030,16 +1030,20 @@ private:
 
   // Separate function to do the actual name (i.e. not _top, _self etc.)
   // searching for FindItemWithName.
   nsresult DoFindItemWithName(const char16_t* aName,
                               nsISupports* aRequestor,
                               nsIDocShellTreeItem* aOriginalRequestor,
                               nsIDocShellTreeItem** aResult);
 
+  // Helper assertion to enforce that mInPrivateBrowsing is in sync with
+  // OriginAttributes.mPrivateBrowsingId
+  void AssertOriginAttributesMatchPrivateBrowsing();
+
   // Notify consumers of a search being loaded through the observer service:
   void MaybeNotifyKeywordSearchLoading(const nsString& aProvider,
                                        const nsString& aKeyword);
 
 #ifdef DEBUG
   // We're counting the number of |nsDocShells| to help find leaks
   static unsigned long gNumberOfDocShells;
 #endif /* DEBUG */
--- a/docshell/test/iframesandbox/mochitest.ini
+++ b/docshell/test/iframesandbox/mochitest.ini
@@ -4,17 +4,21 @@ support-files =
   file_our_auxiliary_navigation_by_location.html
   file_parent_navigation_by_location.html
   file_sibling_navigation_by_location.html
   file_top_navigation_by_location.html
   file_top_navigation_by_location_exotic.html
 
 [test_child_navigation_by_location.html]
 [test_other_auxiliary_navigation_by_location.html]
+tags = openwindow
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #bug 948948, NS_ERROR_FAILURE from nsWindowWatcher::GetPrompt
 [test_our_auxiliary_navigation_by_location.html]
+tags = openwindow
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #bug 948948, NS_ERROR_FAILURE from nsWindowWatcher::GetPrompt
 [test_parent_navigation_by_location.html]
+tags = openwindow
 [test_sibling_navigation_by_location.html]
+tags = openwindow
 [test_top_navigation_by_location_exotic.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || android_version == '18' #bug 948948, NS_ERROR_FAILURE from nsWindowWatcher::GetPrompt
 [test_top_navigation_by_location.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || android_version == '18' #bug 948948, NS_ERROR_FAILURE from nsWindowWatcher::GetPrompt
--- a/docshell/test/navigation/browser.ini
+++ b/docshell/test/navigation/browser.ini
@@ -4,8 +4,9 @@ support-files =
   bug343515_pg2.html
   bug343515_pg3.html
   bug343515_pg3_1.html
   bug343515_pg3_1_1.html
   bug343515_pg3_2.html
 
 [browser_bug343515.js]
 [browser_test-content-chromeflags.js]
+tags = openwindow
\ No newline at end of file
--- a/dom/base/ChromeUtils.cpp
+++ b/dom/base/ChromeUtils.cpp
@@ -174,13 +174,14 @@ ChromeUtils::FillNonDefaultOriginAttribu
 ChromeUtils::IsOriginAttributesEqual(dom::GlobalObject& aGlobal,
                                      const dom::OriginAttributesDictionary& aA,
                                      const dom::OriginAttributesDictionary& aB)
 {
   return aA.mAddonId == aB.mAddonId &&
          aA.mAppId == aB.mAppId &&
          aA.mInIsolatedMozBrowser == aB.mInIsolatedMozBrowser &&
          aA.mSignedPkg == aB.mSignedPkg &&
-         aA.mUserContextId == aB.mUserContextId;
+         aA.mUserContextId == aB.mUserContextId &&
+         aA.mPrivateBrowsingId == aB.mPrivateBrowsingId;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -32,16 +32,17 @@
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 #include "BatteryManager.h"
 #include "mozilla/dom/DeviceStorageAreaListener.h"
 #include "mozilla/dom/PowerManager.h"
 #include "mozilla/dom/WakeLock.h"
 #include "mozilla/dom/power/PowerManagerService.h"
 #include "mozilla/dom/CellBroadcast.h"
+#include "mozilla/dom/FlyWebService.h"
 #include "mozilla/dom/IccManager.h"
 #include "mozilla/dom/InputPortManager.h"
 #include "mozilla/dom/MobileMessageManager.h"
 #include "mozilla/dom/Permissions.h"
 #include "mozilla/dom/Presentation.h"
 #include "mozilla/dom/ServiceWorkerContainer.h"
 #include "mozilla/dom/TCPSocket.h"
 #include "mozilla/dom/Telephony.h"
@@ -1616,16 +1617,30 @@ Navigator::GetDeprecatedBattery(ErrorRes
   if (doc) {
     doc->WarnOnceAbout(nsIDocument::eNavigatorBattery);
   }
 
   return mBatteryManager;
 }
 
 already_AddRefed<Promise>
+Navigator::PublishServer(const nsAString& aName,
+                         const FlyWebPublishOptions& aOptions,
+                         ErrorResult& aRv)
+{
+  RefPtr<FlyWebService> service = FlyWebService::GetOrCreate();
+  if (!service) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  return service->PublishServer(aName, aOptions, mWindow, aRv);
+}
+
+already_AddRefed<Promise>
 Navigator::GetFeature(const nsAString& aName, ErrorResult& aRv)
 {
   nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mWindow);
   RefPtr<Promise> p = Promise::Create(go, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -38,16 +38,18 @@ class Geolocation;
 class systemMessageCallback;
 class MediaDevices;
 struct MediaStreamConstraints;
 class WakeLock;
 class ArrayBufferViewOrBlobOrStringOrFormData;
 struct MobileIdOptions;
 class ServiceWorkerContainer;
 class DOMRequest;
+struct FlyWebPublishOptions;
+struct FlyWebFilter;
 } // namespace dom
 } // namespace mozilla
 
 //*****************************************************************************
 // Navigator: Script "navigator" object
 //*****************************************************************************
 
 namespace mozilla {
@@ -162,16 +164,19 @@ public:
   nsMimeTypeArray* GetMimeTypes(ErrorResult& aRv);
   nsPluginArray* GetPlugins(ErrorResult& aRv);
   Permissions* GetPermissions(ErrorResult& aRv);
   // The XPCOM GetDoNotTrack is ok
   Geolocation* GetGeolocation(ErrorResult& aRv);
   Promise* GetBattery(ErrorResult& aRv);
   battery::BatteryManager* GetDeprecatedBattery(ErrorResult& aRv);
 
+  already_AddRefed<Promise> PublishServer(const nsAString& aName,
+                                          const FlyWebPublishOptions& aOptions,
+                                          ErrorResult& aRv);
   static void AppName(nsAString& aAppName, bool aUsePrefOverriddenValue);
 
   static nsresult GetPlatform(nsAString& aPlatform,
                               bool aUsePrefOverriddenValue);
 
   static nsresult GetAppVersion(nsAString& aAppVersion,
                                 bool aUsePrefOverriddenValue);
 
--- a/dom/base/ScriptSettings.cpp
+++ b/dom/base/ScriptSettings.cpp
@@ -289,31 +289,16 @@ GetWebIDLCallerPrincipal()
   // Once we fix bug 951991, this can all be simplified.
   if (!aes->CxPusherIsStackTop()) {
     return nullptr;
   }
 
   return aes->mWebIDLCallerPrincipal;
 }
 
-static JSContext*
-FindJSContext(nsIGlobalObject* aGlobalObject)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  JSContext *cx = nullptr;
-  nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aGlobalObject);
-  if (sgo && sgo->GetScriptContext()) {
-    cx = sgo->GetScriptContext()->GetNativeContext();
-  }
-  if (!cx) {
-    cx = nsContentUtils::GetSafeJSContext();
-  }
-  return cx;
-}
-
 AutoJSAPI::AutoJSAPI()
   : mCx(nullptr)
   , mOldAutoJSAPIOwnsErrorReporting(false)
   , mIsMainThread(false) // For lack of anything better
 {
 }
 
 AutoJSAPI::~AutoJSAPI()
@@ -339,26 +324,30 @@ AutoJSAPI::~AutoJSAPI()
   }
 }
 
 void
 WarningOnlyErrorReporter(JSContext* aCx, const char* aMessage,
                          JSErrorReport* aRep);
 
 void
-AutoJSAPI::InitInternal(JSObject* aGlobal, JSContext* aCx, bool aIsMainThread)
+AutoJSAPI::InitInternal(nsIGlobalObject* aGlobalObject, JSObject* aGlobal,
+                        JSContext* aCx, bool aIsMainThread)
 {
   MOZ_ASSERT(aCx);
   MOZ_ASSERT(aIsMainThread == NS_IsMainThread());
+  MOZ_ASSERT(bool(aGlobalObject) == bool(aGlobal));
+  MOZ_ASSERT_IF(aGlobalObject, aGlobalObject->GetGlobalJSObject() == aGlobal);
 #ifdef DEBUG
   bool haveException = JS_IsExceptionPending(aCx);
 #endif // DEBUG
 
   mCx = aCx;
   mIsMainThread = aIsMainThread;
+  mGlobalObject = aGlobalObject;
   if (aIsMainThread) {
     // This Rooted<> is necessary only as long as AutoCxPusher::AutoCxPusher
     // can GC, which is only possible because XPCJSContextStack::Push calls
     // nsIPrincipal.Equals. Once that is removed, the Rooted<> will no longer
     // be necessary.
     JS::Rooted<JSObject*> global(JS_GetRuntime(aCx), aGlobal);
     mCxPusher.emplace(mCx);
     mAutoNullableCompartment.emplace(mCx, global);
@@ -446,25 +435,26 @@ AutoJSAPI::AutoJSAPI(nsIGlobalObject* aG
   : mOldAutoJSAPIOwnsErrorReporting(false)
   , mIsMainThread(aIsMainThread)
 {
   MOZ_ASSERT(aGlobalObject);
   MOZ_ASSERT(aGlobalObject->GetGlobalJSObject(), "Must have a JS global");
   MOZ_ASSERT(aCx);
   MOZ_ASSERT(aIsMainThread == NS_IsMainThread());
 
-  InitInternal(aGlobalObject->GetGlobalJSObject(), aCx, aIsMainThread);
+  InitInternal(aGlobalObject, aGlobalObject->GetGlobalJSObject(), aCx,
+               aIsMainThread);
 }
 
 void
 AutoJSAPI::Init()
 {
   MOZ_ASSERT(!mCx, "An AutoJSAPI should only be initialised once");
 
-  InitInternal(/* aGlobal */ nullptr,
+  InitInternal(/* aGlobalObject */ nullptr, /* aGlobal */ nullptr,
                nsContentUtils::GetDefaultJSContextForThread(),
                NS_IsMainThread());
 }
 
 bool
 AutoJSAPI::Init(nsIGlobalObject* aGlobalObject, JSContext* aCx)
 {
   MOZ_ASSERT(!mCx, "An AutoJSAPI should only be initialised once");
@@ -474,17 +464,17 @@ AutoJSAPI::Init(nsIGlobalObject* aGlobal
     return false;
   }
 
   JSObject* global = aGlobalObject->GetGlobalJSObject();
   if (NS_WARN_IF(!global)) {
     return false;
   }
 
-  InitInternal(global, aCx, NS_IsMainThread());
+  InitInternal(aGlobalObject, global, aCx, NS_IsMainThread());
   return true;
 }
 
 bool
 AutoJSAPI::Init(nsIGlobalObject* aGlobalObject)
 {
   return Init(aGlobalObject, nsContentUtils::GetDefaultJSContextForThread());
 }
@@ -646,23 +636,23 @@ AutoJSAPI::StealException(JS::MutableHan
   return true;
 }
 
 AutoEntryScript::AutoEntryScript(nsIGlobalObject* aGlobalObject,
                                  const char *aReason,
                                  bool aIsMainThread,
                                  JSContext* aCx)
   : AutoJSAPI(aGlobalObject, aIsMainThread,
-              aCx ? aCx : FindJSContext(aGlobalObject))
+              aCx ? aCx : nsContentUtils::GetSafeJSContext())
   , ScriptSettingsStackEntry(aGlobalObject, /* aCandidate = */ true)
   , mWebIDLCallerPrincipal(nullptr)
 {
   MOZ_ASSERT(aGlobalObject);
   MOZ_ASSERT_IF(!aCx, aIsMainThread); // cx is mandatory off-main-thread.
-  MOZ_ASSERT_IF(aCx && aIsMainThread, aCx == FindJSContext(aGlobalObject));
+  MOZ_ASSERT_IF(aCx && aIsMainThread, aCx == nsContentUtils::GetSafeJSContext());
 
   if (aIsMainThread && gRunToCompletionListeners > 0) {
     mDocShellEntryMonitor.emplace(cx(), aReason);
   }
 }
 
 AutoEntryScript::AutoEntryScript(JSObject* aObject,
                                  const char *aReason,
@@ -769,22 +759,16 @@ AutoNoJSAPI::AutoNoJSAPI(bool aIsMainThr
                       /* aAllowNull = */ true);
   }
 }
 
 danger::AutoCxPusher::AutoCxPusher(JSContext* cx, bool allowNull)
 {
   MOZ_ASSERT_IF(!allowNull, cx);
 
-  // Hold a strong ref to the nsIScriptContext, if any. This ensures that we
-  // only destroy the mContext of an nsJSContext when it is not on the cx stack
-  // (and therefore not in use). See nsJSContext::DestroyJSContext().
-  if (cx)
-    mScx = GetScriptContextFromJSContext(cx);
-
   XPCJSContextStack *stack = XPCJSRuntime::Get()->GetJSContextStack();
   stack->Push(cx);
   mStackDepthAfterPush = stack->Count();
 
 #ifdef DEBUG
   mPushedContext = cx;
   mCompartmentDepthOnEntry = cx ? js::GetEnterCompartmentDepth(cx) : 0;
 #endif
@@ -806,17 +790,16 @@ danger::AutoCxPusher::~AutoCxPusher()
   // run into trouble if a Push/Pop are interleaved with a
   // JSAutoEnterCompartment. Make sure the compartment depth right before we
   // pop is the same as it was right after we pushed.
   MOZ_ASSERT_IF(mPushedContext, mCompartmentDepthOnEntry ==
                                 js::GetEnterCompartmentDepth(mPushedContext));
   DebugOnly<JSContext*> stackTop;
   MOZ_ASSERT(mPushedContext == nsXPConnect::XPConnect()->GetCurrentJSContext());
   XPCJSRuntime::Get()->GetJSContextStack()->Pop();
-  mScx = nullptr;
 }
 
 bool
 danger::AutoCxPusher::IsStackTop() const
 {
   uint32_t currentDepth = XPCJSRuntime::Get()->GetJSContextStack()->Count();
   MOZ_ASSERT(currentDepth >= mStackDepthAfterPush);
   return currentDepth == mStackDepthAfterPush;
--- a/dom/base/ScriptSettings.h
+++ b/dom/base/ScriptSettings.h
@@ -35,25 +35,22 @@ namespace danger {
  * in terms of this class.
  */
 class MOZ_STACK_CLASS AutoCxPusher
 {
 public:
   explicit AutoCxPusher(JSContext *aCx, bool aAllowNull = false);
   ~AutoCxPusher();
 
-  nsIScriptContext* GetScriptContext() { return mScx; }
-
   // Returns true if this AutoCxPusher performed the push that is currently at
   // the top of the cx stack.
   bool IsStackTop() const;
 
 private:
   mozilla::Maybe<JSAutoRequest> mAutoRequest;
-  nsCOMPtr<nsIScriptContext> mScx;
   uint32_t mStackDepthAfterPush;
 #ifdef DEBUG
   JSContext* mPushedContext;
   unsigned mCompartmentDepthOnEntry;
 #endif
 };
 
 } /* namespace danger */
@@ -300,27 +297,33 @@ protected:
   // Protected constructor, allowing subclasses to specify a particular cx to
   // be used. This constructor initialises the AutoJSAPI, so Init must NOT be
   // called on subclasses that use this.
   // If aGlobalObject, its associated JS global or aCx are null this will cause
   // an assertion, as will setting aIsMainThread incorrectly.
   AutoJSAPI(nsIGlobalObject* aGlobalObject, bool aIsMainThread, JSContext* aCx);
 
 private:
+  // We need to hold a strong ref to our global object, so it won't go away
+  // while we're being used.  This _could_ become a JS::Rooted<JSObject*> if we
+  // grabbed our JSContext in our constructor instead of waiting for Init(), so
+  // we could construct this at that point.  It might be worth it do to that.
+  RefPtr<nsIGlobalObject> mGlobalObject;
   mozilla::Maybe<danger::AutoCxPusher> mCxPusher;
   mozilla::Maybe<JSAutoNullableCompartment> mAutoNullableCompartment;
   JSContext *mCx;
 
   // Track state between the old and new error reporting modes.
   bool mOldAutoJSAPIOwnsErrorReporting;
   // Whether we're mainthread or not; set when we're initialized.
   bool mIsMainThread;
   Maybe<JSErrorReporter> mOldErrorReporter;
 
-  void InitInternal(JSObject* aGlobal, JSContext* aCx, bool aIsMainThread);
+  void InitInternal(nsIGlobalObject* aGlobalObject, JSObject* aGlobal,
+                    JSContext* aCx, bool aIsMainThread);
 
   AutoJSAPI(const AutoJSAPI&) = delete;
   AutoJSAPI& operator= (const AutoJSAPI&) = delete;
 };
 
 /*
  * A class that represents a new script entry point.
  *
--- a/dom/base/nsDOMJSUtils.h
+++ b/dom/base/nsDOMJSUtils.h
@@ -7,34 +7,16 @@
 #ifndef nsDOMJSUtils_h__
 #define nsDOMJSUtils_h__
 
 #include "nsIScriptContext.h"
 #include "jsapi.h"
 
 class nsIJSArgArray;
 
-// seems like overkill for just this 1 function - but let's see what else
-// falls out first.
-inline nsIScriptContext *
-GetScriptContextFromJSContext(JSContext *cx)
-{
-  if (!(JS::ContextOptionsRef(cx).privateIsNSISupports())) {
-    return nullptr;
-  }
-
-  nsCOMPtr<nsIScriptContext> scx =
-    do_QueryInterface(static_cast<nsISupports *>
-                                 (::JS_GetContextPrivate(cx)));
-
-  // This will return a pointer to something that's about to be
-  // released, but that's ok here.
-  return scx;
-}
-
 // A factory function for turning a JS::Value argv into an nsIArray
 // but also supports an effecient way of extracting the original argv.
 // The resulting object will take a copy of the array, and ensure each
 // element is rooted.
 // Optionally, aArgv may be nullptr, in which case the array is allocated and
 // rooted, but all items remain nullptr.  This presumably means the caller
 // will then QI us for nsIJSArgArray, and set our array elements.
 nsresult NS_CreateJSArgv(JSContext *aContext, uint32_t aArgc,
--- a/dom/base/nsDOMMutationObserver.cpp
+++ b/dom/base/nsDOMMutationObserver.cpp
@@ -624,25 +624,21 @@ nsDOMMutationObserver::Observe(nsINode& 
     aOptions.mAttributes.Value();
   bool characterData =
     aOptions.mCharacterData.WasPassed() &&
     aOptions.mCharacterData.Value();
   bool subtree = aOptions.mSubtree;
   bool attributeOldValue =
     aOptions.mAttributeOldValue.WasPassed() &&
     aOptions.mAttributeOldValue.Value();
-  bool nativeAnonymousChildList = aOptions.mNativeAnonymousChildList &&
-    nsContentUtils::ThreadsafeIsCallerChrome();
+  bool nativeAnonymousChildList = aOptions.mNativeAnonymousChildList;
   bool characterDataOldValue =
     aOptions.mCharacterDataOldValue.WasPassed() &&
     aOptions.mCharacterDataOldValue.Value();
-  bool animations =
-    aOptions.mAnimations.WasPassed() &&
-    aOptions.mAnimations.Value() &&
-    nsContentUtils::ThreadsafeIsCallerChrome();
+  bool animations = aOptions.mAnimations;
 
   if (!aOptions.mAttributes.WasPassed() &&
       (aOptions.mAttributeOldValue.WasPassed() ||
        aOptions.mAttributeFilter.WasPassed())) {
     attributes = true;
   }
 
   if (!aOptions.mCharacterData.WasPassed() &&
@@ -758,17 +754,17 @@ nsDOMMutationObserver::GetObservingInfo(
     nsMutationReceiver* mr = mReceivers[i];
     info.mChildList = mr->ChildList();
     info.mAttributes.Construct(mr->Attributes());
     info.mCharacterData.Construct(mr->CharacterData());
     info.mSubtree = mr->Subtree();
     info.mAttributeOldValue.Construct(mr->AttributeOldValue());
     info.mCharacterDataOldValue.Construct(mr->CharacterDataOldValue());
     info.mNativeAnonymousChildList = mr->NativeAnonymousChildList();
-    info.mAnimations.Construct(mr->Animations());
+    info.mAnimations = mr->Animations();
     nsCOMArray<nsIAtom>& filters = mr->AttributeFilter();
     if (filters.Count()) {
       info.mAttributeFilter.Construct();
       mozilla::dom::Sequence<nsString>& filtersAsStrings =
         info.mAttributeFilter.Value();
       nsString* strings = filtersAsStrings.AppendElements(filters.Count(),
                                                           mozilla::fallible);
       if (!strings) {
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -159,16 +159,17 @@
 #include "nsEscape.h"
 #include "nsObjectLoadingContent.h"
 #include "nsHtml5TreeOpExecutor.h"
 #include "mozilla/dom/HTMLLinkElement.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/dom/HTMLIFrameElement.h"
 #include "mozilla/dom/HTMLImageElement.h"
 #include "mozilla/dom/MediaSource.h"
+#include "mozilla/dom/FlyWebService.h"
 
 #include "mozAutoDocUpdate.h"
 #include "nsGlobalWindow.h"
 #include "mozilla/dom/EncodingUtils.h"
 #include "nsDOMNavigationTiming.h"
 
 #include "nsSMILAnimationController.h"
 #include "imgIContainer.h"
@@ -8983,16 +8984,23 @@ nsDocument::CanSavePresentation(nsIReque
 #endif
 
   // Don't save presentations for documents containing MSE content, to
   // reduce memory usage.
   if (ContainsMSEContent()) {
     return false;
   }
 
+  // Don't save presentation if there are active FlyWeb connections or FlyWeb
+  // servers.
+  FlyWebService* flyWebService = FlyWebService::GetExisting();
+  if (flyWebService && flyWebService->HasConnectionOrServer(win->WindowID())) {
+    return false;
+  }
+
   if (mSubDocuments) {
     for (auto iter = mSubDocuments->Iter(); !iter.Done(); iter.Next()) {
       auto entry = static_cast<SubDocMapEntry*>(iter.Get());
       nsIDocument* subdoc = entry->mSubDocument;
 
       // The aIgnoreRequest we were passed is only for us, so don't pass it on.
       bool canCache = subdoc ? subdoc->CanSavePresentation(nullptr) : false;
       if (!canCache) {
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -2141,16 +2141,26 @@ nsFrameLoader::MaybeCreateDocShell()
   }
 
   // Grab the userContextId from owner if XUL
   nsresult rv = PopulateUserContextIdFromAttribute(attrs);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
+  bool isPrivate = false;
+  nsCOMPtr<nsILoadContext> parentContext = do_QueryInterface(docShell);
+  NS_ENSURE_STATE(parentContext);
+
+  rv = parentContext->GetUsePrivateBrowsing(&isPrivate);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  attrs.SyncAttributesWithPrivateBrowsing(isPrivate);
+
   nsDocShell::Cast(mDocShell)->SetOriginAttributes(attrs);
 
   if (OwnerIsMozBrowserOrAppFrame()) {
     // For inproc frames, set the docshell properties.
     nsAutoString name;
     if (mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name)) {
       docShell->SetName(name);
     }
@@ -3386,16 +3396,19 @@ nsFrameLoader::GetNewTabContext(MutableT
     attrs.mUserContextId = userContextId;
   }
 
   nsAutoString presentationURLStr;
   mOwnerContent->GetAttr(kNameSpaceID_None,
                          nsGkAtoms::mozpresentation,
                          presentationURLStr);
 
+  bool isPrivate = mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozprivatebrowsing);
+  attrs.SyncAttributesWithPrivateBrowsing(isPrivate);
+
   bool tabContextUpdated =
     aTabContext->SetTabContext(OwnerIsMozBrowserFrame(),
                                mIsPrerendered,
                                ownApp,
                                containingApp,
                                attrs,
                                signedPkgOrigin,
                                presentationURLStr);
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -221,16 +221,17 @@
 #include "mozilla/dom/PopupBlockedEvent.h"
 #include "mozilla/dom/PrimitiveConversions.h"
 #include "mozilla/dom/WindowBinding.h"
 #include "nsITabChild.h"
 #include "mozilla/dom/MediaQueryList.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/NavigatorBinding.h"
 #include "mozilla/dom/ImageBitmap.h"
+#include "mozilla/dom/ImageBitmapBinding.h"
 #include "mozilla/dom/ServiceWorkerRegistration.h"
 #include "mozilla/dom/U2F.h"
 #include "mozilla/dom/WebIDLGlobalNameHash.h"
 #ifdef HAVE_SIDEBAR
 #include "mozilla/dom/ExternalBinding.h"
 #endif
 
 #ifdef MOZ_WEBSPEECH
@@ -1088,17 +1089,19 @@ nsOuterWindowProxy::AppendIndexedPropert
                                                JS::AutoIdVector &props) const
 {
   uint32_t length = GetOuterWindow(proxy)->Length();
   MOZ_ASSERT(int32_t(length) >= 0);
   if (!props.reserve(props.length() + length)) {
     return false;
   }
   for (int32_t i = 0; i < int32_t(length); ++i) {
-    props.append(INT_TO_JSID(i));
+    if (!props.append(INT_TO_JSID(i))) {
+      return false;
+    }
   }
 
   return true;
 }
 
 bool
 nsOuterWindowProxy::watch(JSContext *cx, JS::Handle<JSObject*> proxy,
                           JS::Handle<jsid> id, JS::Handle<JSObject*> callable) const
@@ -2235,17 +2238,17 @@ nsGlobalWindow::GetPopupControlState() c
 {0x0b917c3e, 0xbd50, 0x4683, {0xaf, 0xc9, 0xc7, 0x81, 0x07, 0xae, 0x33, 0x26}}
 
 class WindowStateHolder final : public nsISupports
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(WINDOWSTATEHOLDER_IID)
   NS_DECL_ISUPPORTS
 
-  WindowStateHolder(nsIScriptContext* aContext, nsGlobalWindow *aWindow);
+  explicit WindowStateHolder(nsGlobalWindow *aWindow);
 
   nsGlobalWindow* GetInnerWindow() { return mInnerWindow; }
 
   void DidRestoreWindow()
   {
     mInnerWindow = nullptr;
     mInnerWindowReflector = nullptr;
   }
@@ -2256,20 +2259,19 @@ protected:
   nsGlobalWindow *mInnerWindow;
   // We hold onto this to make sure the inner window doesn't go away. The outer
   // window ends up recalculating it anyway.
   JS::PersistentRooted<JSObject*> mInnerWindowReflector;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(WindowStateHolder, WINDOWSTATEHOLDER_IID)
 
-WindowStateHolder::WindowStateHolder(nsIScriptContext* aContext,
-                                     nsGlobalWindow* aWindow)
+WindowStateHolder::WindowStateHolder(nsGlobalWindow* aWindow)
   : mInnerWindow(aWindow),
-    mInnerWindowReflector(aContext->GetNativeContext(), aWindow->GetWrapper())
+    mInnerWindowReflector(nsContentUtils::RootingCx(), aWindow->GetWrapper())
 {
   NS_PRECONDITION(aWindow, "null window");
   NS_PRECONDITION(aWindow->IsInnerWindow(), "Saving an outer window");
 
   aWindow->SuspendTimeouts();
 
   // When a global goes into the bfcache, we disable script.
   xpc::Scriptability::Get(mInnerWindowReflector).SetDocShellAllowsScript(false);
@@ -2326,17 +2328,17 @@ InitializeLegacyNetscapeObject(JSContext
   NS_ENSURE_TRUE(obj, false);
 
   // We hide enablePrivilege behind a pref because it has been altered in a
   // way that makes it fundamentally insecure to use in production. Mozilla
   // uses this pref during automated testing to support legacy test code that
   // uses enablePrivilege. If you're not doing test automation, you _must_ not
   // flip this pref, or you will be exposing all your users to security
   // vulnerabilities.
-  if (!Preferences::GetBool("security.turn_off_all_security_so_that_viruses_can_take_over_this_computer")) {
+  if (!xpc::IsInAutomation()) {
     return true;
   }
 
   /* Define PrivilegeManager object with the necessary "static" methods. */
   obj = JS_DefineObject(aCx, obj, "PrivilegeManager", nullptr);
   NS_ENSURE_TRUE(obj, false);
 
   return JS_DefineFunctions(aCx, obj, EnablePrivilegeSpec);
@@ -6304,17 +6306,17 @@ FullscreenTransitionTask::Run()
     // should rarely happen, it probably isn't worth to fix. Hence we
     // simply add a timeout here to ensure we never hang forever.
     // In addition, if the page is complicated or the machine is less
     // powerful, layout could take a long time, in which case, staying
     // in black screen for that long could hurt user experience even
     // more than exposing an intermediate state.
     mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
     uint32_t timeout =
-      Preferences::GetUint("full-screen-api.transition.timeout", 500);
+      Preferences::GetUint("full-screen-api.transition.timeout", 1000);
     mTimer->Init(observer, timeout, nsITimer::TYPE_ONE_SHOT);
   } else if (stage == eAfterToggle) {
     Telemetry::AccumulateTimeDelta(Telemetry::FULLSCREEN_TRANSITION_BLACK_MS,
                                    mFullscreenChangeStartTime);
     mWidget->PerformFullscreenTransition(nsIWidget::eAfterFullscreenToggle,
                                          mDuration.mFadeOut, mTransitionData,
                                          this);
   } else if (stage == eEnd) {
@@ -12264,17 +12266,17 @@ nsGlobalWindow::RunTimeoutHandler(nsTime
 
     const char* filename = nullptr;
     uint32_t lineNo = 0, dummyColumn = 0;
     handler->GetLocation(&filename, &lineNo, &dummyColumn);
 
     // New script entry point required, due to the "Create a script" sub-step of
     // http://www.whatwg.org/specs/web-apps/current-work/#timer-initialisation-steps
     nsAutoMicroTask mt;
-    AutoEntryScript aes(this, reason, true, aScx->GetNativeContext());
+    AutoEntryScript aes(this, reason, true);
     JS::CompileOptions options(aes.cx());
     options.setFileAndLine(filename, lineNo)
            .setVersion(JSVERSION_DEFAULT);
     JS::Rooted<JSObject*> global(aes.cx(), FastGetGlobalJSObject());
     nsresult rv =
       nsJSUtils::EvaluateString(aes.cx(), nsDependentString(script),
                                 global, options);
     if (rv == NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE) {
@@ -12941,17 +12943,17 @@ nsGlobalWindow::SaveWindowState()
 
   // Don't do anything else to this inner window! After this point, all
   // calls to SetTimeoutOrInterval will create entries in the timeout
   // list that will only run after this window has come out of the bfcache.
   // Also, while we're frozen, we won't dispatch online/offline events
   // to the page.
   inner->Freeze();
 
-  nsCOMPtr<nsISupports> state = new WindowStateHolder(mContext, inner);
+  nsCOMPtr<nsISupports> state = new WindowStateHolder(inner);
 
 #ifdef DEBUG_PAGE_CACHE
   printf("saving window state, state = %p\n", (void*)state);
 #endif
 
   return state.forget();
 }
 
@@ -14355,27 +14357,53 @@ nsGlobalWindow::FireOnNewGlobalObject()
 #ifdef _WINDOWS_
 #error "Never include windows.h in this file!"
 #endif
 
 already_AddRefed<Promise>
 nsGlobalWindow::CreateImageBitmap(const ImageBitmapSource& aImage,
                                   ErrorResult& aRv)
 {
+  if (aImage.IsArrayBuffer() || aImage.IsArrayBufferView()) {
+    aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+    return nullptr;
+  }
+
   return ImageBitmap::Create(this, aImage, Nothing(), aRv);
 }
 
 already_AddRefed<Promise>
 nsGlobalWindow::CreateImageBitmap(const ImageBitmapSource& aImage,
                                   int32_t aSx, int32_t aSy, int32_t aSw, int32_t aSh,
                                   ErrorResult& aRv)
 {
+  if (aImage.IsArrayBuffer() || aImage.IsArrayBufferView()) {
+    aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+    return nullptr;
+  }
+
   return ImageBitmap::Create(this, aImage, Some(gfx::IntRect(aSx, aSy, aSw, aSh)), aRv);
 }
 
+already_AddRefed<mozilla::dom::Promise>
+nsGlobalWindow::CreateImageBitmap(const ImageBitmapSource& aImage,
+                                  int32_t aOffset, int32_t aLength,
+                                  ImageBitmapFormat aFormat,
+                                  const Sequence<ChannelPixelLayout>& aLayout,
+                                  ErrorResult& aRv)
+{
+  if (aImage.IsArrayBuffer() || aImage.IsArrayBufferView()) {
+    return ImageBitmap::Create(this, aImage, aOffset, aLength, aFormat, aLayout,
+                               aRv);
+  } else {
+    aRv.Throw(NS_ERROR_TYPE_ERR);
+    return nullptr;
+  }
+}
+
 // Helper called by methods that move/resize the window,
 // to ensure the presContext (if any) is aware of resolution
 // change that may happen in multi-monitor configuration.
 void
 nsGlobalWindow::CheckForDPIChange()
 {
   if (mDocShell) {
     RefPtr<nsPresContext> presContext;
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -98,21 +98,23 @@ class nsIIdleService;
 struct nsRect;
 
 class nsWindowSizes;
 
 namespace mozilla {
 class DOMEventTargetHelper;
 namespace dom {
 class BarProp;
+struct ChannelPixelLayout;
 class Console;
 class Crypto;
 class External;
 class Function;
 class Gamepad;
+enum class ImageBitmapFormat : uint32_t;
 class MediaQueryList;
 class MozSelfSupport;
 class Navigator;
 class OwningExternalOrWindowProxy;
 class Promise;
 class PostMessageEvent;
 struct RequestInit;
 class RequestOrUSVString;
@@ -1198,16 +1200,24 @@ public:
   CreateImageBitmap(const mozilla::dom::ImageBitmapSource& aImage,
                     mozilla::ErrorResult& aRv);
 
   already_AddRefed<mozilla::dom::Promise>
   CreateImageBitmap(const mozilla::dom::ImageBitmapSource& aImage,
                     int32_t aSx, int32_t aSy, int32_t aSw, int32_t aSh,
                     mozilla::ErrorResult& aRv);
 
+  already_AddRefed<mozilla::dom::Promise>
+  CreateImageBitmap(const mozilla::dom::ImageBitmapSource& aImage,
+                    int32_t aOffset, int32_t aLength,
+                    mozilla::dom::ImageBitmapFormat aFormat,
+                    const mozilla::dom::Sequence<mozilla::dom::ChannelPixelLayout>& aLayout,
+                    mozilla::ErrorResult& aRv);
+
+
   // ChromeWindow bits.  Do NOT call these unless your window is in
   // fact an nsGlobalChromeWindow.
   uint16_t WindowState();
   nsIBrowserDOMWindow* GetBrowserDOMWindowOuter();
   nsIBrowserDOMWindow* GetBrowserDOMWindow(mozilla::ErrorResult& aError);
   void SetBrowserDOMWindowOuter(nsIBrowserDOMWindow* aBrowserWindow);
   void SetBrowserDOMWindow(nsIBrowserDOMWindow* aBrowserWindow,
                            mozilla::ErrorResult& aError);
--- a/dom/base/nsIScriptContext.h
+++ b/dom/base/nsIScriptContext.h
@@ -12,18 +12,18 @@
 #include "nsISupports.h"
 #include "nsCOMPtr.h"
 #include "jspubtd.h"
 #include "js/GCAPI.h"
 
 class nsIScriptGlobalObject;
 
 #define NS_ISCRIPTCONTEXT_IID \
-{ 0x901f0d5e, 0x217a, 0x45fa, \
-  { 0x9a, 0xca, 0x45, 0x0f, 0xe7, 0x2f, 0x10, 0x9a } }
+{ 0x54cbe9cf, 0x7282, 0x421a, \
+ { 0x91, 0x6f, 0xd0, 0x70, 0x73, 0xde, 0xb8, 0xc0 } }
 
 class nsIOffThreadScriptReceiver;
 
 /**
  * It is used by the application to initialize a runtime and run scripts.
  * A script runtime would implement this interface.
  */
 class nsIScriptContext : public nsISupports
@@ -33,22 +33,16 @@ public:
 
   /**
    * Return the global object.
    *
    **/
   virtual nsIScriptGlobalObject *GetGlobalObject() = 0;
 
   /**
-   * Return the native script context
-   *
-   **/
-  virtual JSContext* GetNativeContext() = 0;
-
-  /**
    * Initialize the context generally. Does not create a global object.
    **/
   virtual nsresult InitContext() = 0;
 
   /**
    * Check to see if context is as yet intialized. Used to prevent
    * reentrancy issues during the initialization process.
    *
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -569,154 +569,86 @@ DumpString(const nsAString &str)
 {
   printf("%s\n", NS_ConvertUTF16toUTF8(str).get());
 }
 #endif
 
 #define JS_OPTIONS_DOT_STR "javascript.options."
 
 static const char js_options_dot_str[]   = JS_OPTIONS_DOT_STR;
-#ifdef JS_GC_ZEAL
-static const char js_zeal_option_str[]        = JS_OPTIONS_DOT_STR "gczeal";
-static const char js_zeal_frequency_str[]     = JS_OPTIONS_DOT_STR "gczeal.frequency";
-#endif
-static const char js_memlog_option_str[]      = JS_OPTIONS_DOT_STR "mem.log";
-static const char js_memnotify_option_str[]   = JS_OPTIONS_DOT_STR "mem.notify";
-
-void
-nsJSContext::JSOptionChangedCallback(const char *pref, void *data)
-{
-  sPostGCEventsToConsole = Preferences::GetBool(js_memlog_option_str);
-  sPostGCEventsToObserver = Preferences::GetBool(js_memnotify_option_str);
-
-#ifdef JS_GC_ZEAL
-  nsJSContext *context = reinterpret_cast<nsJSContext *>(data);
-  int32_t zeal = Preferences::GetInt(js_zeal_option_str, -1);
-  int32_t frequency = Preferences::GetInt(js_zeal_frequency_str, JS_DEFAULT_ZEAL_FREQ);
-  if (zeal >= 0)
-    ::JS_SetGCZeal(context->mContext, (uint8_t)zeal, frequency);
-#endif
-}
 
 nsJSContext::nsJSContext(bool aGCOnDestruction,
                          nsIScriptGlobalObject* aGlobalObject)
   : mWindowProxy(nullptr)
   , mGCOnDestruction(aGCOnDestruction)
   , mGlobalObjectRef(aGlobalObject)
 {
   EnsureStatics();
 
   ++sContextCount;
 
-  mContext = ::JS_NewContext(sRuntime, gStackSize);
-  if (mContext) {
-    ::JS_SetContextPrivate(mContext, static_cast<nsIScriptContext *>(this));
-
-    // Make sure the new context gets the default context options
-    JS::ContextOptionsRef(mContext).setPrivateIsNSISupports(true);
-
-    // Watch for the JS boolean options
-    Preferences::RegisterCallback(JSOptionChangedCallback,
-                                  js_options_dot_str, this);
-  }
   mIsInitialized = false;
   mProcessingScriptTag = false;
   HoldJSObjects(this);
 }
 
 nsJSContext::~nsJSContext()
 {
   mGlobalObjectRef = nullptr;
 
-  DestroyJSContext();
+  Destroy();
 
   --sContextCount;
 
   if (!sContextCount && sDidShutdown) {
     // The last context is being deleted, and we're already in the
     // process of shutting down, release the security manager.
 
     NS_IF_RELEASE(sSecurityManager);
   }
 }
 
-// This function is called either by the destructor or unlink, which means that
-// it can never be called when there is an outstanding ref to the
-// nsIScriptContext on the stack. Our stack-scoped cx pushers hold such a ref,
-// so we can assume here that mContext is not on the stack (and therefore not
-// in use).
 void
-nsJSContext::DestroyJSContext()
+nsJSContext::Destroy()
 {
-  if (!mContext) {
-    return;
-  }
-
-  // Clear our entry in the JSContext, bugzilla bug 66413
-  ::JS_SetContextPrivate(mContext, nullptr);
-
-  // Unregister our "javascript.options.*" pref-changed callback.
-  Preferences::UnregisterCallback(JSOptionChangedCallback,
-                                  js_options_dot_str, this);
-
   if (mGCOnDestruction) {
     PokeGC(JS::gcreason::NSJSCONTEXT_DESTROY);
   }
 
-  JS_DestroyContextNoGC(mContext);
-  mContext = nullptr;
   DropJSObjects(this);
 }
 
 // QueryInterface implementation for nsJSContext
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSContext)
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSContext)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mWindowProxy)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSContext)
-  NS_ASSERTION(!tmp->mContext || !js::ContextHasOutstandingRequests(tmp->mContext),
-               "Trying to unlink a context with outstanding requests.");
   tmp->mIsInitialized = false;
   tmp->mGCOnDestruction = false;
   tmp->mWindowProxy = nullptr;
-  tmp->DestroyJSContext();
+  tmp->Destroy();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobalObjectRef)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsJSContext)
-  NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsJSContext, tmp->GetCCRefcnt())
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSContext)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobalObjectRef)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSContext)
   NS_INTERFACE_MAP_ENTRY(nsIScriptContext)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSContext)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSContext)
 
-nsrefcnt
-nsJSContext::GetCCRefcnt()
-{
-  nsrefcnt refcnt = mRefCnt.get();
-
-  // In the (abnormal) case of synchronous cycle-collection, the context may be
-  // actively running JS code in which case we must keep it alive by adding an
-  // extra refcount.
-  if (mContext && js::ContextHasOutstandingRequests(mContext)) {
-    refcnt++;
-  }
-
-  return refcnt;
-}
-
 #ifdef DEBUG
 bool
 AtomIsEventHandlerName(nsIAtom *aName)
 {
   const char16_t *name = aName->GetUTF16String();
 
   const char16_t *cp;
   char16_t c;
@@ -739,34 +671,24 @@ nsJSContext::GetGlobalObject()
   if (!mWindowProxy) {
     return nullptr;
   }
 
   MOZ_ASSERT(mGlobalObjectRef);
   return mGlobalObjectRef;
 }
 
-JSContext*
-nsJSContext::GetNativeContext()
-{
-  return mContext;
-}
-
 nsresult
 nsJSContext::InitContext()
 {
   // Make sure callers of this use
   // WillInitializeContext/DidInitializeContext around this call.
   NS_ENSURE_TRUE(!mIsInitialized, NS_ERROR_ALREADY_INITIALIZED);
 
-  if (!mContext)
-    return NS_ERROR_OUT_OF_MEMORY;
-
-  JSOptionChangedCallback(js_options_dot_str, this);
-
+  // XXXbz Is there still a point to this function?
   return NS_OK;
 }
 
 nsresult
 nsJSContext::SetProperty(JS::Handle<JSObject*> aTarget, const char* aPropName, nsISupports* aArgs)
 {
   AutoJSAPI jsapi;
   if (NS_WARN_IF(!jsapi.Init(GetGlobalObject()))) {
@@ -1192,17 +1114,16 @@ static const JSFunctionSpec JProfFunctio
     JS_FS_END
 };
 
 #endif /* defined(MOZ_JPROF) */
 
 nsresult
 nsJSContext::InitClasses(JS::Handle<JSObject*> aGlobalObj)
 {
-  JSOptionChangedCallback(js_options_dot_str, this);
   AutoJSAPI jsapi;
   jsapi.Init();
   JSContext* cx = jsapi.cx();
   JSAutoCompartment ac(cx, aGlobalObj);
 
   // Attempt to initialize profiling functions
   ::JS_DefineProfilingFunctions(cx, aGlobalObj);
 
@@ -2618,16 +2539,21 @@ nsJSContext::EnsureStatics()
   Preferences::AddBoolVarCache(&sCompactOnUserInactive,
                                "javascript.options.compact_on_user_inactive",
                                true);
 
   Preferences::AddUintVarCache(&sCompactOnUserInactiveDelay,
                                "javascript.options.compact_on_user_inactive_delay",
                                NS_DEAULT_INACTIVE_GC_DELAY);
 
+  Preferences::AddBoolVarCache(&sPostGCEventsToConsole,
+                               JS_OPTIONS_DOT_STR "mem.log");
+  Preferences::AddBoolVarCache(&sPostGCEventsToObserver,
+                               JS_OPTIONS_DOT_STR "mem.notify");
+
   nsIObserver* observer = new nsJSEnvironmentObserver();
   obs->AddObserver(observer, "memory-pressure", false);
   obs->AddObserver(observer, "user-interaction-inactive", false);
   obs->AddObserver(observer, "user-interaction-active", false);
   obs->AddObserver(observer, "quit-application", false);
   obs->AddObserver(observer, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
 
   sIsInitialized = true;
--- a/dom/base/nsJSEnvironment.h
+++ b/dom/base/nsJSEnvironment.h
@@ -45,17 +45,16 @@ public:
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsJSContext,
                                                          nsIScriptContext)
 
   virtual nsIScriptGlobalObject *GetGlobalObject() override;
   inline nsIScriptGlobalObject *GetGlobalObjectRef() { return mGlobalObjectRef; }
 
-  virtual JSContext* GetNativeContext() override;
   virtual nsresult InitContext() override;
   virtual bool IsContextInitialized() override;
 
   virtual nsresult SetProperty(JS::Handle<JSObject*> aTarget, const char* aPropName, nsISupports* aVal) override;
 
   virtual bool GetProcessingScriptTag() override;
   virtual void SetProcessingScriptTag(bool aResult) override;
 
@@ -145,36 +144,31 @@ protected:
   // Helper to convert xpcom datatypes to jsvals.
   nsresult ConvertSupportsTojsvals(nsISupports *aArgs,
                                    JS::Handle<JSObject*> aScope,
                                    JS::AutoValueVector &aArgsOut);
 
   nsresult AddSupportsPrimitiveTojsvals(nsISupports *aArg, JS::Value *aArgv);
 
 private:
-  void DestroyJSContext();
+  void Destroy();
 
-  nsrefcnt GetCCRefcnt();
-
-  JSContext *mContext;
   JS::Heap<JSObject*> mWindowProxy;
 
   bool mIsInitialized;
   bool mGCOnDestruction;
   bool mProcessingScriptTag;
 
   PRTime mModalStateTime;
   uint32_t mModalStateDepth;
 
   // mGlobalObjectRef ensures that the outer window stays alive as long as the
   // context does. It is eventually collected by the cycle collector.
   nsCOMPtr<nsIScriptGlobalObject> mGlobalObjectRef;
 
-  static void JSOptionChangedCallback(const char *pref, void *data);
-
   static bool DOMOperationCallback(JSContext *cx);
 };
 
 namespace mozilla {
 namespace dom {
 
 void StartupJSEnvironment();
 void ShutdownJSEnvironment();
--- a/dom/base/nsJSUtils.cpp
+++ b/dom/base/nsJSUtils.cpp
@@ -16,17 +16,16 @@
 #include "jsfriendapi.h"
 #include "nsIScriptContext.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIXPConnect.h"
 #include "nsCOMPtr.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsPIDOMWindow.h"
 #include "GeckoProfiler.h"
-#include "nsDOMJSUtils.h" // for GetScriptContextFromJSContext
 #include "nsJSPrincipals.h"
 #include "xpcpublic.h"
 #include "nsContentUtils.h"
 #include "nsGlobalWindow.h"
 
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ScriptSettings.h"
@@ -97,18 +96,16 @@ nsJSUtils::CompileFunction(AutoJSAPI& js
                            const nsAString& aBody,
                            JSObject** aFunctionObject)
 {
   JSContext* cx = jsapi.cx();
   MOZ_ASSERT(js::GetEnterCompartmentDepth(cx) > 0);
   MOZ_ASSERT_IF(aScopeChain.length() != 0,
                 js::IsObjectInContextCompartment(aScopeChain[0], cx));
   MOZ_ASSERT_IF(aOptions.versionSet, aOptions.version != JSVERSION_UNKNOWN);
-  mozilla::DebugOnly<nsIScriptContext*> ctx = GetScriptContextFromJSContext(cx);
-  MOZ_ASSERT_IF(ctx, ctx->IsContextInitialized());
 
   // Do the junk Gecko is supposed to do before calling into JSAPI.
   for (size_t i = 0; i < aScopeChain.length(); ++i) {
     JS::ExposeObjectToActiveJS(aScopeChain[i]);
   }
 
   // Compile.
   JS::Rooted<JSFunction*> fun(cx);
--- a/dom/base/nsScriptLoader.cpp
+++ b/dom/base/nsScriptLoader.cpp
@@ -684,18 +684,17 @@ nsScriptLoader::CreateModuleScript(nsMod
   }
 
   nsCOMPtr<nsIScriptContext> context = globalObject->GetScriptContext();
   if (!context) {
     return NS_ERROR_FAILURE;
   }
 
   nsAutoMicroTask mt;
-  AutoEntryScript aes(globalObject, "CompileModule", true,
-                      context->GetNativeContext());
+  AutoEntryScript aes(globalObject, "CompileModule", true);
 
   bool oldProcessingScriptTag = context->GetProcessingScriptTag();
   context->SetProcessingScriptTag(true);
 
   nsresult rv;
   {
     // Update our current script.
     AutoCurrentScriptUpdater scriptUpdater(this, aRequest->mElement);
@@ -1973,18 +1972,17 @@ nsScriptLoader::EvaluateScript(nsScriptL
   JSVersion version = JSVersion(aRequest->mJSVersion);
   if (version == JSVERSION_UNKNOWN) {
     return NS_OK;
   }
 
   // New script entry point required, due to the "Create a script" sub-step of
   // http://www.whatwg.org/specs/web-apps/current-work/#execute-the-script-block
   nsAutoMicroTask mt;
-  AutoEntryScript aes(globalObject, "<script> element", true,
-                      context->GetNativeContext());
+  AutoEntryScript aes(globalObject, "<script> element", true);
   JS::Rooted<JSObject*> global(aes.cx(),
                                globalObject->GetGlobalJSObject());
 
   bool oldProcessingScriptTag = context->GetProcessingScriptTag();
   context->SetProcessingScriptTag(true);
   nsresult rv;
   {
     // Update our current script.
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -687,16 +687,17 @@ skip-if = (buildapp == 'b2g' && toolkit 
 subsuite = clipboard
 skip-if = buildapp == 'b2g' || toolkit == 'android' #bug 904183 # b2g(clipboard undefined) b2g-debug(clipboard undefined) b2g-desktop(clipboard undefined)
 [test_copypaste.xhtml]
 subsuite = clipboard
 skip-if = buildapp == 'b2g' || toolkit == 'android' #bug 904183 # b2g(bug 904183) b2g-debug(bug 904183) b2g-desktop(bug 904183)
 [test_createHTMLDocument.html]
 [test_declare_stylesheet_obsolete.html]
 [test_dialogArguments.html]
+tags = openwindow
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s # showmodaldialog
 [test_document.all_iteration.html]
 [test_document.all_unqualified.html]
 [test_document_constructor.html]
 [test_document_importNode_document.html]
 [test_document_register.html]
 [test_domcursor.html]
 [test_domparser_null_char.html]
@@ -787,16 +788,17 @@ tags = audiochannel
 [test_NodeIterator_mutations_3.html]
 [test_nodelist_holes.html]
 [test_nonascii_blob_url.html]
 [test_noWebAudioNotification.html]
 tags = audiochannel
 [test_open_null_features.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Fails on b2g-desktop, tracked in bug 1011874
 [test_openDialogChromeOnly.html]
+tags = openwindow
 [test_orientation_alternate.html]
 skip-if = toolkit != 'gonk'
 [test_orientation_frame.html]
 skip-if = toolkit != 'gonk'
 [test_orientation_frame_lock.html]
 skip-if = toolkit != 'gonk'
 [test_orientation_sandbox_no_lock.html]
 skip-if = toolkit != 'gonk'
--- a/dom/base/test/test_mutationobserver_anonymous.html
+++ b/dom/base/test/test_mutationobserver_anonymous.html
@@ -228,18 +228,38 @@ function testSubtree() {
     assertSamePseudoElement("before", records[0].addedNodes[0], getFirstChild(parent));
     is(records[0].removedNodes.length, 0, "Shouldn't have got removedNodes");
 
     is(records[1].addedNodes.length, 1, "Should have got addedNodes");
     assertSamePseudoElement("after", records[1].addedNodes[0], getLastChild(parent));
     is(records[1].removedNodes.length, 0, "Shouldn't have got removedNodes");
 
     observer.disconnect();
-    SimpleTest.finish();
+    testDictionaryWithoutChromePriv();
   });
   SpecialPowers.observeMutationEvents(m, document, true, true);
   parent.style.display = "block";
 }
 
+function testDictionaryWithoutChromePriv()
+{
+  var m = new MutationObserver(function() {});
+  try {
+    m.observe(document, { childList: true, get nativeAnonymousChildList() { throw "Foo1"; } } );
+    ok(true, "Shouldn't throw!");
+  } catch(ex) {
+    ok(false, "Did throw " + ex);
+  }
+
+  try {
+    m.observe(document, { childList: true, get animations() { throw "Foo2"; } } );
+    ok(true, "Shouldn't throw!");
+  } catch(ex) {
+    ok(false, "Did throw " + ex);
+  }
+  
+  SimpleTest.finish();
+}
+
 </script>
 </pre>
 </body>
 </html>
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -3154,19 +3154,25 @@ ForEachHandler(JSContext* aCx, unsigned 
   JS::Rooted<JS::Value>
     maplikeOrSetlikeObj(aCx,
                         js::GetFunctionNativeReserved(&args.callee(),
                                                       FOREACH_MAPLIKEORSETLIKEOBJ_SLOT));
   MOZ_ASSERT(aArgc == 3);
   JS::AutoValueVector newArgs(aCx);
   // Arguments are passed in as value, key, object. Keep value and key, replace
   // object with the maplike/setlike object.
-  newArgs.append(args.get(0));
-  newArgs.append(args.get(1));
-  newArgs.append(maplikeOrSetlikeObj);
+  if (!newArgs.append(args.get(0))) {
+    return false;
+  }
+  if (!newArgs.append(args.get(1))) {
+    return false;
+  }
+  if (!newArgs.append(maplikeOrSetlikeObj)) {
+    return false;
+  }
   JS::Rooted<JS::Value> rval(aCx, JS::UndefinedValue());
   // Now actually call the user specified callback
   return JS::Call(aCx, args.thisv(), callbackFn, newArgs, &rval);
 }
 
 static inline prototypes::ID
 GetProtoIdForNewtarget(JS::Handle<JSObject*> aNewTarget)
 {
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -3271,28 +3271,37 @@ template<class T>
 struct StrongPtrForMember
 {
   typedef typename Conditional<IsRefcounted<T>::value,
                                RefPtr<T>, nsAutoPtr<T>>::Type Type;
 };
 
 inline
 JSObject*
-GetErrorPrototype(JSContext* aCx, JS::Handle<JSObject*> aForObj)
+GetErrorPrototype(JSContext* aCx, JS::Handle<JSObject*>)
 {
   return JS_GetErrorPrototype(aCx);
 }
 
 inline
 JSObject*
-GetIteratorPrototype(JSContext* aCx, JS::Handle<JSObject*> aForObj)
+GetIteratorPrototype(JSContext* aCx, JS::Handle<JSObject*>)
 {
   return JS_GetIteratorPrototype(aCx);
 }
 
+namespace binding_detail {
+inline
+JSObject*
+GetHackedNamespaceProtoObject(JSContext* aCx, JS::Handle<JSObject*>)
+{
+  return JS_NewPlainObject(aCx);
+}
+} // namespace binding_detail
+
 // Resolve an id on the given global object that wants to be included in
 // Exposed=System webidl annotations.  False return value means exception
 // thrown.
 bool SystemGlobalResolve(JSContext* cx, JS::Handle<JSObject*> obj,
                          JS::Handle<jsid> id, bool* resolvedp);
 
 // Enumerate all ids on the given global object that wants to be included in
 // Exposed=System webidl annotations.  False return value means exception
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -493,16 +493,26 @@ DOMInterfaces = {
 'FileReader': {
     'implicitJSContext': [ 'readAsArrayBuffer' ],
 },
 
 'FileReaderSync': {
     'wrapperCache': False,
 },
 
+'FlyWebFetchEvent': {
+    'headerFile': 'FlyWebServerEvents.h',
+    'nativeType': 'mozilla::dom::FlyWebFetchEvent',
+},
+
+'FlyWebWebSocketEvent': {
+    'headerFile': 'FlyWebServerEvents.h',
+    'nativeType': 'mozilla::dom::FlyWebWebSocketEvent',
+},
+
 'FontFaceSet': {
     'implicitJSContext': [ 'load' ],
 },
 
 'FontFaceSetIterator': {
     'wrapperCache': False,
 },
 
@@ -640,16 +650,20 @@ DOMInterfaces = {
     'headerFile': 'IDBEvents.h',
 },
 
 'IID': {
     'nativeType': 'nsIJSID',
     'headerFile': 'xpcjsid.h',
 },
 
+'ImageBitmap': {
+    'implicitJSContext': [ 'mapDataInto' ],
+},
+
 'ImageCapture': {
     'binaryNames': { 'videoStreamTrack': 'GetVideoStreamTrack' }
 },
 
 'ImageData': {
     'wrapperCache': False,
 },
 
@@ -1882,16 +1896,32 @@ DOMInterfaces = {
         'register': False,
         },
 
 'TestSecureContextInterface' : {
         # Keep this in sync with TestExampleInterface
         'headerFile': 'TestBindingHeader.h',
         'register': False
         },
+
+'TestNamespace' : {
+        'headerFile': 'TestBindingHeader.h',
+        'register': False,
+        },
+
+'TestRenamedNamespace' : {
+        'headerFile': 'TestBindingHeader.h',
+        'register': False,
+        },
+
+'TestProtoObjectHackedNamespace' : {
+        'headerFile': 'TestBindingHeader.h',
+        'register': False,
+        },
+
 }
 
 # These are temporary, until they've been converted to use new DOM bindings
 def addExternalIface(iface, nativeType=None, headerFile=None,
                      notflattened=False):
     if iface in DOMInterfaces:
         raise Exception('Interface declared both as WebIDL and External interface')
     domInterface = {
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -620,34 +620,42 @@ class CGPrototypeJSClass(CGThing):
             protoGetter=protoGetter)
 
 
 def NeedsGeneratedHasInstance(descriptor):
     assert descriptor.interface.hasInterfaceObject()
     return descriptor.hasXPConnectImpls or descriptor.interface.isConsequential()
 
 
-def InterfaceObjectProtoGetter(descriptor):
+def InterfaceObjectProtoGetter(descriptor, forXrays=False):
     """
     Returns a tuple with two elements:
 
         1) The name of the function to call to get the prototype to use for the
            interface object as a JSObject*.
 
         2) The name of the function to call to get the prototype to use for the
            interface prototype as a JS::Handle<JSObject*> or None if no such
            function exists.
     """
     parentInterface = descriptor.interface.parent
     if parentInterface:
+        assert not descriptor.interface.isNamespace()
         parentIfaceName = parentInterface.identifier.name
         parentDesc = descriptor.getDescriptor(parentIfaceName)
         prefix = toBindingNamespace(parentDesc.name)
         protoGetter = prefix + "::GetConstructorObject"
         protoHandleGetter = prefix + "::GetConstructorObjectHandle"
+    elif descriptor.interface.isNamespace():
+        if (forXrays or
+            not descriptor.interface.getExtendedAttribute("ProtoObjectHack")):
+            protoGetter = "JS_GetObjectPrototype"
+        else:
+            protoGetter = "binding_detail::GetHackedNamespaceProtoObject"
+        protoHandleGetter = None
     else:
         protoGetter = "JS_GetFunctionPrototype"
         protoHandleGetter = None
     return (protoGetter, protoHandleGetter)
 
 
 class CGInterfaceObjectJSClass(CGThing):
     def __init__(self, descriptor, properties):
@@ -656,35 +664,42 @@ class CGInterfaceObjectJSClass(CGThing):
         self.properties = properties
 
     def declare(self):
         # We're purely for internal consumption
         return ""
 
     def define(self):
         if self.descriptor.interface.ctor():
+            assert not self.descriptor.interface.isNamespace()
             ctorname = CONSTRUCT_HOOK_NAME
+        elif self.descriptor.interface.isNamespace():
+            ctorname = "nullptr"
         else:
             ctorname = "ThrowingConstructor"
         if NeedsGeneratedHasInstance(self.descriptor):
             hasinstance = HASINSTANCE_HOOK_NAME
         elif self.descriptor.interface.hasInterfacePrototypeObject():
             hasinstance = "InterfaceHasInstance"
         else:
             hasinstance = "nullptr"
         prototypeID, depth = PrototypeIDAndDepth(self.descriptor)
         slotCount = "DOM_INTERFACE_SLOTS_BASE"
         if len(self.descriptor.interface.namedConstructors) > 0:
             slotCount += (" + %i /* slots for the named constructors */" %
                           len(self.descriptor.interface.namedConstructors))
-        (protoGetter, _) = InterfaceObjectProtoGetter(self.descriptor)
+        (protoGetter, _) = InterfaceObjectProtoGetter(self.descriptor,
+                                                      forXrays=True)
 
         if ctorname == "ThrowingConstructor" and hasinstance == "InterfaceHasInstance":
             ret = ""
             classOpsPtr = "&sBoringInterfaceObjectClassClassOps"
+        elif ctorname == "nullptr" and hasinstance == "nullptr":
+            ret = ""
+            classOpsPtr = "JS_NULL_CLASS_OPS"
         else:
             ret = fill(
                 """
                 static const js::ClassOps sInterfaceObjectClassOps = {
                     nullptr,               /* addProperty */
                     nullptr,               /* delProperty */
                     nullptr,               /* getProperty */
                     nullptr,               /* setProperty */
@@ -698,43 +713,61 @@ class CGInterfaceObjectJSClass(CGThing):
                     nullptr,               /* trace */
                 };
 
                 """,
                 ctorname=ctorname,
                 hasInstance=hasinstance)
             classOpsPtr = "&sInterfaceObjectClassOps"
 
+        if self.descriptor.interface.isNamespace():
+            classString = self.descriptor.interface.getExtendedAttribute("ClassString")
+            if classString is None:
+                classString = "Object"
+            else:
+                classString = classString[0]
+            toStringResult = "[object %s]" % classString
+            objectOps = "JS_NULL_OBJECT_OPS"
+        else:
+            classString = "Function"
+            toStringResult = ("function %s() {\\n    [native code]\\n}" %
+                              self.descriptor.interface.identifier.name)
+            # We need non-default ObjectOps so we can actually make
+            # use of our toStringResult.
+            objectOps = "&sInterfaceObjectClassObjectOps"
+
         ret = ret + fill(
             """
             static const DOMIfaceAndProtoJSClass sInterfaceObjectClass = {
               {
-                "Function",
+                "${classString}",
                 JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}),
                 ${classOpsPtr},
                 JS_NULL_CLASS_SPEC,
                 JS_NULL_CLASS_EXT,
-                &sInterfaceObjectClassObjectOps
+                ${objectOps}
               },
               eInterface,
               ${prototypeID},
               ${depth},
               ${hooks},
-              "function ${name}() {\\n    [native code]\\n}",
+              "${toStringResult}",
               ${protoGetter}
             };
             """,
+            classString=classString,
             slotCount=slotCount,
             ctorname=ctorname,
             hasInstance=hasinstance,
             classOpsPtr=classOpsPtr,
             hooks=NativePropertyHooks(self.descriptor),
-            name=self.descriptor.interface.identifier.name,
+            objectOps=objectOps,
             prototypeID=prototypeID,
             depth=depth,
+            toStringResult=toStringResult,
             protoGetter=protoGetter)
         return ret
 
 class CGList(CGThing):
     """
     Generate code for a list of GCThings.  Just concatenates them together, with
     an optional joiner string.  "\n" is a common joiner.
     """
@@ -12720,19 +12753,28 @@ class CGDictionary(CGThing):
             "prop": self.makeMemberName(member.identifier.name),
             "convert": string.Template(conversionInfo.template).substitute(replacements),
             "propGet": propGet
         }
         # The conversion code will only run where a default value or a value passed
         # by the author needs to get converted, so we can remember if we have any
         # members present here.
         conversionReplacements["convert"] += "mIsAnyMemberPresent = true;\n"
-        conversion = ("if (!isNull && !${propGet}) {\n"
-                      "  return false;\n"
-                      "}\n")
+        if isChromeOnly(member):
+            conversion = ("if (!isNull) {\n"
+                          "  if (!nsContentUtils::ThreadsafeIsCallerChrome()) {\n"
+                          "    temp->setUndefined();\n"
+                          "  } else if (!${propGet}) {\n"
+                          "    return false;\n"
+                          "  }\n"
+                          "}\n")
+        else:
+            conversion = ("if (!isNull && !${propGet}) {\n"
+                          "  return false;\n"
+                          "}\n")
         if member.defaultValue:
             if (member.type.isUnion() and
                 (not member.type.nullable() or
                  not isinstance(member.defaultValue, IDLNullValue))):
                 # Since this has a default value, it might have been initialized
                 # already.  Go ahead and uninit it before we try to init it
                 # again.
                 memberName = self.makeMemberName(member.identifier.name)
@@ -12832,16 +12874,18 @@ class CGDictionary(CGThing):
         conversion = CGWrapper(
             CGIndenter(conversion),
             pre=("do {\n"
                  "  // block for our 'break' successCode and scope for 'temp' and 'currentValue'\n"),
             post="} while(0);\n")
         if member.canHaveMissingValue():
             # Only do the conversion if we have a value
             conversion = CGIfWrapper(conversion, "%s.WasPassed()" % memberLoc)
+        if isChromeOnly(member):
+            conversion = CGIfWrapper(conversion, "nsContentUtils::ThreadsafeIsCallerChrome()")
         return conversion
 
     def getMemberTrace(self, member):
         type = member.type
         assert typeNeedsRooting(type)
         memberLoc = self.makeMemberName(member.identifier.name)
         if not member.canHaveMissingValue():
             memberData = memberLoc
--- a/dom/bindings/Configuration.py
+++ b/dom/bindings/Configuration.py
@@ -1,13 +1,13 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 
-from WebIDL import IDLInterface, IDLExternalInterface, IDLImplementsStatement
+from WebIDL import IDLImplementsStatement
 import os
 from collections import defaultdict
 
 autogenerated_comment = "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n"
 
 
 class Configuration:
     """
@@ -48,17 +48,17 @@ class Configuration:
                         "defined.  Don't do this unless your right-hand side "
                         "is LegacyQueryInterface.\n"
                         "%s\n"
                         "%s" %
                         (thing.location, thing.implementor.location))
 
             assert not thing.isType()
 
-            if not thing.isInterface():
+            if not thing.isInterface() and not thing.isNamespace():
                 continue
             iface = thing
             self.interfaces[iface.identifier.name] = iface
             if iface.identifier.name not in config:
                 # Completely skip consequential interfaces with no descriptor
                 # if they have no interface object because chances are we
                 # don't need to do anything interesting with them.
                 if iface.isConsequential() and not iface.hasInterfaceObject():
@@ -428,16 +428,17 @@ class Descriptor(DescriptorProvider):
         self.register = desc.get('register', True)
 
         self.hasXPConnectImpls = desc.get('hasXPConnectImpls', False)
 
         # If we're concrete, we need to crawl our ancestor interfaces and mark
         # them as having a concrete descendant.
         self.concrete = (not self.interface.isExternal() and
                          not self.interface.isCallback() and
+                         not self.interface.isNamespace() and
                          desc.get('concrete', True))
         self.hasUnforgeableMembers = (self.concrete and
                                       any(MemberIsUnforgeable(m, self) for m in
                                           self.interface.members))
         self.operations = {
             'IndexedGetter': None,
             'IndexedSetter': None,
             'IndexedCreator': None,
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -153,16 +153,19 @@ class IDLObject(object):
         self.userData = dict()
 
     def filename(self):
         return self.location.filename()
 
     def isInterface(self):
         return False
 
+    def isNamespace(self):
+        return False
+
     def isEnum(self):
         return False
 
     def isCallback(self):
         return False
 
     def isType(self):
         return False
@@ -580,30 +583,30 @@ class IDLExternalInterface(IDLObjectWith
 
     def isNavigatorProperty(self):
         return False
 
     def _getDependentObjects(self):
         return set()
 
 
-class IDLPartialInterface(IDLObject):
-    def __init__(self, location, name, members, nonPartialInterface):
+class IDLPartialInterfaceOrNamespace(IDLObject):
+    def __init__(self, location, name, members, nonPartialInterfaceOrNamespace):
         assert isinstance(name, IDLUnresolvedIdentifier)
 
         IDLObject.__init__(self, location)
         self.identifier = name
         self.members = members
         # propagatedExtendedAttrs are the ones that should get
         # propagated to our non-partial interface.
         self.propagatedExtendedAttrs = []
         self._haveSecureContextExtendedAttribute = False
-        self._nonPartialInterface = nonPartialInterface
+        self._nonPartialInterfaceOrNamespace = nonPartialInterfaceOrNamespace
         self._finished = False
-        nonPartialInterface.addPartialInterface(self)
+        nonPartialInterfaceOrNamespace.addPartialInterface(self)
 
     def addExtendedAttributes(self, attrs):
         for attr in attrs:
             identifier = attr.identifier()
 
             if identifier in ["Constructor", "NamedConstructor"]:
                 self.propagatedExtendedAttrs.append(attr)
             elif identifier == "SecureContext":
@@ -630,28 +633,32 @@ class IDLPartialInterface(IDLObject):
                                   "interface" % identifier,
                                   [attr.location])
 
     def finish(self, scope):
         if self._finished:
             return
         self._finished = True
         if (not self._haveSecureContextExtendedAttribute and
-            self._nonPartialInterface.getExtendedAttribute("SecureContext")):
+            self._nonPartialInterfaceOrNamespace.getExtendedAttribute("SecureContext")):
             # This gets propagated to all our members.
             for member in self.members:
                 if member.getExtendedAttribute("SecureContext"):
                     raise WebIDLError("[SecureContext] specified on both a "
                                       "partial interface member and on the "
                                       "non-partial interface",
-                                      [member.location, self._nonPartialInterface.location])
-                member.addExtendedAttributes([IDLExtendedAttribute(self._nonPartialInterface.location, ("SecureContext",))])
-        # Need to make sure our non-partial interface gets finished so it can
-        # report cases when we only have partial interfaces.
-        self._nonPartialInterface.finish(scope)
+                                      [member.location,
+                                       self._nonPartialInterfaceOrNamespace.location])
+                member.addExtendedAttributes(
+                    [IDLExtendedAttribute(self._nonPartialInterfaceOrNamespace.location,
+                                          ("SecureContext",))])
+        # Need to make sure our non-partial interface or namespace gets
+        # finished so it can report cases when we only have partial
+        # interfaces/namespaces.
+        self._nonPartialInterfaceOrNamespace.finish(scope)
 
     def validate(self):
         pass
 
 
 def convertExposedAttrToGlobalNameSet(exposedAttr, targetSet):
     assert len(targetSet) == 0
     if exposedAttr.hasValue():
@@ -661,17 +668,17 @@ def convertExposedAttrToGlobalNameSet(ex
         targetSet.update(exposedAttr.args())
 
 
 def globalNameSetToExposureSet(globalScope, nameSet, exposureSet):
     for name in nameSet:
         exposureSet.update(globalScope.globalNameMapping[name])
 
 
-class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
+class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
     def __init__(self, location, parentScope, name, parent, members,
                  isKnownNonPartial):
         assert isinstance(parentScope, IDLScope)
         assert isinstance(name, IDLUnresolvedIdentifier)
         assert isKnownNonPartial or not parent
         assert isKnownNonPartial or len(members) == 0
 
         self.parent = None
@@ -707,19 +714,16 @@ class IDLInterface(IDLObjectWithScope, I
         self.iterableInterface = None
 
         IDLObjectWithScope.__init__(self, location, parentScope, name)
         IDLExposureMixins.__init__(self, location)
 
         if isKnownNonPartial:
             self.setNonPartial(location, parent, members)
 
-    def __str__(self):
-        return "Interface '%s'" % self.identifier.name
-
     def ctor(self):
         identifier = IDLUnresolvedIdentifier(self.location, "constructor",
                                              allowForbidden=True)
         try:
             return self._lookupIdentifier(identifier)
         except:
             return None
 
@@ -805,16 +809,30 @@ class IDLInterface(IDLObjectWithScope, I
                                self.parent.identifier.name),
                               [self.location])
         assert not parent or isinstance(parent, IDLInterface)
 
         self.parent = parent
 
         assert iter(self.members)
 
+        if self.isNamespace():
+            assert not self.parent
+            for m in self.members:
+                if m.isAttr() or m.isMethod():
+                    if m.isStatic():
+                        raise WebIDLError("Don't mark things explicitly static "
+                                          "in namespaces",
+                                          [self.location, m.location])
+                    # Just mark all our methods/attributes as static.  The other
+                    # option is to duplicate the relevant InterfaceMembers
+                    # production bits but modified to produce static stuff to
+                    # start with, but that sounds annoying.
+                    m.forceStatic()
+
         if self.parent:
             self.parent.finish(scope)
             self.parent._hasChildInterfaces = True
 
             self.totalMembersInSlots = self.parent.totalMembersInSlots
 
             # Interfaces with [Global] or [PrimaryGlobal] must not
             # have anything inherit from them
@@ -1319,19 +1337,16 @@ class IDLInterface(IDLObjectWithScope, I
             else:
                 assert iterableDecl.isPairIterator()
                 if indexedGetter:
                     raise WebIDLError("Interface with pair iterator supports "
                                       "indexed properties",
                                       [self.location, iterableDecl.location,
                                        indexedGetter.location])
 
-    def isInterface(self):
-        return True
-
     def isExternal(self):
         return False
 
     def setIsConsequentialInterfaceOf(self, other):
         self._consequential = True
         self.interfacesBasedOnSelf.add(other)
 
     def isConsequential(self):
@@ -1372,17 +1387,182 @@ class IDLInterface(IDLObjectWithScope, I
         return any(m.isConst() for m in self.members)
 
     def hasInterfaceObject(self):
         if self.isCallback():
             return self.hasConstants()
         return not hasattr(self, "_noInterfaceObject")
 
     def hasInterfacePrototypeObject(self):
-        return not self.isCallback() and self.getUserData('hasConcreteDescendant', False)
+        return (not self.isCallback() and not self.isNamespace()
+                and self.getUserData('hasConcreteDescendant', False))
+
+    def addImplementedInterface(self, implementedInterface):
+        assert(isinstance(implementedInterface, IDLInterface))
+        self.implementedInterfaces.add(implementedInterface)
+
+    def getInheritedInterfaces(self):
+        """
+        Returns a list of the interfaces this interface inherits from
+        (not including this interface itself).  The list is in order
+        from most derived to least derived.
+        """
+        assert(self._finished)
+        if not self.parent:
+            return []
+        parentInterfaces = self.parent.getInheritedInterfaces()
+        parentInterfaces.insert(0, self.parent)
+        return parentInterfaces
+
+    def getConsequentialInterfaces(self):
+        assert(self._finished)
+        # The interfaces we implement directly
+        consequentialInterfaces = set(self.implementedInterfaces)
+
+        # And their inherited interfaces
+        for iface in self.implementedInterfaces:
+            consequentialInterfaces |= set(iface.getInheritedInterfaces())
+
+        # And now collect up the consequential interfaces of all of those
+        temp = set()
+        for iface in consequentialInterfaces:
+            temp |= iface.getConsequentialInterfaces()
+
+        return consequentialInterfaces | temp
+
+    def findInterfaceLoopPoint(self, otherInterface):
+        """
+        Finds an interface, amongst our ancestors and consequential interfaces,
+        that inherits from otherInterface or implements otherInterface
+        directly.  If there is no such interface, returns None.
+        """
+        if self.parent:
+            if self.parent == otherInterface:
+                return self
+            loopPoint = self.parent.findInterfaceLoopPoint(otherInterface)
+            if loopPoint:
+                return loopPoint
+        if otherInterface in self.implementedInterfaces:
+            return self
+        for iface in self.implementedInterfaces:
+            loopPoint = iface.findInterfaceLoopPoint(otherInterface)
+            if loopPoint:
+                return loopPoint
+        return None
+
+    def getExtendedAttribute(self, name):
+        return self._extendedAttrDict.get(name, None)
+
+    def setNonPartial(self, location, parent, members):
+        assert not parent or isinstance(parent, IDLIdentifierPlaceholder)
+        if self._isKnownNonPartial:
+            raise WebIDLError("Two non-partial definitions for the "
+                              "same %s" %
+                              ("interface" if self.isInterface()
+                               else "namespace"),
+                              [location, self.location])
+        self._isKnownNonPartial = True
+        # Now make it look like we were parsed at this new location, since
+        # that's the place where the interface is "really" defined
+        self.location = location
+        assert not self.parent
+        self.parent = parent
+        # Put the new members at the beginning
+        self.members = members + self.members
+
+    def addPartialInterface(self, partial):
+        assert self.identifier.name == partial.identifier.name
+        self._partialInterfaces.append(partial)
+
+    def getJSImplementation(self):
+        classId = self.getExtendedAttribute("JSImplementation")
+        if not classId:
+            return classId
+        assert isinstance(classId, list)
+        assert len(classId) == 1
+        return classId[0]
+
+    def isJSImplemented(self):
+        return bool(self.getJSImplementation())
+
+    def isProbablyShortLivingObject(self):
+        current = self
+        while current:
+            if current.getExtendedAttribute("ProbablyShortLivingObject"):
+                return True
+            current = current.parent
+        return False
+
+    def isNavigatorProperty(self):
+        naviProp = self.getExtendedAttribute("NavigatorProperty")
+        if not naviProp:
+            return False
+        assert len(naviProp) == 1
+        assert isinstance(naviProp, list)
+        assert len(naviProp[0]) != 0
+        return True
+
+    def getNavigatorProperty(self):
+        naviProp = self.getExtendedAttribute("NavigatorProperty")
+        if not naviProp:
+            return None
+        assert len(naviProp) == 1
+        assert isinstance(naviProp, list)
+        assert len(naviProp[0]) != 0
+        conditionExtendedAttributes = self._extendedAttrDict.viewkeys() & IDLInterfaceOrNamespace.conditionExtendedAttributes
+        attr = IDLAttribute(self.location,
+                            IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"), naviProp[0]),
+                            IDLUnresolvedType(self.location, IDLUnresolvedIdentifier(self.location, self.identifier.name)),
+                            True,
+                            extendedAttrDict={ a: self._extendedAttrDict[a] for a in conditionExtendedAttributes },
+                            navigatorObjectGetter=True)
+        attr._exposureGlobalNames = self._exposureGlobalNames
+        # We're abusing Constant a little bit here, because we need Cached. The
+        # getter will create a new object every time, but we're never going to
+        # clear the cached value.
+        extendedAttrs = [ IDLExtendedAttribute(self.location, ("Throws", )),
+                          IDLExtendedAttribute(self.location, ("Cached", )),
+                          IDLExtendedAttribute(self.location, ("Constant", )) ]
+        attr.addExtendedAttributes(extendedAttrs)
+        return attr
+
+    def hasChildInterfaces(self):
+        return self._hasChildInterfaces
+
+    def isOnGlobalProtoChain(self):
+        return self._isOnGlobalProtoChain
+
+    def _getDependentObjects(self):
+        deps = set(self.members)
+        deps.update(self.implementedInterfaces)
+        if self.parent:
+            deps.add(self.parent)
+        return deps
+
+    def hasMembersInSlots(self):
+        return self._ownMembersInSlots != 0
+
+    conditionExtendedAttributes = [ "Pref", "ChromeOnly", "Func", "AvailableIn",
+                                    "SecureContext",
+                                    "CheckAnyPermissions",
+                                    "CheckAllPermissions" ]
+    def isExposedConditionally(self):
+        return any(self.getExtendedAttribute(a) for a in self.conditionExtendedAttributes)
+
+class IDLInterface(IDLInterfaceOrNamespace):
+    def __init__(self, location, parentScope, name, parent, members,
+                 isKnownNonPartial):
+        IDLInterfaceOrNamespace.__init__(self, location, parentScope, name,
+                                         parent, members, isKnownNonPartial)
+
+    def __str__(self):
+        return "Interface '%s'" % self.identifier.name
+
+    def isInterface(self):
+        return True
 
     def addExtendedAttributes(self, attrs):
         for attr in attrs:
             identifier = attr.identifier()
 
             # Special cased attrs
             if identifier == "TreatNonCallableAsNull":
                 raise WebIDLError("TreatNonCallableAsNull cannot be specified on interfaces",
@@ -1551,165 +1731,56 @@ class IDLInterface(IDLObjectWithScope, I
                                       [attr.location])
             else:
                 raise WebIDLError("Unknown extended attribute %s on interface" % identifier,
                                   [attr.location])
 
             attrlist = attr.listValue()
             self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True
 
-    def addImplementedInterface(self, implementedInterface):
-        assert(isinstance(implementedInterface, IDLInterface))
-        self.implementedInterfaces.add(implementedInterface)
-
-    def getInheritedInterfaces(self):
-        """
-        Returns a list of the interfaces this interface inherits from
-        (not including this interface itself).  The list is in order
-        from most derived to least derived.
-        """
-        assert(self._finished)
-        if not self.parent:
-            return []
-        parentInterfaces = self.parent.getInheritedInterfaces()
-        parentInterfaces.insert(0, self.parent)
-        return parentInterfaces
-
-    def getConsequentialInterfaces(self):
-        assert(self._finished)
-        # The interfaces we implement directly
-        consequentialInterfaces = set(self.implementedInterfaces)
-
-        # And their inherited interfaces
-        for iface in self.implementedInterfaces:
-            consequentialInterfaces |= set(iface.getInheritedInterfaces())
-
-        # And now collect up the consequential interfaces of all of those
-        temp = set()
-        for iface in consequentialInterfaces:
-            temp |= iface.getConsequentialInterfaces()
-
-        return consequentialInterfaces | temp
-
-    def findInterfaceLoopPoint(self, otherInterface):
-        """
-        Finds an interface, amongst our ancestors and consequential interfaces,
-        that inherits from otherInterface or implements otherInterface
-        directly.  If there is no such interface, returns None.
-        """
-        if self.parent:
-            if self.parent == otherInterface:
-                return self
-            loopPoint = self.parent.findInterfaceLoopPoint(otherInterface)
-            if loopPoint:
-                return loopPoint
-        if otherInterface in self.implementedInterfaces:
-            return self
-        for iface in self.implementedInterfaces:
-            loopPoint = iface.findInterfaceLoopPoint(otherInterface)
-            if loopPoint:
-                return loopPoint
-        return None
-
-    def getExtendedAttribute(self, name):
-        return self._extendedAttrDict.get(name, None)
-
-    def setNonPartial(self, location, parent, members):
-        assert not parent or isinstance(parent, IDLIdentifierPlaceholder)
-        if self._isKnownNonPartial:
-            raise WebIDLError("Two non-partial definitions for the "
-                              "same interface",
-                              [location, self.location])
-        self._isKnownNonPartial = True
-        # Now make it look like we were parsed at this new location, since
-        # that's the place where the interface is "really" defined
-        self.location = location
-        assert not self.parent
-        self.parent = parent
-        # Put the new members at the beginning
-        self.members = members + self.members
-
-    def addPartialInterface(self, partial):
-        assert self.identifier.name == partial.identifier.name
-        self._partialInterfaces.append(partial)
-
-    def getJSImplementation(self):
-        classId = self.getExtendedAttribute("JSImplementation")
-        if not classId:
-            return classId
-        assert isinstance(classId, list)
-        assert len(classId) == 1
-        return classId[0]
-
-    def isJSImplemented(self):
-        return bool(self.getJSImplementation())
-
-    def isProbablyShortLivingObject(self):
-        current = self
-        while current:
-            if current.getExtendedAttribute("ProbablyShortLivingObject"):
-                return True
-            current = current.parent
-        return False
-
-    def isNavigatorProperty(self):
-        naviProp = self.getExtendedAttribute("NavigatorProperty")
-        if not naviProp:
-            return False
-        assert len(naviProp) == 1
-        assert isinstance(naviProp, list)
-        assert len(naviProp[0]) != 0
+
+class IDLNamespace(IDLInterfaceOrNamespace):
+    def __init__(self, location, parentScope, name, members, isKnownNonPartial):
+        IDLInterfaceOrNamespace.__init__(self, location, parentScope, name,
+                                         None, members, isKnownNonPartial)
+
+    def __str__(self):
+        return "Namespace '%s'" % self.identifier.name
+
+    def isNamespace(self):
         return True
 
-    def getNavigatorProperty(self):
-        naviProp = self.getExtendedAttribute("NavigatorProperty")
-        if not naviProp:
-            return None
-        assert len(naviProp) == 1
-        assert isinstance(naviProp, list)
-        assert len(naviProp[0]) != 0
-        conditionExtendedAttributes = self._extendedAttrDict.viewkeys() & IDLInterface.conditionExtendedAttributes
-        attr = IDLAttribute(self.location,
-                            IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"), naviProp[0]),
-                            IDLUnresolvedType(self.location, IDLUnresolvedIdentifier(self.location, self.identifier.name)),
-                            True,
-                            extendedAttrDict={ a: self._extendedAttrDict[a] for a in conditionExtendedAttributes },
-                            navigatorObjectGetter=True)
-        attr._exposureGlobalNames = self._exposureGlobalNames
-        # We're abusing Constant a little bit here, because we need Cached. The
-        # getter will create a new object every time, but we're never going to
-        # clear the cached value.
-        extendedAttrs = [ IDLExtendedAttribute(self.location, ("Throws", )),
-                          IDLExtendedAttribute(self.location, ("Cached", )),
-                          IDLExtendedAttribute(self.location, ("Constant", )) ]
-        attr.addExtendedAttributes(extendedAttrs)
-        return attr
-
-    def hasChildInterfaces(self):
-        return self._hasChildInterfaces
-
-    def isOnGlobalProtoChain(self):
-        return self._isOnGlobalProtoChain
-
-    def _getDependentObjects(self):
-        deps = set(self.members)
-        deps.update(self.implementedInterfaces)
-        if self.parent:
-            deps.add(self.parent)
-        return deps
-
-    def hasMembersInSlots(self):
-        return self._ownMembersInSlots != 0
-
-    conditionExtendedAttributes = [ "Pref", "ChromeOnly", "Func", "AvailableIn",
-                                    "SecureContext",
-                                    "CheckAnyPermissions",
-                                    "CheckAllPermissions" ]
-    def isExposedConditionally(self):
-        return any(self.getExtendedAttribute(a) for a in self.conditionExtendedAttributes)
+    def addExtendedAttributes(self, attrs):
+        # The set of things namespaces support is small enough it's simpler
+        # to factor out into a separate method than it is to sprinkle
+        # isNamespace() checks all through
+        # IDLInterfaceOrNamespace.addExtendedAttributes.
+        for attr in attrs:
+            identifier = attr.identifier()
+
+            if identifier == "Exposed":
+                convertExposedAttrToGlobalNameSet(attr,
+                                                  self._exposureGlobalNames)
+            elif identifier == "ClassString":
+                # Takes a string value to override the default "Object" if
+                # desired.
+                if not attr.hasValue():
+                    raise WebIDLError("[%s] must have a value" % identifier,
+                                      [attr.location])
+            elif identifier == "ProtoObjectHack":
+                if not attr.noArguments():
+                    raise WebIDLError("[%s] must not have arguments" % identifier,
+                                      [attr.location])
+            else:
+                raise WebIDLError("Unknown extended attribute %s on namespace" %
+                                  identifier,
+                                  [attr.location])
+
+            attrlist = attr.listValue()
+            self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True
 
 
 class IDLDictionary(IDLObjectWithScope):
     def __init__(self, location, parentScope, name, parent, members):
         assert isinstance(parentScope, IDLScope)
         assert isinstance(name, IDLUnresolvedIdentifier)
         assert not parent or isinstance(parent, IDLIdentifierPlaceholder)
 
@@ -3925,17 +3996,17 @@ class IDLAttribute(IDLInterfaceMember):
         IDLInterfaceMember.__init__(self, location, identifier,
                                     IDLInterfaceMember.Tags.Attr,
                                     extendedAttrDict=extendedAttrDict)
 
         assert isinstance(type, IDLType)
         self.type = type
         self.readonly = readonly
         self.inherit = inherit
-        self.static = static
+        self._static = static
         self.lenientThis = False
         self._unforgeable = False
         self.stringifier = stringifier
         self.enforceRange = False
         self.clamp = False
         self.slotIndices = None
         assert maplikeOrSetlike is None or isinstance(maplikeOrSetlike, IDLMaplikeOrSetlike)
         self.maplikeOrSetlike = maplikeOrSetlike
@@ -3947,17 +4018,20 @@ class IDLAttribute(IDLInterfaceMember):
             raise WebIDLError("The identifier of a static attribute must not be 'prototype'",
                               [location])
 
         if readonly and inherit:
             raise WebIDLError("An attribute cannot be both 'readonly' and 'inherit'",
                               [self.location])
 
     def isStatic(self):
-        return self.static
+        return self._static
+
+    def forceStatic(self):
+        self._static = True
 
     def __str__(self):
         return "'%s' attribute '%s'" % (self.type, self.identifier)
 
     def finish(self, scope):
         IDLInterfaceMember.finish(self, scope)
 
         if not self.type.isComplete():
@@ -4001,26 +4075,63 @@ class IDLAttribute(IDLInterfaceMember):
             raise WebIDLError("An attribute with [PutForwards] must have an "
                               "interface type as its type", [self.location])
 
         if not self.type.isInterface() and self.getExtendedAttribute("SameObject"):
             raise WebIDLError("An attribute with [SameObject] must have an "
                               "interface type as its type", [self.location])
 
     def validate(self):
+        def typeContainsChromeOnlyDictionaryMember(type):
+            if (type.nullable() or
+                type.isSequence() or
+                type.isMozMap()):
+                return typeContainsChromeOnlyDictionaryMember(type.inner)
+
+            if type.isUnion():
+                for memberType in type.flatMemberTypes:
+                    (contains, location) = typeContainsChromeOnlyDictionaryMember(memberType)
+                    if contains:
+                        return (True, location)
+
+            if type.isDictionary():
+                dictionary = type.inner
+                while dictionary:
+                    (contains, location) = dictionaryContainsChromeOnlyMember(dictionary)
+                    if contains:
+                        return (True, location)
+                    dictionary = dictionary.parent
+
+            return (False, None)
+
+        def dictionaryContainsChromeOnlyMember(dictionary):
+            for member in dictionary.members:
+                if member.getExtendedAttribute("ChromeOnly"):
+                    return (True, member.location)
+                (contains, location) = typeContainsChromeOnlyDictionaryMember(member.type)
+                if contains:
+                    return (True, location)
+            return (False, None)
+
         IDLInterfaceMember.validate(self)
 
-        if ((self.getExtendedAttribute("Cached") or
-             self.getExtendedAttribute("StoreInSlot")) and
-            not self.affects == "Nothing"):
-            raise WebIDLError("Cached attributes and attributes stored in "
-                              "slots must be Constant or Pure or "
-                              "Affects=Nothing, since the getter won't always "
-                              "be called.",
-                              [self.location])
+        if (self.getExtendedAttribute("Cached") or
+            self.getExtendedAttribute("StoreInSlot")):
+            if not self.affects == "Nothing":
+                raise WebIDLError("Cached attributes and attributes stored in "
+                                  "slots must be Constant or Pure or "
+                                  "Affects=Nothing, since the getter won't always "
+                                  "be called.",
+                                  [self.location])
+            (contains, location) = typeContainsChromeOnlyDictionaryMember(self.type)
+            if contains:
+                raise WebIDLError("[Cached] and [StoreInSlot] must not be used "
+                                  "on an attribute whose type contains a "
+                                  "[ChromeOnly] dictionary member",
+                                  [self.location, location])
         if self.getExtendedAttribute("Frozen"):
             if (not self.type.isSequence() and not self.type.isDictionary() and
                 not self.type.isMozMap()):
                 raise WebIDLError("[Frozen] is only allowed on "
                                   "sequence-valued, dictionary-valued, and "
                                   "MozMap-valued attributes",
                                   [self.location])
         if not self.type.unroll().isExposedInAllOf(self.exposureSet):
@@ -4256,16 +4367,17 @@ class IDLArgument(IDLObjectWithIdentifie
         self.optional = optional
         self.defaultValue = defaultValue
         self.variadic = variadic
         self.dictionaryMember = dictionaryMember
         self._isComplete = False
         self.enforceRange = False
         self.clamp = False
         self._allowTreatNonCallableAsNull = False
+        self._extendedAttrDict = {}
 
         assert not variadic or optional
         assert not variadic or not defaultValue
 
     def addExtendedAttributes(self, attrs):
         attrs = self.checkForStringHandlingExtendedAttributes(
             attrs,
             isDictionaryMember=self.dictionaryMember,
@@ -4285,21 +4397,31 @@ class IDLArgument(IDLObjectWithIdentifie
                     raise WebIDLError("[EnforceRange] must take no arguments",
                                       [attribute.location])
                 if self.clamp:
                     raise WebIDLError("[EnforceRange] and [Clamp] are mutually exclusive",
                                       [self.location])
                 self.enforceRange = True
             elif identifier == "TreatNonCallableAsNull":
                 self._allowTreatNonCallableAsNull = True
+            elif self.dictionaryMember and identifier == "ChromeOnly":
+                if not self.optional:
+                    raise WebIDLError("[ChromeOnly] must not be used on a required "
+                                      "dictionary member",
+                                      [attribute.location])
             else:
                 raise WebIDLError("Unhandled extended attribute on %s" %
                                   ("a dictionary member" if self.dictionaryMember else
                                    "an argument"),
                                   [attribute.location])
+            attrlist = attribute.listValue()
+            self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True
+
+    def getExtendedAttribute(self, name):
+        return self._extendedAttrDict.get(name, None)
 
     def isComplete(self):
         return self._isComplete
 
     def complete(self, scope):
         if self._isComplete:
             return
 
@@ -4560,16 +4682,19 @@ class IDLMethod(IDLInterfaceMember, IDLS
             assert len(self._overloads) == 1
             overload = self._overloads[0]
             assert len(overload.arguments) == 0
             assert overload.returnType == BuiltinTypes[IDLBuiltinType.Types.object]
 
     def isStatic(self):
         return self._static
 
+    def forceStatic(self):
+        self._static = True
+
     def isGetter(self):
         return self._getter
 
     def isSetter(self):
         return self._setter
 
     def isCreator(self):
         return self._creator
@@ -5139,17 +5264,18 @@ class Tokenizer(object):
         "=": "EQUALS",
         "<": "LT",
         ">": "GT",
         "ArrayBuffer": "ARRAYBUFFER",
         "SharedArrayBuffer": "SHAREDARRAYBUFFER",
         "or": "OR",
         "maplike": "MAPLIKE",
         "setlike": "SETLIKE",
-        "iterable": "ITERABLE"
+        "iterable": "ITERABLE",
+        "namespace": "NAMESPACE"
         }
 
     tokens.extend(keywords.values())
 
     def t_error(self, t):
         raise WebIDLError("Unrecognized Input",
                           [Location(lexer=self.lexer,
                                     lineno=self.lexer.lineno,
@@ -5236,17 +5362,18 @@ class Parser(Tokenizer):
         """
             Definitions :
         """
         p[0] = []
 
     def p_Definition(self, p):
         """
             Definition : CallbackOrInterface
-                       | PartialInterface
+                       | Namespace
+                       | Partial
                        | Dictionary
                        | Exception
                        | Enum
                        | Typedef
                        | ImplementsStatement
         """
         p[0] = p[1]
         assert p[1]  # We might not have implemented something ...
@@ -5270,43 +5397,64 @@ class Parser(Tokenizer):
     def p_CallbackRestOrInterface(self, p):
         """
             CallbackRestOrInterface : CallbackRest
                                     | Interface
         """
         assert p[1]
         p[0] = p[1]
 
+    def handleNonPartialObject(self, location, identifier, constructor,
+                               constructorArgs, nonPartialArgs):
+        """
+        This handles non-partial objects (interfaces and namespaces) by
+        checking for an existing partial object, and promoting it to
+        non-partial as needed.  The return value is the non-partial object.
+
+        constructorArgs are all the args for the constructor except the last
+        one: isKnownNonPartial.
+
+        nonPartialArgs are the args for the setNonPartial call.
+        """
+        # The name of the class starts with "IDL", so strip that off.
+        # Also, starts with a capital letter after that, so nix that
+        # as well.
+        prettyname = constructor.__name__[3:].lower()
+
+        try:
+            existingObj = self.globalScope()._lookupIdentifier(identifier)
+            if existingObj:
+                if not isinstance(existingObj, constructor):
+                    raise WebIDLError("%s has the same name as "
+                                      "non-%s object" %
+                                      (prettyname.capitalize(), prettyname),
+                                      [location, existingObj.location])
+                existingObj.setNonPartial(*nonPartialArgs)
+                return existingObj
+        except Exception, ex:
+            if isinstance(ex, WebIDLError):
+                raise ex
+            pass
+
+        # True for isKnownNonPartial
+        return constructor(*(constructorArgs + [True]))
+
     def p_Interface(self, p):
         """
             Interface : INTERFACE IDENTIFIER Inheritance LBRACE InterfaceMembers RBRACE SEMICOLON
         """
         location = self.getLocation(p, 1)
         identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
         members = p[5]
         parent = p[3]
 
-        try:
-            existingObj = self.globalScope()._lookupIdentifier(identifier)
-            if existingObj:
-                p[0] = existingObj
-                if not isinstance(p[0], IDLInterface):
-                    raise WebIDLError("Interface has the same name as "
-                                      "non-interface object",
-                                      [location, p[0].location])
-                p[0].setNonPartial(location, parent, members)
-                return
-        except Exception, ex:
-            if isinstance(ex, WebIDLError):
-                raise ex
-            pass
-
-        iface = IDLInterface(location, self.globalScope(), identifier, parent,
-                            members, isKnownNonPartial=True)
-        p[0] = iface
+        p[0] = self.handleNonPartialObject(
+            location, identifier, IDLInterface,
+            [location, self.globalScope(), identifier, parent, members],
+            [location, parent, members])
 
     def p_InterfaceForwardDecl(self, p):
         """
             Interface : INTERFACE IDENTIFIER SEMICOLON
         """
         location = self.getLocation(p, 1)
         identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
 
@@ -5321,44 +5469,110 @@ class Parser(Tokenizer):
                 return
         except Exception, ex:
             if isinstance(ex, WebIDLError):
                 raise ex
             pass
 
         p[0] = IDLExternalInterface(location, self.globalScope(), identifier)
 
-    def p_PartialInterface(self, p):
-        """
-            PartialInterface : PARTIAL INTERFACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON
-        """
-        location = self.getLocation(p, 2)
-        identifier = IDLUnresolvedIdentifier(self.getLocation(p, 3), p[3])
-        members = p[5]
-
-        nonPartialInterface = None
+    def p_Namespace(self, p):
+        """
+            Namespace : NAMESPACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON
+        """
+        location = self.getLocation(p, 1)
+        identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
+        members = p[4]
+
+        p[0] = self.handleNonPartialObject(
+            location, identifier, IDLNamespace,
+            [location, self.globalScope(), identifier, members],
+            [location, None, members])
+
+    def p_Partial(self, p):
+        """
+            Partial : PARTIAL PartialDefinition
+        """
+        p[0] = p[2]
+
+    def p_PartialDefinition(self, p):
+        """
+            PartialDefinition : PartialInterface
+                              | PartialNamespace
+        """
+        p[0] = p[1]
+
+    def handlePartialObject(self, location, identifier, nonPartialConstructor,
+                            nonPartialConstructorArgs,
+                            partialConstructorArgs):
+        """
+        This handles partial objects (interfaces and namespaces) by checking for
+        an existing non-partial object, and adding ourselves to it as needed.
+        The return value is our partial object.  For now we just use
+        IDLPartialInterfaceOrNamespace for partial objects.
+
+        nonPartialConstructorArgs are all the args for the non-partial
+        constructor except the last two: members and isKnownNonPartial.
+
+        partialConstructorArgs are the arguments for the
+        IDLPartialInterfaceOrNamespace constructor, except the last one (the
+        non-partial object).
+        """
+        # The name of the class starts with "IDL", so strip that off.
+        # Also, starts with a capital letter after that, so nix that
+        # as well.
+        prettyname = nonPartialConstructor.__name__[3:].lower()
+
+        nonPartialObject = None
         try:
-            nonPartialInterface = self.globalScope()._lookupIdentifier(identifier)
-            if nonPartialInterface:
-                if not isinstance(nonPartialInterface, IDLInterface):
-                    raise WebIDLError("Partial interface has the same name as "
-                                      "non-interface object",
-                                      [location, nonPartialInterface.location])
+            nonPartialObject = self.globalScope()._lookupIdentifier(identifier)
+            if nonPartialObject:
+                if not isinstance(nonPartialObject, nonPartialConstructor):
+                    raise WebIDLError("Partial %s has the same name as "
+                                      "non-%s object" %
+                                      (prettyname, prettyname),
+                                      [location, nonPartialObject.location])
         except Exception, ex:
             if isinstance(ex, WebIDLError):
                 raise ex
             pass
 
-        if not nonPartialInterface:
-            nonPartialInterface = IDLInterface(location, self.globalScope(),
-                                               identifier, None,
-                                               [], isKnownNonPartial=False)
-        partialInterface = IDLPartialInterface(location, identifier, members,
-                                               nonPartialInterface)
-        p[0] = partialInterface
+        if not nonPartialObject:
+            nonPartialObject = nonPartialConstructor(
+                # No members, False for isKnownNonPartial
+                *(nonPartialConstructorArgs + [[], False]))
+        partialInterface = IDLPartialInterfaceOrNamespace(
+            *(partialConstructorArgs + [nonPartialObject]))
+        return partialInterface
+
+    def p_PartialInterface(self, p):
+        """
+            PartialInterface : INTERFACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON
+        """
+        location = self.getLocation(p, 1)
+        identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
+        members = p[4]
+
+        p[0] = self.handlePartialObject(
+            location, identifier, IDLInterface,
+            [location, self.globalScope(), identifier, None],
+            [location, identifier, members])
+
+    def p_PartialNamespace(self, p):
+        """
+            PartialNamespace : NAMESPACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON
+        """
+        location = self.getLocation(p, 1)
+        identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
+        members = p[4]
+
+        p[0] = self.handlePartialObject(
+            location, identifier, IDLNamespace,
+            [location, self.globalScope(), identifier],
+            [location, identifier, members])
 
     def p_Inheritance(self, p):
         """
             Inheritance : COLON ScopedName
         """
         p[0] = IDLIdentifierPlaceholder(self.getLocation(p, 2), p[2])
 
     def p_InheritanceEmpty(self, p):
@@ -6009,16 +6223,17 @@ class Parser(Tokenizer):
                          | SERIALIZER
                          | SETLIKE
                          | SETTER
                          | STATIC
                          | STRINGIFIER
                          | JSONIFIER
                          | TYPEDEF
                          | UNRESTRICTED
+                         | NAMESPACE
         """
         p[0] = p[1]
 
     def p_AttributeName(self, p):
         """
             AttributeName : IDENTIFIER
                           | REQUIRED
         """
@@ -6609,28 +6824,30 @@ class Parser(Tokenizer):
                               [self._filename])
         else:
             raise WebIDLError("invalid syntax", [Location(self.lexer, p.lineno, p.lexpos, self._filename)])
 
     def __init__(self, outputdir='', lexer=None):
         Tokenizer.__init__(self, outputdir, lexer)
 
         logger = SqueakyCleanLogger()
-        self.parser = yacc.yacc(module=self,
-                                outputdir=outputdir,
-                                tabmodule='webidlyacc',
-                                errorlog=logger
-                                # Pickling the grammar is a speedup in
-                                # some cases (older Python?) but a
-                                # significant slowdown in others.
-                                # We're not pickling for now, until it
-                                # becomes a speedup again.
-                                # , picklefile='WebIDLGrammar.pkl'
-                                )
-        logger.reportGrammarErrors()
+        try:
+            self.parser = yacc.yacc(module=self,
+                                    outputdir=outputdir,
+                                    tabmodule='webidlyacc',
+                                    errorlog=logger
+                                    # Pickling the grammar is a speedup in
+                                    # some cases (older Python?) but a
+                                    # significant slowdown in others.
+                                    # We're not pickling for now, until it
+                                    # becomes a speedup again.
+                                    # , picklefile='WebIDLGrammar.pkl'
+            )
+        finally:
+            logger.reportGrammarErrors()
 
         self._globalScope = IDLScope(BuiltinLocation("<Global Scope>"), None, None)
         # To make our test harness work, pretend like we have a primary global already.
         # Note that we _don't_ set _globalScope.primaryGlobalAttr,
         # so we'll still be able to detect multiple PrimaryGlobal extended attributes.
         self._globalScope.primaryGlobalName = "FakeTestPrimaryGlobal"
         self._globalScope.globalNames.add("FakeTestPrimaryGlobal")
         self._globalScope.globalNameMapping["FakeTestPrimaryGlobal"].add("FakeTestPrimaryGlobal")
@@ -6691,20 +6908,21 @@ class Parser(Tokenizer):
 
         iterableIteratorIface = None
         for iface in interfaceStatements:
             navigatorProperty = iface.getNavigatorProperty()
             if navigatorProperty:
                 # We're generating a partial interface to add a readonly
                 # property to the Navigator interface for every interface
                 # annotated with NavigatorProperty.
-                partialInterface = IDLPartialInterface(iface.location,
-                                                       IDLUnresolvedIdentifier(iface.location, "Navigator"),
-                                                       [ navigatorProperty ],
-                                                       navigatorInterface)
+                partialInterface = IDLPartialInterfaceOrNamespace(
+                    iface.location,
+                    IDLUnresolvedIdentifier(iface.location, "Navigator"),
+                    [ navigatorProperty ],
+                    navigatorInterface)
                 self._productions.append(partialInterface)
 
             iterable = None
             # We haven't run finish() on the interface yet, so we don't know
             # whether our interface is maplike/setlike/iterable or not. This
             # means we have to loop through the members to see if we have an
             # iterable member.
             for m in iface.members:
new file mode 100644
--- /dev/null
+++ b/dom/bindings/parser/tests/test_conditional_dictionary_member.py
@@ -0,0 +1,110 @@
+def WebIDLTest(parser, harness):
+    parser.parse("""
+      dictionary Dict {
+        any foo;
+        [ChromeOnly] any bar;
+      };
+    """)
+    results = parser.finish()
+    harness.check(len(results), 1, "Should have a dictionary")
+    members = results[0].members;
+    harness.check(len(members), 2, "Should have two members")
+    # Note that members are ordered lexicographically, so "bar" comes
+    # before "foo".
+    harness.ok(members[0].getExtendedAttribute("ChromeOnly"),
+               "First member is not ChromeOnly")
+    harness.ok(not members[1].getExtendedAttribute("ChromeOnly"),
+               "Second member is ChromeOnly")
+
+    parser = parser.reset()
+    parser.parse("""
+      dictionary Dict {
+        any foo;
+        any bar;
+      };
+
+      interface Iface {
+        [Constant, Cached] readonly attribute Dict dict;
+      };
+    """)
+    results = parser.finish()
+    harness.check(len(results), 2, "Should have a dictionary and an interface")
+
+    parser = parser.reset()
+    exception = None
+    try:
+      parser.parse("""
+        dictionary Dict {
+          any foo;
+          [ChromeOnly] any bar;
+        };
+
+        interface Iface {
+          [Constant, Cached] readonly attribute Dict dict;
+        };
+      """)
+      results = parser.finish()
+    except Exception, exception:
+        pass
+
+    harness.ok(exception, "Should have thrown.")
+    harness.check(exception.message,
+                  "[Cached] and [StoreInSlot] must not be used on an attribute "
+                  "whose type contains a [ChromeOnly] dictionary member",
+                  "Should have thrown the right exception")
+
+    parser = parser.reset()
+    exception = None
+    try:
+      parser.parse("""
+        dictionary ParentDict {
+          [ChromeOnly] any bar;
+        };
+
+        dictionary Dict : ParentDict {
+          any foo;
+        };
+
+        interface Iface {
+          [Constant, Cached] readonly attribute Dict dict;
+        };
+      """)
+      results = parser.finish()
+    except Exception, exception:
+        pass
+
+    harness.ok(exception, "Should have thrown (2).")
+    harness.check(exception.message,
+                  "[Cached] and [StoreInSlot] must not be used on an attribute "
+                  "whose type contains a [ChromeOnly] dictionary member",
+                  "Should have thrown the right exception (2)")
+
+    parser = parser.reset()
+    exception = None
+    try:
+      parser.parse("""
+        dictionary GrandParentDict {
+          [ChromeOnly] any baz;
+        };
+
+        dictionary ParentDict : GrandParentDict {
+          any bar;
+        };
+
+        dictionary Dict : ParentDict {
+          any foo;
+        };
+
+        interface Iface {
+          [Constant, Cached] readonly attribute Dict dict;
+        };
+      """)
+      results = parser.finish()
+    except Exception, exception:
+        pass
+
+    harness.ok(exception, "Should have thrown (3).")
+    harness.check(exception.message,
+                  "[Cached] and [StoreInSlot] must not be used on an attribute "
+                  "whose type contains a [ChromeOnly] dictionary member",
+                  "Should have thrown the right exception (3)")
new file mode 100644
--- /dev/null
+++ b/dom/bindings/parser/tests/test_namespace.py
@@ -0,0 +1,223 @@
+def WebIDLTest(parser, harness):
+    parser.parse(
+        """
+        namespace MyNamespace {
+          attribute any foo;
+          any bar();
+        };
+        """)
+
+    results = parser.finish()
+    harness.check(len(results), 1, "Should have a thing.")
+    harness.ok(results[0].isNamespace(), "Our thing should be a namespace");
+    harness.check(len(results[0].members), 2,
+                  "Should have two things in our namespace")
+    harness.ok(results[0].members[0].isAttr(), "First member is attribute")
+    harness.ok(results[0].members[0].isStatic(), "Attribute should be static")
+    harness.ok(results[0].members[1].isMethod(), "Second member is method")
+    harness.ok(results[0].members[1].isStatic(), "Operation should be static")
+
+    parser = parser.reset()
+    parser.parse(
+        """
+        namespace MyNamespace {
+          attribute any foo;
+        };
+        partial namespace MyNamespace {
+          any bar();
+        };
+        """)
+
+    results = parser.finish()
+    harness.check(len(results), 2, "Should have things.")
+    harness.ok(results[0].isNamespace(), "Our thing should be a namespace");
+    harness.check(len(results[0].members), 2,
+                  "Should have two things in our namespace")
+    harness.ok(results[0].members[0].isAttr(), "First member is attribute")
+    harness.ok(results[0].members[0].isStatic(), "Attribute should be static");
+    harness.ok(results[0].members[1].isMethod(), "Second member is method")
+    harness.ok(results[0].members[1].isStatic(), "Operation should be static");
+
+    parser = parser.reset()
+    parser.parse(
+        """
+        partial namespace MyNamespace {
+          any bar();
+        };
+        namespace MyNamespace {
+          attribute any foo;
+        };
+        """)
+
+    results = parser.finish()
+    harness.check(len(results), 2, "Should have things.")
+    harness.ok(results[1].isNamespace(), "Our thing should be a namespace");
+    harness.check(len(results[1].members), 2,
+                  "Should have two things in our namespace")
+    harness.ok(results[1].members[0].isAttr(), "First member is attribute")
+    harness.ok(results[1].members[0].isStatic(), "Attribute should be static");
+    harness.ok(results[1].members[1].isMethod(), "Second member is method")
+    harness.ok(results[1].members[1].isStatic(), "Operation should be static");
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse(
+            """
+            namespace MyNamespace {
+              static attribute any foo;
+            };
+        """)
+
+        results = parser.finish()
+    except Exception, x:
+        threw = True
+    harness.ok(threw, "Should have thrown.")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse(
+            """
+            namespace MyNamespace {
+              static any bar();
+            };
+        """)
+
+        results = parser.finish()
+    except Exception, x:
+        threw = True
+    harness.ok(threw, "Should have thrown.")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse(
+            """
+            namespace MyNamespace {
+              any bar();
+            };
+
+            interface MyNamespace {
+              any baz();
+            };
+        """)
+
+        results = parser.finish()
+    except Exception, x:
+        threw = True
+    harness.ok(threw, "Should have thrown.")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse(
+            """
+            interface MyNamespace {
+              any baz();
+            };
+
+            namespace MyNamespace {
+              any bar();
+            };
+        """)
+
+        results = parser.finish()
+    except Exception, x:
+        threw = True
+    harness.ok(threw, "Should have thrown.")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse(
+            """
+            namespace MyNamespace {
+              any baz();
+            };
+
+            namespace MyNamespace {
+              any bar();
+            };
+        """)
+
+        results = parser.finish()
+    except Exception, x:
+        threw = True
+    harness.ok(threw, "Should have thrown.")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse(
+            """
+            partial namespace MyNamespace {
+              any baz();
+            };
+
+            interface MyNamespace {
+              any bar();
+            };
+        """)
+
+        results = parser.finish()
+    except Exception, x:
+        threw = True
+    harness.ok(threw, "Should have thrown.")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse(
+            """
+            namespace MyNamespace {
+              any bar();
+            };
+
+            partial interface MyNamespace {
+              any baz();
+            };
+        """)
+
+        results = parser.finish()
+    except Exception, x:
+        threw = True
+    harness.ok(threw, "Should have thrown.")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse(
+            """
+            partial interface MyNamespace {
+              any baz();
+            };
+
+            namespace MyNamespace {
+              any bar();
+            };
+        """)
+
+        results = parser.finish()
+    except Exception, x:
+        threw = True
+    harness.ok(threw, "Should have thrown.")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse(
+            """
+            interface MyNamespace {
+              any bar();
+            };
+
+            partial namespace MyNamespace {
+              any baz();
+            };
+        """)
+
+        results = parser.finish()
+    except Exception, x:
+        threw = True
+    harness.ok(threw, "Should have thrown.")
--- a/dom/bindings/test/TestBindingHeader.h
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -1419,12 +1419,25 @@ public:
   already_AddRefed<TestSecureContextInterface>
     Constructor(const GlobalObject&, ErrorResult&);
 
   static void AlsoSecureContext(const GlobalObject&);
 
   virtual nsISupports* GetParentObject();
 };
 
+class TestNamespace {
+public:
+  static bool Foo(const GlobalObject&);
+  static int32_t Bar(const GlobalObject&);
+  static void Baz(const GlobalObject&);
+};
+
+class TestRenamedNamespace {
+};
+
+class TestProtoObjectHackedNamespace {
+};
+
 } // namespace dom
 } // namespace mozilla
 
 #endif /* TestBindingHeader_h */
--- a/dom/bindings/test/TestCodeGen.webidl
+++ b/dom/bindings/test/TestCodeGen.webidl
@@ -1212,12 +1212,29 @@ interface TestDeprecatedInterface {
   static void alsoDeprecated();
 };
 
 
 [Constructor(Promise<void> promise)]
 interface TestInterfaceWithPromiseConstructorArg {
 };
 
+namespace TestNamespace {
+  readonly attribute boolean foo;
+  long bar();
+};
+
+partial namespace TestNamespace {
+  void baz();
+};
+
+[ClassString="RenamedNamespaceClassName"]
+namespace TestRenamedNamespace {
+};
+
+[ProtoObjectHack]
+namespace TestProtoObjectHackedNamespace {
+};
+
 [SecureContext]
 interface TestSecureContextInterface {
   static void alsoSecureContext();
 };
--- a/dom/canvas/ImageBitmap.cpp
+++ b/dom/canvas/ImageBitmap.cpp
@@ -7,19 +7,21 @@
 #include "mozilla/dom/ImageBitmap.h"
 
 #include "mozilla/dom/ImageBitmapBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/StructuredCloneTags.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/WorkerRunnable.h"
 #include "mozilla/gfx/2D.h"
+#include "ImageBitmapColorUtils.h"
+#include "ImageBitmapUtils.h"
+#include "ImageUtils.h"
 #include "imgTools.h"
 #include "libyuv.h"
-#include "nsLayoutUtils.h"
 
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 
 namespace mozilla {
 namespace dom {
 
 using namespace workers;
@@ -390,18 +392,20 @@ HasRasterImage(HTMLImageElement& aImageE
   return false;
 }
 
 ImageBitmap::ImageBitmap(nsIGlobalObject* aGlobal, layers::Image* aData,
                          bool aIsPremultipliedAlpha /* = true */)
   : mParent(aGlobal)
   , mData(aData)
   , mSurface(nullptr)
+  , mDataWrapper(new ImageUtils(mData))
   , mPictureRect(0, 0, aData->GetSize().width, aData->GetSize().height)
   , mIsPremultipliedAlpha(aIsPremultipliedAlpha)
+  , mIsCroppingAreaOutSideOfSourceImage(false)
 {
   MOZ_ASSERT(aData, "aData is null in ImageBitmap constructor.");
 }
 
 ImageBitmap::~ImageBitmap()
 {
 }
 
@@ -420,16 +424,107 @@ ImageBitmap::Close()
 }
 
 void
 ImageBitmap::SetPictureRect(const IntRect& aRect, ErrorResult& aRv)
 {
   mPictureRect = FixUpNegativeDimension(aRect, aRv);
 }
 
+void
+ImageBitmap::SetIsCroppingAreaOutSideOfSourceImage(const IntSize& aSourceSize,
+                                                   const Maybe<IntRect>& aCroppingRect)
+{
+  // No cropping at all.
+  if (aCroppingRect.isNothing()) {
+    mIsCroppingAreaOutSideOfSourceImage = false;
+    return;
+  }
+
+  if (aCroppingRect->X() < 0 || aCroppingRect->Y() < 0 ||
+      aCroppingRect->Width() > aSourceSize.width ||
+      aCroppingRect->Height() > aSourceSize.height) {
+    mIsCroppingAreaOutSideOfSourceImage = true;
+  }
+}
+
+static already_AddRefed<SourceSurface>
+ConvertColorFormatIfNeeded(RefPtr<SourceSurface> aSurface)
+{
+  const SurfaceFormat srcFormat = aSurface->GetFormat();
+  if (srcFormat == SurfaceFormat::R8G8B8A8 ||
+      srcFormat == SurfaceFormat::B8G8R8A8 ||
+      srcFormat == SurfaceFormat::R8G8B8X8 ||
+      srcFormat == SurfaceFormat::B8G8R8X8 ||
+      srcFormat == SurfaceFormat::A8R8G8B8 ||
+      srcFormat == SurfaceFormat::X8R8G8B8) {
+    return aSurface.forget();
+  }
+
+  if (srcFormat == SurfaceFormat::A8 ||
+      srcFormat == SurfaceFormat::Depth) {
+    return nullptr;
+  }
+
+  const int bytesPerPixel = BytesPerPixel(SurfaceFormat::B8G8R8A8);
+  const IntSize dstSize = aSurface->GetSize();
+  const uint32_t dstStride = dstSize.width * bytesPerPixel;
+
+  RefPtr<DataSourceSurface> dstDataSurface =
+    Factory::CreateDataSourceSurfaceWithStride(dstSize,
+                                               SurfaceFormat::B8G8R8A8,
+                                               dstStride);
+
+  RefPtr<DataSourceSurface> srcDataSurface = aSurface->GetDataSurface();
+  if (NS_WARN_IF(!srcDataSurface)) {
+    return nullptr;
+  }
+
+  DataSourceSurface::ScopedMap srcMap(srcDataSurface, DataSourceSurface::READ);
+  DataSourceSurface::ScopedMap dstMap(dstDataSurface, DataSourceSurface::WRITE);
+  if (NS_WARN_IF(!srcMap.IsMapped()) || NS_WARN_IF(!dstMap.IsMapped())) {
+    return nullptr;
+  }
+
+  int rv = 0;
+  if (srcFormat == SurfaceFormat::R8G8B8) {
+    rv = RGB24ToBGRA32(srcMap.GetData(), srcMap.GetStride(),
+                       dstMap.GetData(), dstMap.GetStride(),
+                       dstSize.width, dstSize.height);
+  } else if (srcFormat == SurfaceFormat::B8G8R8) {
+    rv = BGR24ToBGRA32(srcMap.GetData(), srcMap.GetStride(),
+                       dstMap.GetData(), dstMap.GetStride(),
+                       dstSize.width, dstSize.height);
+  } else if (srcFormat == SurfaceFormat::HSV) {
+    rv = HSVToBGRA32((const float*)srcMap.GetData(), srcMap.GetStride(),
+                     dstMap.GetData(), dstMap.GetStride(),
+                     dstSize.width, dstSize.height);
+  } else if (srcFormat == SurfaceFormat::Lab) {
+    rv = LabToBGRA32((const float*)srcMap.GetData(), srcMap.GetStride(),
+                     dstMap.GetData(), dstMap.GetStride(),
+                     dstSize.width, dstSize.height);
+  }
+
+  if (NS_WARN_IF(rv != 0)) {
+    return nullptr;
+  }
+
+  return dstDataSurface.forget();
+}
+
+/*
+ * The functionality of PrepareForDrawTarget method:
+ * (1) Get a SourceSurface from the mData (which is a layers::Image).
+ * (2) Convert the SourceSurface to format B8G8R8A8 if the original format is
+ *     R8G8B8, B8G8R8, HSV or Lab.
+ *     Note: if the original format is A8 or Depth, then return null directly.
+ * (3) Do cropping if the size of SourceSurface does not equal to the
+ *     mPictureRect.
+ * (4) Pre-multiply alpha if needed.
+ */
 already_AddRefed<SourceSurface>
 ImageBitmap::PrepareForDrawTarget(gfx::DrawTarget* aTarget)
 {
   MOZ_ASSERT(aTarget);
 
   if (!mData) {
     return nullptr;
   }
@@ -437,16 +532,24 @@ ImageBitmap::PrepareForDrawTarget(gfx::D
   if (!mSurface) {
     mSurface = mData->GetAsSourceSurface();
 
     if (!mSurface) {
       return nullptr;
     }
   }
 
+  // Check if we need to convert the format.
+  // Convert R8G8B8/B8G8R8/HSV/Lab to B8G8R8A8.
+  // Return null if the original format is A8 or Depth.
+  mSurface = ConvertColorFormatIfNeeded(mSurface);
+  if (NS_WARN_IF(!mSurface)) {
+    return nullptr;
+  }
+
   RefPtr<DrawTarget> target = aTarget;
   IntRect surfRect(0, 0, mSurface->GetSize().width, mSurface->GetSize().height);
 
   // Check if we still need to crop our surface
   if (!mPictureRect.IsEqualEdges(surfRect)) {
 
     IntRect surfPortion = surfRect.Intersect(mPictureRect);
 
@@ -476,17 +579,22 @@ ImageBitmap::PrepareForDrawTarget(gfx::D
     mSurface = target->Snapshot();
 
     // Make mCropRect match new surface we've cropped to
     mPictureRect.MoveTo(0, 0);
   }
 
   // Pre-multiply alpha here.
   // Apply pre-multiply alpha only if mIsPremultipliedAlpha is false.
-  if (!mIsPremultipliedAlpha) {
+  // Ignore this step if the source surface does not have alpha channel; this
+  // kind of source surfaces might come form layers::PlanarYCbCrImage.
+  if (!mIsPremultipliedAlpha &&
+      mSurface->GetFormat() != SurfaceFormat::B8G8R8X8 &&
+      mSurface->GetFormat() != SurfaceFormat::R8G8B8X8 &&
+      mSurface->GetFormat() != SurfaceFormat::X8R8G8B8) {
     MOZ_ASSERT(mSurface->GetFormat() == SurfaceFormat::R8G8B8A8 ||
                mSurface->GetFormat() == SurfaceFormat::B8G8R8A8 ||
                mSurface->GetFormat() == SurfaceFormat::A8R8G8B8);
 
     RefPtr<DataSourceSurface> dstSurface = mSurface->GetDataSurface();
     MOZ_ASSERT(dstSurface);
 
     RefPtr<DataSourceSurface> srcSurface;
@@ -576,32 +684,36 @@ ImageBitmap::TransferAsImage()
 }
 
 ImageBitmapCloneData*
 ImageBitmap::ToCloneData()
 {
   ImageBitmapCloneData* result = new ImageBitmapCloneData();
   result->mPictureRect = mPictureRect;
   result->mIsPremultipliedAlpha = mIsPremultipliedAlpha;
+  result->mIsCroppingAreaOutSideOfSourceImage = mIsCroppingAreaOutSideOfSourceImage;
   RefPtr<SourceSurface> surface = mData->GetAsSourceSurface();
   result->mSurface = surface->GetDataSurface();
   MOZ_ASSERT(result->mSurface);
 
   return result;
 }
 
 /* static */ already_AddRefed<ImageBitmap>
 ImageBitmap::CreateFromCloneData(nsIGlobalObject* aGlobal,
                                  ImageBitmapCloneData* aData)
 {
-  RefPtr<layers::Image> data =
-    CreateImageFromSurface(aData->mSurface);
+  RefPtr<layers::Image> data = CreateImageFromSurface(aData->mSurface);
 
   RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data,
                                             aData->mIsPremultipliedAlpha);
+
+  ret->mIsCroppingAreaOutSideOfSourceImage =
+    aData->mIsCroppingAreaOutSideOfSourceImage;
+
   ErrorResult rv;
   ret->SetPictureRect(aData->mPictureRect, rv);
   return ret.forget();
 }
 
 /* static */ already_AddRefed<ImageBitmap>
 ImageBitmap::CreateFromOffscreenCanvas(nsIGlobalObject* aGlobal,
                                        OffscreenCanvas& aOffscreenCanvas,
@@ -665,16 +777,19 @@ ImageBitmap::CreateInternal(nsIGlobalObj
 
   RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
 
   // Set the picture rectangle.
   if (ret && aCropRect.isSome()) {
     ret->SetPictureRect(aCropRect.ref(), aRv);
   }
 
+  // Set mIsCroppingAreaOutSideOfSourceImage.
+  ret->SetIsCroppingAreaOutSideOfSourceImage(surface->GetSize(), aCropRect);
+
   return ret.forget();
 }
 
 /* static */ already_AddRefed<ImageBitmap>
 ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLVideoElement& aVideoEl,
                             const Maybe<IntRect>& aCropRect, ErrorResult& aRv)
 {
   // Check network state.
@@ -710,16 +825,19 @@ ImageBitmap::CreateInternal(nsIGlobalObj
   layers::Image* data = lockImage.GetImage();
   RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
 
   // Set the picture rectangle.
   if (ret && aCropRect.isSome()) {
     ret->SetPictureRect(aCropRect.ref(), aRv);
   }
 
+  // Set mIsCroppingAreaOutSideOfSourceImage.
+  ret->SetIsCroppingAreaOutSideOfSourceImage(data->GetSize(), aCropRect);
+
   return ret.forget();
 }
 
 /* static */ already_AddRefed<ImageBitmap>
 ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLCanvasElement& aCanvasEl,
                             const Maybe<IntRect>& aCropRect, ErrorResult& aRv)
 {
   if (aCanvasEl.Width() == 0 || aCanvasEl.Height() == 0) {
@@ -770,16 +888,19 @@ ImageBitmap::CreateInternal(nsIGlobalObj
 
   RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
 
   // Set the picture rectangle.
   if (ret && aCropRect.isSome()) {
     ret->SetPictureRect(cropRect, aRv);
   }
 
+  // Set mIsCroppingAreaOutSideOfSourceImage.
+  ret->SetIsCroppingAreaOutSideOfSourceImage(surface->GetSize(), aCropRect);
+
   return ret.forget();
 }
 
 /* static */ already_AddRefed<ImageBitmap>
 ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, ImageData& aImageData,
                             const Maybe<IntRect>& aCropRect, ErrorResult& aRv)
 {
   // Copy data into SourceSurface.
@@ -828,16 +949,19 @@ ImageBitmap::CreateInternal(nsIGlobalObj
 
   // Create an ImageBimtap.
   // ImageData's underlying data is not alpha-premultiplied.
   RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, false);
 
   // The cropping information has been handled in the CreateImageFromRawData()
   // function.
 
+  // Set mIsCroppingAreaOutSideOfSourceImage.
+  ret->SetIsCroppingAreaOutSideOfSourceImage(imageSize, aCropRect);
+
   return ret.forget();
 }
 
 /* static */ already_AddRefed<ImageBitmap>
 ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, CanvasRenderingContext2D& aCanvasCtx,
                             const Maybe<IntRect>& aCropRect, ErrorResult& aRv)
 {
   // Check origin-clean.
@@ -868,16 +992,19 @@ ImageBitmap::CreateInternal(nsIGlobalObj
 
   RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
 
   // Set the picture rectangle.
   if (ret && aCropRect.isSome()) {
     ret->SetPictureRect(aCropRect.ref(), aRv);
   }
 
+  // Set mIsCroppingAreaOutSideOfSourceImage.
+  ret->SetIsCroppingAreaOutSideOfSourceImage(surface->GetSize(), aCropRect);
+
   return ret.forget();
 }
 
 /* static */ already_AddRefed<ImageBitmap>
 ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, ImageBitmap& aImageBitmap,
                             const Maybe<IntRect>& aCropRect, ErrorResult& aRv)
 {
   if (!aImageBitmap.mData) {
@@ -888,16 +1015,24 @@ ImageBitmap::CreateInternal(nsIGlobalObj
   RefPtr<layers::Image> data = aImageBitmap.mData;
   RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, aImageBitmap.mIsPremultipliedAlpha);
 
   // Set the picture rectangle.
   if (ret && aCropRect.isSome()) {
     ret->SetPictureRect(aCropRect.ref(), aRv);
   }
 
+  // Set mIsCroppingAreaOutSideOfSourceImage.
+  if (aImageBitmap.mIsCroppingAreaOutSideOfSourceImage == true) {
+    ret->mIsCroppingAreaOutSideOfSourceImage = true;
+  } else {
+    ret->SetIsCroppingAreaOutSideOfSourceImage(aImageBitmap.mPictureRect.Size(),
+                                               aCropRect);
+  }
+
   return ret.forget();
 }
 
 class FulfillImageBitmapPromise
 {
 protected:
   FulfillImageBitmapPromise(Promise* aPromise, ImageBitmap* aImageBitmap)
   : mPromise(aPromise)
@@ -1001,25 +1136,29 @@ DecodeBlob(Blob& aBlob)
   if (NS_WARN_IF(!surface)) {
     return nullptr;
   }
 
   return surface.forget();
 }
 
 static already_AddRefed<layers::Image>
-DecodeAndCropBlob(Blob& aBlob, Maybe<IntRect>& aCropRect)
+DecodeAndCropBlob(Blob& aBlob, Maybe<IntRect>& aCropRect,
+                  /*Output*/ IntSize& sourceSize)
 {
   // Decode the blob into a SourceSurface.
   RefPtr<SourceSurface> surface = DecodeBlob(aBlob);
 
   if (NS_WARN_IF(!surface)) {
     return nullptr;
   }
 
+  // Set the _sourceSize_ output parameter.
+  sourceSize = surface->GetSize();
+
   // Crop the source surface if needed.
   RefPtr<SourceSurface> croppedSurface = surface;
 
   if (aCropRect.isSome()) {
     // The blob is just decoded into a RasterImage and not optimized yet, so the
     // _surface_ we get is a DataSourceSurface which wraps the RasterImage's
     // raw buffer.
     //
@@ -1122,65 +1261,80 @@ public:
   {
     DoCreateImageBitmapFromBlob();
     return NS_OK;
   }
 
 private:
   already_AddRefed<ImageBitmap> CreateImageBitmap() override
   {
-    RefPtr<layers::Image> data = DecodeAndCropBlob(*mBlob, mCropRect);
+    // _sourceSize_ is used to get the original size of the source image,
+    // before being cropped.
+    IntSize sourceSize;
+
+    // Keep the orignal cropping rectangle because the mCropRect might be
+    // modified in DecodeAndCropBlob().
+    Maybe<IntRect> originalCropRect = mCropRect;
+
+    RefPtr<layers::Image> data = DecodeAndCropBlob(*mBlob, mCropRect, sourceSize);
 
     if (NS_WARN_IF(!data)) {
       mPromise->MaybeRejectWithNull();
       return nullptr;
     }
 
     // Create ImageBitmap object.
     RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(mGlobalObject, data);
+
+    // Set mIsCroppingAreaOutSideOfSourceImage.
+    imageBitmap->SetIsCroppingAreaOutSideOfSourceImage(sourceSize, originalCropRect);
+
     return imageBitmap.forget();
   }
 };
 
 class CreateImageBitmapFromBlobWorkerTask final : public WorkerSameThreadRunnable,
                                                   public CreateImageBitmapFromBlob
 {
   // This is a synchronous task.
   class DecodeBlobInMainThreadSyncTask final : public WorkerMainThreadRunnable
   {
   public:
     DecodeBlobInMainThreadSyncTask(WorkerPrivate* aWorkerPrivate,
                                    Blob& aBlob,
                                    Maybe<IntRect>& aCropRect,
-                                   layers::Image** aImage)
+                                   layers::Image** aImage,
+                                   IntSize& aSourceSize)
     : WorkerMainThreadRunnable(aWorkerPrivate,
                                NS_LITERAL_CSTRING("ImageBitmap :: Create Image from Blob"))
     , mBlob(aBlob)
     , mCropRect(aCropRect)
     , mImage(aImage)
+    , mSourceSize(aSourceSize)
     {
     }
 
     bool MainThreadRun() override
     {
-      RefPtr<layers::Image> image = DecodeAndCropBlob(mBlob, mCropRect);
+      RefPtr<layers::Image> image = DecodeAndCropBlob(mBlob, mCropRect, mSourceSize);
 
       if (NS_WARN_IF(!image)) {
         return true;
       }
 
       image.forget(mImage);
 
       return true;
     }
 
   private:
     Blob& mBlob;
     Maybe<IntRect>& mCropRect;
     layers::Image** mImage;
+    IntSize mSourceSize;
   };
 
 public:
   CreateImageBitmapFromBlobWorkerTask(Promise* aPromise,
                                   nsIGlobalObject* aGlobal,
                                   mozilla::dom::Blob& aBlob,
                                   const Maybe<IntRect>& aCropRect)
   : WorkerSameThreadRunnable(GetCurrentThreadWorkerPrivate()),
@@ -1191,37 +1345,49 @@ public:
   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
     return DoCreateImageBitmapFromBlob();
   }
 
 private:
   already_AddRefed<ImageBitmap> CreateImageBitmap() override
   {
+    // _sourceSize_ is used to get the original size of the source image,
+    // before being cropped.
+    IntSize sourceSize;
+
+    // Keep the orignal cropping rectangle because the mCropRect might be
+    // modified in DecodeAndCropBlob().
+    Maybe<IntRect> originalCropRect = mCropRect;
+
     RefPtr<layers::Image> data;
 
     ErrorResult rv;
     RefPtr<DecodeBlobInMainThreadSyncTask> task =
       new DecodeBlobInMainThreadSyncTask(mWorkerPrivate, *mBlob, mCropRect,
-                                         getter_AddRefs(data));
+                                         getter_AddRefs(data), sourceSize);
     task->Dispatch(rv); // This is a synchronous call.
 
     if (NS_WARN_IF(rv.Failed())) {
       // XXXbz does this really make sense if we're shutting down?  Ah, well.
       mPromise->MaybeReject(rv);
       return nullptr;
     }
 
     if (NS_WARN_IF(!data)) {
       mPromise->MaybeRejectWithNull();
       return nullptr;
     }
 
     // Create ImageBitmap object.
     RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(mGlobalObject, data);
+
+    // Set mIsCroppingAreaOutSideOfSourceImage.
+    imageBitmap->SetIsCroppingAreaOutSideOfSourceImage(sourceSize, originalCropRect);
+
     return imageBitmap.forget();
   }
 
 };
 
 static void
 AsyncCreateImageBitmapFromBlob(Promise* aPromise, nsIGlobalObject* aGlobal,
                                Blob& aBlob, const Maybe<IntRect>& aCropRect)
@@ -1301,21 +1467,22 @@ ImageBitmap::ReadStructuredClone(JSConte
   MOZ_ASSERT(aReader);
   // aParent might be null.
 
   uint32_t picRectX_;
   uint32_t picRectY_;
   uint32_t picRectWidth_;
   uint32_t picRectHeight_;
   uint32_t isPremultipliedAlpha_;
-  uint32_t dummy_;
+  uint32_t isCroppingAreaOutSideOfSourceImage_;
 
   if (!JS_ReadUint32Pair(aReader, &picRectX_, &picRectY_) ||
       !JS_ReadUint32Pair(aReader, &picRectWidth_, &picRectHeight_) ||
-      !JS_ReadUint32Pair(aReader, &isPremultipliedAlpha_, &dummy_)) {
+      !JS_ReadUint32Pair(aReader, &isPremultipliedAlpha_,
+                                  &isCroppingAreaOutSideOfSourceImage_)) {
     return nullptr;
   }
 
   int32_t picRectX = BitwiseCast<int32_t>(picRectX_);
   int32_t picRectY = BitwiseCast<int32_t>(picRectY_);
   int32_t picRectWidth = BitwiseCast<int32_t>(picRectWidth_);
   int32_t picRectHeight = BitwiseCast<int32_t>(picRectHeight_);
 
@@ -1329,16 +1496,19 @@ ImageBitmap::ReadStructuredClone(JSConte
   // JSObject* type means that JSObject* is on the stack as a raw pointer
   // while destructors are running.
   JS::Rooted<JS::Value> value(aCx);
   {
     RefPtr<layers::Image> img = CreateImageFromSurface(aClonedSurfaces[aIndex]);
     RefPtr<ImageBitmap> imageBitmap =
       new ImageBitmap(aParent, img, isPremultipliedAlpha_);
 
+    imageBitmap->mIsCroppingAreaOutSideOfSourceImage =
+      isCroppingAreaOutSideOfSourceImage_;
+
     ErrorResult error;
     imageBitmap->SetPictureRect(IntRect(picRectX, picRectY,
                                         picRectWidth, picRectHeight), error);
     if (NS_WARN_IF(error.Failed())) {
       error.SuppressException();
       return nullptr;
     }
 
@@ -1358,24 +1528,26 @@ ImageBitmap::WriteStructuredClone(JSStru
   MOZ_ASSERT(aWriter);
   MOZ_ASSERT(aImageBitmap);
 
   const uint32_t picRectX = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.x);
   const uint32_t picRectY = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.y);
   const uint32_t picRectWidth = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.width);
   const uint32_t picRectHeight = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.height);
   const uint32_t isPremultipliedAlpha = aImageBitmap->mIsPremultipliedAlpha ? 1 : 0;
+  const uint32_t isCroppingAreaOutSideOfSourceImage = aImageBitmap->mIsCroppingAreaOutSideOfSourceImage ? 1 : 0;
 
   // Indexing the cloned surfaces and send the index to the receiver.
   uint32_t index = aClonedSurfaces.Length();
 
   if (NS_WARN_IF(!JS_WriteUint32Pair(aWriter, SCTAG_DOM_IMAGEBITMAP, index)) ||
       NS_WARN_IF(!JS_WriteUint32Pair(aWriter, picRectX, picRectY)) ||
       NS_WARN_IF(!JS_WriteUint32Pair(aWriter, picRectWidth, picRectHeight)) ||
-      NS_WARN_IF(!JS_WriteUint32Pair(aWriter, isPremultipliedAlpha, 0))) {
+      NS_WARN_IF(!JS_WriteUint32Pair(aWriter, isPremultipliedAlpha,
+                                              isCroppingAreaOutSideOfSourceImage))) {
     return false;
   }
 
   RefPtr<SourceSurface> surface =
     aImageBitmap->mData->GetAsSourceSurface();
   RefPtr<DataSourceSurface> snapshot = surface->GetDataSurface();
   RefPtr<DataSourceSurface> dstDataSurface;
   {
@@ -1390,10 +1562,559 @@ ImageBitmap::WriteStructuredClone(JSStru
                                                  true);
   }
   MOZ_ASSERT(dstDataSurface);
   Factory::CopyDataSourceSurface(snapshot, dstDataSurface);
   aClonedSurfaces.AppendElement(dstDataSurface);
   return true;
 }
 
+/*static*/ bool
+ImageBitmap::ExtensionsEnabled(JSContext* aCx, JSObject*)
+{
+  if (NS_IsMainThread()) {
+    return Preferences::GetBool("canvas.imagebitmap_extensions.enabled");
+  } else {
+    WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
+    MOZ_ASSERT(workerPrivate);
+    return workerPrivate->ImageBitmapExtensionsEnabled();
+  }
+}
+
+// ImageBitmap extensions.
+ImageBitmapFormat
+ImageBitmap::FindOptimalFormat(const Optional<Sequence<ImageBitmapFormat>>& aPossibleFormats,
+                               ErrorResult& aRv)
+{
+  MOZ_ASSERT(mDataWrapper, "No ImageBitmapFormatUtils functionalities.");
+
+  ImageBitmapFormat platformFormat = mDataWrapper->GetFormat();
+
+  if (!aPossibleFormats.WasPassed() ||
+      aPossibleFormats.Value().Contains(platformFormat)) {
+    return platformFormat;
+  } else {
+    // If no matching is found, FindBestMatchingFromat() returns
+    // ImageBitmapFormat::EndGuard_ and we throw an exception.
+    ImageBitmapFormat optimalFormat =
+      FindBestMatchingFromat(platformFormat, aPossibleFormats.Value());
+
+    if (optimalFormat == ImageBitmapFormat::EndGuard_) {
+      aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+    }
+
+    return optimalFormat;
+  }
+}
+
+int32_t
+ImageBitmap::MappedDataLength(ImageBitmapFormat aFormat, ErrorResult& aRv)
+{
+  MOZ_ASSERT(mDataWrapper, "No ImageBitmapFormatUtils functionalities.");
+
+  if (aFormat == mDataWrapper->GetFormat()) {
+    return mDataWrapper->GetBufferLength();
+  } else {
+    return CalculateImageBufferSize(aFormat, Width(), Height());
+  }
+}
+
+template<typename T>
+class MapDataIntoBufferSource
+{
+protected:
+  MapDataIntoBufferSource(JSContext* aCx,
+                          Promise *aPromise,
+                          ImageBitmap *aImageBitmap,
+                          const T& aBuffer,
+                          int32_t aOffset,
+                          ImageBitmapFormat aFormat)
+  : mPromise(aPromise)
+  , mImageBitmap(aImageBitmap)
+  , mBuffer(aCx, aBuffer.Obj())
+  , mOffset(aOffset)
+  , mFormat(aFormat)
+  {
+    MOZ_ASSERT(mPromise);
+    MOZ_ASSERT(JS_IsArrayBufferObject(mBuffer) ||
+               JS_IsArrayBufferViewObject(mBuffer));
+  }
+
+  virtual ~MapDataIntoBufferSource() = default;
+
+  void DoMapDataIntoBufferSource()
+  {
+    ErrorResult error;
+
+    // Prepare destination buffer.
+    uint8_t* bufferData = nullptr;
+    uint32_t bufferLength = 0;
+    bool isSharedMemory = false;
+    if (JS_IsArrayBufferObject(mBuffer)) {
+      js::GetArrayBufferLengthAndData(mBuffer, &bufferLength, &isSharedMemory, &bufferData);
+    } else if (JS_IsArrayBufferViewObject(mBuffer)) {
+      js::GetArrayBufferViewLengthAndData(mBuffer, &bufferLength, &isSharedMemory, &bufferData);
+    } else {
+      error.Throw(NS_ERROR_NOT_IMPLEMENTED);
+      mPromise->MaybeReject(error);
+      return;
+    }
+
+    if (NS_WARN_IF(!bufferData) || NS_WARN_IF(!bufferLength)) {
+      error.Throw(NS_ERROR_NOT_AVAILABLE);
+      mPromise->MaybeReject(error);
+      return;
+    }
+
+    // Check length.
+    const int32_t neededBufferLength =
+      mImageBitmap->MappedDataLength(mFormat, error);
+
+    if (((int32_t)bufferLength - mOffset) < neededBufferLength) {
+      error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+      mPromise->MaybeReject(error);
+      return;
+    }
+
+    // Call ImageBitmapFormatUtils.
+    UniquePtr<ImagePixelLayout> layout =
+      mImageBitmap->mDataWrapper->MapDataInto(bufferData,
+                                              mOffset,
+                                              bufferLength,
+                                              mFormat,
+                                              error);
+
+    if (NS_WARN_IF(!layout)) {
+      mPromise->MaybeReject(error);
+      return;
+    }
+
+    mPromise->MaybeResolve(*layout);
+  }
+
+  RefPtr<Promise> mPromise;
+  RefPtr<ImageBitmap> mImageBitmap;
+  JS::PersistentRooted<JSObject*> mBuffer;
+  int32_t mOffset;
+  ImageBitmapFormat mFormat;
+};
+
+template<typename T>
+class MapDataIntoBufferSourceTask final : public Runnable,
+                                          public MapDataIntoBufferSource<T>
+{
+public:
+  MapDataIntoBufferSourceTask(JSContext* aCx,
+                              Promise *aPromise,
+                              ImageBitmap *aImageBitmap,
+                              const T& aBuffer,
+                              int32_t aOffset,
+                              ImageBitmapFormat aFormat)
+  : MapDataIntoBufferSource<T>(aCx, aPromise, aImageBitmap, aBuffer, aOffset, aFormat)
+  {
+  }
+
+  virtual ~MapDataIntoBufferSourceTask() = default;
+
+  NS_IMETHOD Run() override
+  {
+    MapDataIntoBufferSource<T>::DoMapDataIntoBufferSource();
+    return NS_OK;
+  }
+};
+
+template<typename T>
+class MapDataIntoBufferSourceWorkerTask final : public WorkerSameThreadRunnable,
+                                                public MapDataIntoBufferSource<T>
+{
+public:
+  MapDataIntoBufferSourceWorkerTask(JSContext* aCx,
+                                    Promise *aPromise,
+                                    ImageBitmap *aImageBitmap,
+                                    const T& aBuffer,
+                                    int32_t aOffset,
+                                    ImageBitmapFormat aFormat)
+  : WorkerSameThreadRunnable(GetCurrentThreadWorkerPrivate()),
+    MapDataIntoBufferSource<T>(aCx, aPromise, aImageBitmap, aBuffer, aOffset, aFormat)
+  {
+  }
+
+  virtual ~MapDataIntoBufferSourceWorkerTask() = default;
+
+  bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+  {
+    MapDataIntoBufferSource<T>::DoMapDataIntoBufferSource();
+    return true;
+  }
+};
+
+void AsyncMapDataIntoBufferSource(JSContext* aCx,
+                                  Promise *aPromise,
+                                  ImageBitmap *aImageBitmap,
+                                  const ArrayBufferViewOrArrayBuffer& aBuffer,
+                                  int32_t aOffset,
+                                  ImageBitmapFormat aFormat)
+{
+  MOZ_ASSERT(aCx);
+  MOZ_ASSERT(aPromise);
+  MOZ_ASSERT(aImageBitmap);
+
+  if (NS_IsMainThread()) {
+    nsCOMPtr<nsIRunnable> task;
+
+    if (aBuffer.IsArrayBuffer()) {
+      const ArrayBuffer& buffer = aBuffer.GetAsArrayBuffer();
+      task = new MapDataIntoBufferSourceTask<ArrayBuffer>(aCx, aPromise, aImageBitmap, buffer, aOffset, aFormat);
+    } else if (aBuffer.IsArrayBufferView()) {
+      const ArrayBufferView& bufferView = aBuffer.GetAsArrayBufferView();
+      task = new MapDataIntoBufferSourceTask<ArrayBufferView>(aCx, aPromise, aImageBitmap, bufferView, aOffset, aFormat);
+    }
+
+    NS_DispatchToCurrentThread(task); // Actually, to the main-thread.
+  } else {
+    RefPtr<WorkerSameThreadRunnable> task;
+
+    if (aBuffer.IsArrayBuffer()) {
+      const ArrayBuffer& buffer = aBuffer.GetAsArrayBuffer();
+      task = new MapDataIntoBufferSourceWorkerTask<ArrayBuffer>(aCx, aPromise, aImageBitmap, buffer, aOffset, aFormat);
+    } else if (aBuffer.IsArrayBufferView()) {
+      const ArrayBufferView& bufferView = aBuffer.GetAsArrayBufferView();
+      task = new MapDataIntoBufferSourceWorkerTask<ArrayBufferView>(aCx, aPromise, aImageBitmap, bufferView, aOffset, aFormat);
+    }
+
+    task->Dispatch(); // Actually, to the current worker-thread.
+  }
+}
+
+already_AddRefed<Promise>
+ImageBitmap::MapDataInto(JSContext* aCx,
+                         ImageBitmapFormat aFormat,
+                         const ArrayBufferViewOrArrayBuffer& aBuffer,
+                         int32_t aOffset, ErrorResult& aRv)
+{
+  MOZ_ASSERT(mDataWrapper, "No ImageBitmapFormatUtils functionalities.");
+  MOZ_ASSERT(aCx, "No JSContext while calling ImageBitmap::MapDataInto().");
+
+  RefPtr<Promise> promise = Promise::Create(mParent, aRv);
+
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  // Check for cases that should throws.
+  // Case 1:
+  // If image bitmap was cropped to the source rectangle so that it contains any
+  // transparent black pixels (cropping area is outside of the source image),
+  // then reject promise with IndexSizeError and abort these steps.
+  if (mIsCroppingAreaOutSideOfSourceImage) {
+    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+    return promise.forget();
+  }
+
+  // Case 2:
+  // If the image bitmap is going to be accessed in YUV422/YUV422 series with a
+  // cropping area starts at an odd x or y coordinate.
+  if (aFormat == ImageBitmapFormat::YUV422P ||
+      aFormat == ImageBitmapFormat::YUV420P ||
+      aFormat == ImageBitmapFormat::YUV420SP_NV12 ||
+      aFormat == ImageBitmapFormat::YUV420SP_NV21) {
+    if ((mPictureRect.x & 1) || (mPictureRect.y & 1)) {
+      aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+      return promise.forget();
+    }
+  }
+
+  AsyncMapDataIntoBufferSource(aCx, promise, this, aBuffer, aOffset, aFormat);
+  return promise.forget();
+}
+
+// ImageBitmapFactories extensions.
+static SurfaceFormat
+ImageFormatToSurfaceFromat(mozilla::dom::ImageBitmapFormat aFormat)
+{
+  switch(aFormat) {
+  case ImageBitmapFormat::RGBA32:
+    return SurfaceFormat::R8G8B8A8;
+  case ImageBitmapFormat::BGRA32:
+    return SurfaceFormat::B8G8R8A8;
+  case ImageBitmapFormat::RGB24:
+    return SurfaceFormat::R8G8B8;
+  case ImageBitmapFormat::BGR24:
+    return SurfaceFormat::B8G8R8;
+  case ImageBitmapFormat::GRAY8:
+    return SurfaceFormat::A8;
+  case ImageBitmapFormat::HSV:
+    return SurfaceFormat::HSV;
+  case ImageBitmapFormat::Lab:
+    return SurfaceFormat::Lab;
+  case ImageBitmapFormat::DEPTH:
+    return SurfaceFormat::Depth;
+  default:
+    return SurfaceFormat::UNKNOWN;
+  }
+}
+
+static already_AddRefed<layers::Image>
+CreateImageFromBufferSourceRawData(const uint8_t*aBufferData,
+                                   uint32_t aBufferLength,
+                                   mozilla::dom::ImageBitmapFormat aFormat,
+                                   const Sequence<ChannelPixelLayout>& aLayout)
+{
+  MOZ_ASSERT(aBufferData);
+  MOZ_ASSERT(aBufferLength > 0);
+
+  switch(aFormat) {
+  case ImageBitmapFormat::RGBA32:
+  case ImageBitmapFormat::BGRA32:
+  case ImageBitmapFormat::RGB24:
+  case ImageBitmapFormat::BGR24:
+  case ImageBitmapFormat::GRAY8:
+  case ImageBitmapFormat::HSV:
+  case ImageBitmapFormat::Lab:
+  case ImageBitmapFormat::DEPTH:
+  {
+    const nsTArray<ChannelPixelLayout>& channels = aLayout;
+    MOZ_ASSERT(channels.Length() != 0, "Empty Channels.");
+
+    const SurfaceFormat srcFormat = ImageFormatToSurfaceFromat(aFormat);
+    const uint32_t srcStride = channels[0].mStride;
+    const IntSize srcSize(channels[0].mWidth, channels[0].mHeight);
+
+    RefPtr<DataSourceSurface> dstDataSurface =
+      Factory::CreateDataSourceSurfaceWithStride(srcSize, srcFormat, srcStride);
+
+    if (NS_WARN_IF(!dstDataSurface)) {
+      return nullptr;
+    }
+
+    // Copy the raw data into the newly created DataSourceSurface.
+    DataSourceSurface::ScopedMap dstMap(dstDataSurface, DataSourceSurface::WRITE);
+    if (NS_WARN_IF(!dstMap.IsMapped())) {
+      return nullptr;
+    }
+
+    const uint8_t* srcBufferPtr = aBufferData;
+    uint8_t* dstBufferPtr = dstMap.GetData();
+
+    for (int i = 0; i < srcSize.height; ++i) {
+      memcpy(dstBufferPtr, srcBufferPtr, srcStride);
+      srcBufferPtr += srcStride;
+      dstBufferPtr += dstMap.GetStride();
+    }
+
+    // Create an Image from the BGRA SourceSurface.
+    RefPtr<SourceSurface> surface = dstDataSurface;
+    RefPtr<layers::Image> image = CreateImageFromSurface(surface);
+
+    if (NS_WARN_IF(!image)) {
+      return nullptr;
+    }
+
+    return image.forget();
+  }
+  case ImageBitmapFormat::YUV444P:
+  case ImageBitmapFormat::YUV422P:
+  case ImageBitmapFormat::YUV420P:
+  case ImageBitmapFormat::YUV420SP_NV12:
+  case ImageBitmapFormat::YUV420SP_NV21:
+  {
+    // Prepare the PlanarYCbCrData.
+    const ChannelPixelLayout& yLayout = aLayout[0];
+    const ChannelPixelLayout& uLayout = aFormat != ImageBitmapFormat::YUV420SP_NV21 ? aLayout[1] : aLayout[2];
+    const ChannelPixelLayout& vLayout = aFormat != ImageBitmapFormat::YUV420SP_NV21 ? aLayout[2] : aLayout[1];
+
+    layers::PlanarYCbCrData data;
+
+    // Luminance buffer
+    data.mYChannel = const_cast<uint8_t*>(aBufferData + yLayout.mOffset);
+    data.mYStride = yLayout.mStride;
+    data.mYSize = gfx::IntSize(yLayout.mWidth, yLayout.mHeight);
+    data.mYSkip = yLayout.mSkip;
+
+    // Chroma buffers
+    data.mCbChannel = const_cast<uint8_t*>(aBufferData + uLayout.mOffset);
+    data.mCrChannel = const_cast<uint8_t*>(aBufferData + vLayout.mOffset);
+    data.mCbCrStride = uLayout.mStride;
+    data.mCbCrSize = gfx::IntSize(uLayout.mWidth, uLayout.mHeight);
+    data.mCbSkip = uLayout.mSkip;
+    data.mCrSkip = vLayout.mSkip;
+
+    // Picture rectangle.
+    // We set the picture rectangle to exactly the size of the source image to
+    // keep the full original data.
+    data.mPicX = 0;
+    data.mPicY = 0;
+    data.mPicSize = data.mYSize;
+
+    // Create a layers::Image and set data.
+    if (aFormat == ImageBitmapFormat::YUV444P ||
+        aFormat == ImageBitmapFormat::YUV422P ||
+        aFormat == ImageBitmapFormat::YUV420P) {
+      RefPtr<layers::PlanarYCbCrImage> image =
+        new layers::RecyclingPlanarYCbCrImage(new layers::BufferRecycleBin());
+
+      if (NS_WARN_IF(!image)) {
+        return nullptr;
+      }
+
+      // Set Data.
+      if (NS_WARN_IF(!image->CopyData(data))) {
+        return nullptr;
+      }
+
+      return image.forget();
+    } else {
+      RefPtr<layers::NVImage>image = new layers::NVImage();
+
+      if (NS_WARN_IF(!image)) {
+        return nullptr;
+      }
+
+      // Set Data.
+      if (NS_WARN_IF(!image->SetData(data))) {
+        return nullptr;
+      }
+
+      return image.forget();
+    }
+  }
+  default:
+    return nullptr;
+  }
+}
+
+/*
+ * This is a synchronous task.
+ * This class is used to create a layers::CairoImage from raw data in the main
+ * thread. While creating an ImageBitmap from an BufferSource, we need to create
+ * a SouceSurface from the BufferSource raw data and then set the SourceSurface
+ * into a layers::CairoImage. However, the layers::CairoImage asserts the
+ * setting operation in the main thread, so if we are going to create an
+ * ImageBitmap from an BufferSource off the main thread, we post an event to the
+ * main thread to create a layers::CairoImage from an BufferSource raw data.
+ *
+ * TODO: Once the layers::CairoImage is constructible off the main thread, which
+ *       means the SouceSurface could be released anywhere, we do not need this
+ *       task anymore.
+ */
+class CreateImageFromBufferSourceRawDataInMainThreadSyncTask final :
+  public WorkerMainThreadRunnable
+{
+public:
+  CreateImageFromBufferSourceRawDataInMainThreadSyncTask(const uint8_t* aBuffer,
+                                                         uint32_t aBufferLength,
+                                                         mozilla::dom::ImageBitmapFormat aFormat,
+                                                         const Sequence<ChannelPixelLayout>& aLayout,
+                                                         /*output*/ layers::Image** aImage)
+  : WorkerMainThreadRunnable(GetCurrentThreadWorkerPrivate(),
+                             NS_LITERAL_CSTRING("ImageBitmap-extensions :: Create Image from BufferSource Raw Data"))
+  , mImage(aImage)
+  , mBuffer(aBuffer)
+  , mBufferLength(aBufferLength)
+  , mFormat(aFormat)
+  , mLayout(aLayout)
+  {
+    MOZ_ASSERT(!(*aImage), "Don't pass an existing Image into CreateImageFromBufferSourceRawDataInMainThreadSyncTask.");
+  }
+
+  bool MainThreadRun() override
+  {
+    RefPtr<layers::Image> image =
+      CreateImageFromBufferSourceRawData(mBuffer, mBufferLength, mFormat, mLayout);
+
+    if (NS_WARN_IF(!image)) {
+      return true;
+    }
+
+    image.forget(mImage);
+
+    return true;
+  }
+
+private:
+  layers::Image** mImage;
+  const uint8_t* mBuffer;
+  uint32_t mBufferLength;
+  mozilla::dom::ImageBitmapFormat mFormat;
+  const Sequence<ChannelPixelLayout>& mLayout;
+};
+
+/*static*/ already_AddRefed<Promise>
+ImageBitmap::Create(nsIGlobalObject* aGlobal,
+                    const ImageBitmapSource& aBuffer,
+                    int32_t aOffset, int32_t aLength,
+                    mozilla::dom::ImageBitmapFormat aFormat,
+                    const Sequence<ChannelPixelLayout>& aLayout,
+                    ErrorResult& aRv)
+{
+  MOZ_ASSERT(aGlobal);
+
+  RefPtr<Promise> promise = Promise::Create(aGlobal, aRv);
+
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  uint8_t* bufferData = nullptr;
+  uint32_t bufferLength = 0;
+
+  if (aBuffer.IsArrayBuffer()) {
+    const ArrayBuffer& buffer = aBuffer.GetAsArrayBuffer();
+    buffer.ComputeLengthAndData();
+    bufferData = buffer.Data();
+    bufferLength = buffer.Length();
+  } else if (aBuffer.IsArrayBufferView()) {
+    const ArrayBufferView& bufferView = aBuffer.GetAsArrayBufferView();
+    bufferView.ComputeLengthAndData();
+    bufferData = bufferView.Data();
+    bufferLength = bufferView.Length();
+  } else {
+    aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+    return promise.forget();
+  }
+
+  MOZ_ASSERT(bufferData && bufferLength > 0, "Cannot read data from BufferSource.");
+
+  // Check the buffer.
+  if (((uint32_t)(aOffset + aLength) > bufferLength)) {
+    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+    return promise.forget();
+  }
+
+  // Create and Crop the raw data into a layers::Image
+  RefPtr<layers::Image> data;
+  if (NS_IsMainThread()) {
+    data = CreateImageFromBufferSourceRawData(bufferData + aOffset, bufferLength,
+                                              aFormat, aLayout);
+  } else {
+    RefPtr<CreateImageFromBufferSourceRawDataInMainThreadSyncTask> task =
+      new CreateImageFromBufferSourceRawDataInMainThreadSyncTask(bufferData + aOffset,
+                                                                 bufferLength,
+                                                                 aFormat,
+                                                                 aLayout,
+                                                                 getter_AddRefs(data));
+    task->Dispatch(aRv);
+  }
+
+  if (NS_WARN_IF(!data)) {
+    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
+    return promise.forget();
+  }
+
+  // Create an ImageBimtap.
+  // Assume the data from an external buffer is not alpha-premultiplied.
+  RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(aGlobal, data, false);
+
+  // We don't need to call SetPictureRect() here because there is no cropping
+  // supported and the ImageBitmap's mPictureRect is the size of the source
+  // image in default
+
+  // We don't need to set mIsCroppingAreaOutSideOfSourceImage here because there
+  // is no cropping supported and the mIsCroppingAreaOutSideOfSourceImage is
+  // false in default.
+
+  AsyncFulfillImageBitmapPromise(promise, imageBitmap);
+
+  return promise.forget();
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/canvas/ImageBitmap.h
+++ b/dom/canvas/ImageBitmap.h
@@ -36,33 +36,39 @@ class Image;
 
 namespace dom {
 class OffscreenCanvas;
 
 namespace workers {
 class WorkerStructuredCloneClosure;
 }
 
+class ArrayBufferViewOrArrayBuffer;
 class CanvasRenderingContext2D;
+struct ChannelPixelLayout;
+class CreateImageBitmapFromBlob;
+class CreateImageBitmapFromBlobTask;
+class CreateImageBitmapFromBlobWorkerTask;
 class File;
 class HTMLCanvasElement;
 class HTMLImageElement;
 class HTMLVideoElement;
+enum class ImageBitmapFormat : uint32_t;
 class ImageData;
+class ImageUtils;
+template<typename T> class MapDataIntoBufferSource;
 class Promise;
 class PostMessageEvent; // For StructuredClone between windows.
-class CreateImageBitmapFromBlob;
-class CreateImageBitmapFromBlobTask;
-class CreateImageBitmapFromBlobWorkerTask;
 
 struct ImageBitmapCloneData final
 {
   RefPtr<gfx::DataSourceSurface> mSurface;
   gfx::IntRect mPictureRect;
   bool mIsPremultipliedAlpha;
+  bool mIsCroppingAreaOutSideOfSourceImage;
 };
 
 /*
  * ImageBitmap is an opaque handler to several kinds of image-like objects from
  * HTMLImageElement, HTMLVideoElement, HTMLCanvasElement, ImageData to
  * CanvasRenderingContext2D and Image Blob.
  *
  * An ImageBitmap could be painted to a canvas element.
@@ -119,32 +125,60 @@ public:
   CreateFromOffscreenCanvas(nsIGlobalObject* aGlobal,
                             OffscreenCanvas& aOffscreenCanvas,
                             ErrorResult& aRv);
 
   static already_AddRefed<Promise>
   Create(nsIGlobalObject* aGlobal, const ImageBitmapSource& aSrc,
          const Maybe<gfx::IntRect>& aCropRect, ErrorResult& aRv);
 
+  static already_AddRefed<Promise>
+  Create(nsIGlobalObject* aGlobal,
+         const ImageBitmapSource& aBuffer,
+         int32_t aOffset, int32_t aLength,
+         mozilla::dom::ImageBitmapFormat aFormat,
+         const Sequence<mozilla::dom::ChannelPixelLayout>& aLayout,
+         ErrorResult& aRv);
+
   static JSObject*
   ReadStructuredClone(JSContext* aCx,
                       JSStructuredCloneReader* aReader,
                       nsIGlobalObject* aParent,
                       const nsTArray<RefPtr<gfx::DataSourceSurface>>& aClonedSurfaces,
                       uint32_t aIndex);
 
   static bool
   WriteStructuredClone(JSStructuredCloneWriter* aWriter,
                        nsTArray<RefPtr<gfx::DataSourceSurface>>& aClonedSurfaces,
                        ImageBitmap* aImageBitmap);
 
+  // Mozilla Extensions
+  static bool ExtensionsEnabled(JSContext* aCx, JSObject* aObj);
+
   friend CreateImageBitmapFromBlob;
   friend CreateImageBitmapFromBlobTask;
   friend CreateImageBitmapFromBlobWorkerTask;
 
+  template<typename T>
+  friend class MapDataIntoBufferSource;
+
+  // Mozilla Extensions
+  ImageBitmapFormat
+  FindOptimalFormat(const Optional<Sequence<ImageBitmapFormat>>& aPossibleFormats,
+                    ErrorResult& aRv);
+
+  int32_t
+  MappedDataLength(ImageBitmapFormat aFormat, ErrorResult& aRv);
+
+  already_AddRefed<Promise>
+  MapDataInto(JSContext* aCx,
+              ImageBitmapFormat aFormat,
+              const ArrayBufferViewOrArrayBuffer& aBuffer,
+              int32_t aOffset, ErrorResult& aRv);
+
 protected:
 
   /*
    * The default value of aIsPremultipliedAlpha is TRUE because that the
    * data stored in HTMLImageElement, HTMLVideoElement, HTMLCanvasElement,
    * CanvasRenderingContext2D are alpha-premultiplied in default.
    *
    * Actually, if one HTMLCanvasElement's rendering context is WebGLContext, it
@@ -163,16 +197,19 @@ protected:
    */
   ImageBitmap(nsIGlobalObject* aGlobal, layers::Image* aData,
               bool aIsPremultipliedAlpha = true);
 
   virtual ~ImageBitmap();
 
   void SetPictureRect(const gfx::IntRect& aRect, ErrorResult& aRv);
 
+  void SetIsCroppingAreaOutSideOfSourceImage(const gfx::IntSize& aSourceSize,
+                                             const Maybe<gfx::IntRect>& aCroppingRect);
+
   static already_AddRefed<ImageBitmap>
   CreateInternal(nsIGlobalObject* aGlobal, HTMLImageElement& aImageEl,
                  const Maybe<gfx::IntRect>& aCropRect, ErrorResult& aRv);
 
   static already_AddRefed<ImageBitmap>
   CreateInternal(nsIGlobalObject* aGlobal, HTMLVideoElement& aVideoEl,
                  const Maybe<gfx::IntRect>& aCropRect, ErrorResult& aRv);
 
@@ -207,29 +244,46 @@ protected:
    * if the are of mPictureRect is just the same as the mData's size. Or, it is
    * a independent data buffer which is copied and cropped form the mData's data
    * buffer.
    */
   RefPtr<layers::Image> mData;
   RefPtr<gfx::SourceSurface> mSurface;
 
   /*
+   * This is used in the ImageBitmap-Extensions implementation.
+   * ImageUtils is a wrapper to layers::Image, which add some common methods for
+   * accessing the layers::Image's data.
+   */
+  UniquePtr<ImageUtils> mDataWrapper;
+
+  /*
    * The mPictureRect is the size of the source image in default, however, if
    * users specify the cropping area while creating an ImageBitmap, then this
    * mPictureRect is the cropping area.
    *
    * Note that if the CreateInternal() copies and crops data from the source
    * image, then this mPictureRect is just the size of the final mData.
    *
    * The mPictureRect will be used at PrepareForDrawTarget() while user is going
    * to draw this ImageBitmap into a HTMLCanvasElement.
    */
   gfx::IntRect mPictureRect;
 
   const bool mIsPremultipliedAlpha;
+
+  /*
+   * Set mIsCroppingAreaOutSideOfSourceImage if image bitmap was cropped to the
+   * source rectangle so that it contains any transparent black pixels (cropping
+   * area is outside of the source image).
+   * This is used in mapDataInto() to check if we should reject promise with
+   * IndexSizeError.
+   */
+  bool mIsCroppingAreaOutSideOfSourceImage;
+
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_ImageBitmap_h
 
 
new file mode 100644
--- /dev/null
+++ b/dom/canvas/ImageBitmapColorUtils.cpp
@@ -0,0 +1,2080 @@
+/* -*- 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 "ImageBitmapColorUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+/*
+ * Utility function form libyuv source files.
+ */
+static __inline int32 clamp0(int32 v) {
+  return ((-(v) >> 31) & (v));
+}
+
+static __inline int32 clamp255(int32 v) {
+  return (((255 - (v)) >> 31) | (v)) & 255;
+}
+
+static __inline uint32 Clamp(int32 val) {
+  int v = clamp0(val);
+  return (uint32)(clamp255(v));
+}
+
+#define YG 74 /* (int8)(1.164 * 64 + 0.5) */
+
+#define UB 127 /* min(63,(int8)(2.018 * 64)) */
+#define UG -25 /* (int8)(-0.391 * 64 - 0.5) */
+#define UR 0
+
+#define VB 0
+#define VG -52 /* (int8)(-0.813 * 64 - 0.5) */
+#define VR 102 /* (int8)(1.596 * 64 + 0.5) */
+
+// Bias
+#define BB UB * 128 + VB * 128
+#define BG UG * 128 + VG * 128
+#define BR UR * 128 + VR * 128
+
+static __inline void
+YuvPixel(uint8 y, uint8 u, uint8 v, uint8* b, uint8* g, uint8* r)
+{
+  int32 y1 = ((int32)(y) - 16) * YG;
+  *b = Clamp((int32)((u * UB + v * VB) - (BB) + y1) >> 6);
+  *g = Clamp((int32)((u * UG + v * VG) - (BG) + y1) >> 6);
+  *r = Clamp((int32)((u * UR + v * VR) - (BR) + y1) >> 6);
+}
+
+static __inline int
+RGBToY(uint8 r, uint8 g, uint8 b)
+{
+  return (66 * r + 129 * g +  25 * b + 0x1080) >> 8;
+}
+
+static __inline int
+RGBToU(uint8 r, uint8 g, uint8 b)
+{
+  return (112 * b - 74 * g - 38 * r + 0x8080) >> 8;
+}
+
+static __inline int
+RGBToV(uint8 r, uint8 g, uint8 b)
+{
+  return (112 * r - 94 * g - 18 * b + 0x8080) >> 8;
+}
+
+/*
+ * Generic functions.
+ */
+template<int aSrcRIndex, int aSrcGIndex, int aSrcBIndex,
+         int aDstRIndex, int aDstGIndex, int aDstBIndex, int aDstAIndex>
+static int
+RGBFamilyToRGBAFamily(const uint8_t* aSrcBuffer, int aSrcStride,
+                      uint8_t* aDstBuffer, int aDstStride,
+                      int aWidth, int aHeight)
+{
+  static_assert(aSrcRIndex == 0 || aSrcRIndex == 2, "Wrong SrcR index.");
+  static_assert(aSrcGIndex == 1, "Wrong SrcG index.");
+  static_assert(aSrcBIndex == 0 || aSrcBIndex == 2, "Wrong SrcB index.");
+  static_assert(aDstRIndex == 0 || aDstRIndex == 2, "Wrong DstR index.");
+  static_assert(aDstGIndex == 1, "Wrong DstG index.");
+  static_assert(aDstBIndex == 0 || aDstBIndex == 2, "Wrong DstB index.");
+  static_assert(aDstAIndex == 3, "Wrong DstA index.");
+
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    for (int j = 0; j < aWidth; ++j) {
+      uint8_t r = *(srcBuffer + aSrcRIndex);
+      uint8_t g = *(srcBuffer + aSrcGIndex);
+      uint8_t b = *(srcBuffer + aSrcBIndex);
+      *(dstBuffer + aDstRIndex) = r;
+      *(dstBuffer + aDstGIndex) = g;
+      *(dstBuffer + aDstBIndex) = b;
+      *(dstBuffer + aDstAIndex) = 255;
+      srcBuffer += 3;
+      dstBuffer += 4;
+    }
+  }
+
+  return 0;
+}
+
+template<int aSrcRIndex, int aSrcGIndex, int aSrcBIndex,
+         int aDstRIndex, int aDstGIndex, int aDstBIndex>
+static int
+RGBAFamilyToRGBFamily(const uint8_t* aSrcBuffer, int aSrcStride,
+                      uint8_t* aDstBuffer, int aDstStride,
+                      int aWidth, int aHeight)
+{
+  static_assert(aSrcRIndex == 0 || aSrcRIndex == 2, "Wrong SrcR index.");
+  static_assert(aSrcGIndex == 1, "Wrong SrcG index.");
+  static_assert(aSrcBIndex == 0 || aSrcBIndex == 2, "Wrong SrcB index.");
+  static_assert(aDstRIndex == 0 || aDstRIndex == 2, "Wrong DstR index.");
+  static_assert(aDstGIndex == 1, "Wrong DstG index.");
+  static_assert(aDstBIndex == 0 || aDstBIndex == 2, "Wrong DstB index.");
+
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    for (int j = 0; j < aWidth; ++j) {
+      uint8_t r = *(srcBuffer + aSrcRIndex);
+      uint8_t g = *(srcBuffer + aSrcGIndex);
+      uint8_t b = *(srcBuffer + aSrcBIndex);
+      *(dstBuffer + aDstRIndex) = r;
+      *(dstBuffer + aDstGIndex) = g;
+      *(dstBuffer + aDstBIndex) = b;
+      srcBuffer += 4;
+      dstBuffer += 3;
+    }
+  }
+
+  return 0;
+}
+
+template<int aPixel1YOffset, int aPixel1UOffset, int aPixel1VOffset,
+         int aPixel2YOffset, int aPixel2UOffset, int aPixel2VOffset,
+         int aYStep, int aUStep, int aVStep,
+         int aRIndex, int aGIndex, int aBIndex>
+void
+YUVFamilyToRGBFamily_Row(const uint8_t* aYBuffer,
+                         const uint8_t* aUBuffer,
+                         const uint8_t* aVBuffer,
+                         uint8_t* aDstBuffer,
+                         int aWidth)
+{
+  static_assert(aRIndex == 0 || aRIndex == 2, "Wrong R index.");
+  static_assert(aGIndex == 1, "Wrong G index.");
+  static_assert(aBIndex == 0 || aBIndex == 2, "Wrong B index.");
+
+  for (int j = 0; j < aWidth - 1; j += 2) {
+    YuvPixel(aYBuffer[aPixel1YOffset], aUBuffer[aPixel1UOffset], aVBuffer[aPixel1VOffset],
+             aDstBuffer + aBIndex, aDstBuffer + aGIndex, aDstBuffer + aRIndex);
+    YuvPixel(aYBuffer[aPixel2YOffset], aUBuffer[aPixel2UOffset], aVBuffer[aPixel2VOffset],
+             aDstBuffer + aBIndex + 3, aDstBuffer + aGIndex + 3, aDstBuffer + aRIndex + 3);
+    aYBuffer += aYStep;
+    aUBuffer += aUStep;
+    aVBuffer += aVStep;
+    aDstBuffer += 6;
+  }
+
+  if (aWidth & 1) {
+    YuvPixel(aYBuffer[aPixel1YOffset], aUBuffer[aPixel1UOffset], aVBuffer[aPixel1VOffset],
+             aDstBuffer + aBIndex, aDstBuffer + aGIndex, aDstBuffer + aRIndex);
+  }
+}
+
+template<int aPixel1YOffset, int aPixel1UOffset, int aPixel1VOffset,
+         int aPixel2YOffset, int aPixel2UOffset, int aPixel2VOffset,
+         int aYStep, int aUStep, int aVStep,
+         int aRIndex, int aGIndex, int aBIndex, int aAIndex>
+void
+YUVFamilyToRGBAFamily_Row(const uint8_t* aYBuffer,
+                          const uint8_t* aUBuffer,
+                          const uint8_t* aVBuffer,
+                          uint8_t* aDstBuffer,
+                          int aWidth)
+{
+  static_assert(aRIndex == 0 || aRIndex == 2, "Wrong R index.");
+  static_assert(aGIndex == 1, "Wrong G index.");
+  static_assert(aBIndex == 0 || aBIndex == 2, "Wrong B index.");
+  static_assert(aAIndex == 3, "Wrong A index.");
+
+  for (int j = 0; j < aWidth - 1; j += 2) {
+    YuvPixel(aYBuffer[aPixel1YOffset], aUBuffer[aPixel1UOffset], aVBuffer[aPixel1VOffset],
+             aDstBuffer + aBIndex, aDstBuffer + aGIndex, aDstBuffer + aRIndex);
+    YuvPixel(aYBuffer[aPixel2YOffset], aUBuffer[aPixel2UOffset], aVBuffer[aPixel2VOffset],
+             aDstBuffer + aBIndex + 4, aDstBuffer + aGIndex + 4, aDstBuffer + aRIndex + 4);
+    aDstBuffer[aAIndex] = 255;
+    aDstBuffer[aAIndex + 4] = 255;
+
+    aYBuffer += aYStep;
+    aUBuffer += aUStep;
+    aVBuffer += aVStep;
+    aDstBuffer += 8;
+  }
+
+  if (aWidth & 1) {
+    YuvPixel(aYBuffer[aPixel1YOffset], aUBuffer[aPixel1UOffset], aVBuffer[aPixel1VOffset],
+             aDstBuffer + aBIndex, aDstBuffer + aGIndex, aDstBuffer + aRIndex);
+    aDstBuffer[aAIndex] = 255;
+  }
+}
+
+template< int aRIndex, int aGIndex, int aBIndex>
+static void
+RGBFamilyToY_Row(const uint8_t* aSrcBuffer, uint8_t* aYBuffer, int aWidth)
+{
+  static_assert(aRIndex == 0 || aRIndex == 2, "Wrong R index.");
+  static_assert(aGIndex == 1, "Wrong G index.");
+  static_assert(aBIndex == 0 || aBIndex == 2, "Wrong B index.");
+
+  for (int j = 0; j < aWidth - 1; j += 2) {
+    aYBuffer[0] = RGBToY(aSrcBuffer[aRIndex], aSrcBuffer[aGIndex], aSrcBuffer[aBIndex]);
+    aYBuffer[1] = RGBToY(aSrcBuffer[aRIndex + 3], aSrcBuffer[aGIndex + 3], aSrcBuffer[aBIndex + 3]);
+
+    aYBuffer += 2;
+    aSrcBuffer += 3 * 2;
+  }
+
+  if (aWidth & 1) {
+    aYBuffer[0] = RGBToY(aSrcBuffer[aRIndex], aSrcBuffer[aGIndex], aSrcBuffer[aBIndex]);
+  }
+}
+
+template< int aRIndex, int aGIndex, int aBIndex, int aUStep, int aVStep>
+static void
+RGBFamilyToUV_Row(const uint8_t* aSrcBuffer, int aSrcStride,
+                  uint8_t* aUBuffer, uint8_t* aVBuffer, int aWidth)
+{
+  static_assert(aRIndex == 0 || aRIndex == 2, "Wrong R index.");
+  static_assert(aGIndex == 1, "Wrong G index.");
+  static_assert(aBIndex == 0 || aBIndex == 2, "Wrong B index.");
+
+  uint8_t averageR = 0;
+  uint8_t averageG = 0;
+  uint8_t averageB = 0;
+
+  const uint8_t* aSrcBufferNextRow = aSrcBuffer + aSrcStride;
+  for (int j = 0; j < aWidth - 1; j += 2) {
+    averageR = (aSrcBuffer[aRIndex] + aSrcBuffer[aRIndex + 3] + aSrcBufferNextRow[aRIndex] + aSrcBufferNextRow[aRIndex + 3]) >> 2;
+    averageG = (aSrcBuffer[aGIndex] + aSrcBuffer[aGIndex + 3] + aSrcBufferNextRow[aGIndex] + aSrcBufferNextRow[aGIndex + 3]) >> 2;
+    averageB = (aSrcBuffer[aBIndex] + aSrcBuffer[aBIndex + 3] + aSrcBufferNextRow[aBIndex] + aSrcBufferNextRow[aBIndex + 3]) >> 2;
+
+    aUBuffer[0] = RGBToU(averageR, averageG, averageB);
+    aVBuffer[0] = RGBToV(averageR, averageG, averageB);
+
+    aUBuffer += aUStep;
+    aVBuffer += aVStep;
+    aSrcBuffer += 3 * 2;
+    aSrcBufferNextRow += 3 * 2;
+  }
+
+  if (aWidth & 1) {
+    averageR = (aSrcBuffer[aRIndex] + aSrcBufferNextRow[aRIndex]) >> 1;
+    averageG = (aSrcBuffer[aGIndex] + aSrcBufferNextRow[aGIndex]) >> 1;
+    averageB = (aSrcBuffer[aBIndex] + aSrcBufferNextRow[aBIndex]) >> 1;
+
+    aUBuffer[0] = RGBToU(averageR, averageG, averageB);
+    aVBuffer[0] = RGBToV(averageR, averageG, averageB);
+  }
+}
+
+template< int aRIndex, int aGIndex, int aBIndex>
+static void
+RGBAFamilyToY_Row(const uint8_t* aSrcBuffer, uint8_t* aYBuffer, int aWidth)
+{
+  static_assert(aRIndex == 0 || aRIndex == 2, "Wrong R index.");
+  static_assert(aGIndex == 1, "Wrong G index.");
+  static_assert(aBIndex == 0 || aBIndex == 2, "Wrong B index.");
+
+  for (int j = 0; j < aWidth - 1; j += 2) {
+    aYBuffer[0] = RGBToY(aSrcBuffer[aRIndex], aSrcBuffer[aGIndex], aSrcBuffer[aBIndex]);
+    aYBuffer[1] = RGBToY(aSrcBuffer[aRIndex + 4], aSrcBuffer[aGIndex + 4], aSrcBuffer[aBIndex + 4]);
+
+    aYBuffer += 2;
+    aSrcBuffer += 4 * 2;
+  }
+
+  if (aWidth & 1) {
+    aYBuffer[0] = RGBToY(aSrcBuffer[aRIndex], aSrcBuffer[aGIndex], aSrcBuffer[aBIndex]);
+  }
+}
+
+template< int aRIndex, int aGIndex, int aBIndex, int aUStep, int aVStep>
+static void
+RGBAFamilyToUV_Row(const uint8_t* aSrcBuffer, int aSrcStride,
+                   uint8_t* aUBuffer, uint8_t* aVBuffer, int aWidth)
+{
+  static_assert(aRIndex == 0 || aRIndex == 2, "Wrong R index.");
+  static_assert(aGIndex == 1, "Wrong G index.");
+  static_assert(aBIndex == 0 || aBIndex == 2, "Wrong B index.");
+
+  uint8_t averageR = 0;
+  uint8_t averageG = 0;
+  uint8_t averageB = 0;
+
+  const uint8_t* aSrcBufferNextRow = aSrcBuffer + aSrcStride;
+  for (int j = 0; j < aWidth - 1; j += 2) {
+    averageR = (aSrcBuffer[aRIndex] + aSrcBuffer[aRIndex + 4] + aSrcBufferNextRow[aRIndex] + aSrcBufferNextRow[aRIndex + 4]) >> 2;
+    averageG = (aSrcBuffer[aGIndex] + aSrcBuffer[aGIndex + 4] + aSrcBufferNextRow[aGIndex] + aSrcBufferNextRow[aGIndex + 4]) >> 2;
+    averageB = (aSrcBuffer[aBIndex] + aSrcBuffer[aBIndex + 4] + aSrcBufferNextRow[aBIndex] + aSrcBufferNextRow[aBIndex + 4]) >> 2;
+
+    aUBuffer[0] = RGBToU(averageR, averageG, averageB);
+    aVBuffer[0] = RGBToV(averageR, averageG, averageB);
+
+    aUBuffer += aUStep;
+    aVBuffer += aVStep;
+    aSrcBuffer += 4 * 2;
+    aSrcBufferNextRow += 4 * 2;
+  }
+
+  if (aWidth & 1) {
+    averageR = (aSrcBuffer[aRIndex] + aSrcBufferNextRow[aRIndex]) >> 1;
+    averageG = (aSrcBuffer[aGIndex] + aSrcBufferNextRow[aGIndex]) >> 1;
+    averageB = (aSrcBuffer[aBIndex] + aSrcBufferNextRow[aBIndex]) >> 1;
+
+    aUBuffer[0] = RGBToU(averageR, averageG, averageB);
+    aVBuffer[0] = RGBToV(averageR, averageG, averageB);
+  }
+}
+
+/*
+ * RGB family -> RGBA family.
+ */
+int
+RGB24ToRGBA32(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight)
+{
+  return RGBFamilyToRGBAFamily<0, 1, 2, 0, 1, 2, 3>(aSrcBuffer, aSrcStride,
+                                                    aDstBuffer, aDstStride,
+                                                    aWidth, aHeight);
+}
+
+int
+BGR24ToRGBA32(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight)
+{
+  return RGBFamilyToRGBAFamily<2, 1, 0, 0, 1, 2, 3>(aSrcBuffer, aSrcStride,
+                                                    aDstBuffer, aDstStride,
+                                                    aWidth, aHeight);
+}
+
+int
+RGB24ToBGRA32(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight)
+{
+  return RGBFamilyToRGBAFamily<0, 1, 2, 2, 1, 0, 3>(aSrcBuffer, aSrcStride,
+                                                    aDstBuffer, aDstStride,
+                                                    aWidth, aHeight);
+}
+
+int
+BGR24ToBGRA32(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight)
+{
+  return RGBFamilyToRGBAFamily<2, 1, 0, 2, 1, 0, 3>(aSrcBuffer, aSrcStride,
+                                                    aDstBuffer, aDstStride,
+                                                    aWidth, aHeight);
+}
+
+/*
+ * RGBA family -> RGB family.
+ */
+int
+RGBA32ToRGB24(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight)
+{
+  return RGBAFamilyToRGBFamily<0, 1, 2, 0, 1, 2>(aSrcBuffer, aSrcStride,
+                                                 aDstBuffer, aDstStride,
+                                                 aWidth, aHeight);
+}
+
+int
+BGRA32ToRGB24(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight)
+{
+  return RGBAFamilyToRGBFamily<2, 1, 0, 0, 1, 2>(aSrcBuffer, aSrcStride,
+                                                 aDstBuffer, aDstStride,
+                                                 aWidth, aHeight);
+}
+
+int
+RGBA32ToBGR24(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight)
+{
+  return RGBAFamilyToRGBFamily<0, 1, 2, 2, 1, 0>(aSrcBuffer, aSrcStride,
+                                                 aDstBuffer, aDstStride,
+                                                 aWidth, aHeight);
+}
+
+int
+BGRA32ToBGR24(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight)
+{
+  return RGBAFamilyToRGBFamily<2, 1, 0, 2, 1, 0>(aSrcBuffer, aSrcStride,
+                                                 aDstBuffer, aDstStride,
+                                                 aWidth, aHeight);
+}
+
+/*
+ * Among RGB family.
+ */
+int
+RGB24Copy(const uint8_t* aSrcBuffer, int aSrcStride,
+          uint8_t* aDstBuffer, int aDstStride,
+          int aWidth, int aHeight)
+{
+  MOZ_ASSERT(aSrcStride == aDstStride, "RGB24Copy: aSrcStride != aDstStride");
+
+  const uint32_t length = aHeight * aDstStride;
+  memcpy(aDstBuffer, aSrcBuffer, length);
+  return 0;
+}
+
+int
+RGB24ToBGR24(const uint8_t* aSrcBuffer, int aSrcStride,
+             uint8_t* aDstBuffer, int aDstStride,
+             int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    for (int j = 0; j < aWidth; ++j) {
+      *(dstBuffer + 0) = *(srcBuffer + 2);
+      *(dstBuffer + 1) = *(srcBuffer + 1);
+      *(dstBuffer + 2) = *(srcBuffer + 0);
+      srcBuffer += 3;
+      dstBuffer += 3;
+    }
+  }
+
+  return 0;
+}
+
+/*
+ * YUV family -> RGB family.
+ */
+int
+YUV444PToRGB24(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t* aUBuffer, int aUStride,
+               const uint8_t* aVBuffer, int aVStride,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aUBuffer + aUStride * i;
+    const uint8_t* vBuffer = aVBuffer + aVStride * i;
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBFamily_Row<0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 1, 2>(yBuffer,
+                                                                 uBuffer,
+                                                                 vBuffer,
+                                                                 dstBuffer,
+                                                                 aWidth);
+  }
+
+  return 0;
+}
+
+int
+YUV422PToRGB24(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t* aUBuffer, int aUStride,
+               const uint8_t* aVBuffer, int aVStride,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aUBuffer + aUStride * i;
+    const uint8_t* vBuffer = aVBuffer + aVStride * i;
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBFamily_Row<0, 0, 0, 1, 0, 0, 2, 1, 1, 0, 1, 2>(yBuffer,
+                                                                 uBuffer,
+                                                                 vBuffer,
+                                                                 dstBuffer,
+                                                                 aWidth);
+  }
+
+  return 0;
+}
+
+int
+YUV420PToRGB24(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t* aUBuffer, int aUStride,
+               const uint8_t* aVBuffer, int aVStride,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aUBuffer + aUStride * (i / 2);
+    const uint8_t* vBuffer = aVBuffer + aVStride * (i / 2);
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBFamily_Row<0, 0, 0, 1, 0, 0, 2, 1, 1, 0, 1, 2>(yBuffer,
+                                                                 uBuffer,
+                                                                 vBuffer,
+                                                                 dstBuffer,
+                                                                 aWidth);
+  }
+
+  return 0;
+}
+
+int
+NV12ToRGB24(const uint8_t* aYBuffer, int aYStride,
+            const uint8_t* aUVBuffer, int aUVStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aUVBuffer + aUVStride * (i / 2);
+    const uint8_t* vBuffer = aUVBuffer + aUVStride * (i / 2) + 1;
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBFamily_Row<0, 0, 0, 1, 0, 0, 2, 2, 2, 0, 1, 2>(yBuffer,
+                                                                 uBuffer,
+                                                                 vBuffer,
+                                                                 dstBuffer,
+                                                                 aWidth);
+  }
+
+  return 0;
+}
+
+int
+NV21ToRGB24(const uint8_t* aYBuffer, int aYStride,
+            const uint8_t* aVUBuffer, int aVUStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aVUBuffer + aVUStride * (i / 2) + 1;
+    const uint8_t* vBuffer = aVUBuffer + aVUStride * (i / 2);
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBFamily_Row<0, 0, 0, 1, 0, 0, 2, 2, 2, 0, 1, 2>(yBuffer,
+                                                                 uBuffer,
+                                                                 vBuffer,
+                                                                 dstBuffer,
+                                                                 aWidth);
+  }
+
+  return 0;
+}
+
+int
+YUV444PToBGR24(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t* aUBuffer, int aUStride,
+               const uint8_t* aVBuffer, int aVStride,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aUBuffer + aUStride * i;
+    const uint8_t* vBuffer = aVBuffer + aVStride * i;
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBFamily_Row<0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 1, 0>(yBuffer,
+                                                                 uBuffer,
+                                                                 vBuffer,
+                                                                 dstBuffer,
+                                                                 aWidth);
+  }
+
+  return 0;
+}
+
+int
+YUV422PToBGR24(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t* aUBuffer, int aUStride,
+               const uint8_t* aVBuffer, int aVStride,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aUBuffer + aUStride * i;
+    const uint8_t* vBuffer = aVBuffer + aVStride * i;
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBFamily_Row<0, 0, 0, 1, 0, 0, 2, 1, 1, 2, 1, 0>(yBuffer,
+                                                                 uBuffer,
+                                                                 vBuffer,
+                                                                 dstBuffer,
+                                                                 aWidth);
+  }
+
+  return 0;
+}
+
+int
+YUV420PToBGR24(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t* aUBuffer, int aUStride,
+               const uint8_t* aVBuffer, int aVStride,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aUBuffer + aUStride * (i / 2);
+    const uint8_t* vBuffer = aVBuffer + aVStride * (i / 2);
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBFamily_Row<0, 0, 0, 1, 0, 0, 2, 1, 1, 2, 1, 0>(yBuffer,
+                                                                 uBuffer,
+                                                                 vBuffer,
+                                                                 dstBuffer,
+                                                                 aWidth);
+  }
+
+  return 0;
+}
+
+int
+NV12ToBGR24(const uint8_t* aYBuffer, int aYStride,
+            const uint8_t* aUVBuffer, int aUVStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aUVBuffer + aUVStride * (i / 2);
+    const uint8_t* vBuffer = aUVBuffer + aUVStride * (i / 2) + 1;
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBFamily_Row<0, 0, 0, 1, 0, 0, 2, 2, 2, 2, 1, 0>(yBuffer,
+                                                                 uBuffer,
+                                                                 vBuffer,
+                                                                 dstBuffer,
+                                                                 aWidth);
+  }
+
+  return 0;
+}
+
+int
+NV21ToBGR24(const uint8_t* aYBuffer, int aYStride,
+            const uint8_t* aVUBuffer, int aVUStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aVUBuffer + aVUStride * (i / 2) + 1;
+    const uint8_t* vBuffer = aVUBuffer + aVUStride * (i / 2);
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBFamily_Row<0, 0, 0, 1, 0, 0, 2, 2, 2, 2, 1, 0>(yBuffer,
+                                                                 uBuffer,
+                                                                 vBuffer,
+                                                                 dstBuffer,
+                                                                 aWidth);
+  }
+
+  return 0;
+}
+
+/*
+ * YUV family -> RGBA family.
+ */
+int
+YUV444PToRGBA32(const uint8_t* aYBuffer, int aYStride,
+                const uint8_t* aUBuffer, int aUStride,
+                const uint8_t* aVBuffer, int aVStride,
+                uint8_t* aDstBuffer, int aDstStride,
+                int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aUBuffer + aUStride * i;
+    const uint8_t* vBuffer = aVBuffer + aVStride * i;
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBAFamily_Row<0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 1, 2, 3>(yBuffer,
+                                                                     uBuffer,
+                                                                     vBuffer,
+                                                                     dstBuffer,
+                                                                     aWidth);
+  }
+
+  return 0;
+}
+
+int
+YUV422PToRGBA32(const uint8_t* aYBuffer, int aYStride,
+                const uint8_t* aUBuffer, int aUStride,
+                const uint8_t* aVBuffer, int aVStride,
+                uint8_t* aDstBuffer, int aDstStride,
+                int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aUBuffer + aUStride * i;
+    const uint8_t* vBuffer = aVBuffer + aVStride * i;
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBAFamily_Row<0, 0, 0, 1, 0, 0, 2, 1, 1, 0, 1, 2, 3>(yBuffer,
+                                                                     uBuffer,
+                                                                     vBuffer,
+                                                                     dstBuffer,
+                                                                     aWidth);
+  }
+
+  return 0;
+}
+
+int
+YUV420PToRGBA32(const uint8_t* aYBuffer, int aYStride,
+                const uint8_t* aUBuffer, int aUStride,
+                const uint8_t* aVBuffer, int aVStride,
+                uint8_t* aDstBuffer, int aDstStride,
+                int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aUBuffer + aUStride * (i / 2);
+    const uint8_t* vBuffer = aVBuffer + aVStride * (i / 2);
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBAFamily_Row<0, 0, 0, 1, 0, 0, 2, 1, 1, 0, 1, 2, 3>(yBuffer,
+                                                                     uBuffer,
+                                                                     vBuffer,
+                                                                     dstBuffer,
+                                                                     aWidth);
+  }
+
+  return 0;
+}
+
+int
+NV12ToRGBA32(const uint8_t* aYBuffer, int aYStride,
+             const uint8_t* aUVBuffer, int aUVStride,
+             uint8_t* aDstBuffer, int aDstStride,
+             int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aUVBuffer + aUVStride * (i / 2);
+    const uint8_t* vBuffer = aUVBuffer + aUVStride * (i / 2) + 1;
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBAFamily_Row<0, 0, 0, 1, 0, 0, 2, 2, 2, 0, 1, 2, 3>(yBuffer,
+                                                                     uBuffer,
+                                                                     vBuffer,
+                                                                     dstBuffer,
+                                                                     aWidth);
+  }
+
+  return 0;
+}
+
+int
+NV21ToRGBA32(const uint8_t* aYBuffer, int aYStride,
+             const uint8_t* aVUBuffer, int aVUStride,
+             uint8_t* aDstBuffer, int aDstStride,
+             int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aVUBuffer + aVUStride * (i / 2) + 1;
+    const uint8_t* vBuffer = aVUBuffer + aVUStride * (i / 2);
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBAFamily_Row<0, 0, 0, 1, 0, 0, 2, 2, 2, 0, 1, 2, 3>(yBuffer,
+                                                                     uBuffer,
+                                                                     vBuffer,
+                                                                     dstBuffer,
+                                                                     aWidth);
+  }
+
+  return 0;
+}
+
+int
+YUV444PToBGRA32(const uint8_t* aYBuffer, int aYStride,
+                const uint8_t* aUBuffer, int aUStride,
+                const uint8_t* aVBuffer, int aVStride,
+                uint8_t* aDstBuffer, int aDstStride,
+                int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aUBuffer + aUStride * i;
+    const uint8_t* vBuffer = aVBuffer + aVStride * i;
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBAFamily_Row<0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 1, 0, 3>(yBuffer,
+                                                                     uBuffer,
+                                                                     vBuffer,
+                                                                     dstBuffer,
+                                                                     aWidth);
+  }
+
+  return 0;
+}
+
+int
+YUV422PToBGRA32(const uint8_t* aYBuffer, int aYStride,
+                const uint8_t* aUBuffer, int aUStride,
+                const uint8_t* aVBuffer, int aVStride,
+                uint8_t* aDstBuffer, int aDstStride,
+                int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aUBuffer + aUStride * i;
+    const uint8_t* vBuffer = aVBuffer + aVStride * i;
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBAFamily_Row<0, 0, 0, 1, 0, 0, 2, 1, 1, 2, 1, 0, 3>(yBuffer,
+                                                                     uBuffer,
+                                                                     vBuffer,
+                                                                     dstBuffer,
+                                                                     aWidth);
+  }
+
+  return 0;
+}
+
+int
+YUV420PToBGRA32(const uint8_t* aYBuffer, int aYStride,
+                const uint8_t* aUBuffer, int aUStride,
+                const uint8_t* aVBuffer, int aVStride,
+                uint8_t* aDstBuffer, int aDstStride,
+                int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aUBuffer + aUStride * (i / 2);
+    const uint8_t* vBuffer = aVBuffer + aVStride * (i / 2);
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBAFamily_Row<0, 0, 0, 1, 0, 0, 2, 1, 1, 2, 1, 0, 3>(yBuffer,
+                                                                     uBuffer,
+                                                                     vBuffer,
+                                                                     dstBuffer,
+                                                                     aWidth);
+  }
+
+  return 0;
+}
+
+int
+NV12ToBGRA32(const uint8_t* aYBuffer, int aYStride,
+             const uint8_t* aUVBuffer, int aUVStride,
+             uint8_t* aDstBuffer, int aDstStride,
+             int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aUVBuffer + aUVStride * (i / 2);
+    const uint8_t* vBuffer = aUVBuffer + aUVStride * (i / 2) + 1;
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBAFamily_Row<0, 0, 0, 1, 0, 0, 2, 2, 2, 2, 1, 0, 3>(yBuffer,
+                                                                     uBuffer,
+                                                                     vBuffer,
+                                                                     dstBuffer,
+                                                                     aWidth);
+  }
+
+  return 0;
+}
+
+int
+NV21ToBGRA32(const uint8_t* aYBuffer, int aYStride,
+             const uint8_t* aVUBuffer, int aVUStride,
+             uint8_t* aDstBuffer, int aDstStride,
+             int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aVUBuffer + aVUStride * (i / 2) + 1;
+    const uint8_t* vBuffer = aVUBuffer + aVUStride * (i / 2);
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBAFamily_Row<0, 0, 0, 1, 0, 0, 2, 2, 2, 2, 1, 0, 3>(yBuffer,
+                                                                     uBuffer,
+                                                                     vBuffer,
+                                                                     dstBuffer,
+                                                                     aWidth);
+  }
+
+  return 0;
+}
+
+/*
+ * RGB family -> YUV family.
+ */
+int
+RGB24ToYUV444P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUBuffer + aUStride * i;
+    uint8_t* vBuffer = aVBuffer + aVStride * i;
+
+    for (int j = 0; j < aWidth; ++j) {
+      yBuffer[0] = RGBToY(srcBuffer[0], srcBuffer[1], srcBuffer[2]);
+      uBuffer[0] = RGBToU(srcBuffer[0], srcBuffer[1], srcBuffer[2]);
+      vBuffer[0] = RGBToV(srcBuffer[0], srcBuffer[1], srcBuffer[2]);
+
+      yBuffer += 1;
+      uBuffer += 1;
+      vBuffer += 1;
+      srcBuffer += 3;
+    }
+  }
+
+  return 0;
+}
+
+int
+RGB24ToYUV422P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUBuffer + aUStride * i;
+    uint8_t* vBuffer = aVBuffer + aVStride * i;
+
+    RGBFamilyToY_Row<0, 1, 2>(srcBuffer, yBuffer, aWidth);
+
+    // Pass 0 as the aSrcStride so we don't sample next row's RGB information.
+    RGBFamilyToUV_Row<0, 1, 2, 1, 1>(srcBuffer, 0, uBuffer, vBuffer, aWidth);
+  }
+
+  return 0;
+}
+
+int
+RGB24ToYUV420P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight - 1; i += 2) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUBuffer + aUStride * (i / 2);
+    uint8_t* vBuffer = aVBuffer + aVStride * (i / 2);
+
+    RGBFamilyToY_Row<0, 1, 2>(srcBuffer, yBuffer, aWidth);
+    RGBFamilyToY_Row<0, 1, 2>(srcBuffer + aSrcStride, yBuffer + aYStride, aWidth);
+    RGBFamilyToUV_Row<0, 1, 2, 1, 1>(srcBuffer, aSrcStride, uBuffer, vBuffer, aWidth);
+  }
+
+  if (aHeight & 1) {
+    const int i = aHeight - 1;
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUBuffer + aUStride * (i / 2);
+    uint8_t* vBuffer = aVBuffer + aVStride * (i / 2);
+
+    RGBFamilyToY_Row<0, 1, 2>(srcBuffer, yBuffer, aWidth);
+
+    // Pass 0 as the aSrcStride so we don't sample next row's RGB information.
+    RGBFamilyToUV_Row<0, 1, 2, 1, 1>(srcBuffer, 0, uBuffer, vBuffer, aWidth);
+  }
+
+  return 0;
+}
+
+int
+RGB24ToNV12(const uint8_t* aSrcBuffer, int aSrcStride,
+            uint8_t* aYBuffer, int aYStride,
+            uint8_t* aUVBuffer, int aUVStride,
+            int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight - 1; i += 2) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUVBuffer + aUVStride * (i / 2);
+    uint8_t* vBuffer = aUVBuffer + aUVStride * (i / 2) + 1;
+
+    RGBFamilyToY_Row<0, 1, 2>(srcBuffer, yBuffer, aWidth);
+    RGBFamilyToY_Row<0, 1, 2>(srcBuffer + aSrcStride, yBuffer + aYStride, aWidth);
+    RGBFamilyToUV_Row<0, 1, 2, 2, 2>(srcBuffer, aSrcStride, uBuffer, vBuffer, aWidth);
+  }
+
+  if (aHeight & 1) {
+    const int i = aHeight - 1;
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUVBuffer + aUVStride * (i / 2);
+    uint8_t* vBuffer = aUVBuffer + aUVStride * (i / 2) + 1;
+
+    RGBFamilyToY_Row<0, 1, 2>(srcBuffer, yBuffer, aWidth);
+
+    // Pass 0 as the aSrcStride so we don't sample next row's RGB information.
+    RGBFamilyToUV_Row<0, 1, 2, 2, 2>(srcBuffer, 0, uBuffer, vBuffer, aWidth);
+  }
+
+  return 0;
+}
+
+int
+RGB24ToNV21(const uint8_t* aSrcBuffer, int aSrcStride,
+            uint8_t* aYBuffer, int aYStride,
+            uint8_t* aVUBuffer, int aVUStride,
+            int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight - 1; i += 2) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aVUBuffer + aVUStride * (i / 2) + 1;
+    uint8_t* vBuffer = aVUBuffer + aVUStride * (i / 2);
+
+    RGBFamilyToY_Row<0, 1, 2>(srcBuffer, yBuffer, aWidth);
+    RGBFamilyToY_Row<0, 1, 2>(srcBuffer + aSrcStride, yBuffer + aYStride, aWidth);
+    RGBFamilyToUV_Row<0, 1, 2, 2, 2>(srcBuffer, aSrcStride, uBuffer, vBuffer, aWidth);
+  }
+
+  if (aHeight & 1) {
+    const int i = aHeight - 1;
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aVUBuffer + aVUStride * (i / 2) + 1;
+    uint8_t* vBuffer = aVUBuffer + aVUStride * (i / 2);
+
+    RGBFamilyToY_Row<0, 1, 2>(srcBuffer, yBuffer, aWidth);
+
+    // Pass 0 as the aSrcStride so we don't sample next row's RGB information.
+    RGBFamilyToUV_Row<0, 1, 2, 2, 2>(srcBuffer, 0, uBuffer, vBuffer, aWidth);
+  }
+
+  return 0;
+}
+
+int
+BGR24ToYUV444P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUBuffer + aUStride * i;
+    uint8_t* vBuffer = aVBuffer + aVStride * i;
+
+    for (int j = 0; j < aWidth; ++j) {
+      yBuffer[0] = RGBToY(srcBuffer[2], srcBuffer[1], srcBuffer[0]);
+      uBuffer[0] = RGBToU(srcBuffer[2], srcBuffer[1], srcBuffer[0]);
+      vBuffer[0] = RGBToV(srcBuffer[2], srcBuffer[1], srcBuffer[0]);
+
+      yBuffer += 1;
+      uBuffer += 1;
+      vBuffer += 1;
+      srcBuffer += 3;
+    }
+  }
+
+  return 0;
+}
+
+int
+BGR24ToYUV422P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUBuffer + aUStride * i;
+    uint8_t* vBuffer = aVBuffer + aVStride * i;
+
+    RGBFamilyToY_Row<2, 1, 0>(srcBuffer, yBuffer, aWidth);
+
+    // Pass 0 as the aSrcStride so we don't sample next row's RGB information.
+    RGBFamilyToUV_Row<2, 1, 0, 1, 1>(srcBuffer, 0, uBuffer, vBuffer, aWidth);
+  }
+
+  return 0;
+}
+
+int
+BGR24ToYUV420P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight - 1; i += 2) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUBuffer + aUStride * (i / 2);
+    uint8_t* vBuffer = aVBuffer + aVStride * (i / 2);
+
+    RGBFamilyToY_Row<2, 1, 0>(srcBuffer, yBuffer, aWidth);
+    RGBFamilyToY_Row<2, 1, 0>(srcBuffer + aSrcStride, yBuffer + aYStride, aWidth);
+    RGBFamilyToUV_Row<2, 1, 0, 1, 1>(srcBuffer, aSrcStride, uBuffer, vBuffer, aWidth);
+  }
+
+  if (aHeight & 1) {
+    const int i = aHeight - 1;
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUBuffer + aUStride * (i / 2);
+    uint8_t* vBuffer = aVBuffer + aVStride * (i / 2);
+
+    RGBFamilyToY_Row<2, 1, 0>(srcBuffer, yBuffer, aWidth);
+
+    // Pass 0 as the aSrcStride so we don't sample next row's RGB information.
+    RGBFamilyToUV_Row<2, 1, 0, 1, 1>(srcBuffer, 0, uBuffer, vBuffer, aWidth);
+  }
+
+  return 0;
+}
+
+int
+BGR24ToNV12(const uint8_t* aSrcBuffer, int aSrcStride,
+            uint8_t* aYBuffer, int aYStride,
+            uint8_t* aUVBuffer, int aUVStride,
+            int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight - 1; i += 2) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUVBuffer + aUVStride * (i / 2);
+    uint8_t* vBuffer = aUVBuffer + aUVStride * (i / 2) + 1;
+
+    RGBFamilyToY_Row<2, 1, 0>(srcBuffer, yBuffer, aWidth);
+    RGBFamilyToY_Row<2, 1, 0>(srcBuffer + aSrcStride, yBuffer + aYStride, aWidth);
+    RGBFamilyToUV_Row<2, 1, 0, 2, 2>(srcBuffer, aSrcStride, uBuffer, vBuffer, aWidth);
+  }
+
+  if (aHeight & 1) {
+    const int i = aHeight - 1;
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUVBuffer + aUVStride * (i / 2);
+    uint8_t* vBuffer = aUVBuffer + aUVStride * (i / 2) + 1;
+
+    RGBFamilyToY_Row<2, 1, 0>(srcBuffer, yBuffer, aWidth);
+
+    // Pass 0 as the aSrcStride so we don't sample next row's RGB information.
+    RGBFamilyToUV_Row<2, 1, 0, 2, 2>(srcBuffer, 0, uBuffer, vBuffer, aWidth);
+  }
+
+  return 0;
+}
+
+int
+BGR24ToNV21(const uint8_t* aSrcBuffer, int aSrcStride,
+            uint8_t* aYBuffer, int aYStride,
+            uint8_t* aVUBuffer, int aVUStride,
+            int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight - 1; i += 2) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aVUBuffer + aVUStride * (i / 2) + 1;
+    uint8_t* vBuffer = aVUBuffer + aVUStride * (i / 2);
+
+    RGBFamilyToY_Row<2, 1, 0>(srcBuffer, yBuffer, aWidth);
+    RGBFamilyToY_Row<2, 1, 0>(srcBuffer + aSrcStride, yBuffer + aYStride, aWidth);
+    RGBFamilyToUV_Row<2, 1, 0, 2, 2>(srcBuffer, aSrcStride, uBuffer, vBuffer, aWidth);
+  }
+
+  if (aHeight & 1) {
+    const int i = aHeight - 1;
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aVUBuffer + aVUStride * (i / 2) + 1;
+    uint8_t* vBuffer = aVUBuffer + aVUStride * (i / 2);
+
+    RGBFamilyToY_Row<2, 1, 0>(srcBuffer, yBuffer, aWidth);
+
+    // Pass 0 as the aSrcStride so we don't sample next row's RGB information.
+    RGBFamilyToUV_Row<2, 1, 0, 2, 2>(srcBuffer, 0, uBuffer, vBuffer, aWidth);
+  }
+
+  return 0;
+}
+
+/*
+ * RGBA family -> YUV family.
+ */
+int
+RGBA32ToYUV444P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUBuffer + aUStride * i;
+    uint8_t* vBuffer = aVBuffer + aVStride * i;
+
+    for (int j = 0; j < aWidth; ++j) {
+      yBuffer[0] = RGBToY(srcBuffer[0], srcBuffer[1], srcBuffer[2]);
+      uBuffer[0] = RGBToU(srcBuffer[0], srcBuffer[1], srcBuffer[2]);
+      vBuffer[0] = RGBToV(srcBuffer[0], srcBuffer[1], srcBuffer[2]);
+
+      yBuffer += 1;
+      uBuffer += 1;
+      vBuffer += 1;
+      srcBuffer += 4;
+    }
+  }
+
+  return 0;
+}
+
+int
+RGBA32ToYUV422P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUBuffer + aUStride * i;
+    uint8_t* vBuffer = aVBuffer + aVStride * i;
+
+    RGBAFamilyToY_Row<0, 1, 2>(srcBuffer, yBuffer, aWidth);
+
+    // Pass 0 as the aSrcStride so we don't sample next row's RGB information.
+    RGBAFamilyToUV_Row<0, 1, 2, 1, 1>(srcBuffer, 0, uBuffer, vBuffer, aWidth);
+  }
+
+  return 0;
+}
+
+int
+RGBA32ToYUV420P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight - 1; i += 2) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUBuffer + aUStride * (i / 2);
+    uint8_t* vBuffer = aVBuffer + aVStride * (i / 2);
+
+    RGBAFamilyToY_Row<0, 1, 2>(srcBuffer, yBuffer, aWidth);
+    RGBAFamilyToY_Row<0, 1, 2>(srcBuffer + aSrcStride, yBuffer + aYStride, aWidth);
+    RGBAFamilyToUV_Row<0, 1, 2, 1, 1>(srcBuffer, aSrcStride, uBuffer, vBuffer, aWidth);
+  }
+
+  if (aHeight & 1) {
+    const int i = aHeight - 1;
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUBuffer + aUStride * (i / 2);
+    uint8_t* vBuffer = aVBuffer + aVStride * (i / 2);
+
+    RGBAFamilyToY_Row<0, 1, 2>(srcBuffer, yBuffer, aWidth);
+
+    // Pass 0 as the aSrcStride so we don't sample next row's RGB information.
+    RGBAFamilyToUV_Row<0, 1, 2, 1, 1>(srcBuffer, 0, uBuffer, vBuffer, aWidth);
+  }
+
+  return 0;
+}
+
+int
+RGBA32ToNV12(const uint8_t* aSrcBuffer, int aSrcStride,
+            uint8_t* aYBuffer, int aYStride,
+            uint8_t* aUVBuffer, int aUVStride,
+            int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight - 1; i += 2) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUVBuffer + aUVStride * (i / 2);
+    uint8_t* vBuffer = aUVBuffer + aUVStride * (i / 2) + 1;
+
+    RGBAFamilyToY_Row<0, 1, 2>(srcBuffer, yBuffer, aWidth);
+    RGBAFamilyToY_Row<0, 1, 2>(srcBuffer + aSrcStride, yBuffer + aYStride, aWidth);
+    RGBAFamilyToUV_Row<0, 1, 2, 2, 2>(srcBuffer, aSrcStride, uBuffer, vBuffer, aWidth);
+  }
+
+  if (aHeight & 1) {
+    const int i = aHeight - 1;
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUVBuffer + aUVStride * (i / 2);
+    uint8_t* vBuffer = aUVBuffer + aUVStride * (i / 2) + 1;
+
+    RGBAFamilyToY_Row<0, 1, 2>(srcBuffer, yBuffer, aWidth);
+
+    // Pass 0 as the aSrcStride so we don't sample next row's RGB information.
+    RGBAFamilyToUV_Row<0, 1, 2, 2, 2>(srcBuffer, 0, uBuffer, vBuffer, aWidth);
+  }
+
+  return 0;
+}
+
+int
+RGBA32ToNV21(const uint8_t* aSrcBuffer, int aSrcStride,
+            uint8_t* aYBuffer, int aYStride,
+            uint8_t* aVUBuffer, int aVUStride,
+            int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight - 1; i += 2) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aVUBuffer + aVUStride * (i / 2) + 1;
+    uint8_t* vBuffer = aVUBuffer + aVUStride * (i / 2);
+
+    RGBAFamilyToY_Row<0, 1, 2>(srcBuffer, yBuffer, aWidth);
+    RGBAFamilyToY_Row<0, 1, 2>(srcBuffer + aSrcStride, yBuffer + aYStride, aWidth);
+    RGBAFamilyToUV_Row<0, 1, 2, 2, 2>(srcBuffer, aSrcStride, uBuffer, vBuffer, aWidth);
+  }
+
+  if (aHeight & 1) {
+    const int i = aHeight - 1;
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aVUBuffer + aVUStride * (i / 2) + 1;
+    uint8_t* vBuffer = aVUBuffer + aVUStride * (i / 2);
+
+    RGBAFamilyToY_Row<0, 1, 2>(srcBuffer, yBuffer, aWidth);
+
+    // Pass 0 as the aSrcStride so we don't sample next row's RGB information.
+    RGBAFamilyToUV_Row<0, 1, 2, 2, 2>(srcBuffer, 0, uBuffer, vBuffer, aWidth);
+  }
+
+  return 0;
+}
+
+int
+BGRA32ToYUV444P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUBuffer + aUStride * i;
+    uint8_t* vBuffer = aVBuffer + aVStride * i;
+
+    for (int j = 0; j < aWidth; ++j) {
+      yBuffer[0] = RGBToY(srcBuffer[2], srcBuffer[1], srcBuffer[0]);
+      uBuffer[0] = RGBToU(srcBuffer[2], srcBuffer[1], srcBuffer[0]);
+      vBuffer[0] = RGBToV(srcBuffer[2], srcBuffer[1], srcBuffer[0]);
+
+      yBuffer += 1;
+      uBuffer += 1;
+      vBuffer += 1;
+      srcBuffer += 4;
+    }
+  }
+
+  return 0;
+}
+
+int
+BGRA32ToYUV422P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUBuffer + aUStride * i;
+    uint8_t* vBuffer = aVBuffer + aVStride * i;
+
+    RGBAFamilyToY_Row<2, 1, 0>(srcBuffer, yBuffer, aWidth);
+
+    // Pass 0 as the aSrcStride so we don't sample next row's RGB information.
+    RGBAFamilyToUV_Row<2, 1, 0, 1, 1>(srcBuffer, 0, uBuffer, vBuffer, aWidth);
+  }
+
+  return 0;
+}
+
+int
+BGRA32ToYUV420P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight - 1; i += 2) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUBuffer + aUStride * (i / 2);
+    uint8_t* vBuffer = aVBuffer + aVStride * (i / 2);
+
+    RGBAFamilyToY_Row<2, 1, 0>(srcBuffer, yBuffer, aWidth);
+    RGBAFamilyToY_Row<2, 1, 0>(srcBuffer + aSrcStride, yBuffer + aYStride, aWidth);
+    RGBAFamilyToUV_Row<2, 1, 0, 1, 1>(srcBuffer, aSrcStride, uBuffer, vBuffer, aWidth);
+  }
+
+  if (aHeight & 1) {
+    const int i = aHeight - 1;
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUBuffer + aUStride * (i / 2);
+    uint8_t* vBuffer = aVBuffer + aVStride * (i / 2);
+
+    RGBAFamilyToY_Row<2, 1, 0>(srcBuffer, yBuffer, aWidth);
+
+    // Pass 0 as the aSrcStride so we don't sample next row's RGB information.
+    RGBAFamilyToUV_Row<2, 1, 0, 1, 1>(srcBuffer, 0, uBuffer, vBuffer, aWidth);
+  }
+
+  return 0;
+}
+
+int
+BGRA32ToNV12(const uint8_t* aSrcBuffer, int aSrcStride,
+            uint8_t* aYBuffer, int aYStride,
+            uint8_t* aUVBuffer, int aUVStride,
+            int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight - 1; i += 2) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUVBuffer + aUVStride * (i / 2);
+    uint8_t* vBuffer = aUVBuffer + aUVStride * (i / 2) + 1;
+
+    RGBAFamilyToY_Row<2, 1, 0>(srcBuffer, yBuffer, aWidth);
+    RGBAFamilyToY_Row<2, 1, 0>(srcBuffer + aSrcStride, yBuffer + aYStride, aWidth);
+    RGBAFamilyToUV_Row<2, 1, 0, 2, 2>(srcBuffer, aSrcStride, uBuffer, vBuffer, aWidth);
+  }
+
+  if (aHeight & 1) {
+    const int i = aHeight - 1;
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUVBuffer + aUVStride * (i / 2);
+    uint8_t* vBuffer = aUVBuffer + aUVStride * (i / 2) + 1;
+
+    RGBAFamilyToY_Row<2, 1, 0>(srcBuffer, yBuffer, aWidth);
+
+    // Pass 0 as the aSrcStride so we don't sample next row's RGB information.
+    RGBAFamilyToUV_Row<2, 1, 0, 2, 2>(srcBuffer, 0, uBuffer, vBuffer, aWidth);
+  }
+
+  return 0;
+}
+
+int
+BGRA32ToNV21(const uint8_t* aSrcBuffer, int aSrcStride,
+            uint8_t* aYBuffer, int aYStride,
+            uint8_t* aVUBuffer, int aVUStride,
+            int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight - 1; i += 2) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aVUBuffer + aVUStride * (i / 2) + 1;
+    uint8_t* vBuffer = aVUBuffer + aVUStride * (i / 2);
+
+    RGBAFamilyToY_Row<2, 1, 0>(srcBuffer, yBuffer, aWidth);
+    RGBAFamilyToY_Row<2, 1, 0>(srcBuffer + aSrcStride, yBuffer + aYStride, aWidth);
+    RGBAFamilyToUV_Row<2, 1, 0, 2, 2>(srcBuffer, aSrcStride, uBuffer, vBuffer, aWidth);
+  }
+
+  if (aHeight & 1) {
+    const int i = aHeight - 1;
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aVUBuffer + aVUStride * (i / 2) + 1;
+    uint8_t* vBuffer = aVUBuffer + aVUStride * (i / 2);
+
+    RGBAFamilyToY_Row<2, 1, 0>(srcBuffer, yBuffer, aWidth);
+
+    // Pass 0 as the aSrcStride so we don't sample next row's RGB information.
+    RGBAFamilyToUV_Row<2, 1, 0, 2, 2>(srcBuffer, 0, uBuffer, vBuffer, aWidth);
+  }
+
+  return 0;
+}
+
+/*
+ * RGBA/RGB family -> HSV.
+ * Reference:
+ * (1) https://en.wikipedia.org/wiki/HSL_and_HSV
+ * (2) OpenCV implementation:
+ *     http://docs.opencv.org/3.1.0/de/d25/imgproc_color_conversions.html
+ */
+const float EPSILON = 1e-10f;
+
+template<int aRIndex, int aGIndex, int aBIndex, int aSrcStep>
+int
+RGBFamilyToHSV(const uint8_t* aSrcBuffer, int aSrcStride,
+               float* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    float* dstBuffer = (float*)((uint8_t*)(aDstBuffer) + aDstStride * i);
+
+    for (int j = 0; j < aWidth; ++j) {
+      const float r = (float)(srcBuffer[aRIndex]) / 255.0f;
+      const float g = (float)(srcBuffer[aGIndex]) / 255.0f;
+      const float b = (float)(srcBuffer[aBIndex]) / 255.0f;
+      float& h = dstBuffer[0];
+      float& s = dstBuffer[1];
+      float& v = dstBuffer[2];
+
+      float min = r;
+      if (g < min) min = g;
+      if (b < min) min = b;
+
+      float max = r;
+      if (g > max) max = g;
+      if (b > max) max = b;
+
+      const float diff = max - min + EPSILON; // Prevent dividing by zero.
+
+      // Calculate v.
+      v = max;
+
+      // Calculate s.
+      if (max == 0.0f) {
+        s = 0.0f;
+      } else {
+        s = diff / v;
+      }
+
+      // Calculate h.
+      if (max == r) {
+        h = 60.0f * (g - b) / diff;
+      } else if (max == g) {
+        h = 60.0f * (b - r) / diff + 120.0f;
+      } else if (max == b) {
+        h = 60.0f * (r - g) / diff + 240.0f;
+      }
+
+      if (h < 0.0f) {
+        h += 360.0f;
+      }
+
+      // Step one pixel.
+      srcBuffer += aSrcStep;
+      dstBuffer += 3;
+    }
+  }
+
+  return 0;
+}
+
+static const int sector_data[][3]= {{0,3,1}, {2,0,1}, {1,0,3}, {1,2,0}, {3,1,0}, {0,1,2}};
+
+// If the destination is a RGB24 or BGR24, set the aAIndex to be 0, 1 or 2,
+// so that the r, g or b value will be set to 255 first than to the right value.
+template<int aRIndex, int aGIndex, int aBIndex, int aAIndex, int aDstStep>
+int
+HSVToRGBAFamily(const float* aSrcBuffer, int aSrcStride,
+                uint8_t* aDstBuffer, int aDstStride,
+                int aWidth, int aHeight)
+{
+  static_assert(aRIndex == 0 || aRIndex == 2, "Wrong R index.");
+  static_assert(aGIndex == 1, "Wrong G index.");
+  static_assert(aBIndex == 0 || aBIndex == 2, "Wrong B index.");
+  static_assert(aAIndex == 0 || aAIndex == 1 || aAIndex == 2 || aAIndex == 3, "Wrong A index.");
+
+  for (int i = 0; i < aHeight; ++i) {
+    const float* srcBuffer = (const float*)((const uint8_t*)(aSrcBuffer) + aSrcStride * i);
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    for (int j = 0; j < aWidth; ++j) {
+      const float h = srcBuffer[0];
+      const float s = srcBuffer[1];
+      const float v = srcBuffer[2];
+
+      // Calculate h-prime which should be in range [0, 6). -> h should be in
+      // range [0, 360).
+      float hPrime = h / 60.0f;
+      if (hPrime < 0.0f)
+          do hPrime += 6.0f; while (hPrime < 0.0f);
+      else if (hPrime >= 6.0f)
+          do hPrime -= 6.0f; while (hPrime >= 6.0f);
+      const int sector = floor(hPrime);
+      const float hMod1 = hPrime - sector;
+
+      float values[4];
+      values[0] = v;
+      values[1] = v * (1.0f - s);
+      values[2] = v * (1.0f - s * hMod1);
+      values[3] = v * (1.0f - s * (1.0f - hMod1));
+
+      dstBuffer[aAIndex] = 255;
+      dstBuffer[aRIndex] = Clamp(values[sector_data[sector][0]] * 255.0f);
+      dstBuffer[aGIndex] = Clamp(values[sector_data[sector][1]] * 255.0f);
+      dstBuffer[aBIndex] = Clamp(values[sector_data[sector][2]] * 255.0f);
+
+      // Step one pixel.
+      srcBuffer += 3;
+      dstBuffer += aDstStep;
+    }
+  }
+
+  return 0;
+}
+
+int
+RGBA32ToHSV(const uint8_t* aSrcBuffer, int aSrcStride,
+            float* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight)
+{
+  return RGBFamilyToHSV<0, 1, 2, 4>(aSrcBuffer, aSrcStride,
+                                    aDstBuffer, aDstStride,
+                                    aWidth, aHeight);
+}
+
+int
+BGRA32ToHSV(const uint8_t* aSrcBuffer, int aSrcStride,
+            float* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight)
+{
+  return RGBFamilyToHSV<2, 1, 0, 4>(aSrcBuffer, aSrcStride,
+                                    aDstBuffer, aDstStride,
+                                    aWidth, aHeight);
+}
+
+int
+RGB24ToHSV(const uint8_t* aSrcBuffer, int aSrcStride,
+           float* aDstBuffer, int aDstStride,
+           int aWidth, int aHeight)
+{
+  return RGBFamilyToHSV<0, 1, 2, 3>(aSrcBuffer, aSrcStride,
+                                    aDstBuffer, aDstStride,
+                                    aWidth, aHeight);
+}
+
+int
+BGR24ToHSV(const uint8_t* aSrcBuffer, int aSrcStride,
+           float* aDstBuffer, int aDstStride,
+           int aWidth, int aHeight)
+{
+  return RGBFamilyToHSV<2, 1, 0, 3>(aSrcBuffer, aSrcStride,
+                                    aDstBuffer, aDstStride,
+                                    aWidth, aHeight);
+}
+
+int
+HSVToRGBA32(const float* aSrcBuffer, int aSrcStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight)
+{
+  return HSVToRGBAFamily<0, 1, 2, 3, 4>(aSrcBuffer, aSrcStride,
+                                        aDstBuffer, aDstStride,
+                                        aWidth, aHeight);
+}
+
+int
+HSVToBGRA32(const float* aSrcBuffer, int aSrcStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight)
+{
+  return HSVToRGBAFamily<2, 1, 0, 3, 4>(aSrcBuffer, aSrcStride,
+                                        aDstBuffer, aDstStride,
+                                        aWidth, aHeight);
+}
+
+int
+HSVToRGB24(const float* aSrcBuffer, int aSrcStride,
+           uint8_t* aDstBuffer, int aDstStride,
+           int aWidth, int aHeight)
+{
+  return HSVToRGBAFamily<0, 1, 2, 0, 3>(aSrcBuffer, aSrcStride,
+                                        aDstBuffer, aDstStride,
+                                        aWidth, aHeight);
+}
+
+int
+HSVToBGR24(const float* aSrcBuffer, int aSrcStride,
+           uint8_t* aDstBuffer, int aDstStride,
+           int aWidth, int aHeight)
+{
+  return HSVToRGBAFamily<2, 1, 0, 0, 3>(aSrcBuffer, aSrcStride,
+                                        aDstBuffer, aDstStride,
+                                        aWidth, aHeight);
+}
+
+/*
+ * RGBA/RGB family -> Lab.
+ * Reference:
+ * (1) https://en.wikipedia.org/wiki/SRGB
+ * (2) https://en.wikipedia.org/wiki/Lab_color_space
+ * (3) OpenCV implementation:
+ *     http://docs.opencv.org/3.1.0/de/d25/imgproc_color_conversions.html
+ */
+static const float sRGBToXYZ_D65[] = {0.412453f, 0.357580f, 0.180423f,
+                                      0.212671f, 0.715160f, 0.072169f,
+                                      0.019334f, 0.119193f, 0.950227f};
+static const float XYZTosRGB_D65[] = {3.240479f,  -1.53715f,  -0.498535f,
+                                      -0.969256f, 1.875991f,  0.041556f,
+                                      0.055648f,  -0.204043f, 1.057311f};
+static const float whitept_D65[] = {0.950456f, 1.0f, 1.088754f};
+static const float _magic = std::pow((6.0 / 29.0), 3.0); // should be around 0.008856.
+static const float _1_3 = 1.0f / 3.0f;
+static const float _a = std::pow((29.0 / 6.0), 2.0) / 3.0; // should be around 7.787.
+static const float _b = 16.0f / 116.0f; // should be around 0.1379.
+
+template<int aRIndex, int aGIndex, int aBIndex, int aSrcStep>
+int
+RGBFamilyToLab(const uint8_t* aSrcBuffer, int aSrcStride,
+               float* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight)
+{
+  static_assert(aRIndex == 0 || aRIndex == 2, "Wrong R index.");
+  static_assert(aGIndex == 1, "Wrong G index.");
+  static_assert(aBIndex == 0 || aBIndex == 2, "Wrong B index.");
+
+  const float C0 = sRGBToXYZ_D65[0] / whitept_D65[0],
+              C1 = sRGBToXYZ_D65[1] / whitept_D65[0],
+              C2 = sRGBToXYZ_D65[2] / whitept_D65[0],
+              C3 = sRGBToXYZ_D65[3] / whitept_D65[1],
+              C4 = sRGBToXYZ_D65[4] / whitept_D65[1],
+              C5 = sRGBToXYZ_D65[5] / whitept_D65[1],
+              C6 = sRGBToXYZ_D65[6] / whitept_D65[2],
+              C7 = sRGBToXYZ_D65[7] / whitept_D65[2],
+              C8 = sRGBToXYZ_D65[8] / whitept_D65[2];
+
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    float* dstBuffer = (float*)((uint8_t*)(aDstBuffer) + aDstStride * i);
+
+    for (int j = 0; j < aWidth; ++j) {
+      float r = (float)(srcBuffer[aRIndex]) / 255.0f;
+      float g = (float)(srcBuffer[aGIndex]) / 255.0f;
+      float b = (float)(srcBuffer[aBIndex]) / 255.0f;
+
+      // gamma correction of sRGB
+      r = r <= 0.04045f ? r / 12.92f : std::pow((r + 0.055) / 1.055, 2.4);
+      g = g <= 0.04045f ? g / 12.92f : std::pow((g + 0.055) / 1.055, 2.4);
+      b = b <= 0.04045f ? b / 12.92f : std::pow((b + 0.055) / 1.055, 2.4);
+
+      const float X = C0 * r + C1 * g + C2 * b;
+      const float Y = C3 * r + C4 * g + C5 * b;
+      const float Z = C6 * r + C7 * g + C8 * b;
+
+      const float FX = X > _magic ? std::pow(X, _1_3) : (_a * X + _b);
+      const float FY = Y > _magic ? std::pow(Y, _1_3) : (_a * Y + _b);
+      const float FZ = Z > _magic ? std::pow(Z, _1_3) : (_a * Z + _b);
+
+      dstBuffer[0] = 116.0f * FY - 16.0f;
+      dstBuffer[1] = 500.0f * (FX - FY);
+      dstBuffer[2] = 200.0f * (FY - FZ);
+
+      // Step one pixel.
+      srcBuffer += aSrcStep;
+      dstBuffer += 3;
+    }
+  }
+  return 0;
+}
+
+// If the destination is a RGB24 or BGR24, set the aAIndex to be 0, 1 or 2,
+// so that the r, g or b value will be set to 255 first than to the right value.
+template<int aRIndex, int aGIndex, int aBIndex, int aAIndex, int aDstStep>
+int
+LabToRGBAFamily(const float* aSrcBuffer, int aSrcStride,
+                uint8_t* aDstBuffer, int aDstStride,
+                int aWidth, int aHeight)
+{
+  static_assert(aRIndex == 0 || aRIndex == 2, "Wrong R index.");
+  static_assert(aGIndex == 1, "Wrong G index.");
+  static_assert(aBIndex == 0 || aBIndex == 2, "Wrong B index.");
+  static_assert(aAIndex == 0 || aAIndex == 1 || aAIndex == 2 || aAIndex == 3, "Wrong A index.");
+
+  const float C0 = XYZTosRGB_D65[0] * whitept_D65[0],
+              C1 = XYZTosRGB_D65[1] * whitept_D65[1],
+              C2 = XYZTosRGB_D65[2] * whitept_D65[2],
+              C3 = XYZTosRGB_D65[3] * whitept_D65[0],
+              C4 = XYZTosRGB_D65[4] * whitept_D65[1],
+              C5 = XYZTosRGB_D65[5] * whitept_D65[2],
+              C6 = XYZTosRGB_D65[6] * whitept_D65[0],
+              C7 = XYZTosRGB_D65[7] * whitept_D65[1],
+              C8 = XYZTosRGB_D65[8] * whitept_D65[2];
+
+  for (int i = 0; i < aHeight; ++i) {
+    const float* srcBuffer = (const float*)((const uint8_t*)(aSrcBuffer) + aSrcStride * i);
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    for (int j = 0; j < aWidth; ++j) {
+      const float L = srcBuffer[0];
+      const float a = srcBuffer[1];
+      const float b = srcBuffer[2];
+
+      const float FY = (L + 16.0f) / 116.0f;
+      const float FX = (a / 500.0f) + FY;
+      const float FZ = FY - (b / 200.0f);
+
+      const float X = FX > 6.0f / 29.0f ? std::pow((double)FX, 3.0) : 3.0 * std::pow((6.0 / 29.0), 2.0) * (FX - (4.0 / 29.0));
+      const float Y = FY > 6.0f / 29.0f ? std::pow((double)FY, 3.0) : 3.0 * std::pow((6.0 / 29.0), 2.0) * (FY - (4.0 / 29.0));
+      const float Z = FZ > 6.0f / 29.0f ? std::pow((double)FZ, 3.0) : 3.0 * std::pow((6.0 / 29.0), 2.0) * (FZ - (4.0 / 29.0));
+
+      const float r0 = C0 * X + C1 * Y + C2 * Z;
+      const float g0 = C3 * X + C4 * Y + C5 * Z;
+      const float b0 = C6 * X + C7 * Y + C8 * Z;
+
+      // Apply gamma curve of sRGB to the linear rgb values.
+      dstBuffer[aAIndex] = 255;
+      dstBuffer[aRIndex] = Clamp((r0 <= 0.0031308f ? r0 * 12.92f : 1.055 * std::pow((double)r0, 1.0 / 2.4) - 0.055) * 255.0);
+      dstBuffer[aGIndex] = Clamp((g0 <= 0.0031308f ? g0 * 12.92f : 1.055 * std::pow((double)g0, 1.0 / 2.4) - 0.055) * 255.0);
+      dstBuffer[aBIndex] = Clamp((b0 <= 0.0031308f ? b0 * 12.92f : 1.055 * std::pow((double)b0, 1.0 / 2.4) - 0.055) * 255.0);
+
+      // Step one pixel.
+      srcBuffer += 3;
+      dstBuffer += aDstStep;
+    }
+  }
+  return 0;
+}
+
+int
+RGBA32ToLab(const uint8_t* aSrcBuffer, int aSrcStride,
+            float* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight)
+{
+  return RGBFamilyToLab<0, 1, 2, 4>(aSrcBuffer, aSrcStride,
+                                    aDstBuffer, aDstStride,
+                                    aWidth, aHeight);
+}
+
+int
+BGRA32ToLab(const uint8_t* aSrcBuffer, int aSrcStride,
+            float* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight)
+{
+  return RGBFamilyToLab<2, 1, 0, 4>(aSrcBuffer, aSrcStride,
+                                    aDstBuffer, aDstStride,
+                                    aWidth, aHeight);
+}
+
+int
+RGB24ToLab(const uint8_t* aSrcBuffer, int aSrcStride,
+           float* aDstBuffer, int aDstStride,
+           int aWidth, int aHeight)
+{
+  return RGBFamilyToLab<0, 1, 2, 3>(aSrcBuffer, aSrcStride,
+                                    aDstBuffer, aDstStride,
+                                    aWidth, aHeight);
+}
+
+int
+BGR24ToLab(const uint8_t* aSrcBuffer, int aSrcStride,
+           float* aDstBuffer, int aDstStride,
+           int aWidth, int aHeight)
+{
+  return RGBFamilyToLab<2, 1, 0, 3>(aSrcBuffer, aSrcStride,
+                                    aDstBuffer, aDstStride,
+                                    aWidth, aHeight);
+}
+
+int
+LabToRGBA32(const float* aSrcBuffer, int aSrcStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight)
+{
+  return LabToRGBAFamily<0, 1, 2, 3, 4>(aSrcBuffer, aSrcStride,
+                                        aDstBuffer, aDstStride,
+                                        aWidth, aHeight);
+}
+
+int
+LabToBGRA32(const float* aSrcBuffer, int aSrcStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight)
+{
+  return LabToRGBAFamily<2, 1, 0, 3, 4>(aSrcBuffer, aSrcStride,
+                                        aDstBuffer, aDstStride,
+                                        aWidth, aHeight);
+}
+
+int
+LabToRGB24(const float* aSrcBuffer, int aSrcStride,
+           uint8_t* aDstBuffer, int aDstStride,
+           int aWidth, int aHeight)
+{
+  return LabToRGBAFamily<0, 1, 2, 0, 3>(aSrcBuffer, aSrcStride,
+                                        aDstBuffer, aDstStride,
+                                        aWidth, aHeight);
+}
+
+int
+LabToBGR24(const float* aSrcBuffer, int aSrcStride,
+           uint8_t* aDstBuffer, int aDstStride,
+           int aWidth, int aHeight)
+{
+  return LabToRGBAFamily<2, 1, 0, 0, 3>(aSrcBuffer, aSrcStride,
+                                        aDstBuffer, aDstStride,
+                                        aWidth, aHeight);
+}
+
+/*
+ * RGBA/RGB family -> Gray8.
+ * Reference:
+ * (1) OpenCV implementation:
+ * http://docs.opencv.org/3.1.0/de/d25/imgproc_color_conversions.html
+ */
+template<int aRIndex, int aGIndex, int aBIndex, int aSrcStep>
+int
+RGBFamilyToGray8(const uint8_t* aSrcBuffer, int aSrcStride,
+                 uint8_t* aDstBuffer, int aDstStride,
+                 int aWidth, int aHeight)
+{
+  static_assert(aRIndex == 0 || aRIndex == 2, "Wrong R index.");
+  static_assert(aGIndex == 1, "Wrong G index.");
+  static_assert(aBIndex == 0 || aBIndex == 2, "Wrong B index.");
+
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    for (int j = 0; j < aWidth; ++j) {
+      dstBuffer[j] = 0.299 * srcBuffer[aRIndex] +
+                     0.587 * srcBuffer[aGIndex] +
+                     0.114 * srcBuffer[aBIndex];
+      srcBuffer += aSrcStep;
+    }
+  }
+
+  return 0;
+}
+
+int
+RGB24ToGray8(const uint8_t* aSrcBuffer, int aSrcStride,
+             uint8_t* aDstBuffer, int aDstStride,
+             int aWidth, int aHeight)
+{
+  return RGBFamilyToGray8<0, 1, 2, 3>(aSrcBuffer, aSrcStride,
+                                      aDstBuffer, aDstStride,
+                                      aWidth, aHeight);
+}
+
+int
+BGR24ToGray8(const uint8_t* aSrcBuffer, int aSrcStride,
+             uint8_t* aDstBuffer, int aDstStride,
+             int aWidth, int aHeight)
+{
+  return RGBFamilyToGray8<2, 1, 0, 3>(aSrcBuffer, aSrcStride,
+                                      aDstBuffer, aDstStride,
+                                      aWidth, aHeight);
+}
+
+int
+RGBA32ToGray8(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight)
+{
+  return RGBFamilyToGray8<0, 1, 2, 4>(aSrcBuffer, aSrcStride,
+                                      aDstBuffer, aDstStride,
+                                      aWidth, aHeight);
+}
+
+int
+BGRA32ToGray8(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight)
+{
+  return RGBFamilyToGray8<2, 1, 0, 4>(aSrcBuffer, aSrcStride,
+                                      aDstBuffer, aDstStride,
+                                      aWidth, aHeight);
+}
+
+/*
+ * YUV family -> Gray8.
+ * Reference:
+ * (1) OpenCV implementation:
+ * http://docs.opencv.org/3.1.0/de/d25/imgproc_color_conversions.html
+ */
+int
+YUVFamilyToGray8(const uint8_t* aSrcYBuffer, int aSrcYStride,
+                 uint8_t* aDstBuffer, int aDstStride,
+                 int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* srcYBuffer = aSrcYBuffer + aSrcYStride * i;
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    memcpy(dstBuffer, srcYBuffer, aDstStride);
+  }
+
+  return 0;
+}
+
+int
+YUV444PToGray8(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t*, int,
+               const uint8_t*, int,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight)
+{
+  return YUVFamilyToGray8(aYBuffer, aYStride,
+                          aDstBuffer, aDstStride,
+                          aWidth, aHeight);
+}
+
+int
+YUV422PToGray8(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t*, int,
+               const uint8_t*, int,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight)
+{
+  return YUVFamilyToGray8(aYBuffer, aYStride,
+                          aDstBuffer, aDstStride,
+                          aWidth, aHeight);
+}
+
+int
+YUV420PToGray8(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t*, int,
+               const uint8_t*, int,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight)
+{
+  return YUVFamilyToGray8(aYBuffer, aYStride,
+                          aDstBuffer, aDstStride,
+                          aWidth, aHeight);
+}
+
+int
+NV12ToGray8(const uint8_t* aYBuffer, int aYStride,
+            const uint8_t*, int,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight)
+{
+  return YUVFamilyToGray8(aYBuffer, aYStride,
+                          aDstBuffer, aDstStride,
+                          aWidth, aHeight);
+}
+
+int
+NV21ToGray8(const uint8_t* aYBuffer, int aYStride,
+            const uint8_t*, int,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight)
+{
+  return YUVFamilyToGray8(aYBuffer, aYStride,
+                          aDstBuffer, aDstStride,
+                          aWidth, aHeight);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/canvas/ImageBitmapColorUtils.h
@@ -0,0 +1,501 @@
+/* -*- 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 mozilla_dom_ImageBitmapColorUtils_h
+#define mozilla_dom_ImageBitmapColorUtils_h
+
+#include "mozilla/UniquePtr.h"
+#include "nsTArrayForwardDeclare.h"
+
+namespace mozilla {
+namespace dom {
+
+/*
+ * RGB family -> RGBA family.
+ */
+int
+RGB24ToRGBA32(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight);
+
+int
+BGR24ToRGBA32(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight);
+int
+RGB24ToBGRA32(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight);
+
+int
+BGR24ToBGRA32(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight);
+
+/*
+ * RGBA family -> RGB family.
+ */
+int
+RGBA32ToRGB24(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight);
+
+int
+BGRA32ToRGB24(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight);
+int
+RGBA32ToBGR24(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight);
+
+int
+BGRA32ToBGR24(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight);
+
+/*
+ * Among RGB family.
+ */
+int
+RGB24Copy(const uint8_t* aSrcBuffer, int aSrcStride,
+          uint8_t* aDstBuffer, int aDstStride,
+          int aWidth, int aHeight);
+
+#define BGR24Copy RGB24Copy
+#define RGB24ToRGB24 RGB24Copy
+#define BGR24ToBGR24 BGR24Copy
+
+int
+RGB24ToBGR24(const uint8_t* aSrcBuffer, int aSrcStride,
+             uint8_t* aDstBuffer, int aDstStride,
+             int aWidth, int aHeight);
+
+#define BGR24ToRGB24 RGB24ToBGR24
+
+/*
+ * YUV family -> RGB family.
+ */
+int
+YUV444PToRGB24(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t* aUBuffer, int aUStride,
+               const uint8_t* aVBuffer, int aVStride,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight);
+
+int
+YUV422PToRGB24(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t* aUBuffer, int aUStride,
+               const uint8_t* aVBuffer, int aVStride,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight);
+
+int
+YUV420PToRGB24(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t* aUBuffer, int aUStride,
+               const uint8_t* aVBuffer, int aVStride,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight);
+
+int
+NV12ToRGB24(const uint8_t* aYBuffer, int aYStride,
+            const uint8_t* aUVBuffer, int aUVStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight);
+
+int
+NV21ToRGB24(const uint8_t* aYBuffer, int aYStride,
+            const uint8_t* aVUBuffer, int aVUStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight);
+
+int
+YUV444PToBGR24(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t* aUBuffer, int aUStride,
+               const uint8_t* aVBuffer, int aVStride,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight);
+
+int
+YUV422PToBGR24(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t* aUBuffer, int aUStride,
+               const uint8_t* aVBuffer, int aVStride,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight);
+
+int
+YUV420PToBGR24(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t* aUBuffer, int aUStride,
+               const uint8_t* aVBuffer, int aVStride,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight);
+
+int
+NV12ToBGR24(const uint8_t* aYBuffer, int aYStride,
+            const uint8_t* aUVBuffer, int aUVStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight);
+
+int
+NV21ToBGR24(const uint8_t* aYBuffer, int aYStride,
+            const uint8_t* aVUBuffer, int aVUStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight);
+
+/*
+ * YUV family -> RGBA family.
+ */
+int
+YUV444PToRGBA32(const uint8_t* aYBuffer, int aYStride,
+                const uint8_t* aUBuffer, int aUStride,
+                const uint8_t* aVBuffer, int aVStride,
+                uint8_t* aDstBuffer, int aDstStride,
+                int aWidth, int aHeight);
+
+int
+YUV422PToRGBA32(const uint8_t* aYBuffer, int aYStride,
+                const uint8_t* aUBuffer, int aUStride,
+                const uint8_t* aVBuffer, int aVStride,
+                uint8_t* aDstBuffer, int aDstStride,
+                int aWidth, int aHeight);
+
+int
+YUV420PToRGBA32(const uint8_t* aYBuffer, int aYStride,
+                const uint8_t* aUBuffer, int aUStride,
+                const uint8_t* aVBuffer, int aVStride,
+                uint8_t* aDstBuffer, int aDstStride,
+                int aWidth, int aHeight);
+
+int
+NV12ToRGBA32(const uint8_t* aYBuffer, int aYStride,
+             const uint8_t* aUVBuffer, int aUVStride,
+             uint8_t* aDstBuffer, int aDstStride,
+             int aWidth, int aHeight);
+
+int
+NV21ToRGBA32(const uint8_t* aYBuffer, int aYStride,
+             const uint8_t* aVUBuffer, int aVUStride,
+             uint8_t* aDstBuffer, int aDstStride,
+             int aWidth, int aHeight);
+
+int
+YUV444PToBGRA32(const uint8_t* aYBuffer, int aYStride,
+                const uint8_t* aUBuffer, int aUStride,
+                const uint8_t* aVBuffer, int aVStride,
+                uint8_t* aDstBuffer, int aDstStride,
+                int aWidth, int aHeight);
+
+int
+YUV422PToBGRA32(const uint8_t* aYBuffer, int aYStride,
+                const uint8_t* aUBuffer, int aUStride,
+                const uint8_t* aVBuffer, int aVStride,
+                uint8_t* aDstBuffer, int aDstStride,
+                int aWidth, int aHeight);
+
+int
+YUV420PToBGRA32(const uint8_t* aYBuffer, int aYStride,
+                const uint8_t* aUBuffer, int aUStride,
+                const uint8_t* aVBuffer, int aVStride,
+                uint8_t* aDstBuffer, int aDstStride,
+                int aWidth, int aHeight);
+
+int
+NV12ToBGRA32(const uint8_t* aYBuffer, int aYStride,
+             const uint8_t* aUVBuffer, int aUVStride,
+             uint8_t* aDstBuffer, int aDstStride,
+             int aWidth, int aHeight);
+
+int
+NV21ToBGRA32(const uint8_t* aYBuffer, int aYStride,
+             const uint8_t* aVUBuffer, int aVUStride,
+             uint8_t* aDstBuffer, int aDstStride,
+             int aWidth, int aHeight);
+
+/*
+ * RGB family -> YUV family.
+ */
+int
+RGB24ToYUV444P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight);
+
+int
+RGB24ToYUV422P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight);
+
+int
+RGB24ToYUV420P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight);
+int
+RGB24ToNV12(const uint8_t* aSrcBuffer, int aSrcStride,
+            uint8_t* aYBuffer, int aYStride,
+            uint8_t* aUVBuffer, int aUVStride,
+            int aWidth, int aHeight);
+
+int
+RGB24ToNV21(const uint8_t* aSrcBuffer, int aSrcStride,
+            uint8_t* aYBuffer, int aYStride,
+            uint8_t* aVUBuffer, int aVUStride,
+            int aWidth, int aHeight);
+
+int
+BGR24ToYUV444P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight);
+
+int
+BGR24ToYUV422P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight);
+
+int
+BGR24ToYUV420P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight);
+
+int
+BGR24ToNV12(const uint8_t* aSrcBuffer, int aSrcStride,
+            uint8_t* aYBuffer, int aYStride,
+            uint8_t* aUVBuffer, int aUVStride,
+            int aWidth, int aHeight);
+
+int
+BGR24ToNV21(const uint8_t* aSrcBuffer, int aSrcStride,
+            uint8_t* aYBuffer, int aYStride,
+            uint8_t* aUVBuffer, int aUVStride,
+            int aWidth, int aHeight);
+
+/*
+ * RGBA family -> YUV family.
+ */
+int
+RGBA32ToYUV444P(const uint8_t* aSrcBuffer, int aSrcStride,
+                uint8_t* aYBuffer, int aYStride,
+                uint8_t* aUBuffer, int aUStride,
+                uint8_t* aVBuffer, int aVStride,
+                int aWidth, int aHeight);
+
+int
+RGBA32ToYUV422P(const uint8_t* aSrcBuffer, int aSrcStride,
+                uint8_t* aYBuffer, int aYStride,
+                uint8_t* aUBuffer, int aUStride,
+                uint8_t* aVBuffer, int aVStride,
+                int aWidth, int aHeight);
+
+int
+RGBA32ToYUV420P(const uint8_t* aSrcBuffer, int aSrcStride,
+                uint8_t* aYBuffer, int aYStride,
+                uint8_t* aUBuffer, int aUStride,
+                uint8_t* aVBuffer, int aVStride,
+                int aWidth, int aHeight);
+int
+RGBA32ToNV12(const uint8_t* aSrcBuffer, int aSrcStride,
+             uint8_t* aYBuffer, int aYStride,
+             uint8_t* aUVBuffer, int aUVStride,
+             int aWidth, int aHeight);
+
+int
+RGBA32ToNV21(const uint8_t* aSrcBuffer, int aSrcStride,
+             uint8_t* aYBuffer, int aYStride,
+             uint8_t* aVUBuffer, int aVUStride,
+             int aWidth, int aHeight);
+
+int
+BGRA32ToYUV444P(const uint8_t* aSrcBuffer, int aSrcStride,
+                uint8_t* aYBuffer, int aYStride,
+                uint8_t* aUBuffer, int aUStride,
+                uint8_t* aVBuffer, int aVStride,
+                int aWidth, int aHeight);
+
+int
+BGRA32ToYUV422P(const uint8_t* aSrcBuffer, int aSrcStride,
+                uint8_t* aYBuffer, int aYStride,
+                uint8_t* aUBuffer, int aUStride,
+                uint8_t* aVBuffer, int aVStride,
+                int aWidth, int aHeight);
+
+int
+BGRA32ToYUV420P(const uint8_t* aSrcBuffer, int aSrcStride,
+                uint8_t* aYBuffer, int aYStride,
+                uint8_t* aUBuffer, int aUStride,
+                uint8_t* aVBuffer, int aVStride,
+                int aWidth, int aHeight);
+
+
+int
+BGRA32ToNV12(const uint8_t* aSrcBuffer, int aSrcStride,
+             uint8_t* aYBuffer, int aYStride,
+             uint8_t* aUVBuffer, int aUVStride,
+             int aWidth, int aHeight);
+
+int
+BGRA32ToNV21(const uint8_t* aSrcBuffer, int aSrcStride,
+             uint8_t* aYBuffer, int aYStride,
+             uint8_t* aUVBuffer, int aUVStride,
+             int aWidth, int aHeight);
+
+/*
+ * RGBA/RGB family <-> HSV family.
+ */
+int
+RGBA32ToHSV(const uint8_t* aSrcBuffer, int aSrcStride,
+            float* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight);
+
+int
+BGRA32ToHSV(const uint8_t* aSrcBuffer, int aSrcStride,
+            float* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight);
+
+int
+RGB24ToHSV(const uint8_t* aSrcBuffer, int aSrcStride,
+           float* aDstBuffer, int aDstStride,
+           int aWidth, int aHeight);
+
+int
+BGR24ToHSV(const uint8_t* aSrcBuffer, int aSrcStride,
+           float* aDstBuffer, int aDstStride,
+           int aWidth, int aHeight);
+
+int
+HSVToRGBA32(const float* aSrcBuffer, int aSrcStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight);
+
+int
+HSVToBGRA32(const float* aSrcBuffer, int aSrcStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight);
+
+int
+HSVToRGB24(const float* aSrcBuffer, int aSrcStride,
+           uint8_t* aDstBuffer, int aDstStride,
+           int aWidth, int aHeight);
+
+int
+HSVToBGR24(const float* aSrcBuffer, int aSrcStride,
+           uint8_t* aDstBuffer, int aDstStride,
+           int aWidth, int aHeight);
+
+/*
+ * RGBA/RGB family <-> Lab family.
+ */
+int
+RGBA32ToLab(const uint8_t* aSrcBuffer, int aSrcStride,
+            float* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight);
+
+int
+BGRA32ToLab(const uint8_t* aSrcBuffer, int aSrcStride,
+            float* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight);
+
+int
+RGB24ToLab(const uint8_t* aSrcBuffer, int aSrcStride,
+           float* aDstBuffer, int aDstStride,
+           int aWidth, int aHeight);
+
+int
+BGR24ToLab(const uint8_t* aSrcBuffer, int aSrcStride,
+           float* aDstBuffer, int aDstStride,
+           int aWidth, int aHeight);
+
+int
+LabToRGBA32(const float* aSrcBuffer, int aSrcStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight);
+
+int
+LabToBGRA32(const float* aSrcBuffer, int aSrcStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight);
+
+int
+LabToRGB24(const float* aSrcBuffer, int aSrcStride,
+           uint8_t* aDstBuffer, int aDstStride,
+           int aWidth, int aHeight);
+
+int
+LabToBGR24(const float* aSrcBuffer, int aSrcStride,
+           uint8_t* aDstBuffer, int aDstStride,
+           int aWidth, int aHeight);
+
+/*
+ * RGBA/RGB family -> Gray8.
+ */
+int
+RGB24ToGray8(const uint8_t* aSrcBuffer, int aSrcStride,
+             uint8_t* aDstBuffer, int aDstStride,
+             int aWidth, int aHeight);
+
+int
+BGR24ToGray8(const uint8_t* aSrcBuffer, int aSrcStride,
+             uint8_t* aDstBuffer, int aDstStride,
+             int aWidth, int aHeight);
+
+int
+RGBA32ToGray8(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight);
+
+int
+BGRA32ToGray8(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight);
+
+/*
+ * YUV family -> Gray8.
+ */
+int
+YUV444PToGray8(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t* aUBuffer, int aUStride,
+               const uint8_t* aVBuffer, int aVStride,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight);
+
+int
+YUV422PToGray8(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t* aUBuffer, int aUStride,
+               const uint8_t* aVBuffer, int aVStride,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight);
+
+int
+YUV420PToGray8(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t* aUBuffer, int aUStride,
+               const uint8_t* aVBuffer, int aVStride,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight);
+
+int
+NV12ToGray8(const uint8_t* aYBuffer, int aYStride,
+            const uint8_t* aUBuffer, int aUStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight);
+
+int
+NV21ToGray8(const uint8_t* aYBuffer, int aYStride,
+            const uint8_t* aUBuffer, int aUStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight);
+
+} // namespace dom
+} // namespace mozilla
+#endif // mozilla_dom_ImageBitmapColorUtils_h
--- a/dom/canvas/ImageBitmapSource.h
+++ b/dom/canvas/ImageBitmapSource.h
@@ -6,16 +6,16 @@
 
 #ifndef mozilla_dom_ImageBitmapSource_h
 #define mozilla_dom_ImageBitmapSource_h
 
 namespace mozilla {
 namespace dom {
 
 // So we don't have to forward declare this elsewhere.
-class HTMLImageElementOrHTMLVideoElementOrHTMLCanvasElementOrBlobOrImageDataOrCanvasRenderingContext2DOrImageBitmap;
-typedef HTMLImageElementOrHTMLVideoElementOrHTMLCanvasElementOrBlobOrImageDataOrCanvasRenderingContext2DOrImageBitmap
+class HTMLImageElementOrHTMLVideoElementOrHTMLCanvasElementOrBlobOrImageDataOrCanvasRenderingContext2DOrImageBitmapOrArrayBufferViewOrArrayBuffer;
+typedef HTMLImageElementOrHTMLVideoElementOrHTMLCanvasElementOrBlobOrImageDataOrCanvasRenderingContext2DOrImageBitmapOrArrayBufferViewOrArrayBuffer
         ImageBitmapSource;
 
 }
 }
 
 #endif
new file mode 100644
--- /dev/null
+++ b/dom/canvas/ImageBitmapUtils.cpp
@@ -0,0 +1,2778 @@
+/* -*- 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 "ImageBitmapUtils.h"
+#include "ImageBitmapColorUtils.h"
+#include "ImageContainer.h"
+#include "libyuv.h"
+#include "mozilla/dom/ImageBitmapBinding.h"
+#include "mozilla/Function.h"
+#include "mozilla/gfx/2D.h"
+
+using namespace libyuv;
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace dom {
+namespace imagebitmapformat {
+
+class Utils;
+class Utils_RGBA32;
+class Utils_BGRA32;
+class Utils_RGB24;
+class Utils_BGR24;
+class Utils_Gray8;
+class Utils_YUV444P;
+class Utils_YUV422P;
+class Utils_YUV420P;
+class Utils_YUV420SP_NV12;
+class Utils_YUV420SP_NV21;
+class Utils_HSV;
+class Utils_Lab;
+class Utils_Depth;
+
+static int GetBytesPerPixelValue(ChannelPixelLayoutDataType aDataType)
+{
+  switch (aDataType)
+  {
+  case ChannelPixelLayoutDataType::Uint8:
+    return sizeof(uint8_t);
+  case ChannelPixelLayoutDataType::Int8:
+    return sizeof(int8_t);
+  case ChannelPixelLayoutDataType::Uint16:
+    return sizeof(uint16_t);
+  case ChannelPixelLayoutDataType::Int16:
+    return sizeof(int16_t);
+  case ChannelPixelLayoutDataType::Uint32:
+    return sizeof(uint32_t);
+  case ChannelPixelLayoutDataType::Int32:
+    return sizeof(int32_t);
+  case ChannelPixelLayoutDataType::Float32:
+    return sizeof(float);
+  case ChannelPixelLayoutDataType::Float64:
+    return sizeof(double);
+  default:
+    return 0;
+  }
+}
+
+/*
+ * The UtilsUniquePtr is a UniquePtr to ImageBitmapFormatUtils with a customized
+ * deleter which does nothing. This is used as the return type of
+ * ImageBitmapFormatUtils::GetUtils to prevent users deleting the returned
+ * pointer.
+ */
+struct DoNotDelete { void operator()(void* p) {} };
+using UtilsUniquePtr = UniquePtr<Utils, DoNotDelete>;
+
+/*
+ * ImageBitmapFormatUtils is an abstract class which provides interfaces to
+ * extract information of each ImageBitmapFormat and interfaces to convert
+ * image data between different ImageBitmapFormats. For each kind of
+ * ImageBitmapFromat, we derive a subclass from the ImageBitmapFormatUtils to
+ * implement functionalities that are subject to the specific ImageBitmapFormat.
+ *
+ * ImageBitmapFormatUtils is an abstract class and its sub-classes are designed
+ * as singletons. The singleton instance of sub-classes could be initialized and
+ * accessed via the ImageBitmapFormatUtils::GetUtils() static method. The
+ * singleton instance is a static local variable which does not need to be
+ * released manually and, with the C++11 static initialization, the
+ * initialization is thread-safe.
+ *
+ * ImageBitmapFormatUtils and its sub-classes are designed to unify operations
+ * of ImageBitmap-extensions over different kinds of ImageBitmapFormats; they
+ * provide following functionalities:
+ *
+ * (1) Create default/customized ImagePixelLayout object of each kind of
+ *     ImageBitmapFormat.
+ * (2) Store the channel counts of each ImageBitmapFormat.
+ * (3) Calculate the needed buffer size of each kind of ImageBitmapFormat with
+ *     given width, height and stride.
+ * (4) Perform color conversion between supported ImageBitmapFormats. We use
+ *     _double dispatching_ to identify the source format and destination format
+ *     at run time. The _double dispatching_ here is mainly implemented by
+ *     overriding the _convertTo_ method over the ImageBitmapFormatUtils class
+ *     hierarchy and overloading the _convertFrom_ methods over all sub-classes
+ *     of ImageBitmapFormatUtils.
+ */
+class Utils
+{
+public:
+  // Get the singleton utility instance of the given ImageBitmapFormat.
+  static UtilsUniquePtr GetUtils(ImageBitmapFormat aFormat);
+
+  // Get the needed buffer size to store image data in the current
+  // ImageBitmapFormat with the given width and height.
+  // The current ImageBitmapFormat is the format used to implement the concrete
+  // subclass of which the current instance is initialized.
+  virtual uint32_t NeededBufferSize(uint32_t width, uint32_t height) = 0;
+
+  // Creates a default ImagePixelLayout object of the current ImageBitmapFormat
+  // with the given width, height and stride.
+  virtual UniquePtr<ImagePixelLayout>
+  CreateDefaultLayout(uint32_t aWidth, uint32_t aHeight, uint32_t aStride) = 0;
+
+  // Convert the source image data (stored in the aSrcBuffer and described by
+  // the aSrcLayout) from the current ImageBitmapFormat to the given
+  // ImageBitmapFormat, aDstFormat.
+  // The converted image data is stored in the aDstBuffer and described by the
+  // returned ImagePixelLayout object.
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertTo(Utils* aDstFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) = 0;
+
+  // ConvertFrom():
+  // Convert the source image data (which is in the aSrcFormat format, the pixel
+  // layout is described by the aSrcLayout and the raw data is stored in the
+  // aSrcBuffer) to the current ImageBitmapFormat.
+  // The converted image data is stored in the aDstBuffer and described by the
+  // returned ImagePixelLayout object.
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(Utils_RGBA32* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) = 0;
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(Utils_BGRA32* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) = 0;
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(Utils_RGB24* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) = 0;
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(Utils_BGR24* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) = 0;
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(Utils_Gray8* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) = 0;
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(Utils_YUV444P* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) = 0;
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(Utils_YUV422P* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) = 0;
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(Utils_YUV420P* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) = 0;
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(Utils_YUV420SP_NV12* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) = 0;
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(Utils_YUV420SP_NV21* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) = 0;
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(Utils_HSV* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) = 0;
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(Utils_Lab* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) = 0;
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(Utils_Depth* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) = 0;
+
+  // Check whether or not the current ImageBitmapFormat can be converted from
+  // the given ImageBitmapFormat.
+  virtual bool
+  CanConvertFrom(ImageBitmapFormat aSrcFormat) = 0;
+
+  // Get the number of channels.
+  uint8_t GetChannelCount() const
+  {
+    return mChannels;
+  }
+
+protected:
+  Utils(uint32_t aChannels,
+                         ChannelPixelLayoutDataType aDataType)
+  : mChannels(aChannels)
+  , mBytesPerPixelValue(GetBytesPerPixelValue(aDataType))
+  , mDataType(aDataType)
+  {
+  }
+
+  virtual ~Utils()
+  {
+  }
+
+  const uint8_t mChannels;
+  const int mBytesPerPixelValue;
+  const ChannelPixelLayoutDataType mDataType;
+};
+
+#define DECLARE_Utils(NAME)                          \
+class Utils_ ## NAME : public Utils \
+{                                                                     \
+private:                                                              \
+  explicit Utils_ ## NAME ();                        \
+  ~Utils_ ## NAME () = default;                      \
+  Utils_ ## NAME (Utils_ ## NAME const &) = delete;             \
+  Utils_ ## NAME (Utils_ ## NAME &&) = delete;                  \
+  Utils_ ## NAME & operator=(Utils_ ## NAME const &) = delete;  \
+  Utils_ ## NAME & operator=(Utils_ ## NAME &&) = delete;       \
+                                                                                                  \
+public:                                                     \
+  static Utils_ ## NAME & GetInstance();   \
+                                                            \
+  virtual uint32_t NeededBufferSize(uint32_t aWidth, uint32_t aHeight) override;  \
+                                                                                  \
+  virtual UniquePtr<ImagePixelLayout>                          \
+  CreateDefaultLayout(uint32_t, uint32_t, uint32_t) override;  \
+                                                               \
+  virtual UniquePtr<ImagePixelLayout>                                                             \
+  ConvertTo(Utils*, const uint8_t*, const ImagePixelLayout*, uint8_t*) override; \
+                                                                                                  \
+  virtual UniquePtr<ImagePixelLayout>                                                                       \
+  ConvertFrom(Utils_RGBA32*, const uint8_t*, const ImagePixelLayout*, uint8_t*) override;  \
+                                                                                                            \
+  virtual UniquePtr<ImagePixelLayout>                                                                       \
+  ConvertFrom(Utils_BGRA32*, const uint8_t*, const ImagePixelLayout*, uint8_t*) override;  \
+                                                                                                            \
+  virtual UniquePtr<ImagePixelLayout>                                                                     \
+  ConvertFrom(Utils_RGB24*, const uint8_t*, const ImagePixelLayout*, uint8_t*) override; \
+                                                                                                          \
+  virtual UniquePtr<ImagePixelLayout>                                                                     \
+  ConvertFrom(Utils_BGR24*, const uint8_t*, const ImagePixelLayout*, uint8_t*) override; \
+                                                                                                          \
+  virtual UniquePtr<ImagePixelLayout>                                                                     \
+  ConvertFrom(Utils_Gray8*, const uint8_t*, const ImagePixelLayout*, uint8_t*) override; \
+                                                                                                          \
+  virtual UniquePtr<ImagePixelLayout>                                                                       \
+  ConvertFrom(Utils_YUV444P*, const uint8_t*, const ImagePixelLayout*, uint8_t*) override; \
+                                                                                                            \
+  virtual UniquePtr<ImagePixelLayout>                                                                       \
+  ConvertFrom(Utils_YUV422P*, const uint8_t*, const ImagePixelLayout*, uint8_t*) override; \
+                                                                                                            \
+  virtual UniquePtr<ImagePixelLayout>                                                                       \
+  ConvertFrom(Utils_YUV420P*, const uint8_t*, const ImagePixelLayout*, uint8_t*) override; \
+                                                                                                            \
+  virtual UniquePtr<ImagePixelLayout>                                                                             \
+  ConvertFrom(Utils_YUV420SP_NV12*, const uint8_t*, const ImagePixelLayout*, uint8_t*) override; \
+                                                                                                                  \
+  virtual UniquePtr<ImagePixelLayout>                                                                             \
+  ConvertFrom(Utils_YUV420SP_NV21*, const uint8_t*, const ImagePixelLayout*, uint8_t*) override; \
+                                                                                                                  \
+  virtual UniquePtr<ImagePixelLayout>                                                                   \
+  ConvertFrom(Utils_HSV*, const uint8_t*, const ImagePixelLayout*, uint8_t*) override; \
+                                                                                                        \
+  virtual UniquePtr<ImagePixelLayout>                                                                   \
+  ConvertFrom(Utils_Lab*, const uint8_t*, const ImagePixelLayout*, uint8_t*) override; \
+                                                                                                        \
+  virtual UniquePtr<ImagePixelLayout>                                                                     \
+  ConvertFrom(Utils_Depth*, const uint8_t*, const ImagePixelLayout*, uint8_t*) override; \
+                                                                                                          \
+  virtual bool                                  \
+  CanConvertFrom(ImageBitmapFormat) override;   \
+};
+
+DECLARE_Utils(RGBA32)
+DECLARE_Utils(BGRA32)
+DECLARE_Utils(RGB24)
+DECLARE_Utils(BGR24)
+DECLARE_Utils(Gray8)
+DECLARE_Utils(YUV444P)
+DECLARE_Utils(YUV422P)
+DECLARE_Utils(YUV420P)
+DECLARE_Utils(YUV420SP_NV12)
+DECLARE_Utils(YUV420SP_NV21)
+DECLARE_Utils(HSV)
+DECLARE_Utils(Lab)
+DECLARE_Utils(Depth)
+
+#undef DECLARE_Utils
+
+/*
+ * ImageBitmapFormatUtils.
+ */
+/* static */ UtilsUniquePtr
+Utils::GetUtils(ImageBitmapFormat aFormat)
+{
+  switch(aFormat)
+  {
+  case ImageBitmapFormat::RGBA32:
+    return UtilsUniquePtr(&Utils_RGBA32::GetInstance());
+  case ImageBitmapFormat::BGRA32:
+    return UtilsUniquePtr(&Utils_BGRA32::GetInstance());
+  case ImageBitmapFormat::RGB24:
+    return UtilsUniquePtr(&Utils_RGB24::GetInstance());
+  case ImageBitmapFormat::BGR24:
+    return UtilsUniquePtr(&Utils_BGR24::GetInstance());
+  case ImageBitmapFormat::GRAY8:
+    return UtilsUniquePtr(&Utils_Gray8::GetInstance());
+  case ImageBitmapFormat::YUV444P:
+    return UtilsUniquePtr(&Utils_YUV444P::GetInstance());
+  case ImageBitmapFormat::YUV422P:
+    return UtilsUniquePtr(&Utils_YUV422P::GetInstance());
+  case ImageBitmapFormat::YUV420P:
+    return UtilsUniquePtr(&Utils_YUV420P::GetInstance());
+  case ImageBitmapFormat::YUV420SP_NV12:
+    return UtilsUniquePtr(&Utils_YUV420SP_NV12::GetInstance());
+  case ImageBitmapFormat::YUV420SP_NV21:
+    return UtilsUniquePtr(&Utils_YUV420SP_NV21::GetInstance());
+  case ImageBitmapFormat::HSV:
+    return UtilsUniquePtr(&Utils_HSV::GetInstance());
+  case ImageBitmapFormat::Lab:
+    return UtilsUniquePtr(&Utils_Lab::GetInstance());
+  case ImageBitmapFormat::DEPTH:
+    return UtilsUniquePtr(&Utils_Depth::GetInstance());
+  default:
+    return nullptr;
+  }
+}
+
+/*
+ * Helper functions.
+ */
+template<typename SrcType, typename DstType>
+static UniquePtr<ImagePixelLayout>
+CvtSimpleImgToSimpleImg(Utils* aSrcUtils, const SrcType* aSrcBuffer,
+                        const ImagePixelLayout* aSrcLayout, DstType* aDstBuffer,
+                        ImageBitmapFormat aDstFormat, int aDstChannelCount,
+                        mozilla::function<int (const SrcType*, int, DstType*, int, int, int)> converter)
+{
+  MOZ_ASSERT(aSrcUtils, "Convert color from a null utility object.");
+  MOZ_ASSERT(aSrcBuffer, "Convert color from a null buffer.");
+  MOZ_ASSERT(aSrcLayout, "Convert color from a null layout.");
+  MOZ_ASSERT(aDstBuffer, "Convert color to a null buffer.");
+
+  const nsTArray<ChannelPixelLayout>& channels = *aSrcLayout;
+  MOZ_ASSERT(channels.Length() == aSrcUtils->GetChannelCount(),
+             "The channel count is wrong.");
+
+  const int dstStride = channels[0].mWidth * aDstChannelCount * sizeof(DstType);
+  int rv = converter(aSrcBuffer, channels[0].mStride,
+                     aDstBuffer, dstStride,
+                     channels[0].mWidth, channels[0].mHeight);
+
+  if (NS_WARN_IF(rv != 0)) {
+    return nullptr;
+  }
+
+  return CreateDefaultPixelLayout(aDstFormat, channels[0].mWidth,
+                                  channels[0].mHeight, dstStride);
+}
+
+static UniquePtr<ImagePixelLayout>
+CvtYUVImgToSimpleImg(Utils* aSrcUtils, const uint8_t* aSrcBuffer,
+                     const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer,
+                     ImageBitmapFormat aDstFormat, int aDstChannelCount,
+                     mozilla::function<int (const uint8_t*, int, const uint8_t*, int, const uint8_t*, int, uint8_t*, int, int, int)> converter)
+{
+  MOZ_ASSERT(aSrcUtils, "Convert color from a null utility object.");
+  MOZ_ASSERT(aSrcBuffer, "Convert color from a null buffer.");
+  MOZ_ASSERT(aSrcLayout, "Convert color from a null layout.");
+  MOZ_ASSERT(aDstBuffer, "Convert color to a null buffer.");
+
+  const nsTArray<ChannelPixelLayout>& channels = *aSrcLayout;
+  MOZ_ASSERT(channels.Length() == aSrcUtils->GetChannelCount(),
+             "The channel count is wrong.");
+
+  const int dstStride = channels[0].mWidth * aDstChannelCount * sizeof(uint8_t);
+  int rv = converter(aSrcBuffer + channels[0].mOffset, channels[0].mStride,
+                     aSrcBuffer + channels[1].mOffset, channels[1].mStride,
+                     aSrcBuffer + channels[2].mOffset, channels[2].mStride,
+                     aDstBuffer, dstStride,
+                     channels[0].mWidth, channels[0].mHeight);
+
+  if (NS_WARN_IF(rv != 0)) {
+    return nullptr;
+  }
+
+  return CreateDefaultPixelLayout(aDstFormat, channels[0].mWidth,
+                                  channels[0].mHeight, dstStride);
+}
+
+static UniquePtr<ImagePixelLayout>
+CvtNVImgToSimpleImg(Utils* aSrcUtils, const uint8_t* aSrcBuffer,
+                    const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer,
+                    ImageBitmapFormat aDstFormat, int aDstChannelCount,
+                    mozilla::function<int (const uint8_t*, int, const uint8_t*, int, uint8_t*, int, int, int)> converter)
+{
+  MOZ_ASSERT(aSrcUtils, "Convert color from a null utility object.");
+  MOZ_ASSERT(aSrcBuffer, "Convert color from a null buffer.");
+  MOZ_ASSERT(aSrcLayout, "Convert color from a null layout.");
+  MOZ_ASSERT(aDstBuffer, "Convert color to a null buffer.");
+
+  const nsTArray<ChannelPixelLayout>& channels = *aSrcLayout;
+  MOZ_ASSERT(channels.Length() == aSrcUtils->GetChannelCount(),
+             "The channel count is wrong.");
+
+  const int dstStride = channels[0].mWidth * aDstChannelCount * sizeof(uint8_t);
+  int rv = converter(aSrcBuffer + channels[0].mOffset, channels[0].mStride,
+                     aSrcBuffer + channels[1].mOffset, channels[1].mStride,
+                     aDstBuffer, dstStride,
+                     channels[0].mWidth, channels[0].mHeight);
+
+  if (NS_WARN_IF(rv != 0)) {
+    return nullptr;
+  }
+
+  return CreateDefaultPixelLayout(aDstFormat, channels[0].mWidth,
+                                  channels[0].mHeight, dstStride);
+}
+
+static UniquePtr<ImagePixelLayout>
+CvtSimpleImgToYUVImg(Utils* aSrcUtils, const uint8_t* aSrcBuffer,
+                     const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer,
+                     ImageBitmapFormat aDstFormat,
+                     mozilla::function<int (const uint8_t*, int, uint8_t*, int, uint8_t*, int, uint8_t*, int, int, int)> converter)
+{
+  MOZ_ASSERT(aSrcUtils, "Convert color from a null utility object.");
+  MOZ_ASSERT(aSrcBuffer, "Convert color from a null buffer.");
+  MOZ_ASSERT(aSrcLayout, "Convert color from a null layout.");
+  MOZ_ASSERT(aDstBuffer, "Convert color to a null buffer.");
+
+  UniquePtr<ImagePixelLayout> layout =
+    CreateDefaultPixelLayout(aDstFormat, (*aSrcLayout)[0].mWidth,
+                             (*aSrcLayout)[0].mHeight, (*aSrcLayout)[0].mWidth);
+
+  MOZ_ASSERT(layout, "Cannot create a ImagePixelLayout.");
+
+  const nsTArray<ChannelPixelLayout>& channels = *layout;
+
+  int rv = converter(aSrcBuffer, (*aSrcLayout)[0].mStride,
+                     aDstBuffer + channels[0].mOffset, channels[0].mStride,
+                     aDstBuffer + channels[1].mOffset, channels[1].mStride,
+                     aDstBuffer + channels[2].mOffset, channels[2].mStride,
+                     channels[0].mWidth, channels[0].mHeight);
+
+  if (NS_WARN_IF(rv != 0)) {
+    return nullptr;
+  }
+
+  return layout;
+}
+
+static UniquePtr<ImagePixelLayout>
+CvtSimpleImgToNVImg(Utils* aSrcUtils, const uint8_t* aSrcBuffer,
+                    const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer,
+                    ImageBitmapFormat aDstFormat,
+                    mozilla::function<int (const uint8_t*, int, uint8_t*, int, uint8_t*, int, int, int)> converter)
+{
+  MOZ_ASSERT(aSrcUtils, "Convert color from a null utility object.");
+  MOZ_ASSERT(aSrcBuffer, "Convert color from a null buffer.");
+  MOZ_ASSERT(aSrcLayout, "Convert color from a null layout.");
+  MOZ_ASSERT(aDstBuffer, "Convert color to a null buffer.");
+
+  UniquePtr<ImagePixelLayout> layout =
+    CreateDefaultPixelLayout(aDstFormat, (*aSrcLayout)[0].mWidth,
+                             (*aSrcLayout)[0].mHeight, (*aSrcLayout)[0].mWidth);
+
+  MOZ_ASSERT(layout, "Cannot create a ImagePixelLayout.");
+
+  const nsTArray<ChannelPixelLayout>& channels = *layout;
+
+  int rv = converter(aSrcBuffer, (*aSrcLayout)[0].mStride,
+                     aDstBuffer + channels[0].mOffset, channels[0].mStride,
+                     aDstBuffer + channels[1].mOffset, channels[1].mStride,
+                     channels[0].mWidth, channels[0].mHeight);
+
+  if (NS_WARN_IF(rv != 0)) {
+    return nullptr;
+  }
+
+  return layout;
+}
+
+template<class SrcUtilsType, class DstUtilsType>
+static UniquePtr<ImagePixelLayout>
+TwoPassConversion(SrcUtilsType* aSrcUtils, const uint8_t* aSrcBuffer,
+                  const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer,
+                  ImageBitmapFormat aMiddleFormat, DstUtilsType* aDstUtils)
+{
+  MOZ_ASSERT(aSrcUtils, "Convert color from a null source utility.");
+  MOZ_ASSERT(aSrcBuffer, "Convert color from a null buffer.");
+  MOZ_ASSERT(aSrcLayout, "Convert color from a null layout.");
+  MOZ_ASSERT(aDstBuffer, "Convert color to a null buffer.");
+
+  // I444 -> I420 -> I422
+  UtilsUniquePtr yuv420PUtils = Utils::GetUtils(aMiddleFormat);
+  UniquePtr<uint8_t> yuv420PBuffer(new uint8_t[yuv420PUtils->NeededBufferSize((*aSrcLayout)[0].mWidth, (*aSrcLayout)[0].mHeight)]);
+  UniquePtr<ImagePixelLayout> yuv420PLayout = yuv420PUtils->ConvertFrom(aSrcUtils, aSrcBuffer, aSrcLayout, yuv420PBuffer.get());
+  return yuv420PUtils->ConvertTo(aDstUtils, yuv420PBuffer.get(), yuv420PLayout.get(), aDstBuffer);
+}
+
+static UniquePtr<ImagePixelLayout>
+PureCopy(Utils* aSrcUtils, const uint8_t* aSrcBuffer,
+         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer,
+         ImageBitmapFormat aDstFormat)
+{
+  MOZ_ASSERT(aSrcUtils, "Convert color from a null utility object.");
+  MOZ_ASSERT(aSrcBuffer, "Convert color from a null buffer.");
+  MOZ_ASSERT(aSrcLayout, "Convert color from a null layout.");
+  MOZ_ASSERT(aDstBuffer, "Convert color to a null buffer.");
+
+  const nsTArray<ChannelPixelLayout>& channels = *aSrcLayout;
+  MOZ_ASSERT(channels.Length() == aSrcUtils->GetChannelCount(),
+             "The channel count is wrong.");
+
+
+  uint32_t length = 0;
+
+  if (aDstFormat == ImageBitmapFormat::RGBA32 ||
+      aDstFormat == ImageBitmapFormat::BGRA32 ||
+      aDstFormat == ImageBitmapFormat::RGB24 ||
+      aDstFormat == ImageBitmapFormat::BGR24 ||
+      aDstFormat == ImageBitmapFormat::GRAY8 ||
+      aDstFormat == ImageBitmapFormat::HSV ||
+      aDstFormat == ImageBitmapFormat::Lab ||
+      aDstFormat == ImageBitmapFormat::DEPTH) {
+    length = channels[0].mHeight * channels[0].mStride;
+  } else if (aDstFormat == ImageBitmapFormat::YUV444P ||
+             aDstFormat == ImageBitmapFormat::YUV422P ||
+             aDstFormat == ImageBitmapFormat::YUV420P) {
+    length = channels[0].mHeight * channels[0].mStride +
+             channels[1].mHeight * channels[1].mStride +
+             channels[2].mHeight * channels[2].mStride;
+  } else if (aDstFormat == ImageBitmapFormat::YUV420SP_NV12 ||
+             aDstFormat == ImageBitmapFormat::YUV420SP_NV21) {
+    length = channels[0].mHeight * channels[0].mStride +
+             channels[1].mHeight * channels[1].mStride;
+  }
+
+  memcpy(aDstBuffer, aSrcBuffer, length);
+
+  UniquePtr<ImagePixelLayout> layout(new ImagePixelLayout(*aSrcLayout));
+  return layout;
+}
+
+UniquePtr<ImagePixelLayout>
+CreateDefaultLayoutForSimpleImage(uint32_t aWidth, uint32_t aHeight,
+                                  uint32_t aStride, int aChannels,
+                                  int aBytesPerPixelValue,
+                                  ChannelPixelLayoutDataType aDataType)
+{
+  UniquePtr<ImagePixelLayout> layout(new ImagePixelLayout(aChannels));
+
+  // set mChannels
+  for (uint8_t i = 0; i < aChannels; ++i) {
+    ChannelPixelLayout* channel = layout->AppendElement();
+    channel->mOffset = i * aBytesPerPixelValue;
+    channel->mWidth = aWidth;
+    channel->mHeight = aHeight;
+    channel->mDataType = aDataType; //ChannelPixelLayoutDataType::Uint8;
+    channel->mStride = aStride;
+    channel->mSkip = aChannels - 1;
+  }
+
+  return layout;
+}
+
+/*
+ * Utils_RGBA32.
+ */
+/* static */Utils_RGBA32&
+Utils_RGBA32::GetInstance()
+{
+  static Utils_RGBA32 instance;
+  return instance;
+}
+
+Utils_RGBA32::Utils_RGBA32()
+: Utils(4, ChannelPixelLayoutDataType::Uint8)
+{
+}
+
+uint32_t
+Utils_RGBA32::NeededBufferSize(uint32_t aWidth, uint32_t aHeight)
+{
+  return aWidth * aHeight * (uint32_t)mChannels * mBytesPerPixelValue;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGBA32::ConvertTo(Utils* aDstFormat, const uint8_t* aSrcBuffer,
+                        const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return aDstFormat->ConvertFrom(this, aSrcBuffer, aSrcLayout, aDstBuffer);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGBA32::ConvertFrom(Utils_RGBA32* aSrcUtils, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return PureCopy(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGBA32);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGBA32::ConvertFrom(Utils_BGRA32* aSrcFormat, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, uint8_t>(aSrcFormat, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGBA32, 4, &libyuv::ABGRToARGB);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGBA32::ConvertFrom(Utils_RGB24* aSrcFormat, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, uint8_t>(aSrcFormat, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGBA32, 4, &RGB24ToRGBA32);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGBA32::ConvertFrom(Utils_BGR24* aSrcFormat, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, uint8_t>(aSrcFormat, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGBA32, 4, &BGR24ToRGBA32);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGBA32::ConvertFrom(Utils_Gray8* aSrcFormat, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGBA32::ConvertFrom(Utils_YUV444P* aSrcUtils, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtYUVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGBA32, 4, &YUV444PToRGBA32);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGBA32::ConvertFrom(Utils_YUV422P* aSrcUtils, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtYUVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGBA32, 4, &YUV422PToRGBA32);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGBA32::ConvertFrom(Utils_YUV420P* aSrcUtils, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtYUVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGBA32, 4, &libyuv::I420ToABGR);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGBA32::ConvertFrom(Utils_YUV420SP_NV12* aSrcUtils, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtNVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGBA32, 4, &NV12ToRGBA32);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGBA32::ConvertFrom(Utils_YUV420SP_NV21* aSrcUtils, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtNVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGBA32, 4, &NV21ToRGBA32);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGBA32::ConvertFrom(Utils_HSV* aSrcFormat, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<float, uint8_t>(aSrcFormat, (const float*)aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGBA32, 4, &HSVToRGBA32);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGBA32::ConvertFrom(Utils_Lab* aSrcFormat, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<float, uint8_t>(aSrcFormat, (const float*)aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGBA32, 4, &LabToRGBA32);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGBA32::ConvertFrom(Utils_Depth* aSrcFormat, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+bool
+Utils_RGBA32::CanConvertFrom(ImageBitmapFormat aSrcFormat)
+{
+  if (aSrcFormat == ImageBitmapFormat::GRAY8 ||
+      aSrcFormat == ImageBitmapFormat::DEPTH) {
+    return false;
+  }
+
+  return true;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGBA32::CreateDefaultLayout(uint32_t aWidth, uint32_t aHeight, uint32_t aStride)
+{
+  return CreateDefaultLayoutForSimpleImage(aWidth, aHeight, aStride, mChannels, mBytesPerPixelValue, mDataType);
+}
+
+/*
+ * Utils_BGRA32.
+ */
+/* static */Utils_BGRA32&
+Utils_BGRA32::GetInstance()
+{
+  static Utils_BGRA32 instance;
+  return instance;
+}
+
+Utils_BGRA32::Utils_BGRA32()
+: Utils(4, ChannelPixelLayoutDataType::Uint8)
+{
+}
+
+uint32_t
+Utils_BGRA32::NeededBufferSize(uint32_t aWidth, uint32_t aHeight)
+{
+  return aWidth * aHeight * (uint32_t)mChannels * mBytesPerPixelValue;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGRA32::ConvertTo(Utils* aDstFormat, const uint8_t* aSrcBuffer,
+                        const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return aDstFormat->ConvertFrom(this, aSrcBuffer, aSrcLayout, aDstBuffer);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGRA32::ConvertFrom(Utils_RGBA32* aSrcFormat, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, uint8_t>(aSrcFormat, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGRA32, 4, &libyuv::ABGRToARGB);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGRA32::ConvertFrom(Utils_BGRA32* aSrcUtils, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return PureCopy(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGRA32);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGRA32::ConvertFrom(Utils_RGB24* aSrcFormat, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, uint8_t>(aSrcFormat, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGRA32, 4, &RGB24ToBGRA32);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGRA32::ConvertFrom(Utils_BGR24* aSrcFormat, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, uint8_t>(aSrcFormat, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGRA32, 4, &BGR24ToBGRA32);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGRA32::ConvertFrom(Utils_Gray8* aSrcFormat, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGRA32::ConvertFrom(Utils_YUV444P* aSrcUtils, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtYUVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGRA32, 4, &YUV444PToBGRA32);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGRA32::ConvertFrom(Utils_YUV422P* aSrcUtils, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtYUVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGRA32, 4, &libyuv::I422ToARGB);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGRA32::ConvertFrom(Utils_YUV420P* aSrcUtils, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtYUVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGRA32, 4, &libyuv::I420ToARGB);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGRA32::ConvertFrom(Utils_YUV420SP_NV12* aSrcUtils, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtNVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGRA32, 4, &libyuv::NV12ToARGB);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGRA32::ConvertFrom(Utils_YUV420SP_NV21* aSrcUtils, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtNVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGRA32, 4, &libyuv::NV21ToARGB);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGRA32::ConvertFrom(Utils_HSV* aSrcFormat, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<float, uint8_t>(aSrcFormat, (const float*)aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGRA32, 4, &HSVToBGRA32);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGRA32::ConvertFrom(Utils_Lab* aSrcFormat, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<float, uint8_t>(aSrcFormat, (const float*)aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGRA32, 4, &LabToBGRA32);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGRA32::ConvertFrom(Utils_Depth* aSrcFormat, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+bool
+Utils_BGRA32::CanConvertFrom(ImageBitmapFormat aSrcFormat)
+{
+  if (aSrcFormat == ImageBitmapFormat::RGBA32 ||
+      aSrcFormat == ImageBitmapFormat::BGRA32 ||
+      aSrcFormat == ImageBitmapFormat::YUV420P) {
+    return true;
+  }
+
+  return false;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGRA32::CreateDefaultLayout(uint32_t aWidth, uint32_t aHeight, uint32_t aStride)
+{
+  return CreateDefaultLayoutForSimpleImage(aWidth, aHeight, aStride, mChannels, mBytesPerPixelValue, mDataType);
+}
+
+/*
+ * Utils_RGB24.
+ */
+/* static */Utils_RGB24&
+Utils_RGB24::GetInstance()
+{
+  static Utils_RGB24 instance;
+  return instance;
+}
+
+Utils_RGB24::Utils_RGB24()
+: Utils(3, ChannelPixelLayoutDataType::Uint8)
+{
+}
+
+uint32_t
+Utils_RGB24::NeededBufferSize(uint32_t aWidth, uint32_t aHeight)
+{
+  return aWidth * aHeight * (uint32_t)mChannels * mBytesPerPixelValue;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGB24::ConvertTo(Utils* aDstFormat, const uint8_t* aSrcBuffer,
+                       const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return aDstFormat->ConvertFrom(this, aSrcBuffer, aSrcLayout, aDstBuffer);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGB24::ConvertFrom(Utils_RGBA32* aSrcFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, uint8_t>(aSrcFormat, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, 3, &RGBA32ToRGB24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGB24::ConvertFrom(Utils_BGRA32* aSrcFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, uint8_t>(aSrcFormat, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, 3, &BGRA32ToRGB24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGB24::ConvertFrom(Utils_RGB24* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return PureCopy(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGB24::ConvertFrom(Utils_BGR24* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, uint8_t>(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, 3, &BGR24ToRGB24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGB24::ConvertFrom(Utils_Gray8* aSrcFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGB24::ConvertFrom(Utils_YUV444P* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtYUVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, 3, &YUV444PToRGB24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGB24::ConvertFrom(Utils_YUV422P* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtYUVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, 3, &YUV422PToRGB24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGB24::ConvertFrom(Utils_YUV420P* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtYUVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, 3, &YUV420PToRGB24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGB24::ConvertFrom(Utils_YUV420SP_NV12* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtNVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, 3, &NV12ToRGB24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGB24::ConvertFrom(Utils_YUV420SP_NV21* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtNVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, 3, &NV21ToRGB24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGB24::ConvertFrom(Utils_HSV* aSrcFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<float, uint8_t>(aSrcFormat, (const float*)aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, 3, &HSVToRGB24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGB24::ConvertFrom(Utils_Lab* aSrcFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<float, uint8_t>(aSrcFormat, (const float*)aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, 3, &LabToRGB24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGB24::ConvertFrom(Utils_Depth* aSrcFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+bool
+Utils_RGB24::CanConvertFrom(ImageBitmapFormat aSrcFormat)
+{
+  if (aSrcFormat == ImageBitmapFormat::GRAY8 ||
+      aSrcFormat == ImageBitmapFormat::DEPTH) {
+    return false;
+  }
+
+  return true;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGB24::CreateDefaultLayout(uint32_t aWidth, uint32_t aHeight, uint32_t aStride)
+{
+  return CreateDefaultLayoutForSimpleImage(aWidth, aHeight, aStride, mChannels, mBytesPerPixelValue, mDataType);
+}
+
+/*
+ * Utils_BGR24.
+ */
+/* static */Utils_BGR24&
+Utils_BGR24::GetInstance()
+{
+  static Utils_BGR24 instance;
+  return instance;
+}
+
+Utils_BGR24::Utils_BGR24()
+: Utils(3, ChannelPixelLayoutDataType::Uint8)
+{
+}
+
+uint32_t
+Utils_BGR24::NeededBufferSize(uint32_t aWidth, uint32_t aHeight)
+{
+  return aWidth * aHeight * (uint32_t)mChannels * mBytesPerPixelValue;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGR24::ConvertTo(Utils* aDstFormat, const uint8_t* aSrcBuffer,
+                       const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return aDstFormat->ConvertFrom(this, aSrcBuffer, aSrcLayout, aDstBuffer);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGR24::ConvertFrom(Utils_RGBA32* aSrcFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, uint8_t>(aSrcFormat, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGR24, 3, &RGBA32ToBGR24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGR24::ConvertFrom(Utils_BGRA32* aSrcFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, uint8_t>(aSrcFormat, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGR24, 3, &BGRA32ToBGR24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGR24::ConvertFrom(Utils_RGB24* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, uint8_t>(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGR24, 3, &RGB24ToBGR24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGR24::ConvertFrom(Utils_BGR24* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return PureCopy(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGR24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGR24::ConvertFrom(Utils_Gray8* aSrcFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGR24::ConvertFrom(Utils_YUV444P* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtYUVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGR24, 3, &YUV444PToBGR24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGR24::ConvertFrom(Utils_YUV422P* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtYUVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGR24, 3, &YUV422PToBGR24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGR24::ConvertFrom(Utils_YUV420P* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtYUVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGR24, 3, &YUV420PToBGR24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGR24::ConvertFrom(Utils_YUV420SP_NV12* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtNVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGR24, 3, &NV12ToBGR24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGR24::ConvertFrom(Utils_YUV420SP_NV21* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtNVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGR24, 3, &NV21ToBGR24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGR24::ConvertFrom(Utils_HSV* aSrcFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<float, uint8_t>(aSrcFormat, (const float*)aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGR24, 3, &HSVToBGR24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGR24::ConvertFrom(Utils_Lab* aSrcFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<float, uint8_t>(aSrcFormat, (const float*)aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGR24, 3, &LabToBGR24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGR24::ConvertFrom(Utils_Depth* aSrcFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+bool
+Utils_BGR24::CanConvertFrom(ImageBitmapFormat aSrcFormat)
+{
+  if (aSrcFormat == ImageBitmapFormat::GRAY8 ||
+      aSrcFormat == ImageBitmapFormat::DEPTH) {
+    return false;
+  }
+
+  return true;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGR24::CreateDefaultLayout(uint32_t aWidth, uint32_t aHeight, uint32_t aStride)
+{
+  return CreateDefaultLayoutForSimpleImage(aWidth, aHeight, aStride, mChannels, mBytesPerPixelValue, mDataType);
+}
+
+/*
+ * Utils_Gray8.
+ */
+/* static */Utils_Gray8&
+Utils_Gray8::GetInstance()
+{
+  static Utils_Gray8 instance;
+  return instance;
+}
+
+Utils_Gray8::Utils_Gray8()
+: Utils(1, ChannelPixelLayoutDataType::Uint8)
+{
+}
+
+uint32_t
+Utils_Gray8::NeededBufferSize(uint32_t aWidth, uint32_t aHeight)
+{
+  return aWidth * aHeight * (uint32_t)mChannels * mBytesPerPixelValue;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Gray8::ConvertTo(Utils* aDstFormat, const uint8_t* aSrcBuffer,
+                       const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return aDstFormat->ConvertFrom(this, aSrcBuffer, aSrcLayout, aDstBuffer);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Gray8::ConvertFrom(Utils_RGBA32* aSrcFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, uint8_t>(aSrcFormat, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::GRAY8, 1, &RGBA32ToGray8);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Gray8::ConvertFrom(Utils_BGRA32* aSrcFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, uint8_t>(aSrcFormat, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::GRAY8, 1, &BGRA32ToGray8);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Gray8::ConvertFrom(Utils_RGB24* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, uint8_t>(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::GRAY8, 1, &RGB24ToGray8);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Gray8::ConvertFrom(Utils_BGR24* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, uint8_t>(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::GRAY8, 1, &BGR24ToGray8);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Gray8::ConvertFrom(Utils_Gray8* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return PureCopy(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::GRAY8);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Gray8::ConvertFrom(Utils_YUV444P* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtYUVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::GRAY8, 1, &YUV444PToGray8);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Gray8::ConvertFrom(Utils_YUV422P* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtYUVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::GRAY8, 1, &YUV422PToGray8);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Gray8::ConvertFrom(Utils_YUV420P* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtYUVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::GRAY8, 1, &YUV420PToGray8);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Gray8::ConvertFrom(Utils_YUV420SP_NV12* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtNVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::GRAY8, 1, &NV12ToGray8);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Gray8::ConvertFrom(Utils_YUV420SP_NV21* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtNVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::GRAY8, 1, &NV21ToGray8);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Gray8::ConvertFrom(Utils_HSV* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Gray8::ConvertFrom(Utils_Lab* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Gray8::ConvertFrom(Utils_Depth* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+bool
+Utils_Gray8::CanConvertFrom(ImageBitmapFormat aSrcFormat)
+{
+  if (aSrcFormat == ImageBitmapFormat::DEPTH) {
+    return false;
+  }
+
+  return true;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Gray8::CreateDefaultLayout(uint32_t aWidth, uint32_t aHeight, uint32_t aStride)
+{
+  return CreateDefaultLayoutForSimpleImage(aWidth, aHeight, aStride, mChannels, mBytesPerPixelValue, mDataType);
+}
+
+/*
+ * class Utils_YUV444P.
+ */
+/* static */Utils_YUV444P&
+Utils_YUV444P::GetInstance()
+{
+  static Utils_YUV444P instance;
+  return instance;
+}
+
+Utils_YUV444P::Utils_YUV444P()
+: Utils(3, ChannelPixelLayoutDataType::Uint8)
+{
+}
+
+uint32_t
+Utils_YUV444P::NeededBufferSize(uint32_t aWidth, uint32_t aHeight)
+{
+  return aWidth * aHeight * (uint32_t)mChannels * mBytesPerPixelValue;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV444P::ConvertTo(Utils* aDstFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return aDstFormat->ConvertFrom(this, aSrcBuffer, aSrcLayout, aDstBuffer);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV444P::ConvertFrom(Utils_RGBA32* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToYUVImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV444P, &RGBA32ToYUV444P);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV444P::ConvertFrom(Utils_BGRA32* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToYUVImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV444P, &libyuv::ARGBToI444);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV444P::ConvertFrom(Utils_RGB24* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToYUVImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV444P, &RGB24ToYUV444P);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV444P::ConvertFrom(Utils_BGR24* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToYUVImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV444P, &BGR24ToYUV444P);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV444P::ConvertFrom(Utils_Gray8* aSrcFormat, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV444P::ConvertFrom(Utils_YUV444P* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return PureCopy(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV444P);
+}
+
+// TODO: optimize me.
+UniquePtr<ImagePixelLayout>
+Utils_YUV444P::ConvertFrom(Utils_YUV422P* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV420P, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV444P::ConvertFrom(Utils_YUV420P*, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  MOZ_ASSERT(aSrcBuffer, "Convert color from a null buffer.");
+  MOZ_ASSERT(aSrcLayout, "Convert color from a null layout.");
+  MOZ_ASSERT(aDstBuffer, "Convert color to a null buffer.");
+
+  UniquePtr<ImagePixelLayout> layout =
+    CreateDefaultLayout((*aSrcLayout)[0].mWidth,
+                        (*aSrcLayout)[0].mHeight,
+                        (*aSrcLayout)[0].mWidth);
+
+  MOZ_ASSERT(layout, "Cannot create a ImagePixelLayout of YUV444P");
+
+  const nsTArray<ChannelPixelLayout>& channels = *layout;
+
+  const nsTArray<ChannelPixelLayout>& srcChannels = *aSrcLayout;
+
+  int rv = I420ToI444(aSrcBuffer + srcChannels[0].mOffset, srcChannels[0].mStride,
+                      aSrcBuffer + srcChannels[1].mOffset, srcChannels[1].mStride,
+                      aSrcBuffer + srcChannels[2].mOffset, srcChannels[2].mStride,
+                      aDstBuffer + channels[0].mOffset, channels[0].mStride,
+                      aDstBuffer + channels[1].mOffset, channels[1].mStride,
+                      aDstBuffer + channels[2].mOffset, channels[2].mStride,
+                      channels[0].mWidth, channels[0].mHeight);
+
+  if (NS_WARN_IF(rv != 0)) {
+    return nullptr;
+  }
+
+  return layout;
+}
+
+// TODO: optimize me.
+UniquePtr<ImagePixelLayout>
+Utils_YUV444P::ConvertFrom(Utils_YUV420SP_NV12* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV420P, this);
+}
+
+// TODO: optimize me.
+UniquePtr<ImagePixelLayout>
+Utils_YUV444P::ConvertFrom(Utils_YUV420SP_NV21* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV420P, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV444P::ConvertFrom(Utils_HSV* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV444P::ConvertFrom(Utils_Lab* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV444P::ConvertFrom(Utils_Depth* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+bool
+Utils_YUV444P::CanConvertFrom(ImageBitmapFormat aSrcFormat)
+{
+  if (aSrcFormat == ImageBitmapFormat::GRAY8 ||
+      aSrcFormat == ImageBitmapFormat::DEPTH) {
+    return false;
+  }
+
+  return true;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV444P::CreateDefaultLayout(uint32_t aWidth, uint32_t aHeight, uint32_t aStride)
+{
+  UniquePtr<ImagePixelLayout> layout(new ImagePixelLayout(mChannels));
+
+  // set mChannels
+  ChannelPixelLayout* ychannel = layout->AppendElement();
+  ChannelPixelLayout* uchannel = layout->AppendElement();
+  ChannelPixelLayout* vchannel = layout->AppendElement();
+  ychannel->mOffset = 0;
+  ychannel->mWidth  = aWidth;
+  ychannel->mHeight = aHeight;
+  ychannel->mDataType = ChannelPixelLayoutDataType::Uint8;
+  ychannel->mStride = aStride;
+  ychannel->mSkip   = 0; // aYSkip;
+
+  uchannel->mOffset = ychannel->mOffset + ychannel->mStride * ychannel->mHeight;
+  uchannel->mWidth  = aWidth;
+  uchannel->mHeight = aHeight;
+  uchannel->mDataType = ChannelPixelLayoutDataType::Uint8;
+  uchannel->mStride = aStride;
+  uchannel->mSkip   = 0; // aUSkip;
+
+  vchannel->mOffset = uchannel->mOffset + uchannel->mStride * uchannel->mHeight;
+  vchannel->mWidth  = aWidth;
+  vchannel->mHeight = aHeight;
+  vchannel->mDataType = ChannelPixelLayoutDataType::Uint8;
+  vchannel->mStride = aStride;
+  vchannel->mSkip   = 0; // aVSkip;
+
+  return layout;
+}
+
+/*
+ * class Utils_YUV422P.
+ */
+/* static */Utils_YUV422P&
+Utils_YUV422P::GetInstance()
+{
+  static Utils_YUV422P instance;
+  return instance;
+}
+
+Utils_YUV422P::Utils_YUV422P()
+: Utils(3, ChannelPixelLayoutDataType::Uint8)
+{
+}
+
+uint32_t
+Utils_YUV422P::NeededBufferSize(uint32_t aWidth, uint32_t aHeight)
+{
+  return aWidth * aHeight * mBytesPerPixelValue +
+         2 * ((aWidth + 1) / 2) * aHeight * mBytesPerPixelValue;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV422P::ConvertTo(Utils* aDstFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return aDstFormat->ConvertFrom(this, aSrcBuffer, aSrcLayout, aDstBuffer);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV422P::ConvertFrom(Utils_RGBA32* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToYUVImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV422P, &RGBA32ToYUV422P);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV422P::ConvertFrom(Utils_BGRA32* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToYUVImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV422P, &libyuv::ARGBToI422);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV422P::ConvertFrom(Utils_RGB24* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToYUVImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV422P, &RGB24ToYUV422P);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV422P::ConvertFrom(Utils_BGR24* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToYUVImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV422P, &BGR24ToYUV422P);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV422P::ConvertFrom(Utils_Gray8* aSrcFormat, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+// TODO: optimize me.
+UniquePtr<ImagePixelLayout>
+Utils_YUV422P::ConvertFrom(Utils_YUV444P* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV420P, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV422P::ConvertFrom(Utils_YUV422P* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return PureCopy(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV422P);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV422P::ConvertFrom(Utils_YUV420P*, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  MOZ_ASSERT(aSrcBuffer, "Convert color from a null buffer.");
+  MOZ_ASSERT(aSrcLayout, "Convert color from a null layout.");
+  MOZ_ASSERT(aDstBuffer, "Convert color to a null buffer.");
+
+  UniquePtr<ImagePixelLayout> layout =
+    CreateDefaultLayout((*aSrcLayout)[0].mWidth,
+                        (*aSrcLayout)[0].mHeight,
+                        (*aSrcLayout)[0].mWidth);
+
+  MOZ_ASSERT(layout, "Cannot create a ImagePixelLayout of YUV422P");
+
+  const nsTArray<ChannelPixelLayout>& channels = *layout;
+
+  const nsTArray<ChannelPixelLayout>& srcChannels = *aSrcLayout;
+
+  libyuv::I420ToI422(aSrcBuffer + srcChannels[0].mOffset, srcChannels[0].mStride,
+                     aSrcBuffer + srcChannels[1].mOffset, srcChannels[1].mStride,
+                     aSrcBuffer + srcChannels[2].mOffset, srcChannels[2].mStride,
+                     aDstBuffer + channels[0].mOffset, channels[0].mStride,
+                     aDstBuffer + channels[1].mOffset, channels[1].mStride,
+                     aDstBuffer + channels[2].mOffset, channels[2].mStride,
+                     channels[0].mWidth, channels[0].mHeight);
+
+  return layout;
+}
+
+// TODO: optimize me.
+UniquePtr<ImagePixelLayout>
+Utils_YUV422P::ConvertFrom(Utils_YUV420SP_NV12* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV420P, this);
+}
+
+// TODO: optimize me.
+UniquePtr<ImagePixelLayout>
+Utils_YUV422P::ConvertFrom(Utils_YUV420SP_NV21* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV420P, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV422P::ConvertFrom(Utils_HSV* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV422P::ConvertFrom(Utils_Lab* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV422P::ConvertFrom(Utils_Depth* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+bool
+Utils_YUV422P::CanConvertFrom(ImageBitmapFormat aSrcFormat)
+{
+  if (aSrcFormat == ImageBitmapFormat::GRAY8 ||
+      aSrcFormat == ImageBitmapFormat::DEPTH) {
+    return false;
+  }
+
+  return true;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV422P::CreateDefaultLayout(uint32_t aWidth, uint32_t aHeight, uint32_t aStride)
+{
+  UniquePtr<ImagePixelLayout> layout(new ImagePixelLayout(mChannels));
+
+  // set mChannels
+  ChannelPixelLayout* ychannel = layout->AppendElement();
+  ChannelPixelLayout* uchannel = layout->AppendElement();
+  ChannelPixelLayout* vchannel = layout->AppendElement();
+  ychannel->mOffset = 0;
+  ychannel->mWidth  = aWidth;
+  ychannel->mHeight = aHeight;
+  ychannel->mDataType = ChannelPixelLayoutDataType::Uint8;
+  ychannel->mStride = aStride;
+  ychannel->mSkip   = 0; // aYSkip;
+
+  uchannel->mOffset = ychannel->mOffset + ychannel->mStride * ychannel->mHeight;
+  uchannel->mWidth  = (aWidth + 1) / 2;
+  uchannel->mHeight = aHeight;
+  uchannel->mDataType = ChannelPixelLayoutDataType::Uint8;
+  uchannel->mStride = (aStride + 1) / 2;
+  uchannel->mSkip   = 0; // aUSkip;
+
+  vchannel->mOffset = uchannel->mOffset + uchannel->mStride * uchannel->mHeight;
+  vchannel->mWidth  = (aWidth + 1) / 2;
+  vchannel->mHeight = aHeight;
+  vchannel->mDataType = ChannelPixelLayoutDataType::Uint8;