merge mozilla--inbound to mc
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Sun, 29 Sep 2013 11:36:17 +0200
changeset 149176 8f805d3ef377c22dc5bf8fcfd23fe63d0fe4b718
parent 149175 2316f49caa814ed92c36961dd53c3c5b879aa0a9 (current diff)
parent 149158 dd81536b66d1accba2c5efa726fe29142b76aeac (diff)
child 149177 175d74869b972fc06be329c1782420384d5333ac
child 149267 5a49762ee832a24956b69a4b8c24cce103a26bcf
child 149269 0fb5baf17dfe80efe36b1d0f9f87e53686c5642f
child 149289 6fa0889b6a1932d9504b2b604e012e3a7d4c45ae
child 155861 e62689b72cc689fbed4c6768ab87175684db8297
push id34466
push usercbook@mozilla.com
push dateSun, 29 Sep 2013 09:40:58 +0000
treeherdermozilla-inbound@175d74869b97 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone27.0a1
first release with
nightly linux32
8f805d3ef377 / 27.0a1 / 20130929030202 / files
nightly linux64
8f805d3ef377 / 27.0a1 / 20130929030202 / files
nightly mac
8f805d3ef377 / 27.0a1 / 20130929030202 / files
nightly win32
8f805d3ef377 / 27.0a1 / 20130929030202 / files
nightly win64
8f805d3ef377 / 27.0a1 / 20130929030202 / 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 mc
dom/base/Navigator.cpp
dom/base/Navigator.h
dom/indexedDB/nsIIDBKeyRange.idl
ipc/glue/AsyncChannel.h
ipc/glue/RPCChannel.cpp
ipc/glue/RPCChannel.h
ipc/glue/SyncChannel.cpp
ipc/glue/SyncChannel.h
--- a/CLOBBER
+++ b/CLOBBER
@@ -13,9 +13,9 @@
 #          |               |
 #          O <-- Clobber   O  <-- Clobber
 #
 # Note: The description below will be part of the error message shown to users.
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
-Bug 913260 needed a clobber to not break tons of tests 
+Bug 901789 needs a clobber.
--- a/addon-sdk/source/lib/sdk/indexed-db.js
+++ b/addon-sdk/source/lib/sdk/indexed-db.js
@@ -22,24 +22,27 @@ let sanitizeId = function(id){
     replace(/\./g, "-dot-").
     replace(uuidRe, "$1");
 
   return domain
 };
 
 const PSEUDOURI = "indexeddb://" + sanitizeId(id) // https://bugzilla.mozilla.org/show_bug.cgi?id=779197
 
-// Injects `indexedDB` to `this` scope.
-Cc["@mozilla.org/dom/indexeddb/manager;1"].
-	getService(Ci.nsIIndexedDatabaseManager).
-	initWindowless(this);
+// Firefox 26 and earlier releases don't support `indexedDB` in sandboxes
+// automatically, so we need to inject `indexedDB` to `this` scope ourselves.
+if (typeof(indexedDB) === "undefined") {
+  Cc["@mozilla.org/dom/indexeddb/manager;1"].
+    getService(Ci.nsIIndexedDatabaseManager).
+    initWindowless(this);
 
-// Firefox 14 gets this with a prefix
-if (typeof(indexedDB) === "undefined")
-  this.indexedDB = mozIndexedDB;
+  // Firefox 14 gets this with a prefix
+  if (typeof(indexedDB) === "undefined")
+    this.indexedDB = mozIndexedDB;
+}
 
 // Use XPCOM because `require("./url").URL` doesn't expose the raw uri object.
 let principaluri = Cc["@mozilla.org/network/io-service;1"].
               getService(Ci.nsIIOService).
               newURI(PSEUDOURI, null, null);
 
 let principal = Cc["@mozilla.org/scriptsecuritymanager;1"].
 	               getService(Ci.nsIScriptSecurityManager).
--- a/addon-sdk/source/lib/toolkit/loader.js
+++ b/addon-sdk/source/lib/toolkit/loader.js
@@ -176,16 +176,18 @@ const Sandbox = iced(function Sandbox(op
   // Normalize options and rename to match `Cu.Sandbox` expectations.
   options = {
     // Do not expose `Components` if you really need them (bad idea!) you
     // still can expose via prototype.
     wantComponents: false,
     sandboxName: options.name,
     principal: 'principal' in options ? options.principal : systemPrincipal,
     wantXrays: 'wantXrays' in options ? options.wantXrays : true,
+    wantGlobalProperties: 'wantGlobalProperties' in options ?
+                          options.wantGlobalProperties : [],
     sandboxPrototype: 'prototype' in options ? options.prototype : {},
     sameGroupAs: 'sandbox' in options ? options.sandbox : null
   };
 
   // Make `options.sameGroupAs` only if `sandbox` property is passed,
   // otherwise `Cu.Sandbox` will throw.
   if (!options.sameGroupAs)
     delete options.sameGroupAs;
@@ -245,17 +247,18 @@ const load = iced(function load(loader, 
   });
 
   let sandbox = sandboxes[module.uri] = Sandbox({
     name: module.uri,
     // Get an existing module sandbox, if any, so we can reuse its compartment
     // when creating the new one to reduce memory consumption.
     sandbox: sandboxes[keys(sandboxes).shift()],
     prototype: create(globals, descriptors),
-    wantXrays: false
+    wantXrays: false,
+    wantGlobalProperties: module.id == "sdk/indexed-db" ? ["indexedDB"] : []
   });
 
   try {
     evaluate(sandbox, module.uri);
   } catch (error) {
     let { message, fileName, lineNumber } = error;
     let stack = error.stack || Error().stack;
     let frames = parseStack(stack).filter(isntLoaderFrame);
--- a/browser/components/sessionstore/content/content-sessionStore.js
+++ b/browser/components/sessionstore/content/content-sessionStore.js
@@ -5,16 +5,18 @@
 "use strict";
 
 function debug(msg) {
   Services.console.logStringMessage("SessionStoreContent: " + msg);
 }
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
 
+XPCOMUtils.defineLazyModuleGetter(this, "DocShellCapabilities",
+  "resource:///modules/sessionstore/DocShellCapabilities.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "SessionHistory",
   "resource:///modules/sessionstore/SessionHistory.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "SessionStorage",
   "resource:///modules/sessionstore/SessionStorage.jsm");
 
 /**
  * Listens for and handles content events that we need for the
  * session store service to be notified of state changes in content.
@@ -65,33 +67,38 @@ let EventListener = {
 
 /**
  * Listens for and handles messages sent by the session store service.
  */
 let MessageListener = {
 
   MESSAGES: [
     "SessionStore:collectSessionHistory",
-    "SessionStore:collectSessionStorage"
+    "SessionStore:collectSessionStorage",
+    "SessionStore:collectDocShellCapabilities"
   ],
 
   init: function () {
     this.MESSAGES.forEach(m => addMessageListener(m, this));
   },
 
   receiveMessage: function ({name, data: {id}}) {
     switch (name) {
       case "SessionStore:collectSessionHistory":
         let history = SessionHistory.read(docShell);
         sendAsyncMessage(name, {id: id, data: history});
         break;
       case "SessionStore:collectSessionStorage":
         let storage = SessionStorage.serialize(docShell);
         sendAsyncMessage(name, {id: id, data: storage});
         break;
+      case "SessionStore:collectDocShellCapabilities":
+        let disallow = DocShellCapabilities.collect(docShell);
+        sendAsyncMessage(name, {id: id, data: disallow});
+        break;
       default:
         debug("received unknown message '" + name + "'");
         break;
     }
   }
 };
 
 let ProgressListener = {
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/src/DocShellCapabilities.jsm
@@ -0,0 +1,50 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this file,
+* You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+this.EXPORTED_SYMBOLS = ["DocShellCapabilities"];
+
+/**
+ * The external API exported by this module.
+ */
+this.DocShellCapabilities = Object.freeze({
+  collect: function (docShell) {
+    return DocShellCapabilitiesInternal.collect(docShell);
+  },
+
+  restore: function (docShell, disallow) {
+    return DocShellCapabilitiesInternal.restore(docShell, disallow);
+  },
+});
+
+/**
+ * Internal functionality to save and restore the docShell.allow* properties.
+ */
+let DocShellCapabilitiesInternal = {
+  // List of docShell capabilities to (re)store. These are automatically
+  // retrieved from a given docShell if not already collected before.
+  // This is made so they're automatically in sync with all nsIDocShell.allow*
+  // properties.
+  caps: null,
+
+  allCapabilities: function (docShell) {
+    if (!this.caps) {
+      let keys = Object.keys(docShell);
+      this.caps = keys.filter(k => k.startsWith("allow")).map(k => k.slice(5));
+    }
+    return this.caps;
+  },
+
+  collect: function (docShell) {
+    let caps = this.allCapabilities(docShell);
+    return caps.filter(cap => !docShell["allow" + cap]);
+  },
+
+  restore: function (docShell, disallow) {
+    let caps = this.allCapabilities(docShell);
+    for (let cap of caps)
+      docShell["allow" + cap] = !disallow.has(cap);
+  },
+};
--- a/browser/components/sessionstore/src/SessionHistory.jsm
+++ b/browser/components/sessionstore/src/SessionHistory.jsm
@@ -69,17 +69,19 @@ let SessionHistoryInternal = {
       } catch (ex) {
         // In some cases, getEntryAtIndex will throw. This seems to be due to
         // history.count being higher than it should be. By doing this in a
         // try-catch, we'll update history to where it breaks, print an error
         // message, and still save sessionstore.js.
         debug("SessionStore failed gathering complete history " +
               "for the focused window/tab. See bug 669196.");
       }
-      data.index = history.index + 1;
+
+      // Ensure the index isn't out of bounds if an exception was thrown above.
+      data.index = Math.min(history.index + 1, data.entries.length);
     } else {
       let uri = webNavigation.currentURI.spec;
       // We landed here because the history is inaccessible or there are no
       // history entries. In that case we should at least record the docShell's
       // current URL as a single history entry. If the URL is not about:blank
       // or it's a blank tab that was modified (like a custom newtab page),
       // record it. For about:blank we explicitly want an empty array without
       // an 'index' property to denote that there are no history entries.
--- a/browser/components/sessionstore/src/SessionStore.jsm
+++ b/browser/components/sessionstore/src/SessionStore.jsm
@@ -89,44 +89,29 @@ Cu.import("resource://gre/modules/Privat
 Cu.import("resource://gre/modules/Promise.jsm", this);
 Cu.import("resource://gre/modules/Task.jsm", this);
 
 XPCOMUtils.defineLazyServiceGetter(this, "gSessionStartup",
   "@mozilla.org/browser/sessionstartup;1", "nsISessionStartup");
 XPCOMUtils.defineLazyServiceGetter(this, "gScreenManager",
   "@mozilla.org/gfx/screenmanager;1", "nsIScreenManager");
 
-// List of docShell capabilities to (re)store. These are automatically
-// retrieved from a given docShell if not already collected before.
-// This is made so they're automatically in sync with all nsIDocShell.allow*
-// properties.
-let gDocShellCapabilities = (function () {
-  let caps;
-
-  return docShell => {
-    if (!caps) {
-      let keys = Object.keys(docShell);
-      caps = keys.filter(k => k.startsWith("allow")).map(k => k.slice(5));
-    }
-
-    return caps;
-  };
-})();
-
 /**
  * Get nsIURI from string
  * @param string
  * @returns nsIURI
  */
 function makeURI(aString) {
   return Services.io.newURI(aString, null, null);
 }
 
 XPCOMUtils.defineLazyModuleGetter(this, "ScratchpadManager",
   "resource:///modules/devtools/scratchpad-manager.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "DocShellCapabilities",
+  "resource:///modules/sessionstore/DocShellCapabilities.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "DocumentUtils",
   "resource:///modules/sessionstore/DocumentUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Messenger",
   "resource:///modules/sessionstore/Messenger.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PrivacyLevel",
   "resource:///modules/sessionstore/PrivacyLevel.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "SessionSaver",
   "resource:///modules/sessionstore/SessionSaver.jsm");
@@ -2579,17 +2564,18 @@ let SessionStoreInternal = {
    * @param aIdMap
    *        Hash for ensuring unique frame IDs
    * @param aRestoreImmediately
    *        Flag to indicate whether the given set of tabs aTabs should be
    *        restored/loaded immediately even if restore_on_demand = true
    */
   restoreHistory:
     function ssi_restoreHistory(aWindow, aTabs, aTabData, aIdMap, aDocIdentMap,
-                                aRestoreImmediately) {
+                                aRestoreImmediately)
+  {
     // if the tab got removed before being completely restored, then skip it
     while (aTabs.length > 0 && !(this._canRestoreTabHistory(aTabs[0]))) {
       aTabs.shift();
       aTabData.shift();
     }
     if (aTabs.length == 0) {
       // At this point we're essentially ready for consumers to read/write data
       // via the sessionstore API so we'll send the SSWindowStateReady event.
@@ -2626,18 +2612,17 @@ let SessionStoreInternal = {
       if (!tabData.entries[i].url)
         continue;
       history.addEntry(this._deserializeHistoryEntry(tabData.entries[i],
                                                      aIdMap, aDocIdentMap), true);
     }
 
     // make sure to reset the capabilities and attributes, in case this tab gets reused
     let disallow = new Set(tabData.disallow && tabData.disallow.split(","));
-    for (let cap of gDocShellCapabilities(browser.docShell))
-      browser.docShell["allow" + cap] = !disallow.has(cap);
+    DocShellCapabilities.restore(browser.docShell, disallow);
 
     // Restore tab attributes.
     if ("attributes" in tabData) {
       TabAttributes.set(tab, tabData.attributes);
     }
 
     // Restore the tab icon.
     if ("image" in tabData) {
@@ -4331,44 +4316,61 @@ let TabState = {
       throw new TypeError("Expecting a tab");
     }
 
     // Don't collect if we don't need to.
     if (TabStateCache.has(tab)) {
       return Promise.resolve(TabStateCache.get(tab));
     }
 
+    // If the tab hasn't been restored yet, just return the data we
+    // have saved for it.
+    let browser = tab.linkedBrowser;
+    if (!browser.currentURI || (browser.__SS_data && browser.__SS_tabStillLoading)) {
+      let tabData = new TabData(this._collectBaseTabData(tab));
+      return Promise.resolve(tabData);
+    }
+
     let promise = Task.spawn(function task() {
       // Collected session history data asynchronously.
       let history = yield Messenger.send(tab, "SessionStore:collectSessionHistory");
 
       // Collected session storage data asynchronously.
       let storage = yield Messenger.send(tab, "SessionStore:collectSessionStorage");
 
+      // Collect docShell capabilities asynchronously.
+      let disallow = yield Messenger.send(tab, "SessionStore:collectDocShellCapabilities");
+
       // Collect basic tab data, without session history and storage.
-      let options = {omitSessionHistory: true, omitSessionStorage: true};
+      let options = {omitSessionHistory: true,
+                     omitSessionStorage: true,
+                     omitDocShellCapabilities: true};
       let tabData = new TabData(this._collectBaseTabData(tab, options));
 
       // Apply collected data.
       tabData.entries = history.entries;
       tabData.index = history.index;
 
       if (Object.keys(storage).length) {
         tabData.storage = storage;
       }
 
-      // Put tabData into cache when reading scroll and text data succeeds.
-      if (this._updateTextAndScrollDataForTab(tab, tabData)) {
-        // If we're still the latest async collection for the given tab and
-        // the cache hasn't been filled by collect() in the meantime, let's
-        // fill the cache with the data we received.
-        if (this._pendingCollections.get(tab) == promise) {
-          TabStateCache.set(tab, tabData);
-          this._pendingCollections.delete(tab);
-        }
+      if (disallow.length > 0) {
+	tabData.disallow = disallow.join(",");
+      }
+
+      // Save text and scroll data.
+      this._updateTextAndScrollDataForTab(tab, tabData);
+
+      // If we're still the latest async collection for the given tab and
+      // the cache hasn't been filled by collect() in the meantime, let's
+      // fill the cache with the data we received.
+      if (this._pendingCollections.get(tab) == promise) {
+        TabStateCache.set(tab, tabData);
+        this._pendingCollections.delete(tab);
       }
 
       throw new Task.Result(tabData);
     }.bind(this));
 
     // Save the current promise as the latest asynchronous collection that is
     // running. This will be used to check whether the collected data is still
     // valid and will be used to fill the tab state cache.
@@ -4433,16 +4435,17 @@ let TabState = {
    *
    * @param tab
    *        tabbrowser tab
    * @param options
    *        An object that will be passed to session history and session
    *        storage data collection methods.
    *        {omitSessionHistory: true} to skip collecting session history data
    *        {omitSessionStorage: true} to skip collecting session storage data
+   *        {omitDocShellCapabilities: true} to skip collecting docShell allow* attributes
    *
    *        The omit* options have been introduced to enable us collecting
    *        those parts of the tab data asynchronously. We will request basic
    *        tabData without the parts to omit and fill those holes later when
    *        the content script has responded.
    *
    * @returns {object} An object with the basic data for this tab.
    */
@@ -4490,24 +4493,23 @@ let TabState = {
     }
 
     if (tab.pinned)
       tabData.pinned = true;
     else
       delete tabData.pinned;
     tabData.hidden = tab.hidden;
 
-    let disallow = [];
-    for (let cap of gDocShellCapabilities(browser.docShell))
-      if (!browser.docShell["allow" + cap])
-        disallow.push(cap);
-    if (disallow.length > 0)
-      tabData.disallow = disallow.join(",");
-    else if (tabData.disallow)
-      delete tabData.disallow;
+    if (!options || !options.omitDocShellCapabilities) {
+      let disallow = DocShellCapabilities.collect(browser.docShell);
+      if (disallow.length > 0)
+	tabData.disallow = disallow.join(",");
+      else if (tabData.disallow)
+	delete tabData.disallow;
+    }
 
     // Save tab attributes.
     tabData.attributes = TabAttributes.get(tab);
 
     // Store the tab icon.
     let tabbrowser = tab.ownerDocument.defaultView.gBrowser;
     tabData.image = tabbrowser.getIcon(tab);
 
--- a/browser/components/sessionstore/src/moz.build
+++ b/browser/components/sessionstore/src/moz.build
@@ -8,16 +8,17 @@ EXTRA_COMPONENTS += [
     'nsSessionStartup.js',
     'nsSessionStore.js',
     'nsSessionStore.manifest',
 ]
 
 JS_MODULES_PATH = 'modules/sessionstore'
 
 EXTRA_JS_MODULES = [
+    'DocShellCapabilities.jsm',
     'DocumentUtils.jsm',
     'Messenger.jsm',
     'PrivacyLevel.jsm',
     'SessionCookies.jsm',
     'SessionHistory.jsm',
     'SessionMigration.jsm',
     'SessionStorage.jsm',
     'SessionWorker.js',
--- a/browser/config/mozconfigs/linux64/nightly-asan
+++ b/browser/config/mozconfigs/linux64/nightly-asan
@@ -1,11 +1,11 @@
 # We still need to build with debug symbols
 ac_add_options --disable-debug
-ac_add_options --enable-optimize="-O2 -g"
+ac_add_options --enable-optimize="-O2 -gline-tables-only"
 
 # ASan specific options on Linux
 ac_add_options --enable-valgrind
 
 . $topsrcdir/build/unix/mozconfig.asan
 
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
--- a/content/canvas/test/webgl/non-conf-tests/Makefile.in
+++ b/content/canvas/test/webgl/non-conf-tests/Makefile.in
@@ -1,9 +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/.
 
 MOCHITEST_FILES = \
+  driver-info.js \
+  webgl-util.js \
+  \
+  test_webgl_available.html \
   test_webgl_conformance.html \
   test_webgl_request_mismatch.html \
   $(NULL)
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/webgl/non-conf-tests/driver-info.js
@@ -0,0 +1,139 @@
+DriverInfo = (function() {
+  // ---------------------------------------------------------------------------
+  // Debug info handling
+
+  function defaultInfoFunc(str) {
+    console.log('Info: ' + str);
+  }
+
+  var gInfoFunc = defaultInfoFunc;
+  function setInfoFunc(func) {
+    gInfoFunc = func;
+  }
+
+  function info(str) {
+    gInfoFunc(str);
+  }
+
+  // ---------------------------------------------------------------------------
+  // OS and driver identification
+  //   Stolen from content/canvas/test/webgl/test_webgl_conformance_test_suite.html
+  const Cc = SpecialPowers.Cc;
+  const Ci = SpecialPowers.Ci;
+  function detectDriverInfo() {
+    var doc = Cc["@mozilla.org/xmlextras/domparser;1"].createInstance(Ci.nsIDOMParser).parseFromString("<html/>", "text/html");
+
+    var canvas = doc.createElement("canvas");
+    canvas.width = 1;
+    canvas.height = 1;
+
+    var type = "";
+    var gl = null;
+    try {
+      gl = canvas.getContext("experimental-webgl");
+    } catch(e) {}
+
+    if (!gl) {
+      info('Failed to create WebGL context for querying driver info.');
+      throw 'WebGL failed';
+    }
+
+    var ext = gl.getExtension("WEBGL_debug_renderer_info");
+    // this extension is unconditionally available to chrome. No need to check.
+
+    var webglRenderer = gl.getParameter(ext.UNMASKED_RENDERER_WEBGL);
+    var webglVendor = gl.getParameter(ext.UNMASKED_VENDOR_WEBGL);
+    return [webglVendor, webglRenderer];
+  }
+
+  var OS = {
+    WINDOWS: 'windows',
+    MAC: 'mac',
+    LINUX: 'linux',
+    ANDROID: 'android',
+    B2G: 'b2g',
+  };
+
+  var DRIVER = {
+    MESA: 'mesa',
+    NVIDIA: 'nvidia',
+    ANDROID_X86_EMULATOR: 'android x86 emulator',
+  };
+
+  var kOS = null;
+  var kOSVersion = null;
+  var kDriver = null;
+
+  // From reftest.js:
+  var runtime = Cc['@mozilla.org/xre/app-info;1'].getService(Ci.nsIXULRuntime);
+
+  if (navigator.platform.indexOf('Win') == 0) {
+    kOS = OS.WINDOWS;
+
+    // code borrowed from browser/modules/test/browser_taskbar_preview.js
+    var version = SpecialPowers.Services.sysinfo.getProperty('version');
+    kOSVersion = parseFloat(version);
+    // Version 6.0 is Vista, 6.1 is 7.
+
+  } else if (navigator.platform.indexOf('Mac') == 0) {
+    kOS = OS.MAC;
+
+    var versionMatch = /Mac OS X (\d+.\d+)/.exec(navigator.userAgent);
+    kOSVersion = versionMatch ? parseFloat(versionMatch[1]) : null;
+
+  } else if (runtime.widgetToolkit == 'gonk') {
+    kOS = OS.B2G;
+
+  } else if (navigator.appVersion.indexOf('Android') != -1) {
+    kOS = OS.ANDROID;
+    // From layout/tools/reftest/reftest.js:
+    kOSVersion = SpecialPowers.Services.sysinfo.getProperty('version');
+
+  } else if (navigator.platform.indexOf('Linux') == 0) {
+    // Must be checked after android, as android also has a 'Linux' platform string.
+    kOS = OS.LINUX;
+  }
+
+  try {
+    var glVendor, glRenderer;
+    [glVendor, glRenderer] = detectDriverInfo();
+    info('GL vendor: ' + glVendor);
+    info('GL renderer: ' + glRenderer);
+
+    if (glRenderer.contains('llvmpipe')) {
+      kDriver = DRIVER.MESA;
+    } else if (glRenderer.contains('Android Emulator')) {
+      kDriver = DRIVER.ANDROID_X86_EMULATOR;
+    } else if (glVendor.contains('NVIDIA')) {
+      kDriver = DRIVER.NVIDIA;
+    }
+  } catch (e) {
+    // detectDriverInfo is fallible where WebGL fails.
+  }
+
+  if (kOS) {
+    info('OS detected as: ' + kOS);
+    info('  Version: ' + kOSVersion);
+  } else {
+    info('OS not detected.');
+    info('  `platform`:   ' + navigator.platform);
+    info('  `appVersion`: ' + navigator.appVersion);
+    info('  `userAgent`:  ' + navigator.userAgent);
+  }
+  if (kDriver) {
+    info('GL driver detected as: ' + kDriver);
+  } else {
+    info('GL driver not detected.');
+  }
+
+  return {
+    setInfoFunc: setInfoFunc,
+
+    OS: OS,
+    DRIVER: DRIVER,
+    getOS: function() { return kOS; },
+    getDriver: function() { return kDriver; },
+    getOSVersion: function() { return kOSVersion; },
+  };
+})();
+
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/webgl/non-conf-tests/test_webgl_available.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>WebGL test: Check that WebGL works (or not) if it should (or should not).</title>
+<script src="/MochiKit/MochiKit.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<script src="webgl-util.js"></script>
+<script src="driver-info.js"></script>
+</head>
+<body>
+<canvas id="c"></canvas>
+<script>
+
+function test() {
+  ok(SpecialPowers.getBoolPref('webgl.force-enabled'), 'WebGL should be force-enabled.');
+
+  var shouldSucceed = true;
+  var shouldFail = false;
+
+  if (DriverInfo.getOS() == DriverInfo.OS.ANDROID &&
+      DriverInfo.getOSVersion() < 15)
+  {
+    // Consider 'random'. Actually, ARMv6 fails, and ARMv7 succeeds, but we have
+    // not been successful at determining this from JS. (see bug 917478)
+    shouldSucceed = false;
+    shouldFail = false;
+  }
+
+  if (DriverInfo.getOS() == DriverInfo.OS.B2G) {
+    // We don't run these mochitests on hardware with WebGL-capable hardware yet.
+    shouldSucceed = false;
+    shouldFail = true;
+  }
+
+  var gl = WebGLUtil.getWebGL('c');
+  if (shouldSucceed) {
+    ok(gl, 'Expected WebGL creation to succeed.');
+  }
+  if (shouldFail) {
+    ok(!gl, 'Expected WebGL creation to fail.');
+  }
+
+  SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+var prefArrArr = [
+  ['webgl.force-enabled', true]
+];
+var prefEnv = {'set': prefArrArr};
+SpecialPowers.pushPrefEnv(prefEnv, test);
+
+</script>
+</body>
+</html>
--- a/content/canvas/test/webgl/non-conf-tests/test_webgl_conformance.html
+++ b/content/canvas/test/webgl/non-conf-tests/test_webgl_conformance.html
@@ -1,26 +1,30 @@
 <!DOCTYPE HTML>
+<html>
+<head>
 <title>WebGL test: 'webgl' context request</title>
 <script src="/MochiKit/MochiKit.js"></script>
 <script src="/tests/SimpleTest/SimpleTest.js"></script>
 <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<script src="webgl-util.js"></script>
+</head>
 <body>
 <canvas id="c"></canvas>
 <script>
 
-var canvas = document.getElementById('c');
-var gl = canvas.getContext('webgl');
+var gl = WebGLUtil.getWebGL('c', true);
 
 var isMobile = /Mobile/.test(navigator.userAgent);
 var isTablet = /Tablet/.test(navigator.userAgent);
 var shouldBeConformant = !isMobile && !isTablet;
 
 if (shouldBeConformant) {
   // Desktop.
   ok(gl, 'Expected conformance on: ' + navigator.userAgent);
 } else {
   // Fennec, B2G.
   ok(!gl, 'Expected no conformance on: ' + navigator.userAgent);
 }
 
 </script>
-
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/webgl/non-conf-tests/webgl-util.js
@@ -0,0 +1,114 @@
+WebGLUtil = (function() {
+  // ---------------------------------------------------------------------------
+  // Error handling
+
+  function defaultErrorFunc(str) {
+    console.log('Error: ' + str);
+  }
+
+  var gErrorFunc = defaultErrorFunc;
+  function setErrorFunc(func) {
+    gErrorFunc = func;
+  }
+
+  function error(str) {
+    gErrorFunc(str);
+  }
+
+  // ---------------------------------------------------------------------------
+  // WebGL helpers
+
+  function getWebGL(canvasId, requireConformant) {
+    // `requireConformant` will default to falsey if it is not supplied.
+
+    var canvas = document.getElementById(canvasId);
+
+    var gl = null;
+    try {
+      gl = canvas.getContext('webgl');
+    } catch(e) {}
+
+    if (!gl && !requireConformant) {
+      try {
+        gl = canvas.getContext('experimental-webgl');
+      } catch(e) {}
+    }
+
+    if (!gl) {
+      error('WebGL context could not be retrieved from \'' + canvasId + '\'.');
+      return null;
+    }
+
+    return gl;
+  }
+
+  function getContentFromElem(elem) {
+    var str = "";
+    var k = elem.firstChild;
+    while (k) {
+      if (k.nodeType == 3)
+        str += k.textContent;
+
+      k = k.nextSibling;
+    }
+
+    return str;
+  }
+
+  // Returns a valid shader, or null on errors.
+  function createShaderById(gl, id) {
+    var elem = document.getElementById(id);
+    if (!elem) {
+      error('Failed to create shader from non-existent id \'' + id + '\'.');
+      return null;
+    }
+
+    var src = getContentById(id);
+
+    var shader;
+    if (elem.type == "x-shader/x-fragment") {
+      shader = gl.createShader(gl.FRAGMENT_SHADER);
+    } else if (shaderScript.type == "x-shader/x-vertex") {
+      shader = gl.createShader(gl.VERTEX_SHADER);
+    } else {
+      error('Bad MIME type for shader \'' + id + '\': ' + elem.type + '.');
+      return null;
+    }
+
+    gl.shaderSource(shader, str);
+    gl.compileShader(shader);
+
+    return shader;
+  }
+
+  function createProgramByIds(gl, vsId, fsId) {
+    var vs = createShaderById(gl, vsId);
+    var fs = createShaderById(gl, fsId);
+    if (!vs || !fs)
+      return null;
+
+    var prog = gl.createProgram();
+    gl.attachShader(prog, vs);
+    gl.attachShader(prog, fs);
+    gl.linkProgram(prog);
+
+    if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
+      var str = "Shader program linking failed:\n";
+      str += "Shader program info log:\n" + gl.getProgramInfoLog(prog) + "\n\n";
+      str += "Vert shader log:\n" + gl.getShaderInfoLog(vs) + "\n\n";
+      str += "Frag shader log:\n" + gl.getShaderInfoLog(fs);
+      error(str);
+      return null;
+    }
+
+    return prog;
+  }
+
+  return {
+    setErrorFunc: setErrorFunc,
+
+    getWebGL: getWebGL,
+    createShaderById: createShaderById,
+    createProgramByIds: createProgramByIds,
+  };
+})();
--- a/content/events/test/test_eventctors.html
+++ b/content/events/test/test_eventctors.html
@@ -57,16 +57,30 @@ try {
 }
 ok(ex, "2nd parameter should be an object!");
 ex = false;
 
 
 e = new Event("hello");
 ok(e.type, "hello", "Wrong event type!");
 ok(!e.isTrusted, "Event shouldn't be trusted!");
+e.isTrusted = true;
+ok(!e.isTrusted, "Event shouldn't be trusted!");
+
+try {
+  e.__defineGetter__("isTrusted", function() { return true });
+} catch (exp) {
+  ex = true;
+}
+ok(ex, "Shouldn't be able to re-define the getter for isTrusted.");
+ex = false;
+ok(!e.isTrusted, "Event shouldn't be trusted!");
+
+ok(!("isTrusted" in Object.getPrototypeOf(e)))
+
 ok(!e.bubbles, "Event shouldn't bubble!");
 ok(!e.cancelable, "Event shouldn't be cancelable!");
 is(e.eventPhase, Event.NONE, "Wrong event phase");
 document.dispatchEvent(e);
 is(e.eventPhase, Event.NONE, "Wrong event phase");
 is(receivedEvent, e, "Wrong event!");
 
 e = new Event("hello", null);
@@ -170,16 +184,25 @@ try {
   ex = true;
 }
 ok(ex, "First parameter is required!");
 ex = false;
 
 e = new BlobEvent("hello");
 ok(e.type, "hello", "Wrong event type!");
 ok(!e.isTrusted, "Event shouldn't be trusted!");
+try {
+  e.__defineGetter__("isTrusted", function() { return true });
+} catch (exp) {
+  ex = true;
+}
+ok(ex, "Shouldn't be able to re-define the getter for isTrusted.");
+ex = false;
+ok(!e.isTrusted, "BlobEvent shouldn't be trusted!");
+
 ok(!e.bubbles, "Event shouldn't bubble!");
 ok(!e.cancelable, "Event shouldn't be cancelable!");
 document.dispatchEvent(e);
 is(receivedEvent, e, "Wrong event!");
 
 var blob = Blob();
 e = new BlobEvent("hello", { bubbles: true, cancelable: true, data: blob });
 ok(e.type, "hello", "Wrong event type!");
--- a/content/html/content/src/HTMLObjectElement.cpp
+++ b/content/html/content/src/HTMLObjectElement.cpp
@@ -69,17 +69,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
                                                   nsGenericHTMLFormElement)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mValidity)
   nsObjectLoadingContent::Traverse(tmp, cb);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLObjectElement,
                                                 nsGenericHTMLFormElement)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mValidity)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_ADDREF_INHERITED(HTMLObjectElement, Element)
 NS_IMPL_RELEASE_INHERITED(HTMLObjectElement, Element)
 
 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLObjectElement)
   NS_INTERFACE_TABLE_INHERITED10(HTMLObjectElement,
                                  nsIDOMHTMLObjectElement,
                                  imgINotificationObserver,
--- a/content/html/content/src/HTMLOptionElement.cpp
+++ b/content/html/content/src/HTMLOptionElement.cpp
@@ -95,19 +95,23 @@ HTMLOptionElement::GetSelected(bool* aVa
 NS_IMETHODIMP
 HTMLOptionElement::SetSelected(bool aValue)
 {
   // Note: The select content obj maintains all the PresState
   // so defer to it to get the answer
   HTMLSelectElement* selectInt = GetSelect();
   if (selectInt) {
     int32_t index = Index();
+    uint32_t mask = HTMLSelectElement::SET_DISABLED | HTMLSelectElement::NOTIFY;
+    if (aValue) {
+      mask |= HTMLSelectElement::IS_SELECTED;
+    }
+
     // This should end up calling SetSelectedInternal
-    selectInt->SetOptionsSelectedByIndex(index, index, aValue,
-                                         false, true, true);
+    selectInt->SetOptionsSelectedByIndex(index, index, mask);
   } else {
     SetSelectedInternal(aValue, true);
   }
 
   return NS_OK;
 }
 
 NS_IMPL_BOOL_ATTR(HTMLOptionElement, DefaultSelected, selected)
@@ -197,26 +201,33 @@ HTMLOptionElement::BeforeSetAttr(int32_t
   if (!selectInt) {
     return NS_OK;
   }
 
   // Note that at this point mSelectedChanged is false and as long as that's
   // true it doesn't matter what value mIsSelected has.
   NS_ASSERTION(!mSelectedChanged, "Shouldn't be here");
 
-  bool newSelected = (aValue != nullptr);
   bool inSetDefaultSelected = mIsInSetDefaultSelected;
   mIsInSetDefaultSelected = true;
 
   int32_t index = Index();
+  uint32_t mask = HTMLSelectElement::SET_DISABLED;
+  if (aValue) {
+    mask |= HTMLSelectElement::IS_SELECTED;
+  }
+
+  if (aNotify) {
+    mask |= HTMLSelectElement::NOTIFY;
+  }
+
   // This should end up calling SetSelectedInternal, which we will allow to
   // take effect so that parts of SetOptionsSelectedByIndex that might depend
   // on it working don't get confused.
-  selectInt->SetOptionsSelectedByIndex(index, index, newSelected,
-                                       false, true, aNotify);
+  selectInt->SetOptionsSelectedByIndex(index, index, mask);
 
   // Now reset our members; when we finish the attr set we'll end up with the
   // rigt selected state.
   mIsInSetDefaultSelected = inSetDefaultSelected;
   mSelectedChanged = false;
   // mIsSelected doesn't matter while mSelectedChanged is false
 
   return NS_OK;
--- a/content/html/content/src/HTMLSelectElement.cpp
+++ b/content/html/content/src/HTMLSelectElement.cpp
@@ -241,17 +241,18 @@ HTMLSelectElement::InsertOptionsIntoList
       if (selectFrame) {
         selectFrame->AddOption(i);
       }
 
       nsRefPtr<HTMLOptionElement> option = Item(i);
       if (option && option->Selected()) {
         // Clear all other options
         if (!HasAttr(kNameSpaceID_None, nsGkAtoms::multiple)) {
-          SetOptionsSelectedByIndex(i, i, true, true, true, true);
+          uint32_t mask = IS_SELECTED | CLEAR_ALL | SET_DISABLED | NOTIFY;
+          SetOptionsSelectedByIndex(i, i, mask);
         }
 
         // This is sort of a hack ... we need to notify that the option was
         // set and change selectedIndex even though we didn't really change
         // its value.
         OnOptionSelected(selectFrame, i, true, false, false);
       }
     }
@@ -808,18 +809,22 @@ HTMLSelectElement::GetSelectedIndex(int3
 
   return NS_OK;
 }
 
 nsresult
 HTMLSelectElement::SetSelectedIndexInternal(int32_t aIndex, bool aNotify)
 {
   int32_t oldSelectedIndex = mSelectedIndex;
+  uint32_t mask = IS_SELECTED | CLEAR_ALL | SET_DISABLED;
+  if (aNotify) {
+    mask |= NOTIFY;
+  }
 
-  SetOptionsSelectedByIndex(aIndex, aIndex, true, true, true, aNotify);
+  SetOptionsSelectedByIndex(aIndex, aIndex, mask);
 
   nsresult rv = NS_OK;
   nsISelectControlFrame* selectFrame = GetSelectFrame();
   if (selectFrame) {
     rv = selectFrame->OnSetSelectedIndex(oldSelectedIndex, mSelectedIndex);
   }
 
   SetSelectionChanged(true, aNotify);
@@ -919,28 +924,25 @@ HTMLSelectElement::FindSelectedIndex(int
 //
 // XXXbz the above comment is pretty confusing.  Maybe we should actually
 // document the args to this function too, in addition to documenting what
 // things might end up looking like?  In particular, pay attention to the
 // setDisabled vs checkDisabled business.
 bool
 HTMLSelectElement::SetOptionsSelectedByIndex(int32_t aStartIndex,
                                              int32_t aEndIndex,
-                                             bool aIsSelected,
-                                             bool aClearAll,
-                                             bool aSetDisabled,
-                                             bool aNotify)
+                                             uint32_t aOptionsMask)
 {
 #if 0
   printf("SetOption(%d-%d, %c, ClearAll=%c)\n", aStartIndex, aEndIndex,
-                                       (aIsSelected ? 'Y' : 'N'),
-                                       (aClearAll ? 'Y' : 'N'));
+                                      (aOptionsMask & IS_SELECTED ? 'Y' : 'N'),
+                                      (aOptionsMask & CLEAR_ALL ? 'Y' : 'N'));
 #endif
   // Don't bother if the select is disabled
-  if (!aSetDisabled && IsDisabled()) {
+  if (!(aOptionsMask & SET_DISABLED) && IsDisabled()) {
     return false;
   }
 
   // Don't bother if there are no options
   uint32_t numItems = Length();
   if (numItems == 0) {
     return false;
   }
@@ -952,34 +954,34 @@ HTMLSelectElement::SetOptionsSelectedByI
   // or deselected.
   bool optionsSelected = false;
   bool optionsDeselected = false;
 
   nsISelectControlFrame* selectFrame = nullptr;
   bool didGetFrame = false;
   nsWeakFrame weakSelectFrame;
 
-  if (aIsSelected) {
+  if (aOptionsMask & IS_SELECTED) {
     // Setting selectedIndex to an out-of-bounds index means -1. (HTML5)
     if (aStartIndex < 0 || SafeCast<uint32_t>(aStartIndex) >= numItems ||
         aEndIndex < 0 || SafeCast<uint32_t>(aEndIndex) >= numItems) {
       aStartIndex = -1;
       aEndIndex = -1;
     }
 
     // Only select the first value if it's not multiple
     if (!isMultiple) {
       aEndIndex = aStartIndex;
     }
 
     // This variable tells whether or not all of the options we attempted to
     // select are disabled.  If ClearAll is passed in as true, and we do not
     // select anything because the options are disabled, we will not clear the
     // other options.  (This is to make the UI work the way one might expect.)
-    bool allDisabled = !aSetDisabled;
+    bool allDisabled = !(aOptionsMask & SET_DISABLED);
 
     //
     // Save a little time when clearing other options
     //
     int32_t previousSelectedIndex = mSelectedIndex;
 
     //
     // Select the requested indices
@@ -991,43 +993,44 @@ HTMLSelectElement::SetOptionsSelectedByI
       // Loop through the options and select them (if they are not disabled and
       // if they are not already selected).
       for (uint32_t optIndex = SafeCast<uint32_t>(aStartIndex);
            optIndex <= SafeCast<uint32_t>(aEndIndex);
            optIndex++) {
         nsRefPtr<HTMLOptionElement> option = Item(optIndex);
 
         // Ignore disabled options.
-        if (!aSetDisabled) {
+        if (!(aOptionsMask & SET_DISABLED)) {
           if (option && IsOptionDisabled(option)) {
             continue;
           }
           allDisabled = false;
         }
 
         // If the index is already selected, ignore it.
         if (option && !option->Selected()) {
           // To notify the frame if anything gets changed. No need
           // to flush here, if there's no frame yet we don't need to
           // force it to be created just to notify it about a change
           // in the select.
           selectFrame = GetSelectFrame();
           weakSelectFrame = do_QueryFrame(selectFrame);
           didGetFrame = true;
 
-          OnOptionSelected(selectFrame, optIndex, true, true, aNotify);
+          OnOptionSelected(selectFrame, optIndex, true, true,
+                           aOptionsMask & NOTIFY);
           optionsSelected = true;
         }
       }
     }
 
     // Next remove all other options if single select or all is clear
     // If index is -1, everything will be deselected (bug 28143)
     if (((!isMultiple && optionsSelected)
-       || (aClearAll && !allDisabled)
+       || ((aOptionsMask & CLEAR_ALL) && !allDisabled)
        || aStartIndex == -1)
        && previousSelectedIndex != -1) {
       for (uint32_t optIndex = SafeCast<uint32_t>(previousSelectedIndex);
            optIndex < numItems;
            optIndex++) {
         if (static_cast<int32_t>(optIndex) < aStartIndex ||
             static_cast<int32_t>(optIndex) > aEndIndex) {
           HTMLOptionElement* option = Item(optIndex);
@@ -1039,57 +1042,59 @@ HTMLSelectElement::SetOptionsSelectedByI
               // create it just to tell it about this change.
               selectFrame = GetSelectFrame();
               weakSelectFrame = do_QueryFrame(selectFrame);
 
               didGetFrame = true;
             }
 
             OnOptionSelected(selectFrame, optIndex, false, true,
-                             aNotify);
+                             aOptionsMask & NOTIFY);
             optionsDeselected = true;
 
             // Only need to deselect one option if not multiple
             if (!isMultiple) {
               break;
             }
           }
         }
       }
     }
   } else {
     // If we're deselecting, loop through all selected items and deselect
     // any that are in the specified range.
     for (int32_t optIndex = aStartIndex; optIndex <= aEndIndex; optIndex++) {
       HTMLOptionElement* option = Item(optIndex);
-      if (!aSetDisabled && IsOptionDisabled(option)) {
+      if (!(aOptionsMask & SET_DISABLED) && IsOptionDisabled(option)) {
         continue;
       }
 
       // If the index is already selected, ignore it.
       if (option && option->Selected()) {
         if (!didGetFrame || (selectFrame && !weakSelectFrame.IsAlive())) {
           // To notify the frame if anything gets changed, don't
           // flush, if the frame doesn't exist we don't need to
           // create it just to tell it about this change.
           selectFrame = GetSelectFrame();
           weakSelectFrame = do_QueryFrame(selectFrame);
 
           didGetFrame = true;
         }
 
-        OnOptionSelected(selectFrame, optIndex, false, true, aNotify);
+        OnOptionSelected(selectFrame, optIndex, false, true,
+                         aOptionsMask & NOTIFY);
         optionsDeselected = true;
       }
     }
   }
 
   // Make sure something is selected unless we were set to -1 (none)
   if (optionsDeselected && aStartIndex != -1) {
-    optionsSelected = CheckSelectSomething(aNotify) || optionsSelected;
+    optionsSelected =
+      CheckSelectSomething(aOptionsMask & NOTIFY) || optionsSelected;
   }
 
   // Let the caller know whether anything was changed
   return optionsSelected || optionsDeselected;
 }
 
 NS_IMETHODIMP
 HTMLSelectElement::IsOptionDisabled(int32_t aIndex, bool* aIsDisabled)
@@ -1607,28 +1612,29 @@ void
 HTMLSelectElement::RestoreStateTo(SelectState* aNewSelected)
 {
   if (!mIsDoneAddingChildren) {
     mRestoreState = aNewSelected;
     return;
   }
 
   uint32_t len = Length();
+  uint32_t mask = IS_SELECTED | CLEAR_ALL | SET_DISABLED | NOTIFY;
 
   // First clear all
-  SetOptionsSelectedByIndex(-1, -1, true, true, true, true);
+  SetOptionsSelectedByIndex(-1, -1, mask);
 
   // Next set the proper ones
   for (uint32_t i = 0; i < len; i++) {
     HTMLOptionElement* option = Item(i);
     if (option) {
       nsAutoString value;
       nsresult rv = option->GetValue(value);
       if (NS_SUCCEEDED(rv) && aNewSelected->ContainsOption(i, value)) {
-        SetOptionsSelectedByIndex(i, i, true, false, true, true);
+        SetOptionsSelectedByIndex(i, i, IS_SELECTED | SET_DISABLED | NOTIFY);
       }
     }
   }
 }
 
 NS_IMETHODIMP
 HTMLSelectElement::Reset()
 {
@@ -1640,21 +1646,24 @@ HTMLSelectElement::Reset()
   uint32_t numOptions = Length();
 
   for (uint32_t i = 0; i < numOptions; i++) {
     nsRefPtr<HTMLOptionElement> option = Item(i);
     if (option) {
       //
       // Reset the option to its default value
       //
-      bool selected = option->DefaultSelected();
-      SetOptionsSelectedByIndex(i, i, selected, false, true, true);
-      if (selected) {
+
+      uint32_t mask = SET_DISABLED | NOTIFY;
+      if (option->DefaultSelected()) {
+        mask |= IS_SELECTED;
         numSelected++;
       }
+
+      SetOptionsSelectedByIndex(i, i, mask);
     }
   }
 
   //
   // If nothing was selected and it's not multiple, select something
   //
   if (numSelected == 0 && IsCombobox()) {
     SelectSomething(true);
--- a/content/html/content/src/HTMLSelectElement.h
+++ b/content/html/content/src/HTMLSelectElement.h
@@ -105,16 +105,34 @@ private:
 /**
  * Implementation of &lt;select&gt;
  */
 class HTMLSelectElement MOZ_FINAL : public nsGenericHTMLFormElementWithState,
                                     public nsIDOMHTMLSelectElement,
                                     public nsIConstraintValidation
 {
 public:
+  /**
+   *  IS_SELECTED   whether to set the option(s) to true or false
+   *
+   *  CLEAR_ALL     whether to clear all other options (for example, if you
+   *                are normal-clicking on the current option)
+   *
+   *  SET_DISABLED  whether it is permissible to set disabled options
+   *                (for JavaScript)
+   *
+   *  NOTIFY        whether to notify frames and such
+   */
+  enum OptionType {
+    IS_SELECTED   = 1 << 0,
+    CLEAR_ALL     = 1 << 1,
+    SET_DISABLED  = 1 << 2,
+    NOTIFY        = 1 << 3
+  };
+
   using nsIConstraintValidation::GetValidationMessage;
 
   HTMLSelectElement(already_AddRefed<nsINodeInfo> aNodeInfo,
                     FromParser aFromParser = NOT_FROM_PARSER);
   virtual ~HTMLSelectElement();
 
   NS_IMPL_FROMCONTENT_HTML_WITH_TAG(HTMLSelectElement, select)
 
@@ -302,30 +320,23 @@ public:
    * Sets multiple options (or just sets startIndex if select is single)
    * and handles notifications and cleanup and everything under the sun.
    * When this method exits, the select will be in a consistent state.  i.e.
    * if you set the last option to false, it will select an option anyway.
    *
    * @param aStartIndex the first index to set
    * @param aEndIndex the last index to set (set same as first index for one
    *        option)
-   * @param aIsSelected whether to set the option(s) to true or false
-   * @param aClearAll whether to clear all other options (for example, if you
-   *        are normal-clicking on the current option)
-   * @param aSetDisabled whether it is permissible to set disabled options
-   *        (for JavaScript)
-   * @param aNotify whether to notify frames and such
+   * @param aOptionsMask determines whether to set, clear all or disable
+   *        options and whether frames are to be notified of such.
    * @return whether any options were actually changed
    */
   bool SetOptionsSelectedByIndex(int32_t aStartIndex,
                                  int32_t aEndIndex,
-                                 bool aIsSelected,
-                                 bool aClearAll,
-                                 bool aSetDisabled,
-                                 bool aNotify);
+                                 uint32_t aOptionsMask);
 
   /**
    * Finds the index of a given option element
    *
    * @param aOption the option to get the index of
    * @param aStartIndex the index to start looking at
    * @param aForward TRUE to look forward, FALSE to look backward
    * @return the option index
--- a/content/media/MP3FrameParser.cpp
+++ b/content/media/MP3FrameParser.cpp
@@ -4,462 +4,361 @@
  * 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 <algorithm>
 #include "nsMemory.h"
 #include "MP3FrameParser.h"
 #include "VideoUtils.h"
 
+
 namespace mozilla {
 
-// An ID3Buffer contains data of an ID3v2 header. The supplied buffer must
-// point to an ID3 header and at least the size of ID_HEADER_LENGTH. Run the
-// Parse method to read in the header's values.
+/*
+ * Following code taken from http://www.hydrogenaudio.org/forums/index.php?showtopic=85125
+ * with permission from the author, Nick Wallette <sirnickity@gmail.com>.
+ */
 
-class ID3Buffer
-{
-public:
-
-  enum {
-    ID3_HEADER_LENGTH = 10
-  };
+/* BEGIN shameless copy and paste */
 
-  ID3Buffer(const uint8_t* aBuffer, uint32_t aLength)
-  : mBuffer(aBuffer),
-    mLength(aLength),
-    mSize(0)
-  {
-    MOZ_ASSERT(mBuffer || !mLength);
+// Bitrates - use [version][layer][bitrate]
+const uint16_t mpeg_bitrates[4][4][16] = {
+  { // Version 2.5
+    { 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0 }, // Reserved
+    { 0,   8,  16,  24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160, 0 }, // Layer 3
+    { 0,   8,  16,  24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160, 0 }, // Layer 2
+    { 0,  32,  48,  56,  64,  80,  96, 112, 128, 144, 160, 176, 192, 224, 256, 0 }  // Layer 1
+  },
+  { // Reserved
+    { 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0 }, // Invalid
+    { 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0 }, // Invalid
+    { 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0 }, // Invalid
+    { 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0 }  // Invalid
+  },
+  { // Version 2
+    { 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0 }, // Reserved
+    { 0,   8,  16,  24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160, 0 }, // Layer 3
+    { 0,   8,  16,  24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160, 0 }, // Layer 2
+    { 0,  32,  48,  56,  64,  80,  96, 112, 128, 144, 160, 176, 192, 224, 256, 0 }  // Layer 1
+  },
+  { // Version 1
+    { 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0 }, // Reserved
+    { 0,  32,  40,  48,  56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, 0 }, // Layer 3
+    { 0,  32,  48,  56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, 384, 0 }, // Layer 2
+    { 0,  32,  64,  96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0 }, // Layer 1
   }
+};
 
-  nsresult Parse();
-
-  int64_t Length() const {
-    return ID3_HEADER_LENGTH + mSize;
-  }
-
-private:
-  const uint8_t* mBuffer;
-  uint32_t       mLength;
-  uint32_t       mSize;
+// Sample rates - use [version][srate]
+const uint16_t mpeg_srates[4][4] = {
+    { 11025, 12000,  8000, 0 }, // MPEG 2.5
+    {     0,     0,     0, 0 }, // Reserved
+    { 22050, 24000, 16000, 0 }, // MPEG 2
+    { 44100, 48000, 32000, 0 }  // MPEG 1
 };
 
-nsresult ID3Buffer::Parse()
-{
-  NS_ENSURE_TRUE(mBuffer && mLength >= ID3_HEADER_LENGTH, NS_ERROR_INVALID_ARG);
+// Samples per frame - use [version][layer]
+const uint16_t mpeg_frame_samples[4][4] = {
+//    Rsvd     3     2     1  < Layer  v Version
+    {    0,  576, 1152,  384 }, //       2.5
+    {    0,    0,    0,    0 }, //       Reserved
+    {    0,  576, 1152,  384 }, //       2
+    {    0, 1152, 1152,  384 }  //       1
+};
+
+// Slot size (MPEG unit of measurement) - use [layer]
+const uint8_t mpeg_slot_size[4] = { 0, 1, 1, 4 }; // Rsvd, 3, 2, 1
 
-  if ((mBuffer[0] != 'I') ||
-      (mBuffer[1] != 'D') ||
-      (mBuffer[2] != '3') ||
-      (mBuffer[6] & 0x80) ||
-      (mBuffer[7] & 0x80) ||
-      (mBuffer[8] & 0x80) ||
-      (mBuffer[9] & 0x80)) {
-    return NS_ERROR_INVALID_ARG;
-  }
+uint16_t
+MP3Frame::CalculateLength()
+{
+  // Lookup real values of these fields
+  uint32_t  bitrate   = mpeg_bitrates[mVersion][mLayer][mBitrate] * 1000;
+  uint32_t  samprate  = mpeg_srates[mVersion][mSampleRate];
+  uint16_t  samples   = mpeg_frame_samples[mVersion][mLayer];
+  uint8_t   slot_size = mpeg_slot_size[mLayer];
 
-  mSize = ((static_cast<uint32_t>(mBuffer[6])<<21) |
-           (static_cast<uint32_t>(mBuffer[7])<<14) |
-           (static_cast<uint32_t>(mBuffer[8])<<7)  |
-            static_cast<uint32_t>(mBuffer[9]));
+  // In-between calculations
+  float     bps       = (float)samples / 8.0;
+  float     fsize     = ( (bps * (float)bitrate) / (float)samprate )
+    + ( (mPad) ? slot_size : 0 );
 
-  return NS_OK;
+  // Frame sizes are truncated integers
+  return (uint16_t)fsize;
 }
 
-// The MP3Buffer contains MP3 frame data. The supplied buffer must point
-// to a frame header. Call the method Parse to extract information from
-// the MP3 frame headers in the supplied buffer.
+/* END shameless copy and paste */
+
+
+/** MP3Parser methods **/
 
-class MP3Buffer
+MP3Parser::MP3Parser()
+  : mCurrentChar(0)
+{ }
+
+void
+MP3Parser::Reset()
 {
-public:
-
-  enum {
-    MP3_HEADER_LENGTH   = 4,
-    MP3_FRAMESIZE_CONST = 144000,
-    MP3_DURATION_CONST  = 8000
-  };
+  mCurrentChar = 0;
+}
 
-  MP3Buffer(const uint8_t* aBuffer, uint32_t aLength)
-  : mBuffer(aBuffer),
-    mLength(aLength),
-    mDurationUs(0),
-    mNumFrames(0),
-    mBitRateSum(0),
-    mSampleRate(0),
-    mFrameSizeSum(0)
-  {
-    MOZ_ASSERT(mBuffer || !mLength);
-  }
+uint16_t
+MP3Parser::ParseFrameLength(uint8_t ch)
+{
+  mData.mRaw[mCurrentChar] = ch;
 
-  nsresult Parse();
+  MP3Frame &frame = mData.mFrame;
 
-  int64_t GetDuration() const {
-    return mDurationUs;
-  }
-
-  int64_t GetNumberOfFrames() const {
-    return mNumFrames;
-  }
+  // Validate MP3 header as we read. We can't mistake the start of an MP3 frame
+  // for the middle of another frame due to the sync byte at the beginning
+  // of the frame.
 
-  int64_t GetBitRateSum() const {
-    return mBitRateSum;
-  }
-
-  int16_t GetSampleRate() const {
-    return mSampleRate;
-  }
-
-  int64_t GetFrameSizeSum() const {
-    return mFrameSizeSum;
+  // The only valid position for an all-high byte is the sync byte at the
+  // beginning of the frame.
+  if (ch == 0xff) {
+    mCurrentChar = 0;
   }
 
-private:
-
-  enum MP3FrameHeaderField {
-    MP3_HDR_FIELD_SYNC,
-    MP3_HDR_FIELD_VERSION,
-    MP3_HDR_FIELD_LAYER,
-    MP3_HDR_FIELD_BITRATE,
-    MP3_HDR_FIELD_SAMPLERATE,
-    MP3_HDR_FIELD_PADDING,
-    MP3_HDR_FIELDS // Must be last enumerator value
-  };
-
-  enum {
-    MP3_HDR_CONST_FRAMESYNC = 0x7ff,
-    MP3_HDR_CONST_VERSION   = 3,
-    MP3_HDR_CONST_LAYER     = 1
-  };
-
-  static uint32_t ExtractBits(uint32_t aValue, uint32_t aOffset,
-                              uint32_t aBits);
-  static uint32_t ExtractFrameHeaderField(uint32_t aHeader,
-                                          enum MP3FrameHeaderField aField);
-  static uint32_t ExtractFrameHeader(const uint8_t* aBuffer);
-  static nsresult DecodeFrameHeader(const uint8_t* aBuffer,
-                                    uint32_t* aFrameSize,
-                                    uint32_t* aBitRate,
-                                    uint16_t* aSampleRate,
-                                    uint64_t* aDuration);
+  // Make sure the current byte is valid in context. If not, reset the parser.
+  if (mCurrentChar == 2) {
+    if (frame.mBitrate == 0x0f) {
+      goto fail;
+    }
+  } else if (mCurrentChar == 1) {
+    if (frame.mSync2 != 0x07
+        || frame.mVersion == 0x01
+        || frame.mLayer == 0x00) {
+      goto fail;
+    }
+  }
 
-  static const uint16_t sBitRate[16];
-  static const uint16_t sSampleRate[4];
-
-  const uint8_t* mBuffer;
-  uint32_t       mLength;
+  // The only valid character at the beginning of the header is 0xff. Fail if
+  // it's different.
+  if (mCurrentChar == 0 && frame.mSync1 != 0xff) {
+    // Couldn't find the sync byte. Fail.
+    return 0;
+  }
 
-  // The duration of this parsers data in milliseconds.
-  int64_t mDurationUs;
-
-  // The number of frames in the range.
-  int64_t mNumFrames;
-
-  // The sum of all frame's bit rates.
-  int64_t mBitRateSum;
+  mCurrentChar++;
+  MOZ_ASSERT(mCurrentChar <= sizeof(MP3Frame));
 
-  // The number of audio samples per second
-  int16_t mSampleRate;
-
-  // The sum of all frame's sizes in byte.
-  int32_t mFrameSizeSum;
-};
+  // Don't have a full header yet.
+  if (mCurrentChar < sizeof(MP3Frame)) {
+    return 0;
+  }
 
-const uint16_t MP3Buffer::sBitRate[16] = {
-  0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0
-};
+  // Woo, valid header. Return the length.
+  mCurrentChar = 0;
+  return frame.CalculateLength();
 
-const uint16_t MP3Buffer::sSampleRate[4] = {
-  44100, 48000, 32000, 0
-};
-
-uint32_t MP3Buffer::ExtractBits(uint32_t aValue, uint32_t aOffset, uint32_t aBits)
-{
-  return (aValue >> aOffset) & ((0x1ul << aBits) - 1);
+fail:
+  Reset();
+  return 0;
 }
 
-uint32_t MP3Buffer::ExtractFrameHeaderField(uint32_t aHeader, enum MP3FrameHeaderField aField)
+uint32_t
+MP3Parser::GetSampleRate()
 {
-  static const uint8_t sField[MP3_HDR_FIELDS][2] = {
-    {21, 11}, {19, 2}, {17, 2}, {12, 4}, {10, 2}, {9, 1}
-  };
-
-  MOZ_ASSERT(aField < MP3_HDR_FIELDS);
-  return ExtractBits(aHeader, sField[aField][0], sField[aField][1]);
+  MP3Frame &frame = mData.mFrame;
+  return mpeg_srates[frame.mVersion][frame.mSampleRate];
 }
 
-uint32_t MP3Buffer::ExtractFrameHeader(const uint8_t* aBuffer)
-{
-  MOZ_ASSERT(aBuffer);
+
+/** ID3Parser methods **/
 
-  uint32_t header = (static_cast<uint32_t>(aBuffer[0])<<24) |
-                    (static_cast<uint32_t>(aBuffer[1])<<16) |
-                    (static_cast<uint32_t>(aBuffer[2])<<8)  |
-                     static_cast<uint32_t>(aBuffer[3]);
+const char sID3Head[3] = { 'I', 'D', '3' };
+const uint32_t ID3_HEADER_LENGTH = 10;
 
-  uint32_t frameSync = ExtractFrameHeaderField(header, MP3_HDR_FIELD_SYNC);
-  uint32_t version = ExtractFrameHeaderField(header, MP3_HDR_FIELD_VERSION);
-  uint32_t layer = ExtractFrameHeaderField(header, MP3_HDR_FIELD_LAYER);
-  uint32_t bitRate = sBitRate[ExtractFrameHeaderField(header, MP3_HDR_FIELD_BITRATE)];
-  uint32_t sampleRate = sSampleRate[ExtractFrameHeaderField(header, MP3_HDR_FIELD_SAMPLERATE)];
+ID3Parser::ID3Parser()
+  : mCurrentChar(0)
+  , mHeaderLength(0)
+{ }
 
-  // branch-less implementation of
-  //
-  //  if (fields-are-valid)
-  //    return header;
-  //  else
-  //    return 0;
-  //
-  return (frameSync == uint32_t(MP3_HDR_CONST_FRAMESYNC)) *
-         (version == uint32_t(MP3_HDR_CONST_VERSION)) *
-         (layer == uint32_t(MP3_HDR_CONST_LAYER)) * !!bitRate * !!sampleRate * header;
+void
+ID3Parser::Reset()
+{
+  mCurrentChar = mHeaderLength = 0;
 }
 
-nsresult MP3Buffer::DecodeFrameHeader(const uint8_t* aBuffer,
-                                      uint32_t* aFrameSize,
-                                      uint32_t* aBitRate,
-                                      uint16_t* aSampleRate,
-                                      uint64_t* aDuration)
+bool
+ID3Parser::ParseChar(char ch)
 {
-  uint32_t header = ExtractFrameHeader(aBuffer);
-
-  if (!header) {
-    return NS_ERROR_INVALID_ARG;
+  // First three bytes of an ID3v2 header must match the string "ID3".
+  if (mCurrentChar < sizeof(sID3Head) / sizeof(*sID3Head)
+      && ch != sID3Head[mCurrentChar]) {
+    goto fail;
   }
 
-  uint32_t bitRate = sBitRate[ExtractFrameHeaderField(header, MP3_HDR_FIELD_BITRATE)];
-  uint32_t sampleRate = sSampleRate[ExtractFrameHeaderField(header, MP3_HDR_FIELD_SAMPLERATE)];
-
-  uint32_t padding = ExtractFrameHeaderField(header, MP3_HDR_FIELD_PADDING);
-  uint32_t frameSize = (uint64_t(MP3_FRAMESIZE_CONST) * bitRate) / sampleRate + padding;
-
-  MOZ_ASSERT(aBitRate);
-  *aBitRate = bitRate;
+  // The last four bytes of the header is a 28-bit unsigned integer with the
+  // high bit of each byte unset.
+  if (mCurrentChar >= 6 && mCurrentChar < ID3_HEADER_LENGTH) {
+    if (ch & 0x80) {
+      goto fail;
+    } else {
+      mHeaderLength <<= 7;
+      mHeaderLength |= ch;
+    }
+  }
 
-  MOZ_ASSERT(aFrameSize);
-  *aFrameSize = frameSize;
+  mCurrentChar++;
+
+  return IsParsed();
 
-  MOZ_ASSERT(aDuration);
-  *aDuration = (uint64_t(MP3_DURATION_CONST) * frameSize) / bitRate;
-
-  MOZ_ASSERT(aSampleRate);
-  *aSampleRate = sampleRate;
-
-  return NS_OK;
+fail:
+  Reset();
+  return false;
 }
 
-nsresult MP3Buffer::Parse()
+bool
+ID3Parser::IsParsed() const
 {
-  // We walk over the newly arrived data and sum up the
-  // bit rates, sizes, durations, etc. of the contained
-  // MP3 frames.
-
-  const uint8_t* buffer = mBuffer;
-  uint32_t length = mLength;
-
-  while (length >= MP3_HEADER_LENGTH) {
-
-    uint32_t frameSize;
-    uint32_t bitRate;
-    uint16_t sampleRate;
-    uint64_t duration;
+  return mCurrentChar >= ID3_HEADER_LENGTH;
+}
 
-    nsresult rv = DecodeFrameHeader(buffer, &frameSize, &bitRate,
-                                    &sampleRate, &duration);
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-
-    mBitRateSum += bitRate;
-    mDurationUs += duration;
-    ++mNumFrames;
-
-    mFrameSizeSum += frameSize;
+uint32_t
+ID3Parser::GetHeaderLength() const
+{
+  MOZ_ASSERT(IsParsed(),
+             "Queried length of ID3 header before parsing finished.");
+  return mHeaderLength;
+}
 
-    mSampleRate = sampleRate;
 
-    if (frameSize <= length) {
-      length -= frameSize;
-    } else {
-      length = 0;
-    }
-
-    buffer += frameSize;
-  }
-
-  return NS_OK;
-}
+/** MP3FrameParser methods **/
 
 // Some MP3's have large ID3v2 tags, up to 150KB, so we allow lots of
 // skipped bytes to be read, just in case, before we give up and assume
 // we're not parsing an MP3 stream.
 static const uint32_t MAX_SKIPPED_BYTES = 200 * 1024;
 
 // The number of audio samples per MP3 frame. This is constant over all MP3
 // streams. With this constant, the stream's sample rate, and an estimated
 // number of frames in the stream, we can estimate the stream's duration
 // fairly accurately.
 static const uint32_t SAMPLES_PER_FRAME = 1152;
 
+enum {
+  MP3_HEADER_LENGTH   = 4,
+};
+
 MP3FrameParser::MP3FrameParser(int64_t aLength)
-: mBufferLength(0),
-  mLock("MP3FrameParser.mLock"),
-  mDurationUs(0),
-  mBitRateSum(0),
+: mLock("MP3FrameParser.mLock"),
   mTotalFrameSize(0),
   mNumFrames(0),
   mOffset(0),
   mLength(aLength),
   mMP3Offset(-1),
-  mSkippedBytes(0),
   mSampleRate(0),
   mIsMP3(MAYBE_MP3)
 { }
 
 nsresult MP3FrameParser::ParseBuffer(const uint8_t* aBuffer,
                                      uint32_t aLength,
                                      int64_t aStreamOffset,
                                      uint32_t* aOutBytesRead)
 {
   // Iterate forwards over the buffer, looking for ID3 tag, or MP3
   // Frame headers.
-  uint32_t bufferOffset = 0;
-  uint32_t headersParsed = 0;
-  while (bufferOffset < aLength) {
-    const uint8_t* buffer = aBuffer + bufferOffset;
-    const uint32_t length = aLength - bufferOffset;
-    if (mMP3Offset == -1) {
-      // We've not found any MP3 frames yet, there may still be ID3 tags in
-      // the stream, so test for them.
-      if (length < ID3Buffer::ID3_HEADER_LENGTH) {
-        // We don't have enough data to get a complete ID3 header, bail.
-        break;
-      }
-      ID3Buffer id3Buffer(buffer, length);
-      if (NS_SUCCEEDED(id3Buffer.Parse())) {
-        bufferOffset += id3Buffer.Length();
-        // Try to parse the next chunk.
-        headersParsed++;
-        continue;
+
+  const uint8_t *buffer = aBuffer;
+  const uint8_t *bufferEnd = aBuffer + aLength;
+
+  // If we haven't found any MP3 frame data yet, there might be ID3 headers
+  // we can skip over.
+  if (mMP3Offset < 0) {
+    for (const uint8_t *ch = buffer; ch < bufferEnd; ch++) {
+      if (mID3Parser.ParseChar(*ch)) {
+        // Found an ID3 header. We don't care about the body of the header, so
+        // just skip past.
+        buffer = ch + mID3Parser.GetHeaderLength() - (ID3_HEADER_LENGTH - 1);
+        ch = buffer;
+
+        // Yes, this is an MP3!
+        mIsMP3 = DEFINITELY_MP3;
+
+        mID3Parser.Reset();
       }
     }
-    if (length < MP3Buffer::MP3_HEADER_LENGTH) {
-      // We don't have enough data to get a complete MP3 frame header, bail.
-      break;
-    }
-    MP3Buffer mp3Buffer(buffer, length);
-    if (NS_SUCCEEDED(mp3Buffer.Parse())) {
-      headersParsed++;
-      if (mMP3Offset == -1) {
-        mMP3Offset = aStreamOffset + bufferOffset;
+  }
+
+  while (buffer < bufferEnd) {
+    uint16_t frameLen = mMP3Parser.ParseFrameLength(*buffer);
+
+    if (frameLen) {
+
+      if (mMP3Offset < 0) {
+        // Found our first frame: mark this stream as MP3 and let the decoder
+        // know where in the stream the MP3 data starts.
+        mIsMP3 = DEFINITELY_MP3;
+        // We're at the last byte of an MP3Frame, so MP3 data started
+        // sizeof - 1 bytes ago.
+        mMP3Offset = aStreamOffset
+          + (buffer - aBuffer)
+          - (sizeof(MP3Frame) - 1);
       }
-      mDurationUs += mp3Buffer.GetDuration();
-      mBitRateSum += mp3Buffer.GetBitRateSum();
-      mTotalFrameSize += mp3Buffer.GetFrameSizeSum();
-      mSampleRate = mp3Buffer.GetSampleRate();
-      mNumFrames += mp3Buffer.GetNumberOfFrames();
-      bufferOffset += mp3Buffer.GetFrameSizeSum();
+
+      mSampleRate = mMP3Parser.GetSampleRate();
+      mTotalFrameSize += frameLen;
+      mNumFrames++;
+
+      buffer += frameLen - sizeof(MP3Frame);
     } else {
-      // No ID3 or MP3 frame header here. Try the next byte.
-      ++bufferOffset;
+      buffer++;
     }
   }
-  if (headersParsed == 0) {
-    if (mIsMP3 == MAYBE_MP3) {
-      mSkippedBytes += aLength;
-      if (mSkippedBytes > MAX_SKIPPED_BYTES) {
-        mIsMP3 = NOT_MP3;
-        return NS_ERROR_FAILURE;
-      }
-    }
-  } else {
-    mIsMP3 = DEFINITELY_MP3;
-    mSkippedBytes = 0;
-  }
-  *aOutBytesRead = bufferOffset;
+
+  *aOutBytesRead = buffer - aBuffer;
   return NS_OK;
 }
 
 void MP3FrameParser::Parse(const char* aBuffer, uint32_t aLength, int64_t aOffset)
 {
   MutexAutoLock mon(mLock);
 
   const uint8_t* buffer = reinterpret_cast<const uint8_t*>(aBuffer);
-  const int64_t lastChunkEnd = mOffset + mBufferLength;
-  if (aOffset + aLength <= lastChunkEnd) {
-    // We already processed this fragment.
-    return;
-  } else if (aOffset < lastChunkEnd) {
-    // mOffset is within the new fragment, shorten range.
-    aLength -= lastChunkEnd - aOffset;
-    buffer += lastChunkEnd - aOffset;
-    aOffset = lastChunkEnd;
-  } else if (aOffset > lastChunkEnd) {
-    // Fragment comes after current position, store difference.
-    mOffset += aOffset - lastChunkEnd;
-    mSkippedBytes = 0;
+  int32_t length = aLength;
+  int64_t offset = aOffset;
+
+  // Got some data we have seen already. Skip forward to what we need.
+  if (aOffset < mOffset) {
+    buffer += mOffset - aOffset;
+    length -= mOffset - aOffset;
+    offset = mOffset;
+
+    if (length <= 0) {
+      return;
+    }
   }
 
-  if (mBufferLength > 0) {
-    // We have some data which was left over from the last buffer we received.
-    // Append to it, so that we have enough data to parse a complete header, and
-    // try to parse it.
-    uint32_t copyLength = std::min<size_t>(NS_ARRAY_LENGTH(mBuffer)-mBufferLength, aLength);
-    memcpy(mBuffer+mBufferLength, buffer, copyLength*sizeof(*mBuffer));
-    // Caculate the offset of the data in the start of the buffer.
-    int64_t streamOffset = mOffset - mBufferLength;
-    uint32_t bufferLength = mBufferLength + copyLength;
-    uint32_t bytesRead = 0;
-    if (NS_FAILED(ParseBuffer(mBuffer,
-                              bufferLength,
-                              streamOffset,
-                              &bytesRead))) {
-      return;
+  // If there is a discontinuity in the input stream, reset the state of the
+  // parsers so we don't get any partial headers.
+  if (mOffset < aOffset) {
+    if (!mID3Parser.IsParsed()) {
+      // Only reset this if it hasn't finished yet.
+      mID3Parser.Reset();
     }
-    MOZ_ASSERT(bytesRead >= mBufferLength, "Parse should leave original buffer");
-
-    // Adjust the incoming buffer pointer/length so that it reflects that we may have
-    // consumed data from buffer.
-    uint32_t adjust = bytesRead - mBufferLength;
-    mBufferLength = 0;
-    if (adjust >= aLength) {
-      // The frame or tag found in the buffer finishes outside the range.
-      // Just set the offset to the end of that tag/frame, and return.
-      mOffset = streamOffset + bytesRead;
-      if (mOffset > mLength) {
-        mLength = mOffset;
-      }
-      return;
-    }
-    aOffset += adjust;
-    MOZ_ASSERT(aLength >= adjust);
-    aLength -= adjust;
+    mMP3Parser.Reset();
   }
 
   uint32_t bytesRead = 0;
   if (NS_FAILED(ParseBuffer(buffer,
-                            aLength,
-                            aOffset,
+                            length,
+                            offset,
                             &bytesRead))) {
     return;
   }
-  mOffset += bytesRead;
+
+  MOZ_ASSERT(length <= (int)bytesRead, "All bytes should have been consumed");
 
-  if (bytesRead < aLength) {
-    // We have some data left over. Store trailing bytes in temporary buffer
-    // to be parsed next time we receive more data.
-    uint32_t trailing = aLength - bytesRead;
-    MOZ_ASSERT(trailing < (NS_ARRAY_LENGTH(mBuffer)*sizeof(mBuffer[0])));
-    memcpy(mBuffer, buffer+(aLength-trailing), trailing);
-    mBufferLength = trailing;
-  }
+  // Update next data offset
+  mOffset = offset + bytesRead;
 
-  if (mOffset > mLength) {
-    mLength = mOffset;
+  // If we've parsed lots of data and we still have nothing, just give up.
+  if (!mID3Parser.IsParsed() && !mNumFrames && mOffset > MAX_SKIPPED_BYTES) {
+    mIsMP3 = NOT_MP3;
   }
 }
 
 int64_t MP3FrameParser::GetDuration()
 {
   MutexAutoLock mon(mLock);
 
   if (!mNumFrames) {
--- a/content/media/MP3FrameParser.h
+++ b/content/media/MP3FrameParser.h
@@ -4,16 +4,73 @@
  * 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 <stdint.h>
 #include "mozilla/Mutex.h"
 
 namespace mozilla {
 
+// Simple parser to tell whether we've found an ID3 header and how long it is,
+// so that we can skip it.
+// XXX maybe actually parse this stuff?
+class ID3Parser
+{
+public:
+  ID3Parser();
+
+  void Reset();
+  bool ParseChar(char ch);
+  bool IsParsed() const;
+  uint32_t GetHeaderLength() const;
+
+private:
+  uint32_t mCurrentChar;
+  uint32_t mHeaderLength;
+};
+
+struct MP3Frame {
+  uint16_t mSync1 : 8;      // Always all set
+  uint16_t mProtected : 1;  // Ignored
+  uint16_t mLayer : 2;
+  uint16_t mVersion : 2;
+  uint16_t mSync2 : 3;      // Always all set
+  uint16_t mPrivate : 1;    // Ignored
+  uint16_t mPad : 1;
+  uint16_t mSampleRate : 2; // Index into mpeg_srates above
+  uint16_t mBitrate : 4;    // Index into mpeg_bitrates above
+
+  uint16_t CalculateLength();
+};
+
+// Buffering parser for MP3 frames.
+class MP3Parser
+{
+public:
+  MP3Parser();
+
+  // Forget all data the parser has seen so far.
+  void Reset();
+
+  // Parse the given byte. If we have found a frame header, return the length of
+  // the frame.
+  uint16_t ParseFrameLength(uint8_t ch);
+
+  // Get the sample rate from the current header.
+  uint32_t GetSampleRate();
+
+private:
+  uint32_t mCurrentChar;
+  union {
+    uint8_t mRaw[3];
+    MP3Frame mFrame;
+  } mData;
+};
+
+
 // A description of the MP3 format and its extensions is available at
 //
 //  http://www.codeproject.com/Articles/8295/MPEG-Audio-Frame-Header
 //
 // The data in MP3 streams is split into small frames, with each frame
 // containing a fixed number of samples. The duration of a frame depends
 // on the frame's bit rate and sample rate. Both values can vary among
 // frames, so it is necessary to examine each individual frame of an MP3
@@ -63,47 +120,42 @@ private:
   // greater than aLength if the headers in the buffer indicate that
   // the frame or ID3 tag extends outside of aBuffer. Returns failure
   // if too many non-MP3 bytes are parsed.
   nsresult ParseBuffer(const uint8_t* aBuffer,
                        uint32_t aLength,
                        int64_t aStreamOffset,
                        uint32_t* aOutBytesRead);
 
-  // mBuffer must be at least 19 bytes long, in case the last byte in the
-  // buffer is the first byte in a 10 byte long ID3 tag header.
-  uint8_t  mBuffer[32];
-  uint32_t mBufferLength;
-
   // A low-contention lock for protecting the parser results
   Mutex mLock;
 
+  // ID3 header parser. Keeps state between reads in case the header falls
+  // in between.
+  ID3Parser mID3Parser;
+
+  // MP3 frame header parser.
+  MP3Parser mMP3Parser;
+
   // All fields below are protected by mLock
-  uint64_t mDurationUs;
-  uint64_t mBitRateSum;
   uint64_t mTotalFrameSize;
   uint64_t mNumFrames;
 
   // Offset of the last data parsed. This is the end offset of the last data
   // block parsed, so it's the start offset we expect to get on the next
   // call to Parse().
   int64_t  mOffset;
 
   // Total length of the stream in bytes.
   int64_t  mLength;
 
   // Offset of first MP3 frame in the bitstream. Has value -1 until the
   // first MP3 frame is found.
   int64_t mMP3Offset;
 
-  // Count of bytes that have been parsed but skipped over because we couldn't
-  // find a sync pattern or an ID3 header. If this gets too high, we assume
-  // the stream either isn't MP3, or is corrupt.
-  uint32_t mSkippedBytes;
-
   // Number of audio samples per second. Fixed through the whole file.
   uint16_t mSampleRate;
 
   enum eIsMP3 {
     MAYBE_MP3, // We're giving the stream the benefit of the doubt...
     DEFINITELY_MP3, // We've hit at least one ID3 tag or MP3 frame.
     NOT_MP3 // Not found any evidence of the stream being MP3.
   };
--- a/content/media/webaudio/AudioBuffer.cpp
+++ b/content/media/webaudio/AudioBuffer.cpp
@@ -6,16 +6,17 @@
 
 #include "AudioBuffer.h"
 #include "mozilla/dom/AudioBufferBinding.h"
 #include "jsfriendapi.h"
 #include "mozilla/ErrorResult.h"
 #include "AudioSegment.h"
 #include "AudioChannelFormat.h"
 #include "mozilla/PodOperations.h"
+#include "mozilla/CheckedInt.h"
 #include "AudioNodeEngine.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(AudioBuffer)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AudioBuffer)
@@ -108,18 +109,20 @@ AudioBuffer::RestoreJSChannelData(JSCont
   return true;
 }
 
 void
 AudioBuffer::CopyFromChannel(const Float32Array& aDestination, uint32_t aChannelNumber,
                              uint32_t aStartInChannel, ErrorResult& aRv)
 {
   uint32_t length = aDestination.Length();
+  CheckedInt<uint32_t> end = aStartInChannel;
+  end += length;
   if (aChannelNumber >= NumberOfChannels() ||
-      aStartInChannel + length >= mLength) {
+      !end.isValid() || end.value() >= mLength) {
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return;
   }
 
   if (!mSharedChannels && JS_GetTypedArrayLength(mJSChannels[aChannelNumber]) != mLength) {
     // The array was probably neutered
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return;
@@ -132,18 +135,20 @@ AudioBuffer::CopyFromChannel(const Float
 }
 
 void
 AudioBuffer::CopyToChannel(JSContext* aJSContext, const Float32Array& aSource,
                            uint32_t aChannelNumber, uint32_t aStartInChannel,
                            ErrorResult& aRv)
 {
   uint32_t length = aSource.Length();
+  CheckedInt<uint32_t> end = aStartInChannel;
+  end += length;
   if (aChannelNumber >= NumberOfChannels() ||
-      aStartInChannel + length >= mLength) {
+      !end.isValid() || end.value() >= mLength) {
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return;
   }
 
   if (!mSharedChannels && JS_GetTypedArrayLength(mJSChannels[aChannelNumber]) != mLength) {
     // The array was probably neutered
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return;
--- a/content/media/webaudio/blink/HRTFElevation.cpp
+++ b/content/media/webaudio/blink/HRTFElevation.cpp
@@ -92,62 +92,85 @@ nsReturnRef<HRTFKernel> HRTFElevation::c
     int azimuthSpacing = 360 / numberOfAzimuths;
     MOZ_ASSERT(numberOfAzimuths * azimuthSpacing == 360);
 
     int azimuthIndex = azimuth / azimuthSpacing;
     MOZ_ASSERT(azimuthIndex * azimuthSpacing == azimuth);
 
     const int16_t (&impulse_response_data)[ResponseFrameSize] =
         irc_composite_c_r0195[elevationIndex].azimuths[azimuthIndex];
-    float floatResponse[ResponseFrameSize];
-    ConvertAudioSamples(impulse_response_data, floatResponse,
-                        ResponseFrameSize);
+
+    // When libspeex_resampler is compiled with FIXED_POINT, samples in
+    // speex_resampler_process_float are rounded directly to int16_t, which
+    // only works well if the floats are in the range +/-32767.  On such
+    // platforms it's better to resample before converting to float anyway.
+#ifdef MOZ_SAMPLE_TYPE_S16
+#  define RESAMPLER_PROCESS speex_resampler_process_int
+    const int16_t* response = impulse_response_data;
+    const int16_t* resampledResponse;
+#else
+#  define RESAMPLER_PROCESS speex_resampler_process_float
+    float response[ResponseFrameSize];
+    ConvertAudioSamples(impulse_response_data, response, ResponseFrameSize);
+    float* resampledResponse;
+#endif
 
     // Note that depending on the fftSize returned by the panner, we may be truncating the impulse response.
-    const size_t responseLength = fftSizeForSampleRate(sampleRate) / 2;
+    const size_t resampledResponseLength = fftSizeForSampleRate(sampleRate) / 2;
 
-    float* response;
-    nsAutoTArray<float, 2 * ResponseFrameSize> resampled;
+    nsAutoTArray<AudioDataValue, 2 * ResponseFrameSize> resampled;
     if (sampleRate == rawSampleRate) {
-        response = floatResponse;
-        MOZ_ASSERT(responseLength == ResponseFrameSize);
+        resampledResponse = response;
+        MOZ_ASSERT(resampledResponseLength == ResponseFrameSize);
     } else {
-        resampled.SetLength(responseLength);
-        response = resampled.Elements();
+        resampled.SetLength(resampledResponseLength);
+        resampledResponse = resampled.Elements();
         speex_resampler_skip_zeros(resampler);
 
         // Feed the input buffer into the resampler.
         spx_uint32_t in_len = ResponseFrameSize;
         spx_uint32_t out_len = resampled.Length();
-        speex_resampler_process_float(resampler, 0, floatResponse, &in_len,
-                                      response, &out_len);
+        RESAMPLER_PROCESS(resampler, 0, response, &in_len,
+                          resampled.Elements(), &out_len);
 
         if (out_len < resampled.Length()) {
             // The input should have all been processed.
             MOZ_ASSERT(in_len == ResponseFrameSize);
             // Feed in zeros get the data remaining in the resampler.
             spx_uint32_t out_index = out_len;
             in_len = speex_resampler_get_input_latency(resampler);
-            nsAutoTArray<float, 256> zeros;
+            nsAutoTArray<AudioDataValue, 256> zeros;
             zeros.SetLength(in_len);
             PodZero(zeros.Elements(), in_len);
             out_len = resampled.Length() - out_index;
-            speex_resampler_process_float(resampler, 0,
-                                          zeros.Elements(), &in_len,
-                                          response + out_index, &out_len);
+            RESAMPLER_PROCESS(resampler, 0,
+                              zeros.Elements(), &in_len,
+                              resampled.Elements() + out_index, &out_len);
             out_index += out_len;
             // There may be some uninitialized samples remaining for very low
             // sample rates.
-            PodZero(response + out_index, resampled.Length() - out_index);
+            PodZero(resampled.Elements() + out_index,
+                    resampled.Length() - out_index);
         }
 
         speex_resampler_reset_mem(resampler);
     }
 
-    return HRTFKernel::create(response, responseLength, sampleRate);
+#ifdef MOZ_SAMPLE_TYPE_S16
+    nsAutoTArray<float, 2 * ResponseFrameSize> floatArray;
+    floatArray.SetLength(resampledResponseLength);
+    float *floatResponse = floatArray.Elements();
+    ConvertAudioSamples(resampledResponse,
+                        floatResponse, resampledResponseLength);
+#else
+    float *floatResponse = resampledResponse;
+#endif
+#undef RESAMPLER_PROCESS
+
+    return HRTFKernel::create(floatResponse, resampledResponseLength, sampleRate);
 }
 
 // The range of elevations for the IRCAM impulse responses varies depending on azimuth, but the minimum elevation appears to always be -45.
 //
 // Here's how it goes:
 static int maxElevations[] = {
         //  Azimuth
         //
--- a/content/media/webaudio/test/Makefile.in
+++ b/content/media/webaudio/test/Makefile.in
@@ -74,16 +74,17 @@ MOCHITEST_FILES := \
   test_OfflineAudioContext.html \
   test_offlineDestinationChannelCountLess.html \
   test_offlineDestinationChannelCountMore.html \
   test_oscillatorNode.html \
   test_oscillatorNode2.html \
   test_oscillatorNodeStart.html \
   test_pannerNode.html \
   test_pannerNodeAbove.html \
+  test_pannerNodeChannelCount.html \
   test_pannerNode_equalPower.html \
   test_periodicWave.html \
   test_scriptProcessorNode.html \
   test_scriptProcessorNodeChannelCount.html \
   test_scriptProcessorNodeZeroInputOutput.html \
   test_singleSourceDest.html \
   test_waveShaper.html \
   test_waveShaperNoCurve.html \
@@ -103,14 +104,8 @@ MOCHITEST_FILES := \
   invalid.txt \
   noaudio.webm \
   audio.ogv \
   audio-expected.wav \
   audio-mono-expected.wav \
   audio-mono-expected-2.wav \
   audio-quad.wav \
   $(NULL)
-
-ifneq ($(OS_TARGET),Android) # bug 912474
-MOCHITEST_FILES += \
-  test_pannerNodeChannelCount.html \
-  $(NULL)
-endif
--- a/dom/activities/src/ActivitiesService.jsm
+++ b/dom/activities/src/ActivitiesService.jsm
@@ -18,38 +18,33 @@ XPCOMUtils.defineLazyServiceGetter(this,
                                    "nsIMessageBroadcaster");
 
 XPCOMUtils.defineLazyServiceGetter(this, "NetUtil",
                                    "@mozilla.org/network/util;1",
                                    "nsINetUtil");
 
 this.EXPORTED_SYMBOLS = [];
 
-let idbGlobal = this;
-
 function debug(aMsg) {
   //dump("-- ActivitiesService.jsm " + Date.now() + " " + aMsg + "\n");
 }
 
 const DB_NAME    = "activities";
 const DB_VERSION = 1;
 const STORE_NAME = "activities";
 
 function ActivitiesDb() {
 
 }
 
 ActivitiesDb.prototype = {
   __proto__: IndexedDBHelper.prototype,
 
   init: function actdb_init() {
-    let idbManager = Cc["@mozilla.org/dom/indexeddb/manager;1"]
-                       .getService(Ci.nsIIndexedDatabaseManager);
-    idbManager.initWindowless(idbGlobal);
-    this.initDBHelper(DB_NAME, DB_VERSION, [STORE_NAME], idbGlobal);
+    this.initDBHelper(DB_NAME, DB_VERSION, [STORE_NAME]);
   },
 
   /**
    * Create the initial database schema.
    *
    * The schema of records stored is as follows:
    *
    * {
--- a/dom/alarm/AlarmDB.jsm
+++ b/dom/alarm/AlarmDB.jsm
@@ -18,28 +18,27 @@ const { classes: Cc, interfaces: Ci, uti
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
 
 const ALARMDB_NAME    = "alarms";
 const ALARMDB_VERSION = 1;
 const ALARMSTORE_NAME = "alarms";
 
-this.AlarmDB = function AlarmDB(aGlobal) {
+this.AlarmDB = function AlarmDB() {
   debug("AlarmDB()");
-  this._global = aGlobal;
 }
 
 AlarmDB.prototype = {
   __proto__: IndexedDBHelper.prototype,
 
-  init: function init(aGlobal) {
+  init: function init() {
     debug("init()");
 
-    this.initDBHelper(ALARMDB_NAME, ALARMDB_VERSION, [ALARMSTORE_NAME], aGlobal);
+    this.initDBHelper(ALARMDB_NAME, ALARMDB_VERSION, [ALARMSTORE_NAME]);
   },
 
   upgradeSchema: function upgradeSchema(aTransaction, aDb, aOldVersion, aNewVersion) {
     debug("upgradeSchema()");
 
     let objectStore = aDb.createObjectStore(ALARMSTORE_NAME, { keyPath: "id", autoIncrement: true });
 
     objectStore.createIndex("date",           "date",           { unique: false });
--- a/dom/alarm/AlarmService.jsm
+++ b/dom/alarm/AlarmService.jsm
@@ -31,18 +31,16 @@ XPCOMUtils.defineLazyServiceGetter(this,
 XPCOMUtils.defineLazyGetter(this, "messenger", function() {
   return Cc["@mozilla.org/system-message-internal;1"].getService(Ci.nsISystemMessagesInternal);
 });
 
 XPCOMUtils.defineLazyGetter(this, "powerManagerService", function() {
   return Cc["@mozilla.org/power/powermanagerservice;1"].getService(Ci.nsIPowerManagerService);
 });
 
-let myGlobal = this;
-
 /**
  * AlarmService provides an API to schedule alarms using the device's RTC.
  *
  * AlarmService is primarily used by the mozAlarms API (navigator.mozAlarms)
  * which uses IPC to communicate with the service.
  *
  * AlarmService can also be used by Gecko code by importing the module and then
  * using AlarmService.add() and AlarmService.remove(). Only Gecko code running
@@ -68,21 +66,18 @@ this.AlarmService = {
     this._messages = ["AlarmsManager:GetAll",
                       "AlarmsManager:Add",
                       "AlarmsManager:Remove"];
     this._messages.forEach(function addMessage(msgName) {
       ppmm.addMessageListener(msgName, this);
     }.bind(this));
 
     // Set the indexeddb database.
-    let idbManager = Cc["@mozilla.org/dom/indexeddb/manager;1"]
-                     .getService(Ci.nsIIndexedDatabaseManager);
-    idbManager.initWindowless(myGlobal);
-    this._db = new AlarmDB(myGlobal);
-    this._db.init(myGlobal);
+    this._db = new AlarmDB();
+    this._db.init();
 
     // Variable to save alarms waiting to be set.
     this._alarmQueue = [];
 
     this._restoreAlarmsFromDb();
   },
 
   // Getter/setter to access the current alarm set in system.
--- a/dom/base/IndexedDBHelper.jsm
+++ b/dom/base/IndexedDBHelper.jsm
@@ -43,17 +43,17 @@ IndexedDBHelper.prototype = {
    * @param successCb
    *        Success callback to call once database is open.
    * @param failureCb
    *        Error callback to call when an error is encountered.
    */
   open: function open(aSuccessCb, aFailureCb) {
     let self = this;
     if (DEBUG) debug("Try to open database:" + self.dbName + " " + self.dbVersion);
-    let req = this.dbGlobal.indexedDB.open(this.dbName, this.dbVersion);
+    let req = indexedDB.open(this.dbName, this.dbVersion);
     req.onsuccess = function (event) {
       if (DEBUG) debug("Opened database:" + self.dbName + " " + self.dbVersion);
       self._db = event.target.result;
       self._db.onversionchange = function(event) {
         if (DEBUG) debug("WARNING: DB modified from a different window.");
       }
       aSuccessCb();
     };
@@ -145,18 +145,15 @@ IndexedDBHelper.prototype = {
    * Initialize the DB. Does not call open.
    *
    * @param aDBName
    *        DB name for the open call.
    * @param aDBVersion
    *        Current DB version. User has to implement upgradeSchema.
    * @param aDBStoreName
    *        ObjectStore that is used.
-   * @param aGlobal
-   *        Global object that has indexedDB property.
    */
-  initDBHelper: function initDBHelper(aDBName, aDBVersion, aDBStoreNames, aGlobal) {
+  initDBHelper: function initDBHelper(aDBName, aDBVersion, aDBStoreNames) {
     this.dbName = aDBName;
     this.dbVersion = aDBVersion;
     this.dbStoreNames = aDBStoreNames;
-    this.dbGlobal = aGlobal;
   }
 }
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -704,59 +704,55 @@ Navigator::RemoveIdleObserver(MozIdleObs
   }
   CallbackObjectHolder<MozIdleObserver, nsIIdleObserver> holder(&aIdleObserver);
   nsCOMPtr<nsIIdleObserver> obs = holder.ToXPCOMCallback();
   if (NS_FAILED(mWindow->UnregisterIdleObserver(obs))) {
     NS_WARNING("Failed to remove idle observer.");
   }
 }
 
-void
-Navigator::Vibrate(uint32_t aDuration, ErrorResult& aRv)
+bool
+Navigator::Vibrate(uint32_t aDuration)
 {
   nsAutoTArray<uint32_t, 1> pattern;
   pattern.AppendElement(aDuration);
-  Vibrate(pattern, aRv);
+  return Vibrate(pattern);
 }
 
-void
-Navigator::Vibrate(const nsTArray<uint32_t>& aPattern, ErrorResult& aRv)
+bool
+Navigator::Vibrate(const nsTArray<uint32_t>& aPattern)
 {
   if (!mWindow) {
-    aRv.Throw(NS_ERROR_UNEXPECTED);
-    return;
+    return false;
   }
+
   nsCOMPtr<nsIDocument> doc = mWindow->GetExtantDoc();
   if (!doc) {
-    aRv.Throw(NS_ERROR_FAILURE);
-    return;
+    return false;
   }
+
   if (doc->Hidden()) {
     // Hidden documents cannot start or stop a vibration.
-    return;
+    return false;
   }
 
   if (aPattern.Length() > sMaxVibrateListLen) {
-    // XXXbz this should be returning false instead
-    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-    return;
+    return false;
   }
 
   for (size_t i = 0; i < aPattern.Length(); ++i) {
     if (aPattern[i] > sMaxVibrateMS) {
-      // XXXbz this should be returning false instead
-      aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-      return;
+      return false;
     }
   }
 
   // The spec says we check sVibratorEnabled after we've done the sanity
   // checking on the pattern.
-  if (!sVibratorEnabled) {
-    return;
+  if (aPattern.IsEmpty() || !sVibratorEnabled) {
+    return true;
   }
 
   // Add a listener to cancel the vibration if the document becomes hidden,
   // and remove the old visibility listener, if there was one.
 
   if (!gVibrateWindowListener) {
     // If gVibrateWindowListener is null, this is the first time we've vibrated,
     // and we need to register a listener to clear gVibrateWindowListener on
@@ -764,16 +760,17 @@ Navigator::Vibrate(const nsTArray<uint32
     ClearOnShutdown(&gVibrateWindowListener);
   }
   else {
     gVibrateWindowListener->RemoveListener();
   }
   gVibrateWindowListener = new VibrateWindowListener(mWindow, doc);
 
   hal::Vibrate(aPattern, mWindow);
+  return true;
 }
 
 //*****************************************************************************
 //    Navigator::nsIDOMClientInformation
 //*****************************************************************************
 
 void
 Navigator::RegisterContentHandler(const nsAString& aMIMEType,
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -170,18 +170,18 @@ public:
                                const nsAString& aTitle, ErrorResult& aRv);
   void RegisterContentHandler(const nsAString& aMIMEType, const nsAString& aURL,
                               const nsAString& aTitle, ErrorResult& aRv);
   nsMimeTypeArray* GetMimeTypes(ErrorResult& aRv);
   nsPluginArray* GetPlugins(ErrorResult& aRv);
   // The XPCOM GetDoNotTrack is ok
   Geolocation* GetGeolocation(ErrorResult& aRv);
   battery::BatteryManager* GetBattery(ErrorResult& aRv);
-  void Vibrate(uint32_t aDuration, ErrorResult& aRv);
-  void Vibrate(const nsTArray<uint32_t>& aDuration, ErrorResult& aRv);
+  bool Vibrate(uint32_t aDuration);
+  bool Vibrate(const nsTArray<uint32_t>& aDuration);
   void GetAppCodeName(nsString& aAppCodeName, ErrorResult& aRv)
   {
     aRv = GetAppCodeName(aAppCodeName);
   }
   void GetOscpu(nsString& aOscpu, ErrorResult& aRv)
   {
     aRv = GetOscpu(aOscpu);
   }
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -144,17 +144,16 @@
 // Workers
 #include "mozilla/dom/workers/Workers.h"
 
 #include "nsIDOMFile.h"
 #include "nsDOMBlobBuilder.h" // nsDOMMultipartFile
 
 #include "nsIEventListenerService.h"
 #include "nsIMessageManager.h"
-#include "mozilla/dom/indexedDB/IDBKeyRange.h"
 #include "nsIDOMMediaQueryList.h"
 
 #include "nsDOMTouchEvent.h"
 
 #include "nsWrapperCacheInlines.h"
 #include "mozilla/dom/HTMLCollectionBinding.h"
 
 #include "nsIDOMWakeLock.h"
@@ -495,19 +494,16 @@ static nsDOMClassInfoData sClassInfoData
   NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA(ContentFrameMessageManager, nsEventTargetSH,
                                        DOM_DEFAULT_SCRIPTABLE_FLAGS |
                                        nsIXPCScriptable::IS_GLOBAL_OBJECT)
   NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA(ChromeMessageBroadcaster, nsDOMGenericSH,
                                        DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA(ChromeMessageSender, nsDOMGenericSH,
                                        DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
-  NS_DEFINE_CLASSINFO_DATA(IDBKeyRange, nsDOMGenericSH,
-                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
-
 
   NS_DEFINE_CLASSINFO_DATA(MozCSSKeyframeRule, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(MozCSSKeyframesRule, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(CSSPageRule, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
@@ -1283,20 +1279,16 @@ nsDOMClassInfo::Init()
 
   DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(ChromeMessageSender, nsISupports)
     DOM_CLASSINFO_MAP_ENTRY(nsIProcessChecker)
     DOM_CLASSINFO_MAP_ENTRY(nsIFrameScriptLoader)
     DOM_CLASSINFO_MAP_ENTRY(nsIMessageListenerManager)
     DOM_CLASSINFO_MAP_ENTRY(nsIMessageSender)
   DOM_CLASSINFO_MAP_END
 
-  DOM_CLASSINFO_MAP_BEGIN(IDBKeyRange, nsIIDBKeyRange)
-    DOM_CLASSINFO_MAP_ENTRY(nsIIDBKeyRange)
-  DOM_CLASSINFO_MAP_END
-
   DOM_CLASSINFO_MAP_BEGIN(MozCSSKeyframeRule, nsIDOMMozCSSKeyframeRule)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozCSSKeyframeRule)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(MozCSSKeyframesRule, nsIDOMMozCSSKeyframesRule)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozCSSKeyframesRule)
   DOM_CLASSINFO_MAP_END
 
@@ -2727,22 +2719,16 @@ nsDOMConstructor::ResolveInterfaceConsta
     class_iid = class_name_struct->mData->mProtoChainInterface;
   } else {
     return NS_OK;
   }
 
   nsresult rv = DefineInterfaceConstants(cx, obj, class_iid);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // Special case for |IDBKeyRange| which gets funny "static" functions.
-  if (class_iid->Equals(NS_GET_IID(nsIIDBKeyRange)) &&
-      !indexedDB::IDBKeyRange::DefineConstructors(cx, obj)) {
-    return NS_ERROR_FAILURE;
-  }
-
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMConstructor::ToString(nsAString &aResult)
 {
   aResult.AssignLiteral("[object ");
   aResult.Append(mClassName);
@@ -2857,22 +2843,16 @@ ResolvePrototype(nsIXPConnect *aXPConnec
   const char *class_parent_name = nullptr;
 
   if (!primary_iid->Equals(NS_GET_IID(nsISupports))) {
     JSAutoCompartment ac(cx, class_obj);
 
     rv = DefineInterfaceConstants(cx, class_obj, primary_iid);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    // Special case for |IDBKeyRange| which gets funny "static" functions.
-    if (primary_iid->Equals(NS_GET_IID(nsIIDBKeyRange)) &&
-        !indexedDB::IDBKeyRange::DefineConstructors(cx, class_obj)) {
-      return NS_ERROR_FAILURE;
-    }
-
     nsCOMPtr<nsIInterfaceInfoManager>
       iim(do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID));
     NS_ENSURE_TRUE(iim, NS_ERROR_NOT_AVAILABLE);
 
     iim->GetInfoForIID(primary_iid, getter_AddRefs(if_info));
     NS_ENSURE_TRUE(if_info, NS_ERROR_UNEXPECTED);
 
     const nsIID *iid = nullptr;
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -104,18 +104,16 @@ DOMCI_CLASS(CSSFontFaceRule)
 DOMCI_CLASS(DataTransfer)
 
 DOMCI_CLASS(EventListenerInfo)
 
 DOMCI_CLASS(ContentFrameMessageManager)
 DOMCI_CLASS(ChromeMessageBroadcaster)
 DOMCI_CLASS(ChromeMessageSender)
 
-DOMCI_CLASS(IDBKeyRange)
-
 DOMCI_CLASS(MozCSSKeyframeRule)
 DOMCI_CLASS(MozCSSKeyframesRule)
 
 DOMCI_CLASS(CSSPageRule)
 
 DOMCI_CLASS(MediaQueryList)
 
 #ifdef MOZ_B2G_RIL
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -613,16 +613,21 @@ DOMInterfaces = {
 'IDBIndex': {
     'nativeType': 'mozilla::dom::indexedDB::IDBIndex',
     'binaryNames': {
         'mozGetAll': 'getAll',
         'mozGetAllKeys': 'getAllKeys',
     }
 },
 
+'IDBKeyRange': {
+    'nativeType': 'mozilla::dom::indexedDB::IDBKeyRange',
+    'wrapperCache': False,
+},
+
 'IDBObjectStore': {
     'nativeType': 'mozilla::dom::indexedDB::IDBObjectStore',
     'implicitJSContext': [ 'createIndex' ],
     'binaryNames': {
         'mozGetAll': 'getAll'
     }
 },
 
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -10781,17 +10781,26 @@ class CGEventGetter(CGNativeMember):
             ret += "  return " + memberName + ";"
             return ret;
         if type.isAny():
             ret =  "  JS::ExposeValueToActiveJS("+ memberName + ");\n"
             ret += "  return " + memberName + ";"
             return ret;
         raise TypeError("Event code generator does not support this type!")
 
+    def declare(self, cgClass):
+        if getattr(self.member, "originatingInterface",
+                   cgClass.descriptor.interface) != cgClass.descriptor.interface:
+            return ""
+        return CGNativeMember.declare(self, cgClass)
+
     def define(self, cgClass):
+        if getattr(self.member, "originatingInterface",
+                   cgClass.descriptor.interface) != cgClass.descriptor.interface:
+            return ""
         ret = self.retval(self.member.type);
         methodName = self.descriptorProvider.name + '::' + self.name
         args = (', '.join([a.declare() for a in self.args]))
         body = self.getMethodBody()
         return ret + "\n" + methodName + '(' + args + ') const\n{\n' + body + "\n}\n"
 
 class CGEventSetter(CGNativeMember):
     def __init__(self):
@@ -10840,16 +10849,21 @@ class CGEventMethod(CGNativeMember):
     def define(self, cgClass):
         self.args = list(self.originalArgs)
         members = ""
         holdJS = ""
         iface = self.descriptorProvider.interface
         while iface.identifier.name != "Event":
             for m in self.descriptorProvider.getDescriptor(iface.identifier.name).interface.members:
                 if m.isAttr():
+                    # We initialize all the other member variables in the
+                    # Constructor except those ones coming from the Event.
+                    if getattr(m, "originatingInterface",
+                               cgClass.descriptor.interface).identifier.name == "Event":
+                        continue
                     name = CGDictionary.makeMemberName(m.identifier.name)
                     members += "e->%s = %s.%s;\n" % (name, self.args[1].name, name)
                     if m.type.isAny() or m.type.isObject() or m.type.isSpiderMonkeyInterface():
                         holdJS = "mozilla::HoldJSObjects(e.get());\n"
             iface = iface.parent
 
         self.body = (
             "nsRefPtr<${nativeType}> e = new ${nativeType}(aOwner);\n"
@@ -10886,16 +10900,19 @@ class CGEventClass(CGBindingImplClass):
     """
     Codegen for the actual Event class implementation for this descriptor
     """
     def __init__(self, descriptor):
         CGBindingImplClass.__init__(self, descriptor, CGEventMethod, CGEventGetter, CGEventSetter, False)
         members = []
         for m in descriptor.interface.members:
             if m.isAttr():
+                if getattr(m, "originatingInterface",
+                           descriptor.interface) != descriptor.interface:
+                    continue
                 if m.type.isPrimitive() and m.type.tag() in builtinNames:
                     nativeType = CGGeneric(builtinNames[m.type.tag()])
                     if m.type.nullable():
                         nativeType = CGTemplatedType("Nullable", nativeType)
                     nativeType = nativeType.define()
                 elif m.type.isEnum():
                     nativeType = m.type.unroll().inner.identifier.name
                     if m.type.nullable():
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -672,16 +672,21 @@ class IDLInterface(IDLObjectWithScope):
             self.members.extend(additionalMembers)
             iface.interfacesImplementingSelf.add(self)
 
         for ancestor in self.getInheritedInterfaces():
             ancestor.interfacesBasedOnSelf.add(self)
             for ancestorConsequential in ancestor.getConsequentialInterfaces():
                 ancestorConsequential.interfacesBasedOnSelf.add(self)
 
+        for member in self.members:
+            if (member.isAttr() and member.isUnforgeable() and
+                not hasattr(member, "originatingInterface")):
+                member.originatingInterface = self
+
         if self.parent:
             # Make sure we don't shadow any of the [Unforgeable] attributes on
             # our ancestor interfaces.  We don't have to worry about
             # consequential interfaces here, because those have already been
             # imported into the relevant .members lists.  And we don't have to
             # worry about anything other than our parent, because it has already
             # imported its ancestors unforgeable attributes into its member
             # list.
--- a/dom/contacts/fallback/ContactDB.jsm
+++ b/dom/contacts/fallback/ContactDB.jsm
@@ -1082,17 +1082,17 @@ ContactDB.prototype = {
             if (tmp === "" || NON_SEARCHABLE_CHARS.test(lowerCase)) {
               if (DEBUG) debug("Call continue!");
               continue;
             }
             lowerCase = tmp;
           }
         }
         if (DEBUG) debug("lowerCase: " + lowerCase);
-        let range = this.dbGlobal.IDBKeyRange.bound(lowerCase, lowerCase + "\uFFFF");
+        let range = IDBKeyRange.bound(lowerCase, lowerCase + "\uFFFF");
         let index = store.index(key + "LowerCase");
         request = index.mozGetAll(range, limit);
       }
       if (!txn.result)
         txn.result = {};
 
       request.onsuccess = function (event) {
         if (DEBUG) debug("Request successful. Record count: " + event.target.result.length);
@@ -1129,12 +1129,12 @@ ContactDB.prototype = {
     this.substringMatching = aDigits;
   },
 
   disableSubstringMatching: function disableSubstringMatching() {
     if (DEBUG) debug("MCC disabling substring matching");
     delete this.substringMatching;
   },
 
-  init: function init(aGlobal) {
-    this.initDBHelper(DB_NAME, DB_VERSION, [STORE_NAME, SAVED_GETALL_STORE_NAME, REVISION_STORE], aGlobal);
+  init: function init() {
+    this.initDBHelper(DB_NAME, DB_VERSION, [STORE_NAME, SAVED_GETALL_STORE_NAME, REVISION_STORE]);
   }
 };
--- a/dom/contacts/fallback/ContactService.jsm
+++ b/dom/contacts/fallback/ContactService.jsm
@@ -17,46 +17,41 @@ Cu.import("resource://gre/modules/XPCOMU
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/ContactDB.jsm");
 Cu.import("resource://gre/modules/PhoneNumberUtils.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
                                    "@mozilla.org/parentprocessmessagemanager;1",
                                    "nsIMessageListenerManager");
 
-let myGlobal = this;
-
 let ContactService = {
   init: function() {
     if (DEBUG) debug("Init");
     this._messages = ["Contacts:Find", "Contacts:GetAll", "Contacts:GetAll:SendNow",
                       "Contacts:Clear", "Contact:Save",
                       "Contact:Remove", "Contacts:RegisterForMessages",
                       "child-process-shutdown", "Contacts:GetRevision",
                       "Contacts:GetCount"];
     this._children = [];
     this._cursors = {};
     this._messages.forEach(function(msgName) {
       ppmm.addMessageListener(msgName, this);
     }.bind(this));
 
-    var idbManager = Components.classes["@mozilla.org/dom/indexeddb/manager;1"].getService(Ci.nsIIndexedDatabaseManager);
-    idbManager.initWindowless(myGlobal);
     this._db = new ContactDB();
-    this._db.init(myGlobal);
+    this._db.init();
 
     this.configureSubstringMatching();
 
     Services.obs.addObserver(this, "profile-before-change", false);
     Services.prefs.addObserver("ril.lastKnownSimMcc", this, false);
   },
 
   observe: function(aSubject, aTopic, aData) {
     if (aTopic === 'profile-before-change') {
-      myGlobal = null;
       this._messages.forEach(function(msgName) {
         ppmm.removeMessageListener(msgName, this);
       }.bind(this));
       Services.obs.removeObserver(this, "profile-before-change");
       Services.prefs.removeObserver("dom.phonenumber.substringmatching", this);
       ppmm = null;
       this._messages = null;
       if (this._db)
--- a/dom/contacts/tests/test_contacts_upgrade.html
+++ b/dom/contacts/tests/test_contacts_upgrade.html
@@ -143,23 +143,18 @@ function makeFailure(reason) {
     ok(false, reason);
     SimpleTest.finish();
   };
 };
 
 const {Cc, Ci, Cu} = SpecialPowers;
 Cu.import("resource://gre/modules/ContactDB.jsm", window);
 
-let idb = {};
-Cc["@mozilla.org/dom/indexeddb/manager;1"]
-  .getService(Ci.nsIIndexedDatabaseManager)
-  .initWindowless(idb);
-
 let cdb = new ContactDB();
-cdb.init(idb);
+cdb.init();
 
 let CONTACT_PROPS = {
   id: "ab74671e36be41b680f8f030e7e24ea2",
   properties: {
     name: ["magnificentest foo bar the third"],
     givenName: ["foo"],
     familyName: ["bar"],
     honorificPrefix: ["magnificentest"],
@@ -192,17 +187,17 @@ let CONTACT_PROPS = {
     sex: "male",
     genderIdentity: "trisexual",
     key: "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAACXBIWXMAAC4jAAAuIwF4pT92AAACrElEQVQozwXBTW8bRRgA4Hfemf1er7/iJI4Tq7VFlEZN1VZIlapy4MQBTkXcuSH+G/APKnGAAyCVCqmtCHETp64db5zdtdf7NbMzw/OQH378HkCZpmmapqYMy8yrNnadS6026HC/Z7k+SCkEBwKEEKaUQtQAmlDqrucH23nH4BRkJVRcwmod5gcn6LehFgCaEIIalFZaEcLCq73w355RdvY7nfGQGVTlmRXfqMlrUaSUMUQkhCISJIggKj3/YBHt7PRbpy+cwbF7dN/0vEqTMoo3s0tmGAAAoJAgImMq3xZ5WTPbHj4Mho8Nf+QcPtZBLxEkqeQ2WmklkRCtNdNaI1KpVCnqOC3j5ZK++4vnm6xSWZpzwQtRV2mOiBoRpEKtNQAQggjQcCwqinRxJeKlWW93dlqEsa2QRZbF85nWBAAZY4YUgl9fRJWKVuWgmhwHhpD1+ZrfVjAN867rMCne//rq7OuXjWaLCVHnOWHgFDwMw+Tvi09PdhtJXoVC7bWDIi8Lg8qyMk3rYjLzvJh2O30hwK6TpiG7zWDcck9GR17D9wxDcH7/oNtElRa1aZuLDJN4S7/87tssLVg0/eZs/3h0D5R89vR0v+1AVT0YHX31ZDy9uv7IeJrryeyu2+nS50/PqOXM5qt8Nf/jv08UwTfN27vkchldLpPf/nx/nqSz5sbzhkTYzLRppzNYre/ycrMIZwqsHdf96fd/Xr354AYBr/jESWhgGb6zVSuGrrQS1j4Zk8nc2Hs7frFb3Phc6+fOKDGLKOJTHvlj2u85N4t6vbw7OM4YRVquboPdsPNZ9eb8pvfAOf2iN4dN3EzWadnoO5JY19Oo0TYtw1t8TBqBR9v7wbOXROLWtZ3PH937+ZfXrb6BUHEbXL+FCIfDw92e5zebg8GR54r/AaMVcBxE6hgPAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDEyLTA3LTIxVDEwOjUzOjE5LTA0OjAwYyXbYgAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxMi0wNy0yMVQxMDo1MzoxOS0wNDowMBJ4Y94AAAARdEVYdGpwZWc6Y29sb3JzcGFjZQAyLHVVnwAAACB0RVh0anBlZzpzYW1wbGluZy1mYWN0b3IAMXgxLDF4MSwxeDHplfxwAAAAAElFTkSuQmCC"
   }
 };
 
 function deleteDatabase(then) {
   cdb.close();
-  let req = idb.indexedDB.deleteDatabase(DB_NAME);
+  let req = indexedDB.deleteDatabase(DB_NAME);
   req.onsuccess = then;
   req.onblocked = makeFailure("blocked");
   req.onupgradeneeded = makeFailure("onupgradeneeded");
   req.onerror = makeFailure("onerror");
 }
 
 function saveContact() {
   // takes fast upgrade path
@@ -211,17 +206,17 @@ function saveContact() {
       ok(true, "Saved contact successfully");
       next();
     }
   );
 }
 
 function getContact(callback) {
   return function() {
-    let req = idb.indexedDB.open(STORE_NAME, DB_VERSION);
+    let req = indexedDB.open(STORE_NAME, DB_VERSION);
     req.onsuccess = function(event) {
       let db = event.target.result;
       let txn = db.transaction([STORE_NAME], "readonly");
       txn.onabort = makeFailure("Failed to open transaction");
       let r2 = txn.objectStore(STORE_NAME).get(CONTACT_PROPS.id);
       r2.onsuccess = function() {
         db.close();
         callback(r2.result);
@@ -240,17 +235,17 @@ let Tests = [
     savedContact = contact;
     next();
   }),
 
   function() {
     deleteDatabase(function() {
       info("slow upgrade");
       cdb.useFastUpgrade = false;
-      cdb.init(idb);
+      cdb.init();
       next();
     });
   },
 
   saveContact,
 
   getContact(function(contact) {
     checkDBContacts(savedContact, contact);
--- a/dom/imptests/failures/html/dom/test_interfaces.html.json
+++ b/dom/imptests/failures/html/dom/test_interfaces.html.json
@@ -1,17 +1,14 @@
 {
   "DOMException exception: existence and properties of exception interface prototype object": true,
   "DOMException exception: existence and properties of exception interface prototype object's \"name\" property": true,
-  "Event interface: document.createEvent(\"Event\") must have own property \"isTrusted\"": true,
   "Event interface: document.createEvent(\"Event\") must inherit property \"timeStamp\" with the proper type (15)": true,
-  "Event interface: new Event(\"foo\") must have own property \"isTrusted\"": true,
   "Event interface: new Event(\"foo\") must inherit property \"timeStamp\" with the proper type (15)": true,
   "CustomEvent interface: existence and properties of interface object": true,
-  "Event interface: new CustomEvent(\"foo\") must have own property \"isTrusted\"": true,
   "Event interface: new CustomEvent(\"foo\") must inherit property \"timeStamp\" with the proper type (15)": true,
   "EventListener interface: existence and properties of interface prototype object": true,
   "EventListener interface: existence and properties of interface prototype object's \"constructor\" property": true,
   "EventListener interface: operation handleEvent(Event)": true,
   "MutationObserver interface: operation observe(Node,MutationObserverInit)": true,
   "Node interface: existence and properties of interface object": true,
   "Document interface: existence and properties of interface object": true,
   "Document interface: operation prepend(union)": true,
--- a/dom/indexedDB/IDBCursor.cpp
+++ b/dom/indexedDB/IDBCursor.cpp
@@ -31,18 +31,17 @@
 
 USING_INDEXEDDB_NAMESPACE
 using namespace mozilla::dom::indexedDB::ipc;
 using mozilla::dom::Optional;
 using mozilla::dom::OwningIDBObjectStoreOrIDBIndex;
 using mozilla::ErrorResult;
 
 static_assert(sizeof(size_t) >= sizeof(IDBCursor::Direction),
-              "Relying on conversion between size_t and "
-              "IDBCursor::Direction");
+              "Relying on conversion between size_t and IDBCursor::Direction");
 
 namespace {
 
 class CursorHelper : public AsyncConnectionHelper
 {
 public:
   CursorHelper(IDBCursor* aCursor)
   : AsyncConnectionHelper(aCursor->Transaction(), aCursor->Request()),
@@ -57,16 +56,19 @@ public:
 
   virtual nsresult
   PackArgumentsForParentProcess(CursorRequestParams& aParams) = 0;
 
   virtual nsresult
   UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) = 0;
 
 protected:
+  virtual ~CursorHelper()
+  { }
+
   nsRefPtr<IDBCursor> mCursor;
 
 private:
   IndexedDBCursorRequestChild* mActor;
 };
 
 } // anonymous namespace
 
@@ -74,43 +76,46 @@ BEGIN_INDEXEDDB_NAMESPACE
 
 class ContinueHelper : public CursorHelper
 {
 public:
   ContinueHelper(IDBCursor* aCursor,
                  int32_t aCount)
   : CursorHelper(aCursor), mCount(aCount)
   {
-    NS_ASSERTION(aCount > 0, "Must have a count!");
-  }
-
-  ~ContinueHelper()
-  {
-    IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo);
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(aCursor);
+    MOZ_ASSERT(aCount > 0);
   }
 
   virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection)
                                   MOZ_OVERRIDE;
 
   virtual nsresult GetSuccessResult(JSContext* aCx,
-                                    JS::MutableHandle<JS::Value> aVal) MOZ_OVERRIDE;
+                                    JS::MutableHandle<JS::Value> aVal)
+                                    MOZ_OVERRIDE;
 
   virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE;
 
   virtual nsresult
   PackArgumentsForParentProcess(CursorRequestParams& aParams) MOZ_OVERRIDE;
 
   virtual ChildProcessSendResult
   SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE;
 
   virtual nsresult
   UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
                                   MOZ_OVERRIDE;
 
 protected:
+  virtual ~ContinueHelper()
+  {
+    IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo);
+  }
+
   virtual nsresult
   BindArgumentsToStatement(mozIStorageStatement* aStatement) = 0;
 
   virtual nsresult
   GatherResultsFromStatement(mozIStorageStatement* aStatement) = 0;
 
   void UpdateCursorState()
   {
@@ -119,20 +124,20 @@ protected:
     mCursor->mCachedValue = JSVAL_VOID;
     mCursor->mHaveCachedKey = false;
     mCursor->mHaveCachedPrimaryKey = false;
     mCursor->mHaveCachedValue = false;
     mCursor->mContinueCalled = false;
 
     if (mKey.IsUnset()) {
       mCursor->mHaveValue = false;
-    }
-    else {
-      NS_ASSERTION(mCursor->mType == IDBCursor::OBJECTSTORE ||
-                   !mObjectKey.IsUnset(), "Bad key!");
+    } else {
+      MOZ_ASSERT(mCursor->mType == IDBCursor::OBJECTSTORE ||
+                 mCursor->mType == IDBCursor::OBJECTSTOREKEY ||
+                 !mObjectKey.IsUnset());
 
       // Set new values.
       mCursor->mKey = mKey;
       mCursor->mObjectKey = mObjectKey;
       mCursor->mContinueToKey.Unset();
 
       mCursor->mCloneReadInfo.Swap(mCloneReadInfo);
       mCloneReadInfo.mCloneBuffer.clear();
@@ -148,43 +153,70 @@ protected:
 class ContinueObjectStoreHelper : public ContinueHelper
 {
 public:
   ContinueObjectStoreHelper(IDBCursor* aCursor,
                             uint32_t aCount)
   : ContinueHelper(aCursor, aCount)
   { }
 
+protected:
+  virtual ~ContinueObjectStoreHelper()
+  { }
+
 private:
   nsresult BindArgumentsToStatement(mozIStorageStatement* aStatement);
   nsresult GatherResultsFromStatement(mozIStorageStatement* aStatement);
 };
 
+class ContinueObjectStoreKeyHelper : public ContinueObjectStoreHelper
+{
+public:
+  ContinueObjectStoreKeyHelper(IDBCursor* aCursor,
+                               uint32_t aCount)
+  : ContinueObjectStoreHelper(aCursor, aCount)
+  { }
+
+private:
+  virtual ~ContinueObjectStoreKeyHelper()
+  { }
+
+  virtual nsresult
+  GatherResultsFromStatement(mozIStorageStatement* aStatement) MOZ_OVERRIDE;
+};
+
 class ContinueIndexHelper : public ContinueHelper
 {
 public:
   ContinueIndexHelper(IDBCursor* aCursor,
                       uint32_t aCount)
   : ContinueHelper(aCursor, aCount)
   { }
 
+protected:
+  virtual ~ContinueIndexHelper()
+  { }
+
 private:
   nsresult BindArgumentsToStatement(mozIStorageStatement* aStatement);
   nsresult GatherResultsFromStatement(mozIStorageStatement* aStatement);
 };
 
 class ContinueIndexObjectHelper : public ContinueIndexHelper
 {
 public:
   ContinueIndexObjectHelper(IDBCursor* aCursor,
                             uint32_t aCount)
   : ContinueIndexHelper(aCursor, aCount)
   { }
 
 private:
+  virtual ~ContinueIndexObjectHelper()
+  { }
+
   nsresult GatherResultsFromStatement(mozIStorageStatement* aStatement);
 };
 
 END_INDEXEDDB_NAMESPACE
 
 // static
 already_AddRefed<IDBCursor>
 IDBCursor::Create(IDBRequest* aRequest,
@@ -212,16 +244,42 @@ IDBCursor::Create(IDBRequest* aRequest,
 
   return cursor.forget();
 }
 
 // static
 already_AddRefed<IDBCursor>
 IDBCursor::Create(IDBRequest* aRequest,
                   IDBTransaction* aTransaction,
+                  IDBObjectStore* aObjectStore,
+                  Direction aDirection,
+                  const Key& aRangeKey,
+                  const nsACString& aContinueQuery,
+                  const nsACString& aContinueToQuery,
+                  const Key& aKey)
+{
+  MOZ_ASSERT(aObjectStore);
+  MOZ_ASSERT(!aKey.IsUnset());
+
+  nsRefPtr<IDBCursor> cursor =
+    IDBCursor::CreateCommon(aRequest, aTransaction, aObjectStore, aDirection,
+                            aRangeKey, aContinueQuery, aContinueToQuery);
+  NS_ASSERTION(cursor, "This shouldn't fail!");
+
+  cursor->mObjectStore = aObjectStore;
+  cursor->mType = OBJECTSTOREKEY;
+  cursor->mKey = aKey;
+
+  return cursor.forget();
+}
+
+// static
+already_AddRefed<IDBCursor>
+IDBCursor::Create(IDBRequest* aRequest,
+                  IDBTransaction* aTransaction,
                   IDBIndex* aIndex,
                   Direction aDirection,
                   const Key& aRangeKey,
                   const nsACString& aContinueQuery,
                   const nsACString& aContinueToQuery,
                   const Key& aKey,
                   const Key& aObjectKey)
 {
@@ -288,17 +346,17 @@ IDBCursor::ConvertDirection(mozilla::dom
 
     case mozilla::dom::IDBCursorDirection::Prev:
       return PREV;
 
     case mozilla::dom::IDBCursorDirection::Prevunique:
       return PREV_UNIQUE;
 
     default:
-      MOZ_CRASH("Unknown direction!");
+      MOZ_ASSUME_UNREACHABLE("Unknown direction!");
   }
 }
 
 // static
 already_AddRefed<IDBCursor>
 IDBCursor::CreateCommon(IDBRequest* aRequest,
                         IDBTransaction* aTransaction,
                         IDBObjectStore* aObjectStore,
@@ -391,54 +449,55 @@ IDBCursor::DropJSObjects()
   mRooted = false;
   mHaveValue = false;
   mozilla::DropJSObjects(this);
 }
 
 void
 IDBCursor::ContinueInternal(const Key& aKey, int32_t aCount, ErrorResult& aRv)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(aCount > 0, "Must have a count!");
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aCount > 0);
 
   if (!mTransaction->IsOpen()) {
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
     return;
   }
 
   if (!mHaveValue || mContinueCalled) {
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
     return;
   }
 
   mContinueToKey = aKey;
 
-#ifdef DEBUG
-  NS_ASSERTION(mRequest->ReadyState() == IDBRequestReadyState::Done,
-               "Should be DONE!");
-#endif
+  MOZ_ASSERT(mRequest->ReadyState() == IDBRequestReadyState::Done);
 
   mRequest->Reset();
 
   nsRefPtr<ContinueHelper> helper;
   switch (mType) {
     case OBJECTSTORE:
       helper = new ContinueObjectStoreHelper(this, aCount);
       break;
 
+    case OBJECTSTOREKEY:
+      helper = new ContinueObjectStoreKeyHelper(this, aCount);
+      break;
+
     case INDEXKEY:
       helper = new ContinueIndexHelper(this, aCount);
       break;
 
     case INDEXOBJECT:
       helper = new ContinueIndexObjectHelper(this, aCount);
       break;
 
     default:
-      NS_NOTREACHED("Unknown cursor type!");
+      MOZ_ASSUME_UNREACHABLE("Unknown cursor type!");
   }
 
   nsresult rv = helper->DispatchToTransactionPool();
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to dispatch!");
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
     return;
   }
@@ -484,66 +543,83 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBCursor)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBCursor)
 
 JSObject*
 IDBCursor::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
-  return mType != INDEXKEY
-          ? IDBCursorWithValueBinding::Wrap(aCx, aScope, this)
-          : IDBCursorBinding::Wrap(aCx, aScope, this);
+  MOZ_ASSERT(NS_IsMainThread());
+
+  switch (mType) {
+    case OBJECTSTORE:
+    case INDEXOBJECT:
+      return IDBCursorWithValueBinding::Wrap(aCx, aScope, this);
+
+    case OBJECTSTOREKEY:
+    case INDEXKEY:
+      return IDBCursorBinding::Wrap(aCx, aScope, this);
+
+    default:
+      MOZ_ASSUME_UNREACHABLE("Bad type!");
+  }
 }
 
 mozilla::dom::IDBCursorDirection
 IDBCursor::GetDirection() const
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 
   switch (mDirection) {
     case NEXT:
       return mozilla::dom::IDBCursorDirection::Next;
 
     case NEXT_UNIQUE:
       return mozilla::dom::IDBCursorDirection::Nextunique;
 
     case PREV:
       return mozilla::dom::IDBCursorDirection::Prev;
 
     case PREV_UNIQUE:
       return mozilla::dom::IDBCursorDirection::Prevunique;
 
-    case DIRECTION_INVALID:
     default:
-      MOZ_CRASH("Unknown direction!");
-      return mozilla::dom::IDBCursorDirection::Next;
+      MOZ_ASSUME_UNREACHABLE("Bad direction!");
   }
 }
 
-
 void
 IDBCursor::GetSource(OwningIDBObjectStoreOrIDBIndex& aSource) const
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread());
+
+  switch (mType) {
+    case OBJECTSTORE:
+    case OBJECTSTOREKEY:
+      MOZ_ASSERT(mObjectStore);
+      aSource.SetAsIDBObjectStore() = mObjectStore;
+      break;
 
-  if (mType == OBJECTSTORE) {
-    aSource.SetAsIDBObjectStore() = mObjectStore;
-  }
-  else {
-    aSource.SetAsIDBIndex() = mIndex;
+    case INDEXKEY:
+    case INDEXOBJECT:
+      MOZ_ASSERT(mIndex);
+      aSource.SetAsIDBIndex() = mIndex;
+      break;
+
+    default:
+      MOZ_ASSUME_UNREACHABLE("Bad type!");
   }
 }
 
 JS::Value
 IDBCursor::GetKey(JSContext* aCx, ErrorResult& aRv)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  NS_ASSERTION(!mKey.IsUnset() || !mHaveValue, "Bad key!");
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!mKey.IsUnset() || !mHaveValue);
 
   if (!mHaveValue) {
     return JSVAL_VOID;
   }
 
   if (!mHaveCachedKey) {
     if (!mRooted) {
       mozilla::HoldJSObjects(this);
@@ -557,49 +633,46 @@ IDBCursor::GetKey(JSContext* aCx, ErrorR
   }
 
   return mCachedKey;
 }
 
 JS::Value
 IDBCursor::GetPrimaryKey(JSContext* aCx, ErrorResult& aRv)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 
   if (!mHaveValue) {
     return JSVAL_VOID;
   }
 
   if (!mHaveCachedPrimaryKey) {
     if (!mRooted) {
       mozilla::HoldJSObjects(this);
       mRooted = true;
     }
 
-    JSAutoRequest ar(aCx);
-
-    NS_ASSERTION(mType == OBJECTSTORE ? !mKey.IsUnset() :
-                                        !mObjectKey.IsUnset(), "Bad key!");
-
-    const Key& key = mType == OBJECTSTORE ? mKey : mObjectKey;
+    const Key& key =
+      (mType == OBJECTSTORE || mType == OBJECTSTOREKEY) ? mKey : mObjectKey;
+    MOZ_ASSERT(!key.IsUnset());
 
     aRv = key.ToJSVal(aCx, mCachedPrimaryKey);
     ENSURE_SUCCESS(aRv, JSVAL_VOID);
 
     mHaveCachedPrimaryKey = true;
   }
 
   return mCachedPrimaryKey;
 }
 
 JS::Value
 IDBCursor::GetValue(JSContext* aCx, ErrorResult& aRv)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(mType != INDEXKEY, "GetValue shouldn't exist on index keys");
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mType == OBJECTSTORE || mType == INDEXOBJECT);
 
   if (!mHaveValue) {
     return JSVAL_VOID;
   }
 
   if (!mHaveCachedValue) {
     if (!mRooted) {
       mozilla::HoldJSObjects(this);
@@ -621,17 +694,17 @@ IDBCursor::GetValue(JSContext* aCx, Erro
   return mCachedValue;
 }
 
 void
 IDBCursor::Continue(JSContext* aCx,
                     const Optional<JS::Handle<JS::Value> >& aKey,
                     ErrorResult &aRv)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 
   Key key;
   if (aKey.WasPassed()) {
     aRv = key.SetFromJSVal(aCx, aKey.Value());
     ENSURE_SUCCESS_VOID(aRv);
   }
 
   if (!key.IsUnset()) {
@@ -648,27 +721,27 @@ IDBCursor::Continue(JSContext* aCx,
       case IDBCursor::PREV_UNIQUE:
         if (key >= mKey) {
           aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
           return;
         }
         break;
 
       default:
-        NS_NOTREACHED("Unknown direction type!");
+        MOZ_ASSUME_UNREACHABLE("Unknown direction type!");
     }
   }
 
   ContinueInternal(key, 1, aRv);
   if (aRv.Failed()) {
     return;
   }
 
 #ifdef IDB_PROFILER_USE_MARKS
-  if (mType == OBJECTSTORE) {
+  if (mType == OBJECTSTORE || mType == OBJECTSTOREKEY) {
     IDB_PROFILER_MARK("IndexedDB Request %llu: "
                       "database(%s).transaction(%s).objectStore(%s).cursor(%s)."
                       "continue(%s)",
                       "IDBRequest[%llu] MT IDBCursor.continue()",
                       Request()->GetSerialNumber(),
                       IDB_PROFILER_STRING(Transaction()->Database()),
                       IDB_PROFILER_STRING(Transaction()),
                       IDB_PROFILER_STRING(mObjectStore),
@@ -690,40 +763,39 @@ IDBCursor::Continue(JSContext* aCx,
   }
 #endif
 }
 
 already_AddRefed<IDBRequest>
 IDBCursor::Update(JSContext* aCx, JS::Handle<JS::Value> aValue,
                   ErrorResult& aRv)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 
   if (!mTransaction->IsOpen()) {
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
     return nullptr;
   }
 
   if (!mTransaction->IsWriteAllowed()) {
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
     return nullptr;
   }
 
-  if (!mHaveValue || mType == INDEXKEY) {
+  if (!mHaveValue || mType == OBJECTSTOREKEY || mType == INDEXKEY) {
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
     return nullptr;
   }
 
-  NS_ASSERTION(mObjectStore, "This cannot be null!");
-  NS_ASSERTION(!mKey.IsUnset() , "Bad key!");
-  NS_ASSERTION(mType != INDEXOBJECT || !mObjectKey.IsUnset(), "Bad key!");
+  MOZ_ASSERT(mObjectStore);
+  MOZ_ASSERT(!mKey.IsUnset());
+  MOZ_ASSERT(mType == OBJECTSTORE || mType == INDEXOBJECT);
+  MOZ_ASSERT_IF(mType == INDEXOBJECT, !mObjectKey.IsUnset());
 
-  JSAutoRequest ar(aCx);
-
-  Key& objectKey = (mType == OBJECTSTORE) ? mKey : mObjectKey;
+  const Key& objectKey = (mType == OBJECTSTORE) ? mKey : mObjectKey;
 
   nsRefPtr<IDBRequest> request;
   if (mObjectStore->HasValidKeyPath()) {
     // Make sure the object given has the correct keyPath value set on it.
     const KeyPath& keyPath = mObjectStore->GetKeyPath();
     Key key;
 
     aRv = keyPath.ExtractKey(aCx, aValue, key);
@@ -791,46 +863,45 @@ IDBCursor::Update(JSContext* aCx, JS::Ha
 #endif
 
   return request.forget();
 }
 
 already_AddRefed<IDBRequest>
 IDBCursor::Delete(JSContext* aCx, ErrorResult& aRv)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 
   if (!mTransaction->IsOpen()) {
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
     return nullptr;
   }
 
   if (!mTransaction->IsWriteAllowed()) {
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
     return nullptr;
   }
 
-  if (!mHaveValue || mType == INDEXKEY) {
+  if (!mHaveValue || mType == OBJECTSTOREKEY || mType == INDEXKEY) {
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
     return nullptr;
   }
 
-  NS_ASSERTION(mObjectStore, "This cannot be null!");
-  NS_ASSERTION(!mKey.IsUnset() , "Bad key!");
+  MOZ_ASSERT(mObjectStore);
+  MOZ_ASSERT(mType == OBJECTSTORE || mType == INDEXOBJECT);
+  MOZ_ASSERT(!mKey.IsUnset());
 
-  Key& objectKey = (mType == OBJECTSTORE) ? mKey : mObjectKey;
+  const Key& objectKey = (mType == OBJECTSTORE) ? mKey : mObjectKey;
 
   JS::Rooted<JS::Value> key(aCx);
   aRv = objectKey.ToJSVal(aCx, &key);
   ENSURE_SUCCESS(aRv, nullptr);
 
   nsRefPtr<IDBRequest> request = mObjectStore->Delete(aCx, key, aRv);
-  if (aRv.Failed()) {
-    return nullptr;
-  }
+  ENSURE_SUCCESS(aRv, nullptr);
 
 #ifdef IDB_PROFILER_USE_MARKS
   {
     uint64_t requestSerial = request->GetSerialNumber();
     if (mType == OBJECTSTORE) {
       IDB_PROFILER_MARK("IndexedDB Request %llu: "
                         "database(%s).transaction(%s).objectStore(%s)."
                         "cursor(%s).delete(%s)",
@@ -861,30 +932,30 @@ IDBCursor::Delete(JSContext* aCx, ErrorR
 #endif
 
   return request.forget();
 }
 
 void
 IDBCursor::Advance(uint32_t aCount, ErrorResult &aRv)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 
   if (aCount < 1) {
     aRv.ThrowTypeError(MSG_INVALID_ADVANCE_COUNT);
     return;
   }
 
   Key key;
   ContinueInternal(key, int32_t(aCount), aRv);
   ENSURE_SUCCESS_VOID(aRv);
 
 #ifdef IDB_PROFILER_USE_MARKS
   {
-    if (mType == OBJECTSTORE) {
+    if (mType == OBJECTSTORE || mType == OBJECTSTOREKEY) {
       IDB_PROFILER_MARK("IndexedDB Request %llu: "
                         "database(%s).transaction(%s).objectStore(%s)."
                         "cursor(%s).advance(%ld)",
                         "IDBRequest[%llu] MT IDBCursor.advance()",
                         Request()->GetSerialNumber(),
                         IDB_PROFILER_STRING(Transaction()->Database()),
                         IDB_PROFILER_STRING(Transaction()),
                         IDB_PROFILER_STRING(mObjectStore),
@@ -1130,16 +1201,19 @@ ContinueHelper::UnpackResponseFromParent
                                        mCloneReadInfo.mFiles);
   return NS_OK;
 }
 
 nsresult
 ContinueObjectStoreHelper::BindArgumentsToStatement(
                                                mozIStorageStatement* aStatement)
 {
+  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_ASSERT(aStatement);
+
   // Bind object store id.
   nsresult rv = aStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"),
                                             mCursor->mObjectStore->Id());
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   NS_NAMED_LITERAL_CSTRING(currentKeyName, "current_key");
   NS_NAMED_LITERAL_CSTRING(rangeKeyName, "range_key");
 
@@ -1161,22 +1235,39 @@ ContinueObjectStoreHelper::BindArguments
 
   return NS_OK;
 }
 
 nsresult
 ContinueObjectStoreHelper::GatherResultsFromStatement(
                                                mozIStorageStatement* aStatement)
 {
+  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_ASSERT(aStatement);
+
   // Figure out what kind of key we have next.
   nsresult rv = mKey.SetFromStatement(aStatement, 0);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(aStatement, 1, 2,
-    mDatabase, mCloneReadInfo);
+                                                               mDatabase,
+                                                               mCloneReadInfo);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+nsresult
+ContinueObjectStoreKeyHelper::GatherResultsFromStatement(
+                                               mozIStorageStatement* aStatement)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_ASSERT(aStatement);
+
+  nsresult rv = mKey.SetFromStatement(aStatement, 0);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 nsresult
 ContinueIndexHelper::BindArgumentsToStatement(mozIStorageStatement* aStatement)
 {
--- a/dom/indexedDB/IDBCursor.h
+++ b/dom/indexedDB/IDBCursor.h
@@ -50,16 +50,17 @@ class IDBCursor MOZ_FINAL : public nsISu
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBCursor)
 
   enum Type
   {
     OBJECTSTORE = 0,
+    OBJECTSTOREKEY,
     INDEXKEY,
     INDEXOBJECT
   };
 
   enum Direction
   {
     NEXT = 0,
     NEXT_UNIQUE,
@@ -78,16 +79,28 @@ public:
          IDBObjectStore* aObjectStore,
          Direction aDirection,
          const Key& aRangeKey,
          const nsACString& aContinueQuery,
          const nsACString& aContinueToQuery,
          const Key& aKey,
          StructuredCloneReadInfo& aCloneReadInfo);
 
+  // For OBJECTSTOREKEY cursors.
+  static
+  already_AddRefed<IDBCursor>
+  Create(IDBRequest* aRequest,
+         IDBTransaction* aTransaction,
+         IDBObjectStore* aObjectStore,
+         Direction aDirection,
+         const Key& aRangeKey,
+         const nsACString& aContinueQuery,
+         const nsACString& aContinueToQuery,
+         const Key& aKey);
+
   // For INDEXKEY cursors.
   static
   already_AddRefed<IDBCursor>
   Create(IDBRequest* aRequest,
          IDBTransaction* aTransaction,
          IDBIndex* aIndex,
          Direction aDirection,
          const Key& aRangeKey,
--- a/dom/indexedDB/IDBFactory.cpp
+++ b/dom/indexedDB/IDBFactory.cpp
@@ -183,16 +183,21 @@ IDBFactory::Create(JSContext* aCx,
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aCx, "Null context!");
   NS_ASSERTION(aOwningObject, "Null object!");
   NS_ASSERTION(JS_GetGlobalForObject(aCx, aOwningObject) == aOwningObject,
                "Not a global object!");
   NS_ASSERTION(nsContentUtils::IsCallerChrome(), "Only for chrome!");
 
+  // Make sure that the manager is up before we do anything here since lots of
+  // decisions depend on which process we're running in.
+  IndexedDatabaseManager* mgr = IndexedDatabaseManager::GetOrCreate();
+  NS_ENSURE_TRUE(mgr, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
   nsCString group;
   nsCString origin;
   StoragePrivilege privilege;
   PersistenceType defaultPersistenceType;
   QuotaManager::GetInfoForChrome(&group, &origin, &privilege,
                                  &defaultPersistenceType);
 
   nsRefPtr<IDBFactory> factory = new IDBFactory();
--- a/dom/indexedDB/IDBIndex.cpp
+++ b/dom/indexedDB/IDBIndex.cpp
@@ -3,18 +3,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "base/basictypes.h"
 
 #include "IDBIndex.h"
 
-#include "nsIIDBKeyRange.h"
-
 #include <algorithm>
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ipc/Blob.h"
 #include "mozilla/storage.h"
 #include "nsEventDispatcher.h"
 #include "nsThreadUtils.h"
 #include "xpcpublic.h"
@@ -1986,17 +1984,17 @@ OpenKeyCursorHelper::ReleaseMainThreadOb
 nsresult
 OpenKeyCursorHelper::PackArgumentsForParentProcess(IndexRequestParams& aParams)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   PROFILER_MAIN_THREAD_LABEL("IndexedDB",
                              "OpenKeyCursorHelper::"
-                             "PackArgumentsForParentProcess");
+                             "PackArgumentsForParentProcess [IDBIndex.cpp]");
 
   OpenKeyCursorParams params;
 
   if (mKeyRange) {
     KeyRange keyRange;
     mKeyRange->ToSerializedKeyRange(keyRange);
     params.optionalKeyRange() = keyRange;
   }
--- a/dom/indexedDB/IDBKeyRange.cpp
+++ b/dom/indexedDB/IDBKeyRange.cpp
@@ -11,64 +11,28 @@
 #include "nsIXPConnect.h"
 
 #include "nsJSUtils.h"
 #include "nsThreadUtils.h"
 #include "nsContentUtils.h"
 #include "nsDOMClassInfoID.h"
 #include "Key.h"
 
+#include "mozilla/dom/IDBKeyRangeBinding.h"
 #include "mozilla/dom/indexedDB/PIndexedDBIndex.h"
 #include "mozilla/dom/indexedDB/PIndexedDBObjectStore.h"
 
+using namespace mozilla;
+using namespace mozilla::dom;
 USING_INDEXEDDB_NAMESPACE
 using namespace mozilla::dom::indexedDB::ipc;
 
 namespace {
 
-inline
-bool
-ReturnKeyRange(JSContext* aCx,
-               jsval* aVp,
-               IDBKeyRange* aKeyRange)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(aCx, "Null pointer!");
-  NS_ASSERTION(aVp, "Null pointer!");
-  NS_ASSERTION(aKeyRange, "Null pointer!");
-
-  nsIXPConnect* xpc = nsContentUtils::XPConnect();
-  NS_ASSERTION(xpc, "This should never be null!");
-
-  JSObject* global = JS::CurrentGlobalOrNull(aCx);
-  if (!global) {
-    NS_WARNING("Couldn't get global object!");
-    return false;
-  }
-
-  nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
-  if (NS_FAILED(xpc->WrapNative(aCx, global, aKeyRange,
-                                NS_GET_IID(nsIIDBKeyRange),
-                                getter_AddRefs(holder)))) {
-    JS_ReportError(aCx, "Couldn't wrap IDBKeyRange object.");
-    return false;
-  }
-
-  JS::Rooted<JSObject*> result(aCx, holder->GetJSObject());
-  if (!result) {
-    JS_ReportError(aCx, "Couldn't get JSObject from wrapper.");
-    return false;
-  }
-
-  JS_SET_RVAL(aCx, aVp, OBJECT_TO_JSVAL(result));
-  return true;
-}
-
-inline
-nsresult
+inline nsresult
 GetKeyFromJSVal(JSContext* aCx,
                 jsval aVal,
                 Key& aKey,
                 bool aAllowUnset = false)
 {
   nsresult rv = aKey.SetFromJSVal(aCx, aVal);
   if (NS_FAILED(rv)) {
     NS_ASSERTION(NS_ERROR_GET_MODULE(rv) == NS_ERROR_MODULE_DOM_INDEXEDDB,
@@ -78,218 +42,62 @@ GetKeyFromJSVal(JSContext* aCx,
 
   if (aKey.IsUnset() && !aAllowUnset) {
     return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
   }
 
   return NS_OK;
 }
 
-inline
-void
-ThrowException(JSContext* aCx,
-               nsresult aErrorCode)
-{
-  NS_ASSERTION(NS_FAILED(aErrorCode), "Not an error code!");
-  xpc::Throw(aCx, aErrorCode);
-}
-
-inline
-bool
-GetKeyFromJSValOrThrow(JSContext* aCx,
-                       jsval aVal,
-                       Key& aKey)
-{
-  nsresult rv = GetKeyFromJSVal(aCx, aVal, aKey);
-  if (NS_FAILED(rv)) {
-    ThrowException(aCx, rv);
-    return false;
-  }
-  return true;
-}
-
-bool
-MakeOnlyKeyRange(JSContext* aCx,
-                 unsigned aArgc,
-                 jsval* aVp)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  JS::Rooted<JS::Value> val(aCx);
-  if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v", val.address())) {
-    return false;
-  }
-
-  nsRefPtr<IDBKeyRange> keyRange = new IDBKeyRange(false, false, true);
-
-  if (!GetKeyFromJSValOrThrow(aCx, val, keyRange->Lower())) {
-    return false;
-  }
-
-  return ReturnKeyRange(aCx, aVp, keyRange);
-}
-
-bool
-MakeLowerBoundKeyRange(JSContext* aCx,
-                       unsigned aArgc,
-                       jsval* aVp)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  JS::Rooted<JS::Value> val(aCx);
-  bool open = false;
-  if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v/b", val.address(),
-                           &open)) {
-    return false;
-  }
-
-  nsRefPtr<IDBKeyRange> keyRange = new IDBKeyRange(open, true, false);
-
-  if (!GetKeyFromJSValOrThrow(aCx, val, keyRange->Lower())) {
-    return false;
-  }
-
-  return ReturnKeyRange(aCx, aVp, keyRange);
-}
-
-bool
-MakeUpperBoundKeyRange(JSContext* aCx,
-                       unsigned aArgc,
-                       jsval* aVp)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  JS::Rooted<JS::Value> val(aCx);
-  bool open = false;
-  if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v/b", val.address(),
-                           &open)) {
-    return false;
-  }
-
-  nsRefPtr<IDBKeyRange> keyRange = new IDBKeyRange(true, open, false);
-
-  if (!GetKeyFromJSValOrThrow(aCx, val, keyRange->Upper())) {
-    return false;
-  }
-
-  return ReturnKeyRange(aCx, aVp, keyRange);
-}
-
-bool
-MakeBoundKeyRange(JSContext* aCx,
-                  unsigned aArgc,
-                  jsval* aVp)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  JS::Rooted<JS::Value> lowerVal(aCx), upperVal(aCx);
-  bool lowerOpen = false, upperOpen = false;
-  if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "vv/bb",
-                           lowerVal.address(), upperVal.address(),
-                           &lowerOpen, &upperOpen)) {
-    return false;
-  }
-
-  nsRefPtr<IDBKeyRange> keyRange = new IDBKeyRange(lowerOpen, upperOpen, false);
-
-  if (!GetKeyFromJSValOrThrow(aCx, lowerVal, keyRange->Lower()) ||
-      !GetKeyFromJSValOrThrow(aCx, upperVal, keyRange->Upper())) {
-    return false;
-  }
-
-  if (keyRange->Lower() > keyRange->Upper() ||
-      (keyRange->Lower() == keyRange->Upper() && (lowerOpen || upperOpen))) {
-    ThrowException(aCx, NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
-    return false;
-  }
-
-  return ReturnKeyRange(aCx, aVp, keyRange);
-}
-
-#define KEYRANGE_FUNCTION_FLAGS (JSPROP_ENUMERATE | JSPROP_PERMANENT)
-
-const JSFunctionSpec gKeyRangeConstructors[] = {
-  JS_FN("only", MakeOnlyKeyRange, 1, KEYRANGE_FUNCTION_FLAGS),
-  JS_FN("lowerBound", MakeLowerBoundKeyRange, 1, KEYRANGE_FUNCTION_FLAGS),
-  JS_FN("upperBound", MakeUpperBoundKeyRange, 1, KEYRANGE_FUNCTION_FLAGS),
-  JS_FN("bound", MakeBoundKeyRange, 2, KEYRANGE_FUNCTION_FLAGS),
-  JS_FS_END
-};
-
-#undef KEYRANGE_FUNCTION_FLAGS
-
 } // anonymous namespace
 
 // static
-bool
-IDBKeyRange::DefineConstructors(JSContext* aCx,
-                                JSObject* aObject)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(aCx, "Null pointer!");
-  NS_ASSERTION(aObject, "Null pointer!");
-
-  // Add the constructor methods for key ranges.
-  return JS_DefineFunctions(aCx, aObject, gKeyRangeConstructors);
-}
-
-// static
 nsresult
 IDBKeyRange::FromJSVal(JSContext* aCx,
                        const jsval& aVal,
                        IDBKeyRange** aKeyRange)
 {
-  nsresult rv;
   nsRefPtr<IDBKeyRange> keyRange;
 
-  if (!aVal.isNullOrUndefined()) {
+  if (aVal.isNullOrUndefined()) {
     // undefined and null returns no IDBKeyRange.
+    keyRange.forget(aKeyRange);
+    return NS_OK;
+  }
 
-    JS::RootedObject obj(aCx, aVal.isObject() ? &aVal.toObject() : NULL);
-    if (aVal.isPrimitive() || JS_IsArrayObject(aCx, obj) || JS_ObjectIsDate(aCx, obj)) {
-      // A valid key returns an 'only' IDBKeyRange.
-      keyRange = new IDBKeyRange(false, false, true);
+  JS::RootedObject obj(aCx, aVal.isObject() ? &aVal.toObject() : nullptr);
+  if (aVal.isPrimitive() || JS_IsArrayObject(aCx, obj) ||
+      JS_ObjectIsDate(aCx, obj)) {
+    // A valid key returns an 'only' IDBKeyRange.
+    keyRange = new IDBKeyRange(nullptr, false, false, true);
 
-      rv = GetKeyFromJSVal(aCx, aVal, keyRange->Lower());
-      if (NS_FAILED(rv)) {
-        return rv;
-      }
+    nsresult rv = GetKeyFromJSVal(aCx, aVal, keyRange->Lower());
+    if (NS_FAILED(rv)) {
+      return rv;
     }
-    else {
-      // An object is not permitted unless it's another IDBKeyRange.
-      nsIXPConnect* xpc = nsContentUtils::XPConnect();
-      NS_ASSERTION(xpc, "This should never be null!");
-
-      nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
-      rv = xpc->GetWrappedNativeOfJSObject(aCx, obj, getter_AddRefs(wrapper));
-      if (NS_FAILED(rv)) {
-        return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
-      }
-
-      nsCOMPtr<nsIIDBKeyRange> iface;
-      if (!wrapper || !(iface = do_QueryInterface(wrapper->Native()))) {
-        // Some random JS object?
-        return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
-      }
-
-      keyRange = static_cast<IDBKeyRange*>(iface.get());
+  }
+  else {
+    MOZ_ASSERT(aVal.isObject());
+    // An object is not permitted unless it's another IDBKeyRange.
+    if (NS_FAILED(UNWRAP_OBJECT(IDBKeyRange, aCx, obj, keyRange))) {
+      return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
     }
   }
 
   keyRange.forget(aKeyRange);
   return NS_OK;
 }
 
 // static
 template <class T>
 already_AddRefed<IDBKeyRange>
 IDBKeyRange::FromSerializedKeyRange(const T& aKeyRange)
 {
   nsRefPtr<IDBKeyRange> keyRange =
-    new IDBKeyRange(aKeyRange.lowerOpen(), aKeyRange.upperOpen(),
+    new IDBKeyRange(nullptr, aKeyRange.lowerOpen(), aKeyRange.upperOpen(),
                     aKeyRange.isOnly());
   keyRange->Lower() = aKeyRange.lower();
   if (!keyRange->IsOnly()) {
     keyRange->Upper() = aKeyRange.upper();
   }
   return keyRange.forget();
 }
 
@@ -305,39 +113,37 @@ IDBKeyRange::ToSerializedKeyRange(T& aKe
   if (!IsOnly()) {
     aKeyRange.upper() = Upper();
   }
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBKeyRange)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBKeyRange)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBKeyRange)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedLowerVal)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedUpperVal)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBKeyRange)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
   tmp->DropJSObjects();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBKeyRange)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
-  NS_INTERFACE_MAP_ENTRY(nsIIDBKeyRange)
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(IDBKeyRange)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBKeyRange)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBKeyRange)
 
-DOMCI_DATA(IDBKeyRange, IDBKeyRange)
-
 void
 IDBKeyRange::DropJSObjects()
 {
   if (!mRooted) {
     return;
   }
   mCachedLowerVal = JSVAL_VOID;
   mCachedUpperVal = JSVAL_VOID;
@@ -347,77 +153,148 @@ IDBKeyRange::DropJSObjects()
   mozilla::DropJSObjects(this);
 }
 
 IDBKeyRange::~IDBKeyRange()
 {
   DropJSObjects();
 }
 
-NS_IMETHODIMP
-IDBKeyRange::GetLower(JSContext* aCx,
-                      jsval* aLower)
+JSObject*
+IDBKeyRange::WrapObject(JSContext* aCx, JS::HandleObject aScope)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  return IDBKeyRangeBinding::Wrap(aCx, aScope, this);
+}
+
+JS::Value
+IDBKeyRange::GetLower(JSContext* aCx, ErrorResult& aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
 
   if (!mHaveCachedLowerVal) {
     if (!mRooted) {
       mozilla::HoldJSObjects(this);
       mRooted = true;
     }
 
-    nsresult rv = Lower().ToJSVal(aCx, mCachedLowerVal);
-    NS_ENSURE_SUCCESS(rv, rv);
+    aRv = Lower().ToJSVal(aCx, mCachedLowerVal);
+    if (aRv.Failed()) {
+      return JS::UndefinedValue();
+    }
 
     mHaveCachedLowerVal = true;
   }
 
-  *aLower = mCachedLowerVal;
-  return NS_OK;
+  return mCachedLowerVal;
 }
 
-NS_IMETHODIMP
-IDBKeyRange::GetUpper(JSContext* aCx,
-                      jsval* aUpper)
+JS::Value
+IDBKeyRange::GetUpper(JSContext* aCx, ErrorResult& aRv)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
 
   if (!mHaveCachedUpperVal) {
     if (!mRooted) {
       mozilla::HoldJSObjects(this);
       mRooted = true;
     }
 
-    nsresult rv = Upper().ToJSVal(aCx, mCachedUpperVal);
-    NS_ENSURE_SUCCESS(rv, rv);
+    aRv = Upper().ToJSVal(aCx, mCachedUpperVal);
+    if (aRv.Failed()) {
+      return JS::UndefinedValue();
+    }
 
     mHaveCachedUpperVal = true;
   }
 
-  *aUpper = mCachedUpperVal;
-  return NS_OK;
+  return mCachedUpperVal;
+}
+
+// static
+already_AddRefed<IDBKeyRange>
+IDBKeyRange::Only(const GlobalObject& aGlobal, JSContext* aCx,
+                  JS::HandleValue aValue, ErrorResult& aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+
+  nsRefPtr<IDBKeyRange> keyRange =
+    new IDBKeyRange(aGlobal.GetAsSupports(), false, false, true);
+
+  aRv = GetKeyFromJSVal(aCx, aValue, keyRange->Lower());
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  return keyRange.forget();
+}
+
+// static
+already_AddRefed<IDBKeyRange>
+IDBKeyRange::LowerBound(const GlobalObject& aGlobal, JSContext* aCx,
+                        JS::HandleValue aValue, bool aOpen, ErrorResult& aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+
+  nsRefPtr<IDBKeyRange> keyRange =
+    new IDBKeyRange(aGlobal.GetAsSupports(), aOpen, true, false);
+
+  aRv = GetKeyFromJSVal(aCx, aValue, keyRange->Lower());
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  return keyRange.forget();
 }
 
-NS_IMETHODIMP
-IDBKeyRange::GetLowerOpen(bool* aLowerOpen)
+// static
+already_AddRefed<IDBKeyRange>
+IDBKeyRange::UpperBound(const GlobalObject& aGlobal, JSContext* aCx,
+                        JS::HandleValue aValue, bool aOpen, ErrorResult& aRv)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+
+  nsRefPtr<IDBKeyRange> keyRange =
+    new IDBKeyRange(aGlobal.GetAsSupports(), true, aOpen, false);
 
-  *aLowerOpen = mLowerOpen;
-  return NS_OK;
+  aRv = GetKeyFromJSVal(aCx, aValue, keyRange->Upper());
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  return keyRange.forget();
 }
 
-
-NS_IMETHODIMP
-IDBKeyRange::GetUpperOpen(bool* aUpperOpen)
+// static
+already_AddRefed<IDBKeyRange>
+IDBKeyRange::Bound(const GlobalObject& aGlobal, JSContext* aCx,
+                   JS::HandleValue aLower, JS::HandleValue aUpper,
+                   bool aLowerOpen, bool aUpperOpen, ErrorResult& aRv)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+
+  nsRefPtr<IDBKeyRange> keyRange =
+    new IDBKeyRange(aGlobal.GetAsSupports(), aLowerOpen, aUpperOpen, false);
+
+  aRv = GetKeyFromJSVal(aCx, aLower, keyRange->Lower());
+  if (aRv.Failed()) {
+    return nullptr;
+  }
 
-  *aUpperOpen = mUpperOpen;
-  return NS_OK;
+  aRv = GetKeyFromJSVal(aCx, aUpper, keyRange->Upper());
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  if (keyRange->Lower() > keyRange->Upper() ||
+      (keyRange->Lower() == keyRange->Upper() && (aLowerOpen || aUpperOpen))) {
+    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
+    return nullptr;
+  }
+
+  return keyRange.forget();
 }
 
 // Explicitly instantiate for all our key range types... Grumble.
 template already_AddRefed<IDBKeyRange>
 IDBKeyRange::FromSerializedKeyRange<KeyRange> (const KeyRange& aKeyRange);
 
 template void
 IDBKeyRange::ToSerializedKeyRange<KeyRange> (KeyRange& aKeyRange);
--- a/dom/indexedDB/IDBKeyRange.h
+++ b/dom/indexedDB/IDBKeyRange.h
@@ -5,54 +5,49 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_indexeddb_idbkeyrange_h__
 #define mozilla_dom_indexeddb_idbkeyrange_h__
 
 #include "mozilla/dom/indexedDB/IndexedDatabase.h"
 #include "mozilla/dom/indexedDB/Key.h"
 
-#include "nsIIDBKeyRange.h"
+#include "nsISupports.h"
 
+#include "mozilla/ErrorResult.h"
 #include "nsCycleCollectionParticipant.h"
 
 class mozIStorageStatement;
 
+namespace mozilla {
+namespace dom {
+class GlobalObject;
+} // namespace dom
+} // namespace mozilla
+
 BEGIN_INDEXEDDB_NAMESPACE
 
 namespace ipc {
 class KeyRange;
 } // namespace ipc
 
-class IDBKeyRange MOZ_FINAL : public nsIIDBKeyRange
+class IDBKeyRange MOZ_FINAL : public nsISupports
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_NSIIDBKEYRANGE
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBKeyRange)
 
-  static bool DefineConstructors(JSContext* aCx,
-                                 JSObject* aObject);
-
   static nsresult FromJSVal(JSContext* aCx,
                             const jsval& aVal,
                             IDBKeyRange** aKeyRange);
 
   template <class T>
   static already_AddRefed<IDBKeyRange>
   FromSerializedKeyRange(const T& aKeyRange);
 
-  IDBKeyRange(bool aLowerOpen,
-              bool aUpperOpen,
-              bool aIsOnly)
-  : mCachedLowerVal(JSVAL_VOID), mCachedUpperVal(JSVAL_VOID),
-    mLowerOpen(aLowerOpen), mUpperOpen(aUpperOpen), mIsOnly(aIsOnly),
-    mHaveCachedLowerVal(false), mHaveCachedUpperVal(false), mRooted(false)
-  { }
-
   const Key& Lower() const
   {
     return mLower;
   }
 
   Key& Lower()
   {
     return mLower;
@@ -63,16 +58,17 @@ public:
     return mIsOnly ? mLower : mUpper;
   }
 
   Key& Upper()
   {
     return mIsOnly ? mLower : mUpper;
   }
 
+  // TODO: Remove these in favour of LowerOpen() / UpperOpen(), bug 900578.
   bool IsLowerOpen() const
   {
     return mLowerOpen;
   }
 
   bool IsUpperOpen() const
   {
     return mUpperOpen;
@@ -145,26 +141,81 @@ public:
     return NS_OK;
   }
 
   template <class T>
   void ToSerializedKeyRange(T& aKeyRange);
 
   void DropJSObjects();
 
+  // WebIDL
+  JSObject*
+  WrapObject(JSContext* aCx, JS::HandleObject aScope);
+
+  nsISupports*
+  GetParentObject() const
+  {
+    return mGlobal;
+  }
+
+  JS::Value
+  GetLower(JSContext* aCx, ErrorResult& aRv);
+
+  JS::Value
+  GetUpper(JSContext* aCx, ErrorResult& aRv);
+
+  bool
+  LowerOpen() const
+  {
+    return mLowerOpen;
+  }
+
+  bool
+  UpperOpen() const
+  {
+    return mUpperOpen;
+  }
+
+  static already_AddRefed<IDBKeyRange>
+  Only(const GlobalObject& aGlobal, JSContext* aCx, JS::HandleValue aValue,
+       ErrorResult& aRv);
+
+  static already_AddRefed<IDBKeyRange>
+  LowerBound(const GlobalObject& aGlobal, JSContext* aCx,
+             JS::HandleValue aValue, bool aOpen, ErrorResult& aRv);
+
+  static already_AddRefed<IDBKeyRange>
+  UpperBound(const GlobalObject& aGlobal, JSContext* aCx,
+             JS::HandleValue aValue, bool aOpen, ErrorResult& aRv);
+
+  static already_AddRefed<IDBKeyRange>
+  Bound(const GlobalObject& aGlobal, JSContext* aCx, JS::HandleValue aLower,
+        JS::HandleValue aUpper, bool aLowerOpen, bool aUpperOpen,
+        ErrorResult& aRv);
+
 private:
+  IDBKeyRange(nsISupports* aGlobal,
+              bool aLowerOpen,
+              bool aUpperOpen,
+              bool aIsOnly)
+  : mGlobal(aGlobal), mCachedLowerVal(JSVAL_VOID), mCachedUpperVal(JSVAL_VOID),
+    mLowerOpen(aLowerOpen), mUpperOpen(aUpperOpen), mIsOnly(aIsOnly),
+    mHaveCachedLowerVal(false), mHaveCachedUpperVal(false), mRooted(false)
+  { }
+
   ~IDBKeyRange();
 
+  nsCOMPtr<nsISupports> mGlobal;
   Key mLower;
   Key mUpper;
   JS::Heap<JS::Value> mCachedLowerVal;
   JS::Heap<JS::Value> mCachedUpperVal;
-  bool mLowerOpen;
-  bool mUpperOpen;
-  bool mIsOnly;
+  const bool mLowerOpen;
+  const bool mUpperOpen;
+  const bool mIsOnly;
   bool mHaveCachedLowerVal;
   bool mHaveCachedUpperVal;
   bool mRooted;
 };
 
 END_INDEXEDDB_NAMESPACE
 
 #endif // mozilla_dom_indexeddb_idbkeyrange_h__
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -340,16 +340,67 @@ private:
   nsCString mContinueToQuery;
   Key mRangeKey;
 
   // Only used in the parent process.
   nsRefPtr<IDBCursor> mCursor;
   SerializedStructuredCloneReadInfo mSerializedCloneReadInfo;
 };
 
+class OpenKeyCursorHelper MOZ_FINAL : public ObjectStoreHelper
+{
+public:
+  OpenKeyCursorHelper(IDBTransaction* aTransaction,
+                      IDBRequest* aRequest,
+                      IDBObjectStore* aObjectStore,
+                      IDBKeyRange* aKeyRange,
+                      IDBCursor::Direction aDirection)
+  : ObjectStoreHelper(aTransaction, aRequest, aObjectStore),
+    mKeyRange(aKeyRange), mDirection(aDirection)
+  { }
+
+  virtual nsresult
+  DoDatabaseWork(mozIStorageConnection* aConnection) MOZ_OVERRIDE;
+
+  virtual nsresult
+  GetSuccessResult(JSContext* aCx, JS::MutableHandleValue aVal) MOZ_OVERRIDE;
+
+  virtual void
+  ReleaseMainThreadObjects() MOZ_OVERRIDE;
+
+  virtual nsresult
+  PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams) MOZ_OVERRIDE;
+
+  virtual ChildProcessSendResult
+  SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE;
+
+  virtual nsresult
+  UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
+                                  MOZ_OVERRIDE;
+
+private:
+  ~OpenKeyCursorHelper()
+  { }
+
+  nsresult EnsureCursor();
+
+  // In-params.
+  nsRefPtr<IDBKeyRange> mKeyRange;
+  const IDBCursor::Direction mDirection;
+
+  // Out-params.
+  Key mKey;
+  nsCString mContinueQuery;
+  nsCString mContinueToQuery;
+  Key mRangeKey;
+
+  // Only used in the parent process.
+  nsRefPtr<IDBCursor> mCursor;
+};
+
 class CreateIndexHelper : public NoRequestObjectStoreHelper
 {
 public:
   CreateIndexHelper(IDBTransaction* aTransaction, IDBIndex* aIndex)
   : NoRequestObjectStoreHelper(aTransaction, aIndex->ObjectStore()),
     mIndex(aIndex)
   {
     if (sTLSIndex == BAD_TLS_INDEX) {
@@ -2363,16 +2414,79 @@ IDBObjectStore::OpenCursorFromChildProce
   NS_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   NS_ASSERTION(!cloneInfo.mCloneBuffer.data(), "Should have swapped!");
 
   cursor.forget(_retval);
   return NS_OK;
 }
 
+nsresult
+IDBObjectStore::OpenCursorFromChildProcess(IDBRequest* aRequest,
+                                           size_t aDirection,
+                                           const Key& aKey,
+                                           IDBCursor** _retval)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aRequest);
+
+  auto direction = static_cast<IDBCursor::Direction>(aDirection);
+
+  nsRefPtr<IDBCursor> cursor =
+    IDBCursor::Create(aRequest, mTransaction, this, direction, Key(),
+                      EmptyCString(), EmptyCString(), aKey);
+  NS_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
+  cursor.forget(_retval);
+  return NS_OK;
+}
+
+already_AddRefed<IDBRequest>
+IDBObjectStore::OpenKeyCursorInternal(IDBKeyRange* aKeyRange, size_t aDirection,
+                                      ErrorResult& aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!mTransaction->IsOpen()) {
+    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
+    return nullptr;
+  }
+
+  nsRefPtr<IDBRequest> request = GenerateRequest(this);
+  if (!request) {
+    NS_WARNING("Failed to generate request!");
+    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+    return nullptr;
+  }
+
+  auto direction = static_cast<IDBCursor::Direction>(aDirection);
+
+  nsRefPtr<OpenKeyCursorHelper> helper =
+    new OpenKeyCursorHelper(mTransaction, request, this, aKeyRange, direction);
+
+  nsresult rv = helper->DispatchToTransactionPool();
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed to dispatch!");
+    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+    return nullptr;
+  }
+
+  IDB_PROFILER_MARK("IndexedDB Request %llu: "
+                    "database(%s).transaction(%s).objectStore(%s)."
+                    "openKeyCursor(%s, %s)",
+                    "IDBRequest[%llu] MT IDBObjectStore.openKeyCursor()",
+                    request->GetSerialNumber(),
+                    IDB_PROFILER_STRING(Transaction()->Database()),
+                    IDB_PROFILER_STRING(Transaction()),
+                    IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange),
+                    IDB_PROFILER_STRING(direction));
+
+  return request.forget();
+}
+
 void
 IDBObjectStore::SetInfo(ObjectStoreInfo* aInfo)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread");
   NS_ASSERTION(aInfo != mInfo, "This is nonsense");
 
   mInfo = aInfo;
 }
@@ -2870,16 +2984,39 @@ IDBObjectStore::GetAllKeys(JSContext* aC
   uint32_t limit = UINT32_MAX;
   if (aLimit.WasPassed() && aLimit.Value() != 0) {
     limit = aLimit.Value();
   }
 
   return GetAllKeysInternal(keyRange, limit, aRv);
 }
 
+already_AddRefed<IDBRequest>
+IDBObjectStore::OpenKeyCursor(JSContext* aCx,
+                              const Optional<JS::HandleValue>& aRange,
+                              IDBCursorDirection aDirection,  ErrorResult& aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!mTransaction->IsOpen()) {
+    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
+    return nullptr;
+  }
+
+  nsRefPtr<IDBKeyRange> keyRange;
+  if (aRange.WasPassed()) {
+    aRv = IDBKeyRange::FromJSVal(aCx, aRange.Value(), getter_AddRefs(keyRange));
+    ENSURE_SUCCESS(aRv, nullptr);
+  }
+
+  IDBCursor::Direction direction = IDBCursor::ConvertDirection(aDirection);
+
+  return OpenKeyCursorInternal(keyRange, static_cast<size_t>(direction), aRv);
+}
+
 inline nsresult
 CopyData(nsIInputStream* aInputStream, nsIOutputStream* aOutputStream)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   PROFILER_LABEL("IndexedDB", "CopyData");
 
@@ -3907,17 +4044,17 @@ OpenCursorHelper::SendResponseToChildPro
       NS_ASSERTION(mSerializedCloneReadInfo.data &&
                    mSerializedCloneReadInfo.dataLength,
                    "Shouldn't be possible!");
 
       ObjectStoreCursorConstructorParams params;
       params.requestParent() = requestActor;
       params.direction() = mDirection;
       params.key() = mKey;
-      params.cloneInfo() = mSerializedCloneReadInfo;
+      params.optionalCloneInfo() = mSerializedCloneReadInfo;
       params.blobsParent().SwapElements(blobsParent);
 
       if (!objectStoreActor->OpenCursor(mCursor, params, openCursorResponse)) {
         return Error;
       }
     }
 
     response = openCursorResponse;
@@ -3964,16 +4101,321 @@ OpenCursorHelper::UnpackResponseFromPare
       NS_NOTREACHED("Unknown response union type!");
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
   return NS_OK;
 }
 
 nsresult
+OpenKeyCursorHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
+
+  PROFILER_LABEL("IndexedDB",
+                 "OpenKeyCursorHelper::DoDatabaseWork [IDBObjectStore.cpp]");
+
+  NS_NAMED_LITERAL_CSTRING(keyValue, "key_value");
+  NS_NAMED_LITERAL_CSTRING(id, "id");
+  NS_NAMED_LITERAL_CSTRING(openLimit, " LIMIT ");
+
+  nsAutoCString queryStart = NS_LITERAL_CSTRING("SELECT ") + keyValue +
+                             NS_LITERAL_CSTRING(" FROM object_data WHERE "
+                                                "object_store_id = :") +
+                             id;
+
+  nsAutoCString keyRangeClause;
+  if (mKeyRange) {
+    mKeyRange->GetBindingClause(keyValue, keyRangeClause);
+  }
+
+  nsAutoCString directionClause = NS_LITERAL_CSTRING(" ORDER BY ") + keyValue;
+  switch (mDirection) {
+    case IDBCursor::NEXT:
+    case IDBCursor::NEXT_UNIQUE:
+      directionClause.AppendLiteral(" ASC");
+      break;
+
+    case IDBCursor::PREV:
+    case IDBCursor::PREV_UNIQUE:
+      directionClause.AppendLiteral(" DESC");
+      break;
+
+    default:
+      MOZ_ASSUME_UNREACHABLE("Unknown direction type!");
+  }
+
+  nsCString firstQuery = queryStart + keyRangeClause + directionClause +
+                         openLimit + NS_LITERAL_CSTRING("1");
+
+  nsCOMPtr<mozIStorageStatement> stmt =
+    mTransaction->GetCachedStatement(firstQuery);
+  NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
+  mozStorageStatementScoper scoper(stmt);
+
+  nsresult rv = stmt->BindInt64ByName(id, mObjectStore->Id());
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
+  if (mKeyRange) {
+    rv = mKeyRange->BindToStatement(stmt);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  bool hasResult;
+  rv = stmt->ExecuteStep(&hasResult);
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
+  if (!hasResult) {
+    mKey.Unset();
+    return NS_OK;
+  }
+
+  rv = mKey.SetFromStatement(stmt, 0);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Now we need to make the query to get the next match.
+  keyRangeClause.Truncate();
+  nsAutoCString continueToKeyRangeClause;
+
+  NS_NAMED_LITERAL_CSTRING(currentKey, "current_key");
+  NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key");
+
+  switch (mDirection) {
+    case IDBCursor::NEXT:
+    case IDBCursor::NEXT_UNIQUE:
+      AppendConditionClause(keyValue, currentKey, false, false,
+                            keyRangeClause);
+      AppendConditionClause(keyValue, currentKey, false, true,
+                            continueToKeyRangeClause);
+      if (mKeyRange && !mKeyRange->Upper().IsUnset()) {
+        AppendConditionClause(keyValue, rangeKey, true,
+                              !mKeyRange->IsUpperOpen(), keyRangeClause);
+        AppendConditionClause(keyValue, rangeKey, true,
+                              !mKeyRange->IsUpperOpen(),
+                              continueToKeyRangeClause);
+        mRangeKey = mKeyRange->Upper();
+      }
+      break;
+
+    case IDBCursor::PREV:
+    case IDBCursor::PREV_UNIQUE:
+      AppendConditionClause(keyValue, currentKey, true, false, keyRangeClause);
+      AppendConditionClause(keyValue, currentKey, true, true,
+                            continueToKeyRangeClause);
+      if (mKeyRange && !mKeyRange->Lower().IsUnset()) {
+        AppendConditionClause(keyValue, rangeKey, false,
+                              !mKeyRange->IsLowerOpen(), keyRangeClause);
+        AppendConditionClause(keyValue, rangeKey, false,
+                              !mKeyRange->IsLowerOpen(),
+                              continueToKeyRangeClause);
+        mRangeKey = mKeyRange->Lower();
+      }
+      break;
+
+    default:
+      MOZ_ASSUME_UNREACHABLE("Unknown direction type!");
+  }
+
+  mContinueQuery = queryStart + keyRangeClause + directionClause + openLimit;
+  mContinueToQuery = queryStart + continueToKeyRangeClause + directionClause +
+                     openLimit;
+
+  return NS_OK;
+}
+
+nsresult
+OpenKeyCursorHelper::EnsureCursor()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  PROFILER_MAIN_THREAD_LABEL("IndexedDB",
+                             "OpenKeyCursorHelper::EnsureCursor "
+                             "[IDBObjectStore.cpp]");
+
+  if (mCursor || mKey.IsUnset()) {
+    return NS_OK;
+  }
+
+  mCursor = IDBCursor::Create(mRequest, mTransaction, mObjectStore, mDirection,
+                              mRangeKey, mContinueQuery, mContinueToQuery,
+                              mKey);
+  NS_ENSURE_TRUE(mCursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
+  return NS_OK;
+}
+
+nsresult
+OpenKeyCursorHelper::GetSuccessResult(JSContext* aCx,
+                                      JS::MutableHandleValue aVal)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  PROFILER_MAIN_THREAD_LABEL("IndexedDB",
+                             "OpenKeyCursorHelper::GetSuccessResult "
+                             "[IDBObjectStore.cpp]");
+
+  nsresult rv = EnsureCursor();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (mCursor) {
+    rv = WrapNative(aCx, mCursor, aVal);
+    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+  }
+  else {
+    aVal.setUndefined();
+  }
+
+  return NS_OK;
+}
+
+void
+OpenKeyCursorHelper::ReleaseMainThreadObjects()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  mKeyRange = nullptr;
+  mCursor = nullptr;
+
+  ObjectStoreHelper::ReleaseMainThreadObjects();
+}
+
+nsresult
+OpenKeyCursorHelper::PackArgumentsForParentProcess(
+                                              ObjectStoreRequestParams& aParams)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!IndexedDatabaseManager::IsMainProcess());
+
+  PROFILER_MAIN_THREAD_LABEL("IndexedDB",
+                             "OpenKeyCursorHelper::"
+                             "PackArgumentsForParentProcess "
+                             "[IDBObjectStore.cpp]");
+
+  OpenKeyCursorParams params;
+
+  if (mKeyRange) {
+    KeyRange keyRange;
+    mKeyRange->ToSerializedKeyRange(keyRange);
+    params.optionalKeyRange() = keyRange;
+  }
+  else {
+    params.optionalKeyRange() = mozilla::void_t();
+  }
+
+  params.direction() = mDirection;
+
+  aParams = params;
+  return NS_OK;
+}
+
+AsyncConnectionHelper::ChildProcessSendResult
+OpenKeyCursorHelper::SendResponseToChildProcess(nsresult aResultCode)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
+  MOZ_ASSERT(!mCursor);
+
+  PROFILER_MAIN_THREAD_LABEL("IndexedDB",
+                             "OpenKeyCursorHelper::SendResponseToChildProcess "
+                             "[IDBObjectStore.cpp]");
+
+  IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
+  MOZ_ASSERT(actor);
+
+  if (NS_SUCCEEDED(aResultCode)) {
+    nsresult rv = EnsureCursor();
+    if (NS_FAILED(rv)) {
+      NS_WARNING("EnsureCursor failed!");
+      aResultCode = rv;
+    }
+  }
+
+  ResponseValue response;
+  if (NS_FAILED(aResultCode)) {
+    response = aResultCode;
+  } else {
+    OpenCursorResponse openCursorResponse;
+
+    if (!mCursor) {
+      openCursorResponse = mozilla::void_t();
+    }
+    else {
+      IndexedDBObjectStoreParent* objectStoreActor =
+        mObjectStore->GetActorParent();
+      MOZ_ASSERT(objectStoreActor);
+
+      IndexedDBRequestParentBase* requestActor = mRequest->GetActorParent();
+      MOZ_ASSERT(requestActor);
+
+      ObjectStoreCursorConstructorParams params;
+      params.requestParent() = requestActor;
+      params.direction() = mDirection;
+      params.key() = mKey;
+      params.optionalCloneInfo() = mozilla::void_t();
+
+      if (!objectStoreActor->OpenCursor(mCursor, params, openCursorResponse)) {
+        return Error;
+      }
+    }
+
+    response = openCursorResponse;
+  }
+
+  if (!actor->SendResponse(response)) {
+    return Error;
+  }
+
+  return Success_Sent;
+}
+
+nsresult
+OpenKeyCursorHelper::UnpackResponseFromParentProcess(
+                                            const ResponseValue& aResponseValue)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!IndexedDatabaseManager::IsMainProcess());
+  MOZ_ASSERT(aResponseValue.type() == ResponseValue::TOpenCursorResponse);
+  MOZ_ASSERT(aResponseValue.get_OpenCursorResponse().type() ==
+               OpenCursorResponse::Tvoid_t ||
+             aResponseValue.get_OpenCursorResponse().type() ==
+               OpenCursorResponse::TPIndexedDBCursorChild);
+  MOZ_ASSERT(!mCursor);
+
+  PROFILER_MAIN_THREAD_LABEL("IndexedDB",
+                             "OpenKeyCursorHelper::"
+                             "UnpackResponseFromParentProcess "
+                             "[IDBObjectStore.cpp]");
+
+  const OpenCursorResponse& response =
+    aResponseValue.get_OpenCursorResponse();
+
+  switch (response.type()) {
+    case OpenCursorResponse::Tvoid_t:
+      break;
+
+    case OpenCursorResponse::TPIndexedDBCursorChild: {
+      IndexedDBCursorChild* actor =
+        static_cast<IndexedDBCursorChild*>(
+          response.get_PIndexedDBCursorChild());
+
+      mCursor = actor->ForgetStrongCursor();
+      NS_ASSERTION(mCursor, "This should never be null!");
+
+    } break;
+
+    default:
+      MOZ_CRASH("Unknown response union type!");
+  }
+
+  return NS_OK;
+}
+
+nsresult
 CreateIndexHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   PROFILER_LABEL("IndexedDB", "CreateIndexHelper::DoDatabaseWork");
 
   if (IndexedDatabaseManager::InLowDiskSpaceMode()) {
--- a/dom/indexedDB/IDBObjectStore.h
+++ b/dom/indexedDB/IDBObjectStore.h
@@ -234,25 +234,36 @@ public:
   CountInternal(IDBKeyRange* aKeyRange,
                 ErrorResult& aRv);
 
   already_AddRefed<IDBRequest>
   OpenCursorInternal(IDBKeyRange* aKeyRange,
                      size_t aDirection,
                      ErrorResult& aRv);
 
+  already_AddRefed<IDBRequest>
+  OpenKeyCursorInternal(IDBKeyRange* aKeyRange,
+                        size_t aDirection,
+                        ErrorResult& aRv);
+
   nsresult
   OpenCursorFromChildProcess(
                             IDBRequest* aRequest,
                             size_t aDirection,
                             const Key& aKey,
                             const SerializedStructuredCloneReadInfo& aCloneInfo,
                             nsTArray<StructuredCloneFile>& aBlobs,
                             IDBCursor** _retval);
 
+  nsresult
+  OpenCursorFromChildProcess(IDBRequest* aRequest,
+                             size_t aDirection,
+                             const Key& aKey,
+                             IDBCursor** _retval);
+
   void
   SetInfo(ObjectStoreInfo* aInfo);
 
   static const JSClass sDummyPropJSClass;
 
   // nsWrapperCache
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
@@ -342,16 +353,20 @@ public:
   already_AddRefed<IDBRequest>
   GetAll(JSContext* aCx, const Optional<JS::Handle<JS::Value> >& aKey,
          const Optional<uint32_t>& aLimit, ErrorResult& aRv);
 
   already_AddRefed<IDBRequest>
   GetAllKeys(JSContext* aCx, const Optional<JS::HandleValue>& aKey,
              const Optional<uint32_t>& aLimit, ErrorResult& aRv);
 
+  already_AddRefed<IDBRequest>
+  OpenKeyCursor(JSContext* aCx, const Optional<JS::HandleValue>& aRange,
+                IDBCursorDirection aDirection, ErrorResult& aRv);
+
 protected:
   IDBObjectStore();
   ~IDBObjectStore();
 
   nsresult GetAddInfo(JSContext* aCx,
                       JS::Handle<JS::Value> aValue,
                       JS::Handle<JS::Value> aKeyVal,
                       StructuredCloneWriteInfo& aCloneWriteInfo,
--- a/dom/indexedDB/IndexedDatabaseManager.cpp
+++ b/dom/indexedDB/IndexedDatabaseManager.cpp
@@ -32,23 +32,27 @@
 #include "IDBFactory.h"
 #include "IDBKeyRange.h"
 #include "IDBRequest.h"
 
 // Bindings for ResolveConstructors
 #include "mozilla/dom/IDBCursorBinding.h"
 #include "mozilla/dom/IDBDatabaseBinding.h"
 #include "mozilla/dom/IDBFactoryBinding.h"
+#include "mozilla/dom/IDBFileHandleBinding.h"
+#include "mozilla/dom/IDBKeyRangeBinding.h"
 #include "mozilla/dom/IDBIndexBinding.h"
 #include "mozilla/dom/IDBObjectStoreBinding.h"
 #include "mozilla/dom/IDBOpenDBRequestBinding.h"
 #include "mozilla/dom/IDBRequestBinding.h"
 #include "mozilla/dom/IDBTransactionBinding.h"
 #include "mozilla/dom/IDBVersionChangeEventBinding.h"
 
+#define IDB_STR "indexedDB"
+
 // The two possible values for the data argument when receiving the disk space
 // observer notification.
 #define LOW_DISK_SPACE_DATA_FULL "full"
 #define LOW_DISK_SPACE_DATA_FREE "free"
 
 USING_INDEXEDDB_NAMESPACE
 using namespace mozilla::dom;
 USING_QUOTA_NAMESPACE
@@ -115,24 +119,27 @@ struct ConstructorInfo {
   jsid id;
 };
 
 ConstructorInfo gConstructorInfo[] = {
 
 #define BINDING_ENTRY(_name) \
   { #_name, _name##Binding::GetConstructorObject, JSID_VOID },
 
-  BINDING_ENTRY(IDBFactory)
+  BINDING_ENTRY(IDBCursor)
+  BINDING_ENTRY(IDBCursorWithValue)
   BINDING_ENTRY(IDBDatabase)
-  BINDING_ENTRY(IDBTransaction)
+  BINDING_ENTRY(IDBFactory)
+  BINDING_ENTRY(IDBFileHandle)
+  BINDING_ENTRY(IDBIndex)
+  BINDING_ENTRY(IDBKeyRange)
   BINDING_ENTRY(IDBObjectStore)
-  BINDING_ENTRY(IDBIndex)
-  BINDING_ENTRY(IDBCursor)
+  BINDING_ENTRY(IDBOpenDBRequest)
   BINDING_ENTRY(IDBRequest)
-  BINDING_ENTRY(IDBOpenDBRequest)
+  BINDING_ENTRY(IDBTransaction)
   BINDING_ENTRY(IDBVersionChangeEvent)
 
 #undef BINDING_ENTRY
 };
 
 class AsyncDeleteFileRunnable MOZ_FINAL : public nsIRunnable
 {
 public:
@@ -195,16 +202,60 @@ struct MOZ_STACK_CLASS InvalidateInfo
   InvalidateInfo(PersistenceType aPersistenceType, const nsACString& aPattern)
   : persistenceType(aPersistenceType), pattern(aPattern)
   { }
 
   PersistenceType persistenceType;
   const nsACString& pattern;
 };
 
+bool
+GetIndexedDB(JSContext* aCx, JS::HandleObject aGlobal,
+             JS::MutableHandleValue aResult)
+{
+  MOZ_ASSERT(nsContentUtils::IsCallerChrome(), "Only for chrome!");
+  MOZ_ASSERT(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL,
+             "Not a global object!");
+
+  nsRefPtr<IDBFactory> factory;
+  if (NS_FAILED(IDBFactory::Create(aCx, aGlobal, nullptr,
+                                   getter_AddRefs(factory)))) {
+    return false;
+  }
+
+  MOZ_ASSERT(factory, "This should never fail for chrome!");
+
+  return !!WrapNewBindingObject(aCx, aGlobal, factory, aResult);
+}
+
+bool
+IndexedDBLazyGetter(JSContext* aCx, JS::HandleObject aGlobal,
+                    JS::HandleId aId, JS::MutableHandleValue aVp)
+{
+  MOZ_ASSERT(nsContentUtils::IsCallerChrome(), "Only for chrome!");
+  MOZ_ASSERT(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL,
+             "Not a global object!");
+  MOZ_ASSERT(JSID_IS_STRING(aId), "Bad id!");
+  MOZ_ASSERT(JS_FlatStringEqualsAscii(JSID_TO_FLAT_STRING(aId), IDB_STR),
+             "Bad id!");
+
+  JS::RootedValue indexedDB(aCx);
+  if (!GetIndexedDB(aCx, aGlobal, &indexedDB)) {
+    return false;
+  }
+
+  if (!JS_DefinePropertyById(aCx, aGlobal, aId, indexedDB, nullptr, nullptr,
+                             JSPROP_ENUMERATE)) {
+    return false;
+  }
+
+  aVp.set(indexedDB);
+  return true;
+}
+
 } // anonymous namespace
 
 IndexedDatabaseManager::IndexedDatabaseManager()
 : mFileMutex("IndexedDatabaseManager.mFileMutex")
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 }
 
@@ -423,16 +474,65 @@ IndexedDatabaseManager::TabContextMayAcc
                                                 aContext.IsBrowserElement(),
                                                 pattern);
 
   return PatternMatchesOrigin(pattern, aOrigin);
 }
 
 // static
 bool
+IndexedDatabaseManager::DefineConstructors(JSContext* aCx,
+                                           JS::HandleObject aGlobal)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  for (uint32_t i = 0; i < mozilla::ArrayLength(gConstructorInfo); i++) {
+    if (!gConstructorInfo[i].resolve(aCx, aGlobal, true)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+// static
+bool
+IndexedDatabaseManager::DefineIndexedDBGetter(JSContext* aCx,
+                                              JS::HandleObject aGlobal)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(nsContentUtils::IsCallerChrome(), "Only for chrome!");
+  MOZ_ASSERT(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL,
+             "Passed object is not a global object!");
+
+  JS::RootedValue indexedDB(aCx);
+  if (!GetIndexedDB(aCx, aGlobal, &indexedDB)) {
+    return false;
+  }
+
+  return JS_DefineProperty(aCx, aGlobal, IDB_STR, indexedDB, nullptr, nullptr,
+                           JSPROP_ENUMERATE);
+}
+
+// static
+bool
+IndexedDatabaseManager::DefineIndexedDBLazyGetter(JSContext* aCx,
+                                                  JS::HandleObject aGlobal)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(nsContentUtils::IsCallerChrome(), "Only for chrome!");
+  MOZ_ASSERT(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL,
+             "Passed object is not a global object!");
+
+  return JS_DefineProperty(aCx, aGlobal, IDB_STR, JSVAL_VOID,
+                           IndexedDBLazyGetter, nullptr, 0);
+}
+
+// static
+bool
 IndexedDatabaseManager::IsClosed()
 {
   return !!gClosed;
 }
 
 #ifdef DEBUG
 // static
 bool
@@ -621,62 +721,37 @@ IndexedDatabaseManager::BlockAndGetFileR
 }
 
 NS_IMPL_ADDREF(IndexedDatabaseManager)
 NS_IMPL_RELEASE_WITH_DESTROY(IndexedDatabaseManager, Destroy())
 NS_IMPL_QUERY_INTERFACE2(IndexedDatabaseManager, nsIIndexedDatabaseManager,
                                                  nsIObserver)
 
 NS_IMETHODIMP
-IndexedDatabaseManager::InitWindowless(const jsval& aObj, JSContext* aCx)
+IndexedDatabaseManager::InitWindowless(const jsval& aGlobal, JSContext* aCx)
 {
   NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
-  NS_ENSURE_ARG(!JSVAL_IS_PRIMITIVE(aObj));
 
-  JS::Rooted<JSObject*> obj(aCx, JSVAL_TO_OBJECT(aObj));
+  JS::RootedObject global(aCx, JSVAL_TO_OBJECT(aGlobal));
+  if (!(js::GetObjectClass(global)->flags & JSCLASS_DOM_GLOBAL)) {
+    NS_WARNING("Passed object is not a global object!");
+    return NS_ERROR_FAILURE;
+  }
 
   bool hasIndexedDB;
-  if (!JS_HasProperty(aCx, obj, "indexedDB", &hasIndexedDB)) {
+  if (!JS_HasProperty(aCx, global, IDB_STR, &hasIndexedDB)) {
     return NS_ERROR_FAILURE;
   }
 
   if (hasIndexedDB) {
     NS_WARNING("Passed object already has an 'indexedDB' property!");
     return NS_ERROR_FAILURE;
   }
 
-  JS::Rooted<JSObject*> global(aCx, JS_GetGlobalForObject(aCx, obj));
-  NS_ASSERTION(global, "What?! No global!");
-
-  nsRefPtr<IDBFactory> factory;
-  nsresult rv =
-    IDBFactory::Create(aCx, global, nullptr, getter_AddRefs(factory));
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-  NS_ASSERTION(factory, "This should never fail for chrome!");
-
-  JS::Rooted<JS::Value> indexedDBVal(aCx);
-  rv = nsContentUtils::WrapNative(aCx, obj, factory, indexedDBVal.address());
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (!JS_DefineProperty(aCx, obj, "indexedDB", indexedDBVal, nullptr,
-                         nullptr, JSPROP_ENUMERATE)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  JS::Rooted<JSObject*> keyrangeObj(aCx,
-    JS_NewObject(aCx, nullptr, nullptr, nullptr));
-  NS_ENSURE_TRUE(keyrangeObj, NS_ERROR_OUT_OF_MEMORY);
-
-  if (!IDBKeyRange::DefineConstructors(aCx, keyrangeObj)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  if (!JS_DefineProperty(aCx, obj, "IDBKeyRange", OBJECT_TO_JSVAL(keyrangeObj),
-                         nullptr, nullptr, JSPROP_ENUMERATE)) {
+  if (!DefineConstructors(aCx, global) || !DefineIndexedDBGetter(aCx, global)) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IndexedDatabaseManager::Observe(nsISupports* aSubject, const char* aTopic,
--- a/dom/indexedDB/IndexedDatabaseManager.h
+++ b/dom/indexedDB/IndexedDatabaseManager.h
@@ -134,16 +134,25 @@ public:
   static nsresult
   FireWindowOnError(nsPIDOMWindow* aOwner,
                     nsEventChainPostVisitor& aVisitor);
 
   static bool
   TabContextMayAccessOrigin(const mozilla::dom::TabContext& aContext,
                             const nsACString& aOrigin);
 
+  static bool
+  DefineConstructors(JSContext* aCx, JS::HandleObject aGlobal);
+
+  static bool
+  DefineIndexedDBGetter(JSContext* aCx, JS::HandleObject aGlobal);
+
+  static bool
+  DefineIndexedDBLazyGetter(JSContext* aCx, JS::HandleObject aGlobal);
+
 private:
   IndexedDatabaseManager();
   ~IndexedDatabaseManager();
 
   nsresult
   Init();
 
   void
--- a/dom/indexedDB/ipc/IndexedDBChild.cpp
+++ b/dom/indexedDB/ipc/IndexedDBChild.cpp
@@ -737,27 +737,50 @@ IndexedDBObjectStoreChild::RecvPIndexedD
     static_cast<IndexedDBObjectStoreRequestChild*>(aParams.requestChild());
   NS_ASSERTION(requestActor, "Must have an actor here!");
 
   nsRefPtr<IDBRequest> request = requestActor->GetRequest();
   NS_ASSERTION(request, "Must have a request here!");
 
   size_t direction = static_cast<size_t>(aParams.direction());
 
-  nsTArray<StructuredCloneFile> blobs;
-  IDBObjectStore::ConvertActorsToBlobs(aParams.blobsChild(), blobs);
+  nsRefPtr<IDBCursor> cursor;
+  nsresult rv;
+
+  typedef ipc::OptionalStructuredCloneReadInfo CursorUnionType;
+
+  switch (aParams.optionalCloneInfo().type()) {
+    case CursorUnionType::TSerializedStructuredCloneReadInfo: {
+      nsTArray<StructuredCloneFile> blobs;
+      IDBObjectStore::ConvertActorsToBlobs(aParams.blobsChild(), blobs);
+
+      const SerializedStructuredCloneReadInfo& cloneInfo =
+        aParams.optionalCloneInfo().get_SerializedStructuredCloneReadInfo();
 
-  nsRefPtr<IDBCursor> cursor;
-  nsresult rv =
-    mObjectStore->OpenCursorFromChildProcess(request, direction, aParams.key(),
-                                             aParams.cloneInfo(), blobs,
-                                             getter_AddRefs(cursor));
-  NS_ENSURE_SUCCESS(rv, false);
+      rv = mObjectStore->OpenCursorFromChildProcess(request, direction,
+                                                    aParams.key(), cloneInfo,
+                                                    blobs,
+                                                    getter_AddRefs(cursor));
+      NS_ENSURE_SUCCESS(rv, false);
+
+      MOZ_ASSERT(blobs.IsEmpty(), "Should have swapped blob elements!");
+    } break;
 
-  MOZ_ASSERT(blobs.IsEmpty(), "Should have swapped blob elements!");
+    case CursorUnionType::Tvoid_t:
+      MOZ_ASSERT(aParams.blobsChild().IsEmpty());
+
+      rv = mObjectStore->OpenCursorFromChildProcess(request, direction,
+                                                    aParams.key(),
+                                                    getter_AddRefs(cursor));
+      NS_ENSURE_SUCCESS(rv, false);
+      break;
+
+    default:
+      MOZ_CRASH("Unknown union type!");
+  }
 
   actor->SetCursor(cursor);
   return true;
 }
 
 PIndexedDBRequestChild*
 IndexedDBObjectStoreChild::AllocPIndexedDBRequestChild(
                                         const ObjectStoreRequestParams& aParams)
@@ -1083,17 +1106,18 @@ IndexedDBObjectStoreRequestChild::Recv__
       break;
     case ResponseValue::TClearResponse:
       MOZ_ASSERT(mRequestType == ParamsUnionType::TClearParams);
       break;
     case ResponseValue::TCountResponse:
       MOZ_ASSERT(mRequestType == ParamsUnionType::TCountParams);
       break;
     case ResponseValue::TOpenCursorResponse:
-      MOZ_ASSERT(mRequestType == ParamsUnionType::TOpenCursorParams);
+      MOZ_ASSERT(mRequestType == ParamsUnionType::TOpenCursorParams ||
+                 mRequestType == ParamsUnionType::TOpenKeyCursorParams);
       break;
 
     default:
       MOZ_CRASH("Received invalid response parameters!");
   }
 
   nsresult rv = mHelper->OnParentProcessRequestComplete(aResponse);
   NS_ENSURE_SUCCESS(rv, false);
--- a/dom/indexedDB/ipc/IndexedDBParams.ipdlh
+++ b/dom/indexedDB/ipc/IndexedDBParams.ipdlh
@@ -1,16 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include "mozilla/dom/indexedDB/SerializationHelpers.h";
 
 using mozilla::dom::indexedDB::Key;
 using mozilla::dom::indexedDB::IDBCursor::Direction;
+using mozilla::dom::indexedDB::SerializedStructuredCloneReadInfo;
 
 using mozilla::void_t;
 
 namespace mozilla {
 namespace dom {
 namespace indexedDB {
 namespace ipc {
 
@@ -52,12 +53,24 @@ struct CountParams
 };
 
 struct OpenCursorParams
 {
   OptionalKeyRange optionalKeyRange;
   Direction direction;
 };
 
+struct OpenKeyCursorParams
+{
+  OptionalKeyRange optionalKeyRange;
+  Direction direction;
+};
+
+union OptionalStructuredCloneReadInfo
+{
+  SerializedStructuredCloneReadInfo;
+  void_t;
+};
+
 } // namespace ipc
 } // namespace indexedDB
 } // namespace dom
 } // namespace mozilla
--- a/dom/indexedDB/ipc/IndexedDBParent.cpp
+++ b/dom/indexedDB/ipc/IndexedDBParent.cpp
@@ -1113,16 +1113,19 @@ IndexedDBObjectStoreParent::RecvPIndexed
       return actor->Clear(aParams.get_ClearParams());
 
     case ObjectStoreRequestParams::TCountParams:
       return actor->Count(aParams.get_CountParams());
 
     case ObjectStoreRequestParams::TOpenCursorParams:
       return actor->OpenCursor(aParams.get_OpenCursorParams());
 
+    case ObjectStoreRequestParams::TOpenKeyCursorParams:
+      return actor->OpenKeyCursor(aParams.get_OpenKeyCursorParams());
+
     default:
       MOZ_CRASH("Unknown type!");
   }
 
   MOZ_CRASH("Should never get here!");
 }
 
 bool
@@ -1770,16 +1773,57 @@ IndexedDBObjectStoreRequestParent::OpenC
     ENSURE_SUCCESS(rv, false);
   }
 
   request->SetActor(this);
   mRequest.swap(request);
   return true;
 }
 
+bool
+IndexedDBObjectStoreRequestParent::OpenKeyCursor(
+                                             const OpenKeyCursorParams& aParams)
+{
+  MOZ_ASSERT(mRequestType == ParamsUnionType::TOpenKeyCursorParams);
+  MOZ_ASSERT(mObjectStore);
+
+  const ipc::OptionalKeyRange keyRangeUnion = aParams.optionalKeyRange();
+
+  nsRefPtr<IDBKeyRange> keyRange;
+
+  switch (keyRangeUnion.type()) {
+    case ipc::OptionalKeyRange::TKeyRange:
+      keyRange =
+        IDBKeyRange::FromSerializedKeyRange(keyRangeUnion.get_KeyRange());
+      break;
+
+    case ipc::OptionalKeyRange::Tvoid_t:
+      break;
+
+    default:
+      MOZ_CRASH("Unknown param type!");
+  }
+
+  size_t direction = static_cast<size_t>(aParams.direction());
+
+  nsRefPtr<IDBRequest> request;
+
+  {
+    AutoSetCurrentTransaction asct(mObjectStore->Transaction());
+
+    ErrorResult rv;
+    request = mObjectStore->OpenKeyCursorInternal(keyRange, direction, rv);
+    ENSURE_SUCCESS(rv, false);
+  }
+
+  request->SetActor(this);
+  mRequest.swap(request);
+  return true;
+}
+
 /*******************************************************************************
  * IndexedDBIndexRequestParent
  ******************************************************************************/
 
 IndexedDBIndexRequestParent::IndexedDBIndexRequestParent(
                                                        IDBIndex* aIndex,
                                                        RequestType aRequestType)
 : mIndex(aIndex), mRequestType(aRequestType)
--- a/dom/indexedDB/ipc/IndexedDBParent.h
+++ b/dom/indexedDB/ipc/IndexedDBParent.h
@@ -705,16 +705,17 @@ class IndexedDBObjectStoreRequestParent 
   typedef ipc::PutParams PutParams;
   typedef ipc::ClearParams ClearParams;
   typedef ipc::DeleteParams DeleteParams;
   typedef ipc::GetParams GetParams;
   typedef ipc::GetAllParams GetAllParams;
   typedef ipc::GetAllKeysParams GetAllKeysParams;
   typedef ipc::CountParams CountParams;
   typedef ipc::OpenCursorParams OpenCursorParams;
+  typedef ipc::OpenKeyCursorParams OpenKeyCursorParams;
 
 public:
   IndexedDBObjectStoreRequestParent(IDBObjectStore* aObjectStore,
                                     RequestType aRequestType);
   virtual ~IndexedDBObjectStoreRequestParent();
 
   bool
   Get(const GetParams& aParams);
@@ -738,16 +739,19 @@ public:
   Clear(const ClearParams& aParams);
 
   bool
   Count(const CountParams& aParams);
 
   bool
   OpenCursor(const OpenCursorParams& aParams);
 
+  bool
+  OpenKeyCursor(const OpenKeyCursorParams& aParams);
+
 protected:
   void
   ConvertBlobActors(const InfallibleTArray<PBlobParent*>& aActors,
                     nsTArray<nsCOMPtr<nsIDOMBlob> >& aBlobs);
 
 private:
   virtual bool
   IsDisconnected() MOZ_OVERRIDE;
--- a/dom/indexedDB/ipc/PIndexedDBIndex.ipdl
+++ b/dom/indexedDB/ipc/PIndexedDBIndex.ipdl
@@ -4,52 +4,38 @@
 
 include protocol PBlob;
 include protocol PIndexedDBCursor;
 include protocol PIndexedDBObjectStore;
 include protocol PIndexedDBRequest;
 
 include IndexedDBParams;
 
-using mozilla::dom::indexedDB::SerializedStructuredCloneReadInfo;
-
 namespace mozilla {
 namespace dom {
 namespace indexedDB {
 
 namespace ipc {
 
 struct GetKeyParams
 {
   KeyRange keyRange;
 };
 
-struct OpenKeyCursorParams
-{
-  OptionalKeyRange optionalKeyRange;
-  Direction direction;
-};
-
 union IndexRequestParams
 {
   GetParams;
   GetKeyParams;
   GetAllParams;
   GetAllKeysParams;
   CountParams;
   OpenCursorParams;
   OpenKeyCursorParams;
 };
 
-union OptionalStructuredCloneReadInfo
-{
-  SerializedStructuredCloneReadInfo;
-  void_t;
-};
-
 struct IndexCursorConstructorParams
 {
   PIndexedDBRequest request;
   Direction direction;
   Key key;
   Key objectKey;
   OptionalStructuredCloneReadInfo optionalCloneInfo;
   PBlob[] blobs;
--- a/dom/indexedDB/ipc/PIndexedDBObjectStore.ipdl
+++ b/dom/indexedDB/ipc/PIndexedDBObjectStore.ipdl
@@ -7,17 +7,16 @@ include protocol PIndexedDBCursor;
 include protocol PIndexedDBIndex;
 include protocol PIndexedDBRequest;
 include protocol PIndexedDBTransaction;
 
 include IndexedDBParams;
 
 using mozilla::dom::indexedDB::IndexInfo;
 using mozilla::dom::indexedDB::IndexUpdateInfo;
-using mozilla::dom::indexedDB::SerializedStructuredCloneReadInfo;
 using mozilla::dom::indexedDB::SerializedStructuredCloneWriteInfo;
 
 namespace mozilla {
 namespace dom {
 namespace indexedDB {
 
 namespace ipc {
 
@@ -54,16 +53,17 @@ union ObjectStoreRequestParams
   GetAllParams;
   GetAllKeysParams;
   AddParams;
   PutParams;
   DeleteParams;
   ClearParams;
   CountParams;
   OpenCursorParams;
+  OpenKeyCursorParams;
 };
 
 struct CreateIndexParams
 {
   IndexInfo info;
 };
 
 struct GetIndexParams
@@ -77,17 +77,17 @@ union IndexConstructorParams
   GetIndexParams;
 };
 
 struct ObjectStoreCursorConstructorParams
 {
   PIndexedDBRequest request;
   Direction direction;
   Key key;
-  SerializedStructuredCloneReadInfo cloneInfo;
+  OptionalStructuredCloneReadInfo optionalCloneInfo;
   PBlob[] blobs;
 };
 
 } // namespace ipc
 
 protocol PIndexedDBObjectStore
 {
   manager PIndexedDBTransaction;
--- a/dom/indexedDB/ipc/unit/xpcshell.ini
+++ b/dom/indexedDB/ipc/unit/xpcshell.ini
@@ -33,16 +33,17 @@ tail =
 [test_key_requirements.js]
 [test_keys.js]
 [test_multientry.js]
 [test_names_sorted.js]
 [test_object_identity.js]
 [test_objectCursors.js]
 [test_objectStore_getAllKeys.js]
 [test_objectStore_inline_autoincrement_key_added_on_put.js]
+[test_objectStore_openKeyCursor.js]
 [test_objectStore_remove_values.js]
 [test_odd_result_order.js]
 [test_open_empty_db.js]
 [test_open_objectStore.js]
 [test_optionalArguments.js]
 [test_overlapping_transactions.js]
 [test_put_get_values.js]
 [test_put_get_values_autoIncrement.js]
--- a/dom/indexedDB/moz.build
+++ b/dom/indexedDB/moz.build
@@ -3,17 +3,16 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DIRS += ['ipc']
 TEST_DIRS += ['test']
 
 XPIDL_SOURCES += [
-    'nsIIDBKeyRange.idl',
     'nsIIndexedDatabaseManager.idl',
 ]
 
 XPIDL_MODULE = 'dom_indexeddb'
 
 MODULE = 'dom'
 
 EXPORTS.mozilla.dom.indexedDB += [
deleted file mode 100644
--- a/dom/indexedDB/nsIIDBKeyRange.idl
+++ /dev/null
@@ -1,25 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsISupports.idl"
-
-/**
- * IDBKeyRange interface.  See
- * http://dev.w3.org/2006/webapi/WebSimpleDB/#idl-def-IDBKeyRange for more
- * information.
- */
-[scriptable, builtinclass, uuid(8aeb8660-76b3-4651-b8c2-9894ae6dfe68)]
-interface nsIIDBKeyRange : nsISupports
-{
-  [implicit_jscontext]
-  readonly attribute jsval lower;
-
-  [implicit_jscontext]
-  readonly attribute jsval upper;
-
-  readonly attribute boolean lowerOpen;
-  readonly attribute boolean upperOpen;
-};
--- a/dom/indexedDB/nsIIndexedDatabaseManager.idl
+++ b/dom/indexedDB/nsIIndexedDatabaseManager.idl
@@ -5,17 +5,21 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 [scriptable, builtinclass, uuid(538d1085-517e-405a-a0f0-eb575cb0b8e5)]
 interface nsIIndexedDatabaseManager : nsISupports
 {
   /**
-   * Defines indexedDB and IDBKeyrange with its static functions on 
-   * aObject and initializes DOM exception providers if needed.
+   * Defines indexedDB and IDBKeyRange with its static functions on aGlobal.
    *
-   * @param aObject
-   *        The object, indexedDB and IDBKeyrange should be defined on.
+   * This method might go away some time in the future, indexedDB and
+   * IDBKeyRange should now be defined in all the spots (content windows,
+   * chrome windows, xpcshell, JS modules, JS components, JS sandboxes,
+   * ipcshell, bootstrapped extensions and Jetpack)
+   *
+   * @param aGlobal
+   *        The global object, indexedDB and IDBKeyRange should be defined on.
    */
   [implicit_jscontext]
-  void initWindowless(in jsval aObject);
+  void initWindowless(in jsval aGlobal);
 };
--- a/dom/indexedDB/test/Makefile.in
+++ b/dom/indexedDB/test/Makefile.in
@@ -48,16 +48,17 @@ MOCHITEST_FILES = \
   test_file_resurrection_transaction_abort.html \
   test_file_sharing.html \
   test_file_transaction_abort.html \
   test_filehandle_quota.html \
   test_filehandle_serialization.html \
   test_filehandle_store_snapshot.html \
   test_getAll.html \
   test_get_filehandle.html \
+  test_globalObjects.html \
   test_global_data.html \
   test_index_empty_keyPath.html \
   test_index_getAll.html \
   test_index_getAllObjects.html \
   test_index_object_cursors.html \
   test_index_update_delete.html \
   test_indexes.html \
   test_indexes_bad_values.html \
@@ -67,16 +68,17 @@ MOCHITEST_FILES = \
   test_keys.html \
   test_leaving_page.html \
   test_lowDiskSpace.html \
   test_multientry.html \
   test_names_sorted.html \
   test_objectCursors.html \
   test_objectStore_getAllKeys.html \
   test_objectStore_inline_autoincrement_key_added_on_put.html \
+  test_objectStore_openKeyCursor.html \
   test_objectStore_remove_values.html \
   test_object_identity.html \
   test_odd_result_order.html \
   test_open_empty_db.html \
   test_open_for_principal.html \
   test_open_objectStore.html \
   test_optionalArguments.html \
   test_overlapping_transactions.html \
@@ -110,16 +112,21 @@ MOCHITEST_FILES = \
   webapp_clearBrowserData.js \
   webapp_clearBrowserData_appFrame.html \
   webapp_clearBrowserData_browserFrame.html \
   $(NULL)
 
 #   test_bug847147.html disabled for timeouts
 #   test_writer_starvation.html  disabled for infinite loops, bug 595368
 
+MOCHITEST_CHROME_FILES = \
+  chromeHelpers.js \
+  test_globalObjects.xul \
+  $(NULL)
+
 ifeq (browser,$(MOZ_BUILD_APP))
 MOCHITEST_BROWSER_FILES = \
   browser_forgetThisSite.js \
   browser_forgetThisSiteAdd.html \
   browser_forgetThisSiteGet.html \
   browserHelpers.js \
   browser_permissionsPrompt.html \
   browser_permissionsPromptAllow.js \
--- a/dom/indexedDB/test/browserHelpers.js
+++ b/dom/indexedDB/test/browserHelpers.js
@@ -34,17 +34,17 @@ function finishTest()
 
 function grabEventAndContinueHandler(event)
 {
   testGenerator.send(event);
 }
 
 function errorHandler(event)
 {
-  throw new Error("indexedDB error, code " + event.target.errorCode);
+  throw new Error("indexedDB error, code " + event.target.error.name);
 }
 
 function continueToNextStep()
 {
   SimpleTest.executeSoon(function() {
     testGenerator.next();
   });
 }
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/chromeHelpers.js
@@ -0,0 +1,40 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const { 'classes': Cc, 'interfaces': Ci, 'utils': Cu } = Components;
+
+let testGenerator = testSteps();
+
+function runTest()
+{
+  SimpleTest.waitForExplicitFinish();
+
+  testGenerator.next();
+}
+
+function finishTest()
+{
+  SimpleTest.executeSoon(function() {
+    testGenerator.close();
+    SimpleTest.finish();
+  });
+}
+
+function grabEventAndContinueHandler(event)
+{
+  testGenerator.send(event);
+}
+
+function continueToNextStep()
+{
+  SimpleTest.executeSoon(function() {
+    testGenerator.next();
+  });
+}
+
+function errorHandler(event)
+{
+  throw new Error("indexedDB error, code " + event.target.error.name);
+}
--- a/dom/indexedDB/test/event_propagation_iframe.html
+++ b/dom/indexedDB/test/event_propagation_iframe.html
@@ -16,17 +16,17 @@
                                 "');", "*");
     }
 
     function grabEventAndContinueHandler(event) {
       testGenerator.send(event);
     }
 
     function errorHandler(event) {
-      ok(false, "indexedDB error, code " + event.target.errorCode);
+      ok(false, "indexedDB error, code " + event.target.error.name);
       finishTest();
     }
 
     function finishTest() {
       // Let window.onerror have a chance to fire
       setTimeout(function() {
         setTimeout(function() {
           testGenerator.close();
--- a/dom/indexedDB/test/exceptions_in_events_iframe.html
+++ b/dom/indexedDB/test/exceptions_in_events_iframe.html
@@ -22,17 +22,17 @@
       ok(a == b, message);
     }
 
     function grabEventAndContinueHandler(event) {
       testGenerator.send(event);
     }
 
     function errorHandler(event) {
-      ok(false, "indexedDB error, code " + event.target.errorCode);
+      ok(false, "indexedDB error, code " + event.target.error.name);
       finishTest();
     }
 
     function unexpectedSuccessHandler(event) {
       ok(false, "got success when it was not expected!");
       finishTest();
     }
 
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/extensions/Makefile.in
@@ -0,0 +1,19 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+XPI_NAME = indexedDB
+
+DIST_FILES = \
+  bootstrap.js \
+  install.rdf \
+  $(NULL)
+
+TEST_EXTENSIONS_DIR = $(DEPTH)/_tests/testing/mochitest/extensions
+
+GENERATED_DIRS = $(TEST_EXTENSIONS_DIR)
+
+include $(topsrcdir)/config/rules.mk
+
+libs::
+	@(cd $(DIST)/xpi-stage && tar $(TAR_CREATE_FLAGS) - $(XPI_NAME)) | (cd $(TEST_EXTENSIONS_DIR) && tar -xf -)
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/extensions/bootstrap.js
@@ -0,0 +1,84 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+function testForExpectedSymbols(stage, data) {
+  const expectedSymbols = [ "IDBKeyRange", "indexedDB" ];
+  for each (var symbol in expectedSymbols) {
+    Services.prefs.setBoolPref("indexeddbtest.bootstrap." + stage + "." +
+                               symbol, symbol in this);
+  }
+}
+
+function GlobalObjectsComponent() {
+  this.wrappedJSObject = this;
+}
+
+GlobalObjectsComponent.prototype =
+{
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
+
+  runTest: function() {
+    const name = "Splendid Test";
+
+    let ok = this.ok;
+    let finishTest = this.finishTest;
+
+    let keyRange = IDBKeyRange.only(42);
+    ok(keyRange, "Got keyRange");
+
+    let request = indexedDB.open(name, 1);
+    request.onerror = function(event) {
+      ok(false, "indexedDB error, '" + event.target.error.name + "'");
+      finishTest();
+    }
+    request.onsuccess = function(event) {
+      let db = event.target.result;
+      ok(db, "Got database");
+      finishTest();
+    }
+  }
+};
+
+var gFactory = {
+  register: function() {
+    var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+
+    var classID = Components.ID("{d6f85dcb-537d-447e-b783-75d4b405622d}");
+    var description = "IndexedDBTest";
+    var contractID = "@mozilla.org/dom/indexeddb/GlobalObjectsComponent;1";
+    var factory = XPCOMUtils._getFactory(GlobalObjectsComponent);
+
+    registrar.registerFactory(classID, description, contractID, factory);
+
+    this.unregister = function() {
+      registrar.unregisterFactory(classID, factory);
+      delete this.unregister;
+    };
+  }
+};
+
+function install(data, reason) {
+  testForExpectedSymbols("install");
+}
+
+function startup(data, reason) {
+  testForExpectedSymbols("startup");
+  gFactory.register();
+}
+
+function shutdown(data, reason) {
+  testForExpectedSymbols("shutdown");
+  gFactory.unregister();
+}
+
+function uninstall(data, reason) {
+  testForExpectedSymbols("uninstall");
+}
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/extensions/install.rdf
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+  <Description about="urn:mozilla:install-manifest">
+    <em:name>IndexedDBTest</em:name>
+    <em:description>IndexedDB functions for use in testing.</em:description>
+    <em:creator>Mozilla</em:creator>
+    <em:version>2013.10.10</em:version>
+#expand    <em:id>__XPI_NAME__-test@mozilla.org</em:id>
+    <em:type>2</em:type>
+    <em:bootstrap>true</em:bootstrap>
+    <em:targetApplication>
+      <Description>
+        <em:id>toolkit@mozilla.org</em:id>
+#expand        <em:minVersion>__MOZILLA_VERSION_U__</em:minVersion>
+#expand        <em:maxVersion>__MOZILLA_VERSION_U__</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+  </Description>
+</RDF>
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/extensions/moz.build
@@ -0,0 +1,6 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
--- a/dom/indexedDB/test/moz.build
+++ b/dom/indexedDB/test/moz.build
@@ -1,9 +1,11 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+DIRS += ['extensions']
+
 TEST_DIRS += ['unit']
 
 XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/test_globalObjects.html
@@ -0,0 +1,38 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>Indexed Database Property Test</title>
+
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+  <script type="text/javascript;version=1.7">
+  function testSteps()
+  {
+    const name = window.location.pathname;
+
+    // Test for IDBKeyRange and indexedDB availability in content windows.
+    let keyRange = IDBKeyRange.only(42);
+    ok(keyRange, "Got keyRange");
+
+    let request = indexedDB.open(name, 1);
+    request.onerror = errorHandler;
+    request.onsuccess = grabEventAndContinueHandler;
+    let event = yield undefined;
+
+    let db = event.target.result;
+    ok(db, "Got database");
+
+    finishTest();
+    yield undefined;
+  }
+  </script>
+  <script type="text/javascript;version=1.7" src="helpers.js"></script>
+</head>
+
+<body onload="runTest();"></body>
+
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/test_globalObjects.xul
@@ -0,0 +1,72 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<window title="Mozilla Bug 832883"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        onload="runTest();">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <script type="application/javascript;version=1.7">
+  <![CDATA[
+  function testSteps() {
+    const name = window.location.pathname;
+
+    // Test for IDBKeyRange and indexedDB availability in chrome windows.
+    var keyRange = IDBKeyRange.only(42);
+    ok(keyRange, "Got keyRange");
+
+    var request = indexedDB.open(name, 1);
+    request.onerror = errorHandler;
+    request.onsuccess = grabEventAndContinueHandler;
+    let event = yield undefined;
+
+    let db = event.target.result;
+    ok(db, "Got database");
+
+    // Test for IDBKeyRange and indexedDB availability in bootstrap files.
+    let test = Cc["@mozilla.org/dom/indexeddb/GlobalObjectsComponent;1"].
+               createInstance(Ci.nsISupports).wrappedJSObject;
+    test.ok = ok;
+    test.finishTest = continueToNextStep;
+    test.runTest();
+    yield undefined;
+
+    Cu.import("resource://gre/modules/AddonManager.jsm");
+    AddonManager.getAddonByID("indexedDB-test@mozilla.org",
+                              grabEventAndContinueHandler);
+    let addon = yield undefined;
+    addon.uninstall();
+
+    Cu.import("resource://gre/modules/Services.jsm");
+    for each (var stage in [ "install", "startup", "shutdown", "uninstall" ]) {
+      for each (var symbol in [ "IDBKeyRange", "indexedDB" ]) {
+        let pref;
+        try {
+          pref = Services.prefs.getBoolPref("indexeddbtest.bootstrap." + stage +
+                                            "." + symbol);
+        }
+        catch(ex) {
+          pref = false;
+        }
+        ok(pref, "Symbol '" + symbol + "' present during '" + stage + "'");
+      }
+    }
+
+    finishTest();
+    yield undefined;
+  }
+  ]]>
+  </script>
+
+  <script type="text/javascript;version=1.7" src="chromeHelpers.js"></script>
+
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=832883"
+     target="_blank">Mozilla Bug 832883</a>
+  </body>
+</window>
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/test_objectStore_openKeyCursor.html
@@ -0,0 +1,19 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>Indexed Database Property Test</title>
+
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+  <script type="text/javascript;version=1.7" src="unit/test_objectStore_openKeyCursor.js"></script>
+  <script type="text/javascript;version=1.7" src="helpers.js"></script>
+
+</head>
+
+<body onload="runTest();"></body>
+
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/unit/GlobalObjectsChild.js
@@ -0,0 +1,36 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function ok(cond, msg) {
+  dump("ok(" + cond + ", \"" + msg + "\")");
+  do_check_true(!!cond, Components.stack.caller);
+}
+
+function finishTest()
+{
+  do_execute_soon(function() {
+    do_test_finished();
+  });
+}
+
+function run_test() {
+  const name = "Splendid Test";
+
+  do_test_pending();
+
+  let keyRange = IDBKeyRange.only(42);
+  ok(keyRange, "Got keyRange");
+
+  let request = indexedDB.open(name, 1);
+  request.onerror = function(event) {
+    ok(false, "indexedDB error, '" + event.target.error.name + "'");
+    finishTest();
+  }
+  request.onsuccess = function(event) {
+    let db = event.target.result;
+    ok(db, "Got database");
+    finishTest();
+  }
+}
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/unit/GlobalObjectsComponent.js
@@ -0,0 +1,40 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+function GlobalObjectsComponent() {
+  this.wrappedJSObject = this;
+}
+
+GlobalObjectsComponent.prototype =
+{
+  classID: Components.ID("{949ebf50-e0da-44b9-8335-cbfd4febfdcc}"),
+
+  QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsISupports]),
+
+  runTest: function() {
+    const name = "Splendid Test";
+
+    let ok = this.ok;
+    let finishTest = this.finishTest;
+
+    let keyRange = IDBKeyRange.only(42);
+    ok(keyRange, "Got keyRange");
+
+    let request = indexedDB.open(name, 1);
+    request.onerror = function(event) {
+      ok(false, "indexedDB error, '" + event.target.error.name + "'");
+      finishTest();
+    }
+    request.onsuccess = function(event) {
+      let db = event.target.result;
+      ok(db, "Got database");
+      finishTest();
+    }
+  }
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([GlobalObjectsComponent]);
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/unit/GlobalObjectsComponent.manifest
@@ -0,0 +1,2 @@
+component {949ebf50-e0da-44b9-8335-cbfd4febfdcc} GlobalObjectsComponent.js
+contract @mozilla.org/dom/indexeddb/GlobalObjectsComponent;1 {949ebf50-e0da-44b9-8335-cbfd4febfdcc}
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/unit/GlobalObjectsModule.jsm
@@ -0,0 +1,34 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+this.EXPORTED_SYMBOLS = [
+  "GlobalObjectsModule"
+];
+
+this.GlobalObjectsModule = function GlobalObjectsModule() {
+}
+
+GlobalObjectsModule.prototype = {
+  runTest: function() {
+    const name = "Splendid Test";
+
+    let ok = this.ok;
+    let finishTest = this.finishTest;
+
+    let keyRange = IDBKeyRange.only(42);
+    ok(keyRange, "Got keyRange");
+
+    let request = indexedDB.open(name, 1);
+    request.onerror = function(event) {
+      ok(false, "indexedDB error, '" + event.target.error.name + "'");
+      finishTest();
+    }
+    request.onsuccess = function(event) {
+      let db = event.target.result;
+      ok(db, "Got database");
+      finishTest();
+    }
+  }
+}
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/unit/GlobalObjectsSandbox.js
@@ -0,0 +1,22 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function runTest() {
+  const name = "Splendid Test";
+
+  let keyRange = IDBKeyRange.only(42);
+  ok(keyRange, "Got keyRange");
+
+  let request = indexedDB.open(name, 1);
+  request.onerror = function(event) {
+    ok(false, "indexedDB error, '" + event.target.error.name + "'");
+    finishTest();
+  }
+  request.onsuccess = function(event) {
+    let db = event.target.result;
+    ok(db, "Got database");
+    finishTest();
+  }
+}
--- a/dom/indexedDB/test/unit/Makefile.in
+++ b/dom/indexedDB/test/unit/Makefile.in
@@ -16,16 +16,18 @@ MOCHITEST_FILES = \
   test_create_objectStore.js \
   test_cursor_mutation.js \
   test_cursor_update_updates_indexes.js \
   test_cursors.js \
   test_deleteDatabase.js \
   test_deleteDatabase_interactions.js \
   test_event_source.js \
   test_getAll.js \
+  test_globalObjects.js \
+  test_globalObjects_ipc.js \
   test_global_data.js \
   test_index_empty_keyPath.js \
   test_index_getAll.js \
   test_index_getAllObjects.js \
   test_index_object_cursors.js \
   test_index_update_delete.js \
   test_indexes.js \
   test_indexes_bad_values.js \
@@ -35,16 +37,17 @@ MOCHITEST_FILES = \
   test_keys.js \
   test_lowDiskSpace.js \
   test_multientry.js \
   test_names_sorted.js \
   test_object_identity.js \
   test_objectCursors.js \
   test_objectStore_getAllKeys.js \
   test_objectStore_inline_autoincrement_key_added_on_put.js \
+  test_objectStore_openKeyCursor.js \
   test_objectStore_remove_values.js \
   test_odd_result_order.js \
   test_open_empty_db.js \
   test_open_for_principal.js \
   test_open_objectStore.js \
   test_optionalArguments.js \
   test_overlapping_transactions.js \
   test_persistenceType.js \
--- a/dom/indexedDB/test/unit/head.js
+++ b/dom/indexedDB/test/unit/head.js
@@ -1,14 +1,14 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-const { 'classes': Cc, 'interfaces': Ci } = Components;
+const { 'classes': Cc, 'interfaces': Ci, 'utils': Cu } = Components;
 
 const DOMException = Ci.nsIDOMDOMException;
 
 function is(a, b, msg) {
   dump("is(" + a + ", " + b + ", \"" + msg + "\")");
   do_check_eq(a, b, Components.stack.caller);
 }
 
@@ -38,20 +38,16 @@ function run_test() {
   runTest();
 };
 
 function runTest()
 {
   // XPCShell does not get a profile by default.
   do_get_profile();
 
-  var idbManager = Cc["@mozilla.org/dom/indexeddb/manager;1"].
-                   getService(Ci.nsIIndexedDatabaseManager);
-  idbManager.initWindowless(this);
-
   enableExperimental();
 
   do_test_pending();
   testGenerator.next();
 }
 
 function finishTest()
 {
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/unit/test_globalObjects.js
@@ -0,0 +1,67 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var testGenerator = testSteps();
+
+function testSteps()
+{
+  const name = "Splendid Test";
+
+  let ioService =
+    Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
+
+  function getSpec(filename) {
+    let file = do_get_file(filename);
+    let uri = ioService.newFileURI(file);
+    return uri.spec;
+  }
+
+  // Test for IDBKeyRange and indexedDB availability in xpcshell.
+  let keyRange = IDBKeyRange.only(42);
+  ok(keyRange, "Got keyRange");
+
+  let request = indexedDB.open(name, 1);
+  request.onerror = errorHandler;
+  request.onsuccess = grabEventAndContinueHandler;
+  let event = yield undefined;
+
+  let db = event.target.result;
+  ok(db, "Got database");
+
+  // Test for IDBKeyRange and indexedDB availability in JS modules.
+  Cu.import(getSpec("GlobalObjectsModule.jsm"));
+  let test = new GlobalObjectsModule();
+  test.ok = ok;
+  test.finishTest = continueToNextStep;
+  test.runTest();
+  yield undefined;
+
+  // Test for IDBKeyRange and indexedDB availability in JS components.
+  do_load_manifest("GlobalObjectsComponent.manifest");
+  test = Cc["@mozilla.org/dom/indexeddb/GlobalObjectsComponent;1"].
+         createInstance(Ci.nsISupports).wrappedJSObject;
+  test.ok = ok;
+  test.finishTest = continueToNextStep;
+  test.runTest();
+  yield undefined;
+
+  // Test for IDBKeyRange and indexedDB availability in JS sandboxes.
+  let principal = Cc["@mozilla.org/systemprincipal;1"].
+                  createInstance(Ci.nsIPrincipal);
+  let sandbox = new Cu.Sandbox(principal,
+                               { wantGlobalProperties: ["indexedDB"] });
+  sandbox.__SCRIPT_URI_SPEC__ = getSpec("GlobalObjectsSandbox.js");
+  Cu.evalInSandbox(
+    "Components.classes['@mozilla.org/moz/jssubscript-loader;1'] \
+               .createInstance(Components.interfaces.mozIJSSubScriptLoader) \
+               .loadSubScript(__SCRIPT_URI_SPEC__);", sandbox, "1.7");
+  sandbox.ok = ok;
+  sandbox.finishTest = continueToNextStep;
+  Cu.evalInSandbox("runTest();", sandbox);
+  yield undefined;
+
+  finishTest();
+  yield undefined;
+}
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/unit/test_globalObjects_ipc.js
@@ -0,0 +1,19 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var testGenerator = testSteps();
+
+function testSteps()
+{
+  // Test for IDBKeyRange and indexedDB availability in ipcshell.
+  run_test_in_child("./GlobalObjectsChild.js", function() {
+    do_test_finished();
+    continueToNextStep();
+  });
+  yield undefined;
+
+  finishTest();
+  yield undefined;
+}
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/unit/test_objectStore_openKeyCursor.js
@@ -0,0 +1,400 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+let testGenerator = testSteps();
+
+function testSteps() {
+  const dbName = this.window ?
+                 window.location.pathname :
+                 "test_objectStore_openKeyCursor";
+  const dbVersion = 1;
+  const objectStoreName = "foo";
+  const keyCount = 100;
+
+  let request = indexedDB.open(dbName, dbVersion);
+  request.onerror = errorHandler;
+  request.onupgradeneeded = grabEventAndContinueHandler;
+  request.onsuccess = unexpectedSuccessHandler;
+
+  let event = yield undefined;
+
+  info("Creating database");
+
+  let db = event.target.result;
+  let objectStore = db.createObjectStore(objectStoreName);
+  for (let i = 0; i < keyCount; i++) {
+    objectStore.add(true, i);
+  }
+
+  request.onupgradeneeded = unexpectedSuccessHandler;
+  request.onsuccess = grabEventAndContinueHandler;
+
+  event = yield undefined;
+
+  db = event.target.result;
+  objectStore = db.transaction(objectStoreName, "readwrite")
+                  .objectStore(objectStoreName);
+
+  info("Getting all keys");
+  objectStore.getAllKeys().onsuccess = grabEventAndContinueHandler;
+  event = yield undefined;
+
+  const allKeys = event.target.result;
+
+  ok(Array.isArray(allKeys), "Got an array result");
+  is(allKeys.length, keyCount, "Got correct array length");
+
+  info("Opening normal key cursor");
+
+  let seenKeys = [];
+  objectStore.openKeyCursor().onsuccess = event => {
+    let cursor = event.target.result;
+    if (!cursor) {
+      continueToNextStepSync();
+      return;
+    }
+
+    is(cursor.source, objectStore, "Correct source");
+    is(cursor.direction, "next", "Correct direction");
+
+    let exception = null;
+    try {
+      cursor.update(10);
+    } catch(e) {
+      exception = e;
+    }
+    ok(!!exception, "update() throws for key cursor");
+
+    exception = null;
+    try {
+      cursor.delete();
+    } catch(e) {
+      exception = e;
+    }
+    ok(!!exception, "delete() throws for key cursor");
+
+    is(cursor.key, cursor.primaryKey, "key and primaryKey match");
+    ok(!("value" in cursor), "No 'value' property on key cursor");
+
+    seenKeys.push(cursor.key);
+    cursor.continue();
+  };
+  yield undefined;
+
+  is(seenKeys.length, allKeys.length, "Saw the right number of keys");
+
+  let match = true;
+  for (let i = 0; i < seenKeys.length; i++) {
+    if (seenKeys[i] !== allKeys[i]) {
+      match = false;
+      break;
+    }
+  }
+  ok(match, "All keys matched");
+
+  info("Opening key cursor with keyRange");
+
+  let keyRange = IDBKeyRange.bound(10, 20, false, true);
+
+  seenKeys = [];
+  objectStore.openKeyCursor(keyRange).onsuccess = event => {
+    let cursor = event.target.result;
+    if (!cursor) {
+      continueToNextStepSync();
+      return;
+    }
+
+    is(cursor.source, objectStore, "Correct source");
+    is(cursor.direction, "next", "Correct direction");
+
+    let exception = null;
+    try {
+      cursor.update(10);
+    } catch(e) {
+      exception = e;
+    }
+    ok(!!exception, "update() throws for key cursor");
+
+    exception = null;
+    try {
+      cursor.delete();
+    } catch(e) {
+      exception = e;
+    }
+    ok(!!exception, "delete() throws for key cursor");
+
+    is(cursor.key, cursor.primaryKey, "key and primaryKey match");
+    ok(!("value" in cursor), "No 'value' property on key cursor");
+
+    seenKeys.push(cursor.key);
+    cursor.continue();
+  };
+  yield undefined;
+
+  is(seenKeys.length, 10, "Saw the right number of keys");
+
+  match = true;
+  for (let i = 0; i < seenKeys.length; i++) {
+    if (seenKeys[i] !== allKeys[i + 10]) {
+      match = false;
+      break;
+    }
+  }
+  ok(match, "All keys matched");
+
+  info("Opening key cursor with unmatched keyRange");
+
+  keyRange = IDBKeyRange.bound(10000, 200000);
+
+  seenKeys = [];
+  objectStore.openKeyCursor(keyRange).onsuccess = event => {
+    let cursor = event.target.result;
+    if (!cursor) {
+      continueToNextStepSync();
+      return;
+    }
+
+    ok(false, "Shouldn't have any keys here");
+    cursor.continue();
+  };
+  yield undefined;
+
+  is(seenKeys.length, 0, "Saw the right number of keys");
+
+  info("Opening reverse key cursor");
+
+  seenKeys = [];
+  objectStore.openKeyCursor(null, "prev").onsuccess = event => {
+    let cursor = event.target.result;
+    if (!cursor) {
+      continueToNextStepSync();
+      return;
+    }
+
+    is(cursor.source, objectStore, "Correct source");
+    is(cursor.direction, "prev", "Correct direction");
+
+    let exception = null;
+    try {
+      cursor.update(10);
+    } catch(e) {
+      exception = e;
+    }
+    ok(!!exception, "update() throws for key cursor");
+
+    exception = null;
+    try {
+      cursor.delete();
+    } catch(e) {
+      exception = e;
+    }
+    ok(!!exception, "delete() throws for key cursor");
+
+    is(cursor.key, cursor.primaryKey, "key and primaryKey match");
+    ok(!("value" in cursor), "No 'value' property on key cursor");
+
+    seenKeys.push(cursor.key);
+    cursor.continue();
+  };
+  yield undefined;
+
+  is(seenKeys.length, allKeys.length, "Saw the right number of keys");
+
+  seenKeys.reverse();
+
+  match = true;
+  for (let i = 0; i < seenKeys.length; i++) {
+    if (seenKeys[i] !== allKeys[i]) {
+      match = false;
+      break;
+    }
+  }
+  ok(match, "All keys matched");
+
+  info("Opening reverse key cursor with key range");
+
+  keyRange = IDBKeyRange.bound(10, 20, false, true);
+
+  seenKeys = [];
+  objectStore.openKeyCursor(keyRange, "prev").onsuccess = event => {
+    let cursor = event.target.result;
+    if (!cursor) {
+      continueToNextStepSync();
+      return;
+    }
+
+    is(cursor.source, objectStore, "Correct source");
+    is(cursor.direction, "prev", "Correct direction");
+
+    let exception = null;
+    try {
+      cursor.update(10);
+    } catch(e) {
+      exception = e;
+    }
+    ok(!!exception, "update() throws for key cursor");
+
+    exception = null;
+    try {
+      cursor.delete();
+    } catch(e) {
+      exception = e;
+    }
+    ok(!!exception, "delete() throws for key cursor");
+
+    is(cursor.key, cursor.primaryKey, "key and primaryKey match");
+    ok(!("value" in cursor), "No 'value' property on key cursor");
+
+    seenKeys.push(cursor.key);
+    cursor.continue();
+  };
+  yield undefined;
+
+  is(seenKeys.length, 10, "Saw the right number of keys");
+
+  seenKeys.reverse();
+
+  match = true;
+  for (let i = 0; i < 10; i++) {
+    if (seenKeys[i] !== allKeys[i + 10]) {
+      match = false;
+      break;
+    }
+  }
+  ok(match, "All keys matched");
+
+  info("Opening reverse key cursor with unmatched key range");
+
+  keyRange = IDBKeyRange.bound(10000, 200000);
+
+  seenKeys = [];
+  objectStore.openKeyCursor(keyRange, "prev").onsuccess = event => {
+    let cursor = event.target.result;
+    if (!cursor) {
+      continueToNextStepSync();
+      return;
+    }
+
+    ok(false, "Shouldn't have any keys here");
+    cursor.continue();
+  };
+  yield undefined;
+
+  is(seenKeys.length, 0, "Saw the right number of keys");
+
+  info("Opening key cursor with advance");
+
+  seenKeys = [];
+  objectStore.openKeyCursor().onsuccess = event => {
+    let cursor = event.target.result;
+    if (!cursor) {
+      continueToNextStepSync();
+      return;
+    }
+
+    is(cursor.source, objectStore, "Correct source");
+    is(cursor.direction, "next", "Correct direction");
+
+    let exception = null;
+    try {
+      cursor.update(10);
+    } catch(e) {
+      exception = e;
+    }
+    ok(!!exception, "update() throws for key cursor");
+
+    exception = null;
+    try {
+      cursor.delete();
+    } catch(e) {
+      exception = e;
+    }
+    ok(!!exception, "delete() throws for key cursor");
+
+    is(cursor.key, cursor.primaryKey, "key and primaryKey match");
+    ok(!("value" in cursor), "No 'value' property on key cursor");
+
+    seenKeys.push(cursor.key);
+    if (seenKeys.length == 1) {
+      cursor.advance(10);
+    } else {
+      cursor.continue();
+    }
+  };
+  yield undefined;
+
+  is(seenKeys.length, allKeys.length - 9, "Saw the right number of keys");
+
+  let match = true;
+  for (let i = 0, j = 0; i < seenKeys.length; i++) {
+    if (seenKeys[i] !== allKeys[i + j]) {
+      match = false;
+      break;
+    }
+    if (i == 0) {
+      j = 9;
+    }
+  }
+  ok(match, "All keys matched");
+
+  info("Opening key cursor with continue-to-key");
+
+  seenKeys = [];
+  objectStore.openKeyCursor().onsuccess = event => {
+    let cursor = event.target.result;
+    if (!cursor) {
+      continueToNextStepSync();
+      return;
+    }
+
+    is(cursor.source, objectStore, "Correct source");
+    is(cursor.direction, "next", "Correct direction");
+
+    let exception = null;
+    try {
+      cursor.update(10);
+    } catch(e) {
+      exception = e;
+    }
+    ok(!!exception, "update() throws for key cursor");
+
+    exception = null;
+    try {
+      cursor.delete();
+    } catch(e) {
+      exception = e;
+    }
+    ok(!!exception, "delete() throws for key cursor");
+
+    is(cursor.key, cursor.primaryKey, "key and primaryKey match");
+    ok(!("value" in cursor), "No 'value' property on key cursor");
+
+    seenKeys.push(cursor.key);
+
+    if (seenKeys.length == 1) {
+      cursor.continue(10);
+    } else {
+      cursor.continue();
+    }
+  };
+  yield undefined;
+
+  is(seenKeys.length, allKeys.length - 9, "Saw the right number of keys");
+
+  let match = true;
+  for (let i = 0, j = 0; i < seenKeys.length; i++) {
+    if (seenKeys[i] !== allKeys[i + j]) {
+      match = false;
+      break;
+    }
+    if (i == 0) {
+      j = 9;
+    }
+  }
+  ok(match, "All keys matched");
+
+  finishTest();
+  yield undefined;
+}
--- a/dom/indexedDB/test/unit/xpcshell.ini
+++ b/dom/indexedDB/test/unit/xpcshell.ini
@@ -1,11 +1,17 @@
 [DEFAULT]
 head = head.js
 tail =
+support-files =
+  GlobalObjectsChild.js
+  GlobalObjectsComponent.js
+  GlobalObjectsComponent.manifest
+  GlobalObjectsModule.jsm
+  GlobalObjectsSandbox.js
 
 # When adding files here please also update ipc/unit/xpcshell.ini!
 
 [test_add_put.js]
 [test_add_twice_failure.js]
 [test_advance.js]
 [test_autoIncrement.js]
 [test_autoIncrement_indexes.js]
@@ -17,16 +23,20 @@ tail =
 [test_create_objectStore.js]
 [test_cursor_mutation.js]
 [test_cursor_update_updates_indexes.js]
 [test_cursors.js]
 [test_deleteDatabase.js]
 [test_deleteDatabase_interactions.js]
 [test_event_source.js]
 [test_getAll.js]
+[test_globalObjects.js]
+[test_globalObjects_ipc.js]
+# FIXME/bug 575918: out-of-process xpcshell is broken on OS X
+skip-if = os == "mac" || os == "android"
 [test_global_data.js]
 [test_index_empty_keyPath.js]
 [test_index_getAll.js]
 [test_index_getAllObjects.js]
 [test_index_object_cursors.js]
 [test_index_update_delete.js]
 [test_indexes.js]
 [test_indexes_bad_values.js]
@@ -36,16 +46,17 @@ tail =
 [test_keys.js]
 [test_lowDiskSpace.js]
 [test_multientry.js]
 [test_names_sorted.js]
 [test_object_identity.js]
 [test_objectCursors.js]
 [test_objectStore_getAllKeys.js]
 [test_objectStore_inline_autoincrement_key_added_on_put.js]
+[test_objectStore_openKeyCursor.js]
 [test_objectStore_remove_values.js]
 [test_odd_result_order.js]
 [test_open_empty_db.js]
 [test_open_for_principal.js]
 [test_open_objectStore.js]
 [test_optionalArguments.js]
 [test_overlapping_transactions.js]
 [test_persistenceType.js]
--- a/dom/ipc/Blob.cpp
+++ b/dom/ipc/Blob.cpp
@@ -463,17 +463,17 @@ private:
   }
 };
 
 template <ActorFlavorEnum ActorFlavor>
 inline
 already_AddRefed<nsIDOMBlob>
 GetBlobFromParams(const SlicedBlobConstructorParams& aParams)
 {
-  static_assert(ActorFlavor == mozilla::dom::ipc::Parent,
+  static_assert(ActorFlavor == Parent,
                 "No other flavor is supported here!");
 
   BlobParent* actor =
     const_cast<BlobParent*>(
       static_cast<const BlobParent*>(aParams.sourceParent()));
   MOZ_ASSERT(actor);
 
   return actor->GetBlob();
--- a/dom/ipc/Blob.h
+++ b/dom/ipc/Blob.h
@@ -164,17 +164,17 @@ public:
   typedef typename BlobTraits<ActorFlavor>::StreamType StreamType;
   typedef typename BlobTraits<ActorFlavor>::ConstructorParamsType
           ConstructorParamsType;
   typedef typename BlobTraits<ActorFlavor>::OtherSideConstructorParamsType
           OtherSideConstructorParamsType;
   typedef typename BlobTraits<ActorFlavor>::BaseType BaseType;
   typedef RemoteBlob<ActorFlavor> RemoteBlobType;
   typedef mozilla::ipc::IProtocolManager<
-                      mozilla::ipc::RPCChannel::RPCListener>::ActorDestroyReason
+                      mozilla::ipc::MessageListener>::ActorDestroyReason
           ActorDestroyReason;
 
 protected:
   nsIDOMBlob* mBlob;
   RemoteBlobType* mRemoteBlob;
   bool mOwnsBlob;
   bool mBlobIsFile;
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -257,17 +257,17 @@ NS_IMETHODIMP
 ContentParentMemoryReporter::CollectReports(nsIMemoryReporterCallback* cb,
                                             nsISupports* aClosure)
 {
     nsAutoTArray<ContentParent*, 16> cps;
     ContentParent::GetAllEvenIfDead(cps);
 
     for (uint32_t i = 0; i < cps.Length(); i++) {
         ContentParent* cp = cps[i];
-        AsyncChannel* channel = cp->GetIPCChannel();
+        MessageChannel* channel = cp->GetIPCChannel();
 
         nsString friendlyName;
         cp->FriendlyName(friendlyName);
 
         cp->AddRef();
         nsrefcnt refcnt = cp->Release();
 
         const char* channelStr = "no channel";
@@ -874,17 +874,17 @@ ContentParent::ShutDownProcess(bool aClo
     if (!aCloseWithError && !mCalledClose) {
         // Close() can only be called once: It kicks off the destruction
         // sequence.
         mCalledClose = true;
         Close();
     }
 
     if (aCloseWithError && !mCalledCloseWithError) {
-        AsyncChannel* channel = GetIPCChannel();
+        MessageChannel* channel = GetIPCChannel();
         if (channel) {
             mCalledCloseWithError = true;
             channel->CloseWithError();
         }
     }
 
     // NB: must MarkAsDead() here so that this isn't accidentally
     // returned from Get*() while in the midst of shutdown.
--- a/dom/mobilemessage/src/gonk/MobileMessageDatabaseService.js
+++ b/dom/mobilemessage/src/gonk/MobileMessageDatabaseService.js
@@ -61,32 +61,24 @@ const NEXT = "next";
 const COLLECT_ID_END = 0;
 const COLLECT_ID_ERROR = -1;
 const COLLECT_TIMESTAMP_UNUSED = 0;
 
 XPCOMUtils.defineLazyServiceGetter(this, "gMobileMessageService",
                                    "@mozilla.org/mobilemessage/mobilemessageservice;1",
                                    "nsIMobileMessageService");
 
-XPCOMUtils.defineLazyServiceGetter(this, "gIDBManager",
-                                   "@mozilla.org/dom/indexeddb/manager;1",
-                                   "nsIIndexedDatabaseManager");
-
-const GLOBAL_SCOPE = this;
-
 /**
  * MobileMessageDatabaseService
  */
 function MobileMessageDatabaseService() {
   // Prime the directory service's cache to ensure that the ProfD entry exists
   // by the time IndexedDB queries for it off the main thread. (See bug 743635.)
   Services.dirsvc.get("ProfD", Ci.nsIFile);
 
-  gIDBManager.initWindowless(GLOBAL_SCOPE);
-
   let that = this;
   this.newTxn(READ_ONLY, function(error, txn, messageStore){
     if (error) {
       return;
     }
     // In order to get the highest key value, we open a key cursor in reverse
     // order and get only the first pointed value.
     let request = messageStore.openCursor(null, PREV);
@@ -152,17 +144,17 @@ MobileMessageDatabaseService.prototype =
     }
 
     let self = this;
     function gotDB(db) {
       self.db = db;
       callback(null, db);
     }
 
-    let request = GLOBAL_SCOPE.indexedDB.open(DB_NAME, DB_VERSION);
+    let request = indexedDB.open(DB_NAME, DB_VERSION);
     request.onsuccess = function (event) {
       if (DEBUG) debug("Opened database:", DB_NAME, DB_VERSION);
       gotDB(event.target.result);
     };
     request.onupgradeneeded = function (event) {
       if (DEBUG) {
         debug("Database needs upgrade:", DB_NAME,
               event.oldVersion, event.newVersion);
--- a/dom/mobilemessage/tests/test_smsdatabaseservice.xul
+++ b/dom/mobilemessage/tests/test_smsdatabaseservice.xul
@@ -32,19 +32,16 @@ function run_next_test() {
 }
 
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
-let gIDBManager = Cc["@mozilla.org/dom/indexeddb/manager;1"]
-                    .getService(Ci.nsIIndexedDatabaseManager);
-
 let gMobileMessageDatabaseService = Cc["@mozilla.org/mobilemessage/rilmobilemessagedatabaseservice;1"]
                                       .getService(Ci.nsIMobileMessageDatabaseService);
 
 let gRegistrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
 
 
 let SmsRequestManagerImpl = {
   notifySmsSent: function notifySmsSent(requestId, message) {
@@ -129,18 +126,16 @@ function fakeSmsRequestManager(obj) {
 }
 
 const DB_NAME = "sms";
 const DB_VERSION = 2;
 const STORE_NAME = "sms";
 const MAX_SMS = 3;
 const LONG_MAX = 2147483647;
 
-gIDBManager.initWindowless(this);
-
 let _db;
 function ensureDB(callback) {
   if (_db) {
     callback(_db);
     return;
   }
   let request;
   try {
--- a/dom/network/src/NetworkStatsDB.jsm
+++ b/dom/network/src/NetworkStatsDB.jsm
@@ -21,22 +21,22 @@ const STORE_NAME_V2 = "net_stats_v2";
 
 // Constant defining the maximum values allowed per interface. If more, older
 // will be erased.
 const VALUES_MAX_LENGTH = 6 * 30;
 
 // Constant defining the rate of the samples. Daily.
 const SAMPLE_RATE = 1000 * 60 * 60 * 24;
 
-this.NetworkStatsDB = function NetworkStatsDB(aGlobal, aConnectionTypes) {
+this.NetworkStatsDB = function NetworkStatsDB(aConnectionTypes) {
   if (DEBUG) {
     debug("Constructor");
   }
   this._connectionTypes = aConnectionTypes;
-  this.initDBHelper(DB_NAME, DB_VERSION, [STORE_NAME_V2], aGlobal);
+  this.initDBHelper(DB_NAME, DB_VERSION, [STORE_NAME_V2]);
 }
 
 NetworkStatsDB.prototype = {
   __proto__: IndexedDBHelper.prototype,
 
   dbNewTxn: function dbNewTxn(txn_type, callback, txnCb) {
     function successCb(result) {
       txnCb(null, result);
@@ -303,17 +303,17 @@ NetworkStatsDB.prototype = {
     }
   },
 
   _removeOldStats: function _removeOldStats(txn, store, appId, connType, date) {
     // Callback function to remove old items when new ones are added.
     let filterDate = date - (SAMPLE_RATE * VALUES_MAX_LENGTH - 1);
     let lowerFilter = [appId, connType, 0];
     let upperFilter = [appId, connType, filterDate];
-    let range = this.dbGlobal.IDBKeyRange.bound(lowerFilter, upperFilter, false, false);
+    let range = IDBKeyRange.bound(lowerFilter, upperFilter, false, false);
     store.openCursor(range).onsuccess = function(event) {
       var cursor = event.target.result;
       if (cursor) {
         cursor.delete();
         cursor.continue();
       }
     }.bind(this);
   },
@@ -337,17 +337,17 @@ NetworkStatsDB.prototype = {
             aOptions.connectionType + " start: " + start + " end: " + end);
       debug("Start time: " + new Date(start));
       debug("End time: " + new Date(end));
     }
 
     this.dbNewTxn("readonly", function(txn, store) {
       let lowerFilter = [aOptions.appId, aOptions.connectionType, start];
       let upperFilter = [aOptions.appId, aOptions.connectionType, end];
-      let range = this.dbGlobal.IDBKeyRange.bound(lowerFilter, upperFilter, false, false);
+      let range = IDBKeyRange.bound(lowerFilter, upperFilter, false, false);
 
       let data = [];
 
       if (!txn.result) {
         txn.result = {};
       }
 
       let request = store.openCursor(range).onsuccess = function(event) {
@@ -382,17 +382,17 @@ NetworkStatsDB.prototype = {
       debug("FindAll: appId: " + aOptions.appId +
             " start: " + start + " end: " + end + "\n");
     }
 
     let self = this;
     this.dbNewTxn("readonly", function(txn, store) {
       let lowerFilter = start;
       let upperFilter = end;
-      let range = this.dbGlobal.IDBKeyRange.bound(lowerFilter, upperFilter, false, false);
+      let range = IDBKeyRange.bound(lowerFilter, upperFilter, false, false);
 
       let data = [];
 
       if (!txn.result) {
         txn.result = {};
       }
 
       let request = store.index("timestamp").openCursor(range).onsuccess = function(event) {
--- a/dom/network/src/NetworkStatsService.jsm
+++ b/dom/network/src/NetworkStatsService.jsm
@@ -22,34 +22,28 @@ const TOPIC_INTERFACE_REGISTERED   = "ne
 const TOPIC_INTERFACE_UNREGISTERED = "network-interface-unregistered";
 const NET_TYPE_WIFI = Ci.nsINetworkInterface.NETWORK_TYPE_WIFI;
 const NET_TYPE_MOBILE = Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE;
 const NET_TYPE_UNKNOWN = Ci.nsINetworkInterface.NETWORK_TYPE_UNKNOWN;
 
 // The maximum traffic amount can be saved in the |cachedAppStats|.
 const MAX_CACHED_TRAFFIC = 500 * 1000 * 1000; // 500 MB
 
-XPCOMUtils.defineLazyServiceGetter(this, "gIDBManager",
-                                   "@mozilla.org/dom/indexeddb/manager;1",
-                                   "nsIIndexedDatabaseManager");
-
 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
                                    "@mozilla.org/parentprocessmessagemanager;1",
                                    "nsIMessageListenerManager");
 
 XPCOMUtils.defineLazyServiceGetter(this, "networkManager",
                                    "@mozilla.org/network/manager;1",
                                    "nsINetworkManager");
 
 XPCOMUtils.defineLazyServiceGetter(this, "appsService",
                                    "@mozilla.org/AppsService;1",
                                    "nsIAppsService");
 
-let myGlobal = this;
-
 this.NetworkStatsService = {
   init: function() {
     if (DEBUG) {
       debug("Service started");
     }
 
     Services.obs.addObserver(this, "xpcom-shutdown", false);
     Services.obs.addObserver(this, TOPIC_INTERFACE_REGISTERED, false);
@@ -70,18 +64,17 @@ this.NetworkStatsService = {
                      "NetworkStats:Types",
                      "NetworkStats:SampleRate",
                      "NetworkStats:MaxStorageSamples"];
 
     this.messages.forEach(function(msgName) {
       ppmm.addMessageListener(msgName, this);
     }, this);
 
-    gIDBManager.initWindowless(myGlobal);
-    this._db = new NetworkStatsDB(myGlobal, this._connectionTypes);
+    this._db = new NetworkStatsDB(this._connectionTypes);
 
     // Stats for all interfaces are updated periodically
     this.timer.initWithCallback(this, this._db.sampleRate,
                                 Ci.nsITimer.TYPE_REPEATING_PRECISE);
 
     // App stats are firstly stored in the cached.
     this.cachedAppStats = Object.create(null);
     this.cachedAppStatsDate = new Date();
--- a/dom/network/tests/unit_stats/test_networkstats_db.js
+++ b/dom/network/tests/unit_stats/test_networkstats_db.js
@@ -516,14 +516,10 @@ add_test(function test_saveMultipleAppSt
         netStatsDb.saveStats(cached[keys[index]], callback);
     });
   });
 });
 
 function run_test() {
   do_get_profile();
 
-  var idbManager = Cc["@mozilla.org/dom/indexeddb/manager;1"].
-                   getService(Ci.nsIIndexedDatabaseManager);
-  idbManager.initWindowless(this);
-
   run_next_test();
 }
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -23,17 +23,17 @@
 extern const PRUnichar* kFlashFullscreenClass;
 using mozilla::gfx::SharedDIBSurface;
 #endif
 #include "gfxSharedImageSurface.h"
 #include "gfxUtils.h"
 #include "gfxAlphaRecovery.h"
 
 #include "mozilla/Util.h"
-#include "mozilla/ipc/SyncChannel.h"
+#include "mozilla/ipc/MessageChannel.h"
 #include "mozilla/AutoRestore.h"
 
 using namespace mozilla;
 using mozilla::ipc::ProcessChild;
 using namespace mozilla::plugins;
 using namespace mozilla::layers;
 using namespace std;
 
@@ -1400,17 +1400,17 @@ PluginInstanceChild::PluginWindowProc(HW
 
 // static
 LRESULT CALLBACK
 PluginInstanceChild::PluginWindowProcInternal(HWND hWnd,
                                               UINT message,
                                               WPARAM wParam,
                                               LPARAM lParam)
 {
-    NS_ASSERTION(!mozilla::ipc::SyncChannel::IsPumpingMessages(),
+    NS_ASSERTION(!mozilla::ipc::MessageChannel::IsPumpingMessages(),
                  "Failed to prevent a nonqueued message from running!");
     PluginInstanceChild* self = reinterpret_cast<PluginInstanceChild*>(
         GetProp(hWnd, kPluginInstanceChildProperty));
     if (!self) {
         NS_NOTREACHED("Badness!");
         return 0;
     }
 
--- a/dom/plugins/ipc/PluginMessageUtils.cpp
+++ b/dom/plugins/ipc/PluginMessageUtils.cpp
@@ -10,17 +10,17 @@
 
 #include "PluginInstanceParent.h"
 #include "PluginInstanceChild.h"
 #include "PluginScriptableObjectParent.h"
 #include "PluginScriptableObjectChild.h"
 
 using std::string;
 
-using mozilla::ipc::RPCChannel;
+using mozilla::ipc::MessageChannel;
 
 namespace {
 
 class DeferNPObjectReleaseRunnable : public nsRunnable
 {
 public:
   DeferNPObjectReleaseRunnable(const NPNetscapeFuncs* f, NPObject* o)
     : mFuncs(f)
@@ -62,31 +62,31 @@ NPRemoteWindow::NPRemoteWindow() :
 #endif
 {
   clipRect.top = 0;
   clipRect.left = 0;
   clipRect.bottom = 0;
   clipRect.right = 0;
 }
 
-RPCChannel::RacyRPCPolicy
-MediateRace(const RPCChannel::Message& parent,
-            const RPCChannel::Message& child)
+ipc::RacyRPCPolicy
+MediateRace(const MessageChannel::Message& parent,
+            const MessageChannel::Message& child)
 {
   switch (parent.type()) {
   case PPluginInstance::Msg_Paint__ID:
   case PPluginInstance::Msg_NPP_SetWindow__ID:
   case PPluginInstance::Msg_NPP_HandleEvent_Shmem__ID:
   case PPluginInstance::Msg_NPP_HandleEvent_IOSurface__ID:
     // our code relies on the frame list not changing during paints and
     // reflows
-    return RPCChannel::RRPParentWins;
+    return ipc::RRPParentWins;
 
   default:
-    return RPCChannel::RRPChildWins;
+    return ipc::RRPChildWins;
   }
 }
 
 #if defined(OS_LINUX)
 static string
 ReplaceAll(const string& haystack, const string& needle, const string& with)
 {
   string munged = haystack;
--- a/dom/plugins/ipc/PluginMessageUtils.h
+++ b/dom/plugins/ipc/PluginMessageUtils.h
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef DOM_PLUGINS_PLUGINMESSAGEUTILS_H
 #define DOM_PLUGINS_PLUGINMESSAGEUTILS_H
 
 #include "ipc/IPCMessageUtils.h"
 #include "base/message_loop.h"
 
-#include "mozilla/ipc/RPCChannel.h"
+#include "mozilla/ipc/MessageChannel.h"
 #include "mozilla/ipc/CrossProcessMutex.h"
 #include "gfxipc/ShadowLayerUtils.h"
 
 #include "npapi.h"
 #include "npruntime.h"
 #include "npfunctions.h"
 #include "nsAutoPtr.h"
 #include "nsString.h"
@@ -38,19 +38,19 @@ namespace plugins {
 using layers::SurfaceDescriptorX11;
 
 enum ScriptableObjectType
 {
   LocalObject,
   Proxy
 };
 
-mozilla::ipc::RPCChannel::RacyRPCPolicy
-MediateRace(const mozilla::ipc::RPCChannel::Message& parent,
-            const mozilla::ipc::RPCChannel::Message& child);
+mozilla::ipc::RacyRPCPolicy
+MediateRace(const mozilla::ipc::MessageChannel::Message& parent,
+            const mozilla::ipc::MessageChannel::Message& child);
 
 std::string
 MungePluginDsoPath(const std::string& path);
 std::string
 UnmungePluginDsoPath(const std::string& munged);
 
 extern PRLogModuleInfo* GetPluginLog();
 
--- a/dom/plugins/ipc/PluginModuleChild.cpp
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -11,17 +11,17 @@
 #include "NestedLoopTimer.h"
 #endif
 
 #include "mozilla/plugins/PluginModuleChild.h"
 
 /* This must occur *after* plugins/PluginModuleChild.h to avoid typedefs conflicts. */
 #include "mozilla/Util.h"
 
-#include "mozilla/ipc/SyncChannel.h"
+#include "mozilla/ipc/MessageChannel.h"
 
 #ifdef MOZ_WIDGET_GTK
 #include <gtk/gtk.h>
 #if (MOZ_WIDGET_GTK == 3)
 #include <gtk/gtkx.h>
 #endif
 #endif
 
--- a/dom/plugins/ipc/PluginModuleChild.h
+++ b/dom/plugins/ipc/PluginModuleChild.h
@@ -69,17 +69,17 @@ static const int kNestedLoopDetectorInte
 
 class PluginScriptableObjectChild;
 class PluginInstanceChild;
 
 class PluginModuleChild : public PPluginModuleChild
 {
     typedef mozilla::dom::PCrashReporterChild PCrashReporterChild;
 protected:
-    virtual mozilla::ipc::RPCChannel::RacyRPCPolicy
+    virtual mozilla::ipc::RacyRPCPolicy
     MediateRPCRace(const Message& parent, const Message& child) MOZ_OVERRIDE
     {
         return MediateRace(parent, child);
     }
 
     virtual bool ShouldContinueFromReplyTimeout() MOZ_OVERRIDE;
 
     // Implement the PPluginModuleChild interface
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -11,17 +11,17 @@
 #include "NestedLoopTimer.h"
 #endif
 
 #include "mozilla/plugins/PluginModuleParent.h"
 
 #include "base/process_util.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/PCrashReporterParent.h"
-#include "mozilla/ipc/SyncChannel.h"
+#include "mozilla/ipc/MessageChannel.h"
 #include "mozilla/plugins/BrowserStreamParent.h"
 #include "mozilla/plugins/PluginInstanceParent.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/unused.h"
 #include "nsAutoPtr.h"
 #include "nsCRT.h"
 #include "nsIFile.h"
@@ -46,17 +46,17 @@
 #elif XP_MACOSX
 #include "PluginInterposeOSX.h"
 #include "PluginUtilsOSX.h"
 #endif
 
 using base::KillProcess;
 
 using mozilla::PluginLibrary;
-using mozilla::ipc::SyncChannel;
+using mozilla::ipc::MessageChannel;
 using mozilla::dom::PCrashReporterParent;
 using mozilla::dom::CrashReporterParent;
 
 using namespace mozilla;
 using namespace mozilla::plugins;
 using namespace mozilla::plugins::parent;
 
 #ifdef MOZ_CRASHREPORTER
@@ -243,17 +243,17 @@ PluginModuleParent::WriteExtraDataForMin
     }
 }
 #endif  // MOZ_CRASHREPORTER
 
 void
 PluginModuleParent::SetChildTimeout(const int32_t aChildTimeout)
 {
     int32_t timeoutMs = (aChildTimeout > 0) ? (1000 * aChildTimeout) :
-                      SyncChannel::kNoTimeout;
+                      MessageChannel::kNoTimeout;
     SetReplyTimeoutMs(timeoutMs);
 }
 
 int
 PluginModuleParent::TimeoutChanged(const char* aPref, void* aModule)
 {
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 #ifndef XP_WIN
--- a/dom/plugins/ipc/PluginModuleParent.h
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -129,17 +129,17 @@ public:
     void TerminateChildProcess(MessageLoop* aMsgLoop);
 
 #ifdef XP_WIN
     void
     ExitedCxxStack() MOZ_OVERRIDE;
 #endif // XP_WIN
 
 protected:
-    virtual mozilla::ipc::RPCChannel::RacyRPCPolicy
+    virtual mozilla::ipc::RacyRPCPolicy
     MediateRPCRace(const Message& parent, const Message& child) MOZ_OVERRIDE
     {
         return MediateRace(parent, child);
     }
 
     virtual bool RecvXXX_HACK_FIXME_cjones(Shmem& mem) { NS_RUNTIMEABORT("not reached"); return false; }
 
     virtual bool ShouldContinueFromReplyTimeout() MOZ_OVERRIDE;
--- a/dom/push/src/PushService.jsm
+++ b/dom/push/src/PushService.jsm
@@ -40,25 +40,22 @@ const kPUSHDB_STORE_NAME = "push";
 const kUDP_WAKEUP_WS_STATUS_CODE = 4774;  // WebSocket Close status code sent
                                           // by server to signal that it can
                                           // wake client up using UDP.
 
 const kCHILD_PROCESS_MESSAGES = ["Push:Register", "Push:Unregister",
                                  "Push:Registrations"];
 
 // This is a singleton
-this.PushDB = function PushDB(aGlobal) {
+this.PushDB = function PushDB() {
   debug("PushDB()");
 
   // set the indexeddb database
-  let idbManager = Cc["@mozilla.org/dom/indexeddb/manager;1"]
-                     .getService(Ci.nsIIndexedDatabaseManager);
-  idbManager.initWindowless(aGlobal);
   this.initDBHelper(kPUSHDB_DB_NAME, kPUSHDB_DB_VERSION,
-                    [kPUSHDB_STORE_NAME], aGlobal);
+                    [kPUSHDB_STORE_NAME]);
 };
 
 this.PushDB.prototype = {
   __proto__: IndexedDBHelper.prototype,
 
   upgradeSchema: function(aTransaction, aDb, aOldVersion, aNewVersion) {
     debug("PushDB.upgradeSchema()")
 
@@ -172,17 +169,17 @@ this.PushDB.prototype = {
     }
 
     let self = this;
     this.newTxn(
       "readonly",
       kPUSHDB_STORE_NAME,
       function txnCb(aTxn, aStore) {
         let index = aStore.index("manifestURL");
-        let range = self.dbGlobal.IDBKeyRange.only(aManifestURL);
+        let range = IDBKeyRange.only(aManifestURL);
         aTxn.result = [];
         index.openCursor(range).onsuccess = function(event) {
           let cursor = event.target.result;
           if (cursor) {
             debug(cursor.value.manifestURL + " " + cursor.value.channelID);
             aTxn.result.push(cursor.value);
             cursor.continue();
           }
@@ -450,17 +447,17 @@ this.PushService = {
     this._ws.sendMsg(msg);
   },
 
   init: function() {
     debug("init()");
     if (!prefs.get("enabled"))
         return null;
 
-    this._db = new PushDB(this);
+    this._db = new PushDB();
 
     let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
                  .getService(Ci.nsIMessageBroadcaster);
 
     kCHILD_PROCESS_MESSAGES.forEach(function addMessage(msgName) {
         ppmm.addMessageListener(msgName, this);
     }.bind(this));
 
--- a/dom/settings/SettingsDB.jsm
+++ b/dom/settings/SettingsDB.jsm
@@ -193,13 +193,13 @@ SettingsDB.prototype = {
 
     // Fall-through, we now have a dictionary object.
     for (let prop in aObject) {
       aObject[prop] = this.prepareValue(aObject[prop]);
     }
     return aObject;
   },
 
-  init: function init(aGlobal) {
+  init: function init() {
     this.initDBHelper(SETTINGSDB_NAME, SETTINGSDB_VERSION,
-                      [SETTINGSSTORE_NAME], aGlobal);
+                      [SETTINGSSTORE_NAME]);
   }
 }
--- a/dom/settings/SettingsManager.js
+++ b/dom/settings/SettingsManager.js
@@ -244,26 +244,20 @@ SettingsLock.prototype = {
     }
   },
 
   classID: Components.ID("{60c9357c-3ae0-4222-8f55-da01428470d5}"),
   contractID: "@mozilla.org/settingsLock;1",
   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
 };
 
-let myGlobal = this;
-
 function SettingsManager() {
   this._locks = new Queue();
-  if (!("indexedDB" in myGlobal)) {
-    let idbManager = Components.classes["@mozilla.org/dom/indexeddb/manager;1"].getService(Ci.nsIIndexedDatabaseManager);
-    idbManager.initWindowless(myGlobal);
-  }
   this._settingsDB = new SettingsDB();
-  this._settingsDB.init(myGlobal);
+  this._settingsDB.init();
 }
 
 SettingsManager.prototype = {
   _callbacks: null,
 
   _wrap: function _wrap(obj) {
     return ObjectWrapper.wrap(obj, this._window);
   },
@@ -284,18 +278,18 @@ SettingsManager.prototype = {
   },
 
   createLock: function() {
     if (DEBUG) debug("get lock!");
     var lock = new SettingsLock(this);
     this._locks.enqueue(lock);
     this._settingsDB.ensureDB(
       function() { lock.createTransactionAndProcess(); },
-      function() { dump("Cannot open Settings DB. Trying to open an old version?\n"); },
-      myGlobal );
+      function() { dump("Cannot open Settings DB. Trying to open an old version?\n"); }
+    );
     this.nextTick(function() { this._open = false; }, lock);
     return lock;
   },
 
   receiveMessage: function(aMessage) {
     if (DEBUG) debug("Settings::receiveMessage: " + aMessage.name);
     let msg = aMessage.json;
 
--- a/dom/settings/SettingsService.js
+++ b/dom/settings/SettingsService.js
@@ -177,46 +177,40 @@ SettingsServiceLock.prototype = {
                                       contractID: SETTINGSSERVICELOCK_CONTRACTID,
                                       classDescription: "SettingsServiceLock",
                                       interfaces: [nsISettingsServiceLock],
                                       flags: nsIClassInfo.DOM_OBJECT })
 };
 
 const SETTINGSSERVICE_CID        = Components.ID("{f656f0c0-f776-11e1-a21f-0800200c9a66}");
 
-let myGlobal = this;
-
 function SettingsService()
 {
   debug("settingsService Constructor");
   this._locks = new Queue();
-  if (!("indexedDB" in myGlobal)) {
-    let idbManager = Components.classes["@mozilla.org/dom/indexeddb/manager;1"].getService(Ci.nsIIndexedDatabaseManager);
-    idbManager.initWindowless(myGlobal);
-  }
   this._settingsDB = new SettingsDB();
-  this._settingsDB.init(myGlobal);
+  this._settingsDB.init();
 }
 
 SettingsService.prototype = {
 
   nextTick: function nextTick(aCallback, thisObj) {
     if (thisObj)
       aCallback = aCallback.bind(thisObj);
 
     Services.tm.currentThread.dispatch(aCallback, Ci.nsIThread.DISPATCH_NORMAL);
   },
 
   createLock: function createLock() {
     var lock = new SettingsServiceLock(this);
     this._locks.enqueue(lock);
     this._settingsDB.ensureDB(
       function() { lock.createTransactionAndProcess(); },
-      function() { dump("SettingsService failed to open DB!\n"); },
-      myGlobal );
+      function() { dump("SettingsService failed to open DB!\n"); }
+    );
     this.nextTick(function() { this._open = false; }, lock);
     return lock;
   },
 
   classID : SETTINGSSERVICE_CID,
   QueryInterface : XPCOMUtils.generateQI([Ci.nsISettingsService]),
   classInfo: XPCOMUtils.generateCI({
     classID: SETTINGSSERVICE_CID,
--- a/dom/tests/mochitest/general/test_vibrator.html
+++ b/dom/tests/mochitest/general/test_vibrator.html
@@ -7,36 +7,25 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 
 <!-- Although we can't test that the vibrator works properly, we can test that
      navigator.vibrate throws an exception where appropriate. -->
 
 <script class="testbody" type="text/javascript;version=1.7">
+var result;
 function expectFailure(param) {
-  try {
-    navigator.vibrate(param);
-  }
-  catch(e) {
-    ok(true, 'vibrate(' + param + ') threw an expected exception.');
-    return;
-  }
-  ok(false, 'vibrate(' + param + ') should have thrown an exception.');
+  result = navigator.vibrate(param);
+  is(result, false, 'vibrate(' + param + ') should have failed.');
 }
 
 function expectSuccess(param) {
-  try {
-    navigator.vibrate(param);
-  }
-  catch(e) {
-    ok(false, 'vibrate(' + param + ') threw an unexpected exception.');
-    return;
-  }
-  ok(true, 'vibrate(' + param + ') did not throw an exception.');
+  result = navigator.vibrate(param);
+  is(result, true, 'vibrate(' + param + ') must succeed.');
 }
 
 function testFailures() {
   expectSuccess(null);
   expectSuccess(undefined);
   expectFailure(-1);
   expectSuccess('a');
   expectFailure([100, -1]);
--- a/dom/tests/unit/test_geolocation_provider.js
+++ b/dom/tests/unit/test_geolocation_provider.js
@@ -60,16 +60,21 @@ function geoHandler(metadata, response)
   response.setStatusLine("1.0", 200, "OK");
   response.setHeader("Cache-Control", "no-cache", false);
   response.setHeader("Content-Type", "aplication/x-javascript", false);
   response.write(position);
 }
 
 function run_test()
 {
+    // XPCShell does not get a profile by default. The geolocation service
+    // depends on the settings service which uses IndexedDB and IndexedDB
+    // needs a place where it can store databases.
+    do_get_profile();
+
     // only kill this test when shutdown is called on the provider.
     do_test_pending();
 
     httpserver = new HttpServer();
     httpserver.registerPathHandler("/geo", geoHandler);
     httpserver.start(-1);
 
     var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
--- a/dom/tests/unit/test_geolocation_timeout.js
+++ b/dom/tests/unit/test_geolocation_timeout.js
@@ -42,16 +42,21 @@ function errorCallback() {
 }
 
 function run_test()
 {
   do_test_pending();
 
   if (Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime)
         .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
+    // XPCShell does not get a profile by default. The geolocation service
+    // depends on the settings service which uses IndexedDB and IndexedDB
+    // needs a place where it can store databases.
+    do_get_profile();
+
     httpserver = new HttpServer();
     httpserver.registerPathHandler("/geo", geoHandler);
     httpserver.start(-1);
     var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
     prefs.setBoolPref("geo.wifi.scan", false);
     prefs.setCharPref("geo.wifi.uri", "http://localhost:" +
                       httpserver.identity.primaryPort + "/geo");
     prefs.setBoolPref("geo.testing.ignore_ipc_principal", true);
--- a/dom/tests/unit/xpcshell.ini
+++ b/dom/tests/unit/xpcshell.ini
@@ -3,10 +3,12 @@ head =
 tail = 
 
 [test_bug319968.js]
 [test_bug465752.js]
 [test_geolocation_provider.js]
 # Bug 684962: test hangs consistently on Android
 skip-if = os == "android"
 [test_geolocation_timeout.js]
+# Bug 919946: test hangs consistently on Android
+skip-if = os == "android"
 [test_geolocation_timeout_wrap.js]
 skip-if = os == "mac" || os == "android"
--- a/dom/webidl/Event.webidl
+++ b/dom/webidl/Event.webidl
@@ -25,16 +25,17 @@ interface Event {
   void stopPropagation();
   void stopImmediatePropagation();
 
   readonly attribute boolean bubbles;
   readonly attribute boolean cancelable;
   void preventDefault();
   readonly attribute boolean defaultPrevented;
 
+  [Unforgeable]
   readonly attribute boolean isTrusted;
   readonly attribute DOMTimeStamp timeStamp;
 
   [Throws]
   void initEvent(DOMString type, boolean bubbles, boolean cancelable);
 };
 
 // Mozilla specific legacy stuff.
new file mode 100644
--- /dev/null
+++ b/dom/webidl/IDBKeyRange.webidl
@@ -0,0 +1,29 @@
+/* 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/. */
+/*
+ * The origin of this IDL file is
+ * https://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html
+ *
+ * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
+ * liability, trademark and document use rules apply.
+ */
+
+interface IDBKeyRange {
+  [Throws]
+  readonly attribute any     lower;
+  [Throws]
+  readonly attribute any     upper;
+  [Constant]
+  readonly attribute boolean lowerOpen;
+  [Constant]
+  readonly attribute boolean upperOpen;
+  [Creator, Throws]
+  static IDBKeyRange only (any value);
+  [Creator, Throws]
+  static IDBKeyRange lowerBound (any lower, optional boolean open = false);
+  [Creator, Throws]
+  static IDBKeyRange upperBound (any upper, optional boolean open = false);
+  [Creator, Throws]
+  static IDBKeyRange bound (any lower, any upper, optional boolean lowerOpen = false, optional boolean upperOpen = false);
+};
--- a/dom/webidl/IDBObjectStore.webidl
+++ b/dom/webidl/IDBObjectStore.webidl
@@ -66,9 +66,12 @@ partial interface IDBObjectStore {
     [Throws]
     IDBRequest mozGetAll (optional any key, optional unsigned long limit);
 
     [Pref="dom.indexedDB.experimental", Throws]
     IDBRequest getAll (optional any key, optional unsigned long limit);
 
     [Pref="dom.indexedDB.experimental", Throws]
     IDBRequest getAllKeys (optional any key, optional unsigned long limit);
+
+    [Pref="dom.indexedDB.experimental", Throws]
+    IDBRequest openKeyCursor (optional any range, optional IDBCursorDirection direction = "next");
 };
--- a/dom/webidl/Navigator.webidl
+++ b/dom/webidl/Navigator.webidl
@@ -107,22 +107,18 @@ interface NavigatorBattery {
     readonly attribute BatteryManager? battery;
 };
 Navigator implements NavigatorBattery;
 
 // http://www.w3.org/TR/vibration/#vibration-interface
 partial interface Navigator {
     // We don't support sequences in unions yet
     //boolean vibrate ((unsigned long or sequence<unsigned long>) pattern);
-    // XXXbz also, per spec we should be returning a boolean, and we just don't.
-    // See bug 884935.
-    [Throws]
-    void vibrate(unsigned long duration);
-    [Throws]
-    void vibrate(sequence<unsigned long> pattern);
+    boolean vibrate(unsigned long duration);
+    boolean vibrate(sequence<unsigned long> pattern);
 };
 
 // Mozilla-specific extensions
 
 callback interface MozIdleObserver {
   // Time is in seconds and is read only when idle observers are added
   // and removed.
   readonly attribute unsigned long time;
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -174,16 +174,17 @@ WEBIDL_FILES = [
     'HTMLTrackElement.webidl',
     'HTMLUListElement.webidl',
     'HTMLVideoElement.webidl',
     'IDBCursor.webidl',
     'IDBDatabase.webidl',
     'IDBFactory.webidl',
     'IDBFileHandle.webidl',
     'IDBIndex.webidl',
+    'IDBKeyRange.webidl',
     'IDBObjectStore.webidl',
     'IDBOpenDBRequest.webidl',
     'IDBRequest.webidl',
     'IDBTransaction.webidl',
     'IDBVersionChangeEvent.webidl',
     'ImageData.webidl',
     'ImageDocument.webidl',
     'InputMethod.webidl',
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -500,17 +500,17 @@ public:
    */
   virtual TemporaryRef<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget) = 0;
 
   /* This copies the path describing the glyphs into a PathBuilder. We use this
    * API rather than a generic API to append paths because it allows easier
    * implementation in some backends, and more efficient implementation in
    * others.
    */
-  virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder) = 0;
+  virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint = nullptr) = 0;
 
   virtual bool GetFontFileData(FontFileDataOutput, void *) { return false; }
 
   void AddUserData(UserDataKey *key, void *userData, void (*destroy)(void*)) {
     mUserData.Add(key, userData, destroy);
   }
   void *GetUserData(UserDataKey *key) {
     return mUserData.Get(key);
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -32,16 +32,18 @@
 #include "cairo-win32.h"
 #endif
 
 #include <algorithm>
 
 namespace mozilla {
 namespace gfx {
 
+cairo_surface_t *DrawTargetCairo::mDummySurface;
+
 namespace {
 
 // An RAII class to prepare to draw a context and optional path. Saves and
 // restores the context on construction/destruction.
 class AutoPrepareForDrawing
 {
 public:
   AutoPrepareForDrawing(DrawTargetCairo* dt, cairo_t* ctx)
@@ -373,25 +375,21 @@ NeedIntermediateSurface(const Pattern& a
   if (aOptions.mAlpha == 1.0)
     return false;
 
   return true;
 }
 
 DrawTargetCairo::DrawTargetCairo()
   : mContext(nullptr)
-  , mPathObserver(nullptr)
 {
 }
 
 DrawTargetCairo::~DrawTargetCairo()
 {
-  if (mPathObserver) {
-    mPathObserver->ForgetDrawTarget();
-  }
   cairo_destroy(mContext);
   if (mSurface) {
     cairo_surface_destroy(mSurface);
   }
 }
 
 IntSize
 DrawTargetCairo::GetSize()
@@ -424,16 +422,28 @@ DrawTargetCairo::Flush()
 }
 
 void
 DrawTargetCairo::PrepareForDrawing(cairo_t* aContext, const Path* aPath /* = nullptr */)
 {
   WillChange(aPath);
 }
 
+cairo_surface_t*
+DrawTargetCairo::GetDummySurface()
+{
+  if (mDummySurface) {
+    return mDummySurface;
+  }
+
+  mDummySurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1);
+
+  return mDummySurface;
+}
+
 void
 DrawTargetCairo::DrawSurface(SourceSurface *aSurface,
                              const Rect &aDest,
                              const Rect &aSource,
                              const DrawSurfaceOptions &aSurfOptions,
                              const DrawOptions &aOptions)
 {
   AutoPrepareForDrawing prep(this, mContext);
@@ -703,33 +713,33 @@ DrawTargetCairo::Stroke(const Path *aPat
                         const DrawOptions &aOptions /* = DrawOptions() */)
 {
   AutoPrepareForDrawing prep(this, mContext, aPath);
 
   if (aPath->GetBackendType() != BACKEND_CAIRO)
     return;
 
   PathCairo* path = const_cast<PathCairo*>(static_cast<const PathCairo*>(aPath));
-  path->CopyPathTo(mContext, this);
+  path->SetPathOnContext(mContext);
 
   DrawPattern(aPattern, aStrokeOptions, aOptions, DRAW_STROKE);
 }
 
 void
 DrawTargetCairo::Fill(const Path *aPath,
                       const Pattern &aPattern,
                       const DrawOptions &aOptions /* = DrawOptions() */)
 {
   AutoPrepareForDrawing prep(this, mContext, aPath);
 
   if (aPath->GetBackendType() != BACKEND_CAIRO)
     return;
 
   PathCairo* path = const_cast<PathCairo*>(static_cast<const PathCairo*>(aPath));
-  path->CopyPathTo(mContext, this);
+  path->SetPathOnContext(mContext);
 
   DrawPattern(aPattern, StrokeOptions(), aOptions, DRAW_FILL);
 }
 
 void
 DrawTargetCairo::FillGlyphs(ScaledFont *aFont,
                             const GlyphBuffer &aBuffer,
                             const Pattern &aPattern,
@@ -831,17 +841,17 @@ DrawTargetCairo::PushClip(const Path *aP
   if (aPath->GetBackendType() != BACKEND_CAIRO) {
     return;
   }
 
   WillChange(aPath);
   cairo_save(mContext);
 
   PathCairo* path = const_cast<PathCairo*>(static_cast<const PathCairo*>(aPath));
-  path->CopyPathTo(mContext, this);
+  path->SetPathOnContext(mContext);
   cairo_clip_preserve(mContext);
 }
 
 void
 DrawTargetCairo::PushClipRect(const Rect& aRect)
 {
   WillChange();
   cairo_save(mContext);
@@ -856,19 +866,17 @@ DrawTargetCairo::PopClip()
 {
   // save/restore does not affect the path, so no need to call WillChange()
   cairo_restore(mContext);
 }
 
 TemporaryRef<PathBuilder>
 DrawTargetCairo::CreatePathBuilder(FillRule aFillRule /* = FILL_WINDING */) const
 {
-  RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(mContext,
-                                                          const_cast<DrawTargetCairo*>(this),
-                                                          aFillRule);
+  RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(aFillRule);
 
   return builder;
 }
 
 void
 DrawTargetCairo::ClearSurfaceForUnboundedSource(const CompositionOp &aOperator)
 {
   if (aOperator != OP_SOURCE)
@@ -1066,31 +1074,16 @@ DrawTargetCairo::MarkSnapshotIndependent
     mSnapshot = nullptr;
   }
 }
 
 void
 DrawTargetCairo::WillChange(const Path* aPath /* = nullptr */)
 {
   MarkSnapshotIndependent();
-
-  if (mPathObserver &&
-      (!aPath || !mPathObserver->ContainsPath(aPath))) {
-    mPathObserver->PathWillChange();
-    mPathObserver = nullptr;
-  }
-}
-
-void
-DrawTargetCairo::SetPathObserver(CairoPathContext* aPathObserver)
-{
-  if (mPathObserver && mPathObserver != aPathObserver) {
-    mPathObserver->PathWillChange();
-  }
-  mPathObserver = aPathObserver;
 }
 
 void
 DrawTargetCairo::SetTransform(const Matrix& aTransform)
 {
   mTransform = aTransform;
 
   cairo_matrix_t mat;
--- a/gfx/2d/DrawTargetCairo.h
+++ b/gfx/2d/DrawTargetCairo.h
@@ -137,25 +137,25 @@ public:
     CreateGradientStops(GradientStop *aStops,
                         uint32_t aNumStops,
                         ExtendMode aExtendMode = EXTEND_CLAMP) const;
 
   virtual void *GetNativeSurface(NativeSurfaceType aType);
 
   bool Init(cairo_surface_t* aSurface, const IntSize& aSize);
 
-  void SetPathObserver(CairoPathContext* aPathObserver);
-
   virtual void SetTransform(const Matrix& aTransform);
 
   // Call to set up aContext for drawing (with the current transform, etc).
   // Pass the path you're going to be using if you have one.
   // Implicitly calls WillChange(aPath).
   void PrepareForDrawing(cairo_t* aContext, const Path* aPath = nullptr);
 
+  static cairo_surface_t *GetDummySurface();
+
 private: // methods
   // Init cairo surface without doing a cairo_surface_reference() call.
   bool InitAlreadyReferenced(cairo_surface_t* aSurface, const IntSize& aSize);
 
   enum DrawPatternType { DRAW_FILL, DRAW_STROKE };
   void DrawPattern(const Pattern& aPattern,
                    const StrokeOptions& aStrokeOptions,
                    const DrawOptions& aOptions,
@@ -176,20 +176,15 @@ private: // methods
 private: // data
   cairo_t* mContext;
   cairo_surface_t* mSurface;
   IntSize mSize;
 
   // The latest snapshot of this surface. This needs to be told when this
   // target is modified. We keep it alive as a cache.
   RefPtr<SourceSurfaceCairo> mSnapshot;
-
-  // It is safe to use a regular pointer here because the CairoPathContext will
-  // deregister itself on destruction. Using a RefPtr would extend the life-
-  // span of the CairoPathContext. This causes a problem when
-  // PathBuilderCairo.Finish()
-  mutable CairoPathContext* mPathObserver;
+  static cairo_surface_t *mDummySurface;
 };
 
 }
 }
 
 #endif // _MOZILLA_GFX_DRAWTARGET_CAIRO_H_
--- a/gfx/2d/PathCairo.cpp
+++ b/gfx/2d/PathCairo.cpp
@@ -8,315 +8,287 @@
 #include "DrawTargetCairo.h"
 #include "Logging.h"
 #include "PathHelpers.h"
 #include "HelpersCairo.h"
 
 namespace mozilla {
 namespace gfx {
 
-CairoPathContext::CairoPathContext(cairo_t* aCtx, DrawTargetCairo* aDrawTarget)
- : mContext(aCtx)
- , mDrawTarget(aDrawTarget)
-{
-  cairo_reference(mContext);
-
-  // A new path in the DrawTarget's context.
-  aDrawTarget->SetPathObserver(this);
-  cairo_new_path(mContext);
-}
-
-CairoPathContext::CairoPathContext(CairoPathContext& aPathContext)
- : mContext(aPathContext.mContext)
- , mDrawTarget(nullptr)
-{
-  cairo_reference(mContext);
-  DuplicateContextAndPath();
-}
-
-CairoPathContext::~CairoPathContext()
-{
-  if (mDrawTarget) {
-    DrawTargetCairo* drawTarget = mDrawTarget;
-    ForgetDrawTarget();
-
-    // We need to set mDrawTarget to nullptr before we tell DrawTarget otherwise
-    // we will think we need to make a defensive copy of the path.
-    drawTarget->SetPathObserver(nullptr);
-  }
-  cairo_destroy(mContext);
-}
-
-void
-CairoPathContext::DuplicateContextAndPath()
-{
-  // Duplicate the path.
-  cairo_path_t* path = cairo_copy_path(mContext);
-
-  // Duplicate the context.
-  cairo_surface_t* surf = cairo_get_target(mContext);
-  cairo_matrix_t matrix;
-  cairo_get_matrix(mContext, &matrix);
-  cairo_destroy(mContext);
-
-  mContext = cairo_create(surf);
-
-  // Set the matrix to match the source context so that the path is copied in
-  // device space. After this point it doesn't matter what the transform is
-  // set to because it's always swapped out before use.
-  cairo_set_matrix(mContext, &matrix);
-
-  // Add the path, and throw away our duplicate.
-  cairo_append_path(mContext, path);
-  cairo_path_destroy(path);
-}
-
-void
-CairoPathContext::ForgetDrawTarget()
+PathBuilderCairo::PathBuilderCairo(FillRule aFillRule)
+  : mFillRule(aFillRule)
 {
-  // We don't need to set the path observer back to nullptr in this case
-  // because ForgetDrawTarget() is trigged when the target has been
-  // grabbed by another path observer.
-  mDrawTarget = nullptr;
 }
 
 void
-CairoPathContext::PathWillChange()
-{
-  // Once we've copied out the context's path, there's no use to holding on to
-  // the draw target. Thus, there's nothing for us to do if we're independent
-  // of the draw target, since we'll have already copied out the context's
-  // path.
-  if (mDrawTarget) {
-    // The context we point to is going to change from under us. To continue
-    // using this path, we need to copy it to a new context.
-    DuplicateContextAndPath();
-    ForgetDrawTarget();
-  }
-}
-
-void
-CairoPathContext::CopyPathTo(cairo_t* aToContext, Matrix& aTransform)
-{
-  if (aToContext != mContext) {
-    CairoTempMatrix tempMatrix(mContext, aTransform);
-    cairo_path_t* path = cairo_copy_path(mContext);
-    cairo_new_path(aToContext);
-    cairo_append_path(aToContext, path);
-    cairo_path_destroy(path);
-  }
-}
-
-bool
-CairoPathContext::ContainsPath(const Path* aPath)
-{
-  if (aPath->GetBackendType() != BACKEND_CAIRO) {
-    return false;
-  }
-
-  const PathCairo* path = static_cast<const PathCairo*>(aPath);
-  RefPtr<CairoPathContext> ctx = const_cast<PathCairo*>(path)->GetPathContext();
-  return ctx == this;
-}
-
-PathBuilderCairo::PathBuilderCairo(CairoPathContext* aPathContext,
-                                   FillRule aFillRule,
-                                   const Matrix& aTransform /* = Matrix() */)
- : mPathContext(aPathContext)
- , mTransform(aTransform)
- , mFillRule(aFillRule)
-{}
-
-PathBuilderCairo::PathBuilderCairo(cairo_t* aCtx, DrawTargetCairo* aDrawTarget, FillRule aFillRule)
- : mPathContext(new CairoPathContext(aCtx, aDrawTarget))
- , mTransform(aDrawTarget->GetTransform())
- , mFillRule(aFillRule)
-{}
-
-void
 PathBuilderCairo::MoveTo(const Point &aPoint)
 {
-  PrepareForWrite();
-  CairoTempMatrix tempMatrix(*mPathContext, mTransform);
-  cairo_move_to(*mPathContext, aPoint.x, aPoint.y);
+  cairo_path_data_t data;
+  data.header.type = CAIRO_PATH_MOVE_TO;
+  data.header.length = 2;
+  mPathData.push_back(data);
+  data.point.x = aPoint.x;
+  data.point.y = aPoint.y;
+  mPathData.push_back(data);
+
+  mBeginPoint = mCurrentPoint = aPoint;
 }
 
 void
 PathBuilderCairo::LineTo(const Point &aPoint)
 {
-  PrepareForWrite();
-  CairoTempMatrix tempMatrix(*mPathContext, mTransform);
-  cairo_line_to(*mPathContext, aPoint.x, aPoint.y);
+  cairo_path_data_t data;
+  data.header.type = CAIRO_PATH_LINE_TO;
+  data.header.length = 2;
+  mPathData.push_back(data);
+  data.point.x = aPoint.x;
+  data.point.y = aPoint.y;
+  mPathData.push_back(data);
+
+  mCurrentPoint = aPoint;
 }
 
 void
 PathBuilderCairo::BezierTo(const Point &aCP1,
                            const Point &aCP2,
                            const Point &aCP3)
 {
-  PrepareForWrite();
-  CairoTempMatrix tempMatrix(*mPathContext, mTransform);
-  cairo_curve_to(*mPathContext, aCP1.x, aCP1.y, aCP2.x, aCP2.y, aCP3.x, aCP3.y);
+  cairo_path_data_t data;
+  data.header.type = CAIRO_PATH_CURVE_TO;
+  data.header.length = 4;
+  mPathData.push_back(data);
+  data.point.x = aCP1.x;
+  data.point.y = aCP1.y;
+  mPathData.push_back(data);
+  data.point.x = aCP2.x;
+  data.point.y = aCP2.y;
+  mPathData.push_back(data);
+  data.point.x = aCP3.x;
+  data.point.y = aCP3.y;
+  mPathData.push_back(data);
+
+  mCurrentPoint = aCP3;
 }
 
 void
 PathBuilderCairo::QuadraticBezierTo(const Point &aCP1,
                                     const Point &aCP2)
 {
-  PrepareForWrite();
-  CairoTempMatrix tempMatrix(*mPathContext, mTransform);
-
   // We need to elevate the degree of this quadratic Bézier to cubic, so we're
   // going to add an intermediate control point, and recompute control point 1.
   // The first and last control points remain the same.
   // This formula can be found on http://fontforge.sourceforge.net/bezier.html
   Point CP0 = CurrentPoint();
   Point CP1 = (CP0 + aCP1 * 2.0) / 3.0;
   Point CP2 = (aCP2 + aCP1 * 2.0) / 3.0;
   Point CP3 = aCP2;
 
-  cairo_curve_to(*mPathContext, CP1.x, CP1.y, CP2.x, CP2.y, CP3.x, CP3.y);
+  cairo_path_data_t data;
+  data.header.type = CAIRO_PATH_CURVE_TO;
+  data.header.length = 4;
+  mPathData.push_back(data);
+  data.point.x = CP1.x;
+  data.point.y = CP1.y;
+  mPathData.push_back(data);
+  data.point.x = CP2.x;
+  data.point.y = CP2.y;
+  mPathData.push_back(data);
+  data.point.x = CP3.x;
+  data.point.y = CP3.y;
+  mPathData.push_back(data);
+
+  mCurrentPoint = aCP2;
 }
 
 void
 PathBuilderCairo::Close()
 {
-  PrepareForWrite();
-  cairo_close_path(*mPathContext);
+  cairo_path_data_t data;
+  data.header.type = CAIRO_PATH_CLOSE_PATH;
+  data.header.length = 1;
+  mPathData.push_back(data);
+
+  mCurrentPoint = mBeginPoint;
 }
 
 void
 PathBuilderCairo::Arc(const Point &aOrigin, float aRadius, float aStartAngle,
                      float aEndAngle, bool aAntiClockwise)
 {
   ArcToBezier(this, aOrigin, aRadius, aStartAngle, aEndAngle, aAntiClockwise);
 }
 
 Point
 PathBuilderCairo::CurrentPoint() const
 {
-  CairoTempMatrix tempMatrix(*mPathContext, mTransform);
-  double x, y;
-  cairo_get_current_point(*mPathContext, &x, &y);
-  return Point((Float)x, (Float)y);
+  return mCurrentPoint;
 }
 
 TemporaryRef<Path>
 PathBuilderCairo::Finish()
 {
-  return new PathCairo(mPathContext, mTransform, mFillRule);
+  return new PathCairo(mFillRule, mPathData, mCurrentPoint);
 }
 
-TemporaryRef<CairoPathContext>
-PathBuilderCairo::GetPathContext()
+PathCairo::PathCairo(FillRule aFillRule, std::vector<cairo_path_data_t> &aPathData, const Point &aCurrentPoint)
+  : mFillRule(aFillRule)
+  , mContainingContext(nullptr)
+  , mCurrentPoint(aCurrentPoint)
 {
-  return mPathContext;
+  mPathData.swap(aPathData);
 }
 
-void
-PathBuilderCairo::PrepareForWrite()
+PathCairo::PathCairo(cairo_t *aContext)
+  : mFillRule(FILL_WINDING)
+  , mContainingContext(nullptr)
 {
-  // Only PathBuilder and PathCairo maintain references to CairoPathContext.
-  // DrawTarget does not. If we're sharing a reference to the context then we
-  // need to create a copy that we can modify. This provides copy on write
-  // behaviour.
-  if (mPathContext->refCount() != 1) {
-    mPathContext = new CairoPathContext(*mPathContext);
+  cairo_path_t *path = cairo_copy_path(aContext);
+
+  // XXX - mCurrentPoint is not properly set here, the same is true for the
+  // D2D Path code, we never require current point when hitting this codepath
+  // but this should be fixed.
+  for (int i = 0; i < path->num_data; i++) {
+    mPathData.push_back(path->data[i]);
+  }
+
+  cairo_path_destroy(path);
+}
+
+PathCairo::~PathCairo()
+{
+  if (mContainingContext) {
+    cairo_destroy(mContainingContext);
   }
 }
 
-PathCairo::PathCairo(CairoPathContext* aPathContext, Matrix& aTransform,
-                     FillRule aFillRule)
- : mPathContext(aPathContext)
- , mTransform(aTransform)
- , mFillRule(aFillRule)
-{}
-
 TemporaryRef<PathBuilder>
 PathCairo::CopyToBuilder(FillRule aFillRule) const
 {
-  return new PathBuilderCairo(mPathContext, aFillRule, mTransform);
+  RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(aFillRule);
+
+  builder->mPathData = mPathData;
+  builder->mCurrentPoint = mCurrentPoint;
+
+  return builder;
 }
 
 TemporaryRef<PathBuilder>
 PathCairo::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const
 {
-  // We are given the transform we would apply from device space to user space.
-  // However in cairo our path is in device space so we view the transform as
-  // being the other way round. We therefore need to apply the inverse transform
-  // to our current cairo transform.
-  Matrix inverse = aTransform;
-  inverse.Invert();
+  RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(aFillRule);
 
-  return new PathBuilderCairo(mPathContext, aFillRule, mTransform * inverse);
+  AppendPathToBuilder(builder, &aTransform);
+  builder->mCurrentPoint = aTransform * mCurrentPoint;
+
+  return builder;
 }
 
 bool
 PathCairo::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const
 {
-  CairoTempMatrix temp(*mPathContext, mTransform);
-
   Matrix inverse = aTransform;
   inverse.Invert();
   Point transformed = inverse * aPoint;
 
-  // Needs the correct fill rule set.
-  cairo_set_fill_rule(*mPathContext, GfxFillRuleToCairoFillRule(mFillRule));
-  return cairo_in_fill(*mPathContext, transformed.x, transformed.y);
+  EnsureContainingContext();
+
+  return cairo_in_fill(mContainingContext, transformed.x, transformed.y);
 }
 
 bool
 PathCairo::StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
                                const Point &aPoint,
                                const Matrix &aTransform) const
 {
-  CairoTempMatrix temp(*mPathContext, mTransform);
-
   Matrix inverse = aTransform;
   inverse.Invert();
   Point transformed = inverse * aPoint;
 
-  SetCairoStrokeOptions(*mPathContext, aStrokeOptions);
-  return cairo_in_stroke(*mPathContext, transformed.x, transformed.y);
+  EnsureContainingContext();
+
+  SetCairoStrokeOptions(mContainingContext, aStrokeOptions);
+
+  return cairo_in_stroke(mContainingContext, transformed.x, transformed.y);
 }
 
 Rect
 PathCairo::GetBounds(const Matrix &aTransform) const
 {
-  CairoTempMatrix temp(*mPathContext, mTransform);
+  EnsureContainingContext();
 
   double x1, y1, x2, y2;
 
-  cairo_path_extents(*mPathContext, &x1, &y1, &x2, &y2);
+  cairo_path_extents(mContainingContext, &x1, &y1, &x2, &y2);
   Rect bounds(Float(x1), Float(y1), Float(x2 - x1), Float(y2 - y1));
   return aTransform.TransformBounds(bounds);
 }
 
 Rect
 PathCairo::GetStrokedBounds(const StrokeOptions &aStrokeOptions,
                             const Matrix &aTransform) const
 {
-  CairoTempMatrix temp(*mPathContext, mTransform);
+  EnsureContainingContext();
 
   double x1, y1, x2, y2;
 
-  SetCairoStrokeOptions(*mPathContext, aStrokeOptions);
+  SetCairoStrokeOptions(mContainingContext, aStrokeOptions);
 
-  cairo_stroke_extents(*mPathContext, &x1, &y1, &x2, &y2);
+  cairo_stroke_extents(mContainingContext, &x1, &y1, &x2, &y2);
   Rect bounds((Float)x1, (Float)y1, (Float)(x2 - x1), (Float)(y2 - y1));
   return aTransform.TransformBounds(bounds);
 }
 
-TemporaryRef<CairoPathContext>
-PathCairo::GetPathContext()
+void
+PathCairo::EnsureContainingContext() const
 {
-  return mPathContext;
+  if (mContainingContext) {
+    return;
+  }
+
+  mContainingContext = cairo_create(DrawTargetCairo::GetDummySurface());
+
+  SetPathOnContext(mContainingContext);
 }
 
 void
-PathCairo::CopyPathTo(cairo_t* aContext, DrawTargetCairo* aDrawTarget)
+PathCairo::SetPathOnContext(cairo_t *aContext) const
+{
+  // Needs the correct fill rule set.
+  cairo_set_fill_rule(aContext, GfxFillRuleToCairoFillRule(mFillRule));
+
+  cairo_new_path(aContext);
+
+  if (mPathData.size()) {
+    cairo_path_t path;
+    path.data = const_cast<cairo_path_data_t*>(&mPathData.front());
+    path.num_data = mPathData.size();
+    path.status = CAIRO_STATUS_SUCCESS;
+    cairo_append_path(aContext, &path);
+  }
+}
+
+void
+PathCairo::AppendPathToBuilder(PathBuilderCairo *aBuilder, const Matrix *aTransform) const
 {
-  mPathContext->CopyPathTo(aContext, mTransform);
-  cairo_set_fill_rule(aContext, GfxFillRuleToCairoFillRule(mFillRule));
+  if (aTransform) {
+    int i = 0;
+    while (i < mPathData.size()) {
+      uint32_t pointCount = mPathData[i].header.length - 1;
+      aBuilder->mPathData.push_back(mPathData[i]);
+      i++;
+      for (int c = 0; c < pointCount; c++) {
+        cairo_path_data_t data;
+        Point newPoint = *aTransform * Point(mPathData[i].point.x, mPathData[i].point.y);
+        data.point.x = newPoint.x;
+        data.point.y = newPoint.y;
+        aBuilder->mPathData.push_back(data);
+        i++;
+      }
+    }
+  } else {
+    for (int i = 0; i < mPathData.size(); i++) {
+      aBuilder->mPathData.push_back(mPathData[i]);
+    }
+  }
 }
 
 }
 }
--- a/gfx/2d/PathCairo.h
+++ b/gfx/2d/PathCairo.h
@@ -3,124 +3,59 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_GFX_PATH_CAIRO_H_
 #define MOZILLA_GFX_PATH_CAIRO_H_
 
 #include "2D.h"
 #include "cairo.h"
+#include <vector>
 
 namespace mozilla {
 namespace gfx {
 
 class DrawTargetCairo;
-
-// A reference to a cairo context that can maintain and set a path.
-//
-// This class exists to make it possible for us to not construct paths manually
-// using cairo_path_t, which in the common case is a speed and memory
-// optimization (as the cairo_t maintains the path for us, and we don't have to
-// use cairo_append_path). Instead, we can share a cairo_t with a DrawTarget,
-// and have it inform us when we need to make a copy of the path.
-//
-// Exactly one Path* object represents the current path on a given DrawTarget's
-// context. That Path* object registers its CairoPathContext with the
-// DrawTarget it's associated with. If that DrawTarget is going to change its
-// path, it has to tell the CairoPathContext beforehand so the path can be
-// saved off.
-// The path ownership is transferred to every new instance of CairoPathContext
-// in the constructor. We inform the draw target of the new context object,
-// which causes us to save off a copy of the path, as we're not going to be
-// informed upon changes any more.
-// Any transformation on aCtx is not applied to this path, though a path can be
-// transformed separately from its context by passing a matrix to the
-// constructor.
-class CairoPathContext : public RefCounted<CairoPathContext>
-{
-public:
-  // Construct a new empty CairoPathContext that uses the given draw target and
-  // its cairo context. Using the existing context may save having to copy the
-  // path later.
-  CairoPathContext(cairo_t* aCtx, DrawTargetCairo* aDrawTarget);
-
-  // Copy the path.
-  CairoPathContext(CairoPathContext& aPathContext);
-
-  ~CairoPathContext();
-
-  // Copy the path on mContext to be the path on aToContext, if they aren't the
-  // same. At this point we set the fill rule for the destination context as
-  // there is little point in doing this earlier.
-  void CopyPathTo(cairo_t* aToContext, Matrix& aTransform);
-
-  // This method must be called by the draw target before it changes the path
-  // currently on the cairo context.
-  void PathWillChange();
-
-  // This method must be called as the draw target is dying. In this case, we
-  // forget our reference to the draw target, and become the only reference to
-  // our context.
-  void ForgetDrawTarget();
-
-  // Create a duplicate context, and copy this path to that context.
-  void DuplicateContextAndPath();
-
-  // Returns true if this CairoPathContext represents path.
-  bool ContainsPath(const Path* path);
-
-  cairo_t* GetContext() const { return mContext; }
-  DrawTargetCairo* GetDrawTarget() const { return mDrawTarget; }
-  operator cairo_t* () const { return mContext; }
-
-private: // data
-  cairo_t* mContext;
-  // Not a RefPtr to avoid cycles.
-  DrawTargetCairo* mDrawTarget;
-};
+class PathCairo;
 
 class PathBuilderCairo : public PathBuilder
 {
 public:
-  // Creates a new empty path. It also implicitly takes ownership of aCtx by
-  // calling aDrawTarget->SetPathObserver(). Therefore, if the draw target has a
-  // path observer, this constructor will cause it to copy out its path.
-  PathBuilderCairo(cairo_t* aCtx, DrawTargetCairo* aDrawTarget, FillRule aFillRule);
-
-  // Creates a path builder out of an existing CairoPathContext with a new fill
-  // rule and transform.
-  PathBuilderCairo(CairoPathContext* aContext, FillRule aFillRule, const Matrix& aTransform = Matrix());
+  PathBuilderCairo(FillRule aFillRule);
 
   virtual void MoveTo(const Point &aPoint);
   virtual void LineTo(const Point &aPoint);
   virtual void BezierTo(const Point &aCP1,
                         const Point &aCP2,
                         const Point &aCP3);
   virtual void QuadraticBezierTo(const Point &aCP1,
                                  const Point &aCP2);
   virtual void Close();
   virtual void Arc(const Point &aOrigin, float aRadius, float aStartAngle,
                    float aEndAngle, bool aAntiClockwise = false);
   virtual Point CurrentPoint() const;
   virtual TemporaryRef<Path> Finish();
 
-  TemporaryRef<CairoPathContext> GetPathContext();
+private: // data
+  friend class PathCairo;
 
-private: // data
-  void PrepareForWrite();
-
-  RefPtr<CairoPathContext> mPathContext;
-  Matrix mTransform;
   FillRule mFillRule;
+  std::vector<cairo_path_data_t> mPathData;
+  // It's easiest to track this here, parsing the path data to find the current
+  // point is a little tricky.
+  Point mCurrentPoint;
+  Point mBeginPoint;
 };
 
 class PathCairo : public Path
 {
 public:
-  PathCairo(CairoPathContext* aPathContex, Matrix& aTransform, FillRule aFillRule);
+  PathCairo(FillRule aFillRule, std::vector<cairo_path_data_t> &aPathData, const Point &aCurrentPoint);
+  PathCairo(cairo_t *aContext);
+  ~PathCairo();
 
   virtual BackendType GetBackendType() const { return BACKEND_CAIRO; }
 
   virtual TemporaryRef<PathBuilder> CopyToBuilder(FillRule aFillRule = FILL_WINDING) const;
   virtual TemporaryRef<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform,
                                                              FillRule aFillRule = FILL_WINDING) const;
 
   virtual bool ContainsPoint(const Point &aPoint, const Matrix &aTransform) const;
@@ -131,25 +66,24 @@ public:
 
   virtual Rect GetBounds(const Matrix &aTransform = Matrix()) const;
 
   virtual Rect GetStrokedBounds(const StrokeOptions &aStrokeOptions,
                                 const Matrix &aTransform = Matrix()) const;
 
   virtual FillRule GetFillRule() const { return mFillRule; }
 
-  TemporaryRef<CairoPathContext> GetPathContext();
+  void SetPathOnContext(cairo_t *aContext) const;
 
-  // Set this path to be the current path for aContext (if it's not already
-  // aContext's path). You must pass the draw target associated with the
-  // context as aDrawTarget.
-  void CopyPathTo(cairo_t* aContext, DrawTargetCairo* aDrawTarget);
+  void AppendPathToBuilder(PathBuilderCairo *aBuilder, const Matrix *aTransform = nullptr) const;
+private:
+  void EnsureContainingContext() const;
 
-private:
-  RefPtr<CairoPathContext> mPathContext;
-  Matrix mTransform;
   FillRule mFillRule;
+  std::vector<cairo_path_data_t> mPathData;
+  mutable cairo_t *mContainingContext;
+  Point mCurrentPoint;
 };
 
 }
 }
 
 #endif /* MOZILLA_GFX_PATH_CAIRO_H_ */
--- a/gfx/2d/ScaledFontBase.cpp
+++ b/gfx/2d/ScaledFontBase.cpp
@@ -8,16 +8,18 @@
 #ifdef USE_SKIA
 #include "PathSkia.h"
 #include "skia/SkPaint.h"
 #include "skia/SkPath.h"
 #endif
 
 #ifdef USE_CAIRO
 #include "PathCairo.h"
+#include "DrawTargetCairo.h"
+#include "HelpersCairo.h"
 #endif
 
 #include <vector>
 #include <cmath>
 
 using namespace std;
 
 namespace mozilla {
@@ -69,59 +71,80 @@ ScaledFontBase::GetPathForGlyphs(const G
     paint.getPosTextPath(&indices.front(), aBuffer.mNumGlyphs*2, &offsets.front(), &path);
     return new PathSkia(path, FILL_WINDING);
   }
 #endif
 #ifdef USE_CAIRO
   if (aTarget->GetType() == BACKEND_CAIRO) {
     MOZ_ASSERT(mScaledFont);
 
-    RefPtr<PathBuilder> builder_iface = aTarget->CreatePathBuilder();
-    PathBuilderCairo* builder = static_cast<PathBuilderCairo*>(builder_iface.get());
+    DrawTarget *dt = const_cast<DrawTarget*>(aTarget);
+    cairo_t *ctx = static_cast<cairo_t*>(dt->GetNativeSurface(NATIVE_SURFACE_CAIRO_CONTEXT));
 
-    // Manually build the path for the PathBuilder.
-    RefPtr<CairoPathContext> context = builder->GetPathContext();
+    bool isNewContext = !ctx;
+    if (!ctx) {
+      ctx = cairo_create(DrawTargetCairo::GetDummySurface());
+      cairo_matrix_t mat;
+      GfxMatrixToCairoMatrix(aTarget->GetTransform(), mat);
+      cairo_set_matrix(ctx, &mat);
+    }
 
-    cairo_set_scaled_font(*context, mScaledFont);
+    cairo_set_scaled_font(ctx, mScaledFont);
 
     // Convert our GlyphBuffer into an array of Cairo glyphs.
     std::vector<cairo_glyph_t> glyphs(aBuffer.mNumGlyphs);
     for (uint32_t i = 0; i < aBuffer.mNumGlyphs; ++i) {
       glyphs[i].index = aBuffer.mGlyphs[i].mIndex;
       glyphs[i].x = aBuffer.mGlyphs[i].mPosition.x;
       glyphs[i].y = aBuffer.mGlyphs[i].mPosition.y;
     }
 
-    cairo_glyph_path(*context, &glyphs[0], aBuffer.mNumGlyphs);
+    cairo_glyph_path(ctx, &glyphs[0], aBuffer.mNumGlyphs);
 
-    return builder->Finish();
+    RefPtr<PathCairo> newPath = new PathCairo(ctx);
+    if (isNewContext) {
+      cairo_destroy(ctx);
+    }
+
+    return newPath;
   }
 #endif
   return nullptr;
 }
 
 void
-ScaledFontBase::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder)
+ScaledFontBase::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint)
 {
 #ifdef USE_CAIRO
   PathBuilderCairo* builder = static_cast<PathBuilderCairo*>(aBuilder);
 
-  RefPtr<CairoPathContext> context = builder->GetPathContext();
+  
+  cairo_t *ctx = cairo_create(DrawTargetCairo::GetDummySurface());
 
-  cairo_set_scaled_font(*context, mScaledFont);
+  if (aTransformHint) {
+    cairo_matrix_t mat;
+    GfxMatrixToCairoMatrix(*aTransformHint, mat);
+    cairo_set_matrix(ctx, &mat);
+  }
 
   // Convert our GlyphBuffer into an array of Cairo glyphs.
   std::vector<cairo_glyph_t> glyphs(aBuffer.mNumGlyphs);
   for (uint32_t i = 0; i < aBuffer.mNumGlyphs; ++i) {
     glyphs[i].index = aBuffer.mGlyphs[i].mIndex;
     glyphs[i].x = aBuffer.mGlyphs[i].mPosition.x;
     glyphs[i].y = aBuffer.mGlyphs[i].mPosition.y;
   }
 
-  cairo_glyph_path(*context, &glyphs[0], aBuffer.mNumGlyphs);
+  cairo_set_scaled_font(ctx, mScaledFont);
+  cairo_glyph_path(ctx, &glyphs[0], aBuffer.mNumGlyphs);
+
+  RefPtr<PathCairo> cairoPath = new PathCairo(ctx);
+  cairo_destroy(ctx);
+
+  cairoPath->AppendPathToBuilder(builder);
 #endif
 }
 
 #ifdef USE_CAIRO_SCALED_FONT
 void
 ScaledFontBase::SetCairoScaledFont(cairo_scaled_font_t* font)
 {
   MOZ_ASSERT(!mScaledFont);
--- a/gfx/2d/ScaledFontBase.h
+++ b/gfx/2d/ScaledFontBase.h
@@ -28,17 +28,17 @@ namespace gfx {
 class ScaledFontBase : public ScaledFont
 {
 public:
   ScaledFontBase(Float aSize);
   virtual ~ScaledFontBase();
 
   virtual TemporaryRef<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
 
-  virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder);
+  virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint);
 
   float GetSize() { return mSize; }
 
 #ifdef USE_SKIA
   virtual SkTypeface* GetSkTypeface() { return mTypeface; }
 #endif
 
   // Not true, but required to instantiate a ScaledFontBase.
--- a/gfx/2d/ScaledFontDWrite.cpp
+++ b/gfx/2d/ScaledFontDWrite.cpp
@@ -319,17 +319,17 @@ ScaledFontDWrite::GetPathForGlyphs(const
     static_cast<PathBuilderD2D*>(pathBuilder.get());
 
   CopyGlyphsToSink(aBuffer, pathBuilderD2D->GetSink());
 
   return pathBuilder->Finish();
 }
 
 void
-ScaledFontDWrite::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder)
+ScaledFontDWrite::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *)
 {
   // XXX - Check path builder type!
   PathBuilderD2D *pathBuilderD2D =
     static_cast<PathBuilderD2D*>(aBuilder);
 
   CopyGlyphsToSink(aBuffer, pathBuilderD2D->GetSink());
 }
 
--- a/gfx/2d/ScaledFontDWrite.h
+++ b/gfx/2d/ScaledFontDWrite.h
@@ -21,17 +21,17 @@ public:
     : mFontFace(aFont)
     , ScaledFontBase(aSize)
   {}
   ScaledFontDWrite(uint8_t *aData, uint32_t aSize, uint32_t aIndex, Float aGlyphSize);
 
   virtual FontType GetType() const { return FONT_DWRITE; }
 
   virtual TemporaryRef<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
-  virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder);
+  virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint);
 
   void CopyGlyphsToSink(const GlyphBuffer &aBuffer, ID2D1GeometrySink *aSink);
 
   virtual bool GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton);
 
   virtual AntialiasMode GetDefaultAAMode();
 
 #ifdef USE_SKIA
--- a/gfx/2d/ScaledFontMac.cpp
+++ b/gfx/2d/ScaledFontMac.cpp
@@ -97,17 +97,17 @@ ScaledFontMac::GetPathForGlyphs(const Gl
       CGPathRelease(path);
       return ret;
   } else {
       return ScaledFontBase::GetPathForGlyphs(aBuffer, aTarget);
   }
 }
 
 void
-ScaledFontMac::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder)
+ScaledFontMac::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *)
 {
   PathBuilderCG *pathBuilderCG =
     static_cast<PathBuilderCG*>(aBuilder);
   // XXX: check builder type
   for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
     // XXX: we could probably fold both of these transforms together to avoid extra work
     CGAffineTransform flip = CGAffineTransformMakeScale(1, -1);
     CGPathRef glyphPath = ::CGFontGetGlyphPath(mFont, &flip, 0, aBuffer.mGlyphs[i].mIndex);
--- a/gfx/2d/ScaledFontMac.h
+++ b/gfx/2d/ScaledFontMac.h
@@ -20,17 +20,17 @@ public:
   ScaledFontMac(CGFontRef aFont, Float aSize);
   virtual ~ScaledFontMac();
 
   virtual FontType GetType() const { return FONT_MAC; }
 #ifdef USE_SKIA
   virtual SkTypeface* GetSkTypeface();
 #endif
   virtual TemporaryRef<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
-  virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder);
+  virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint);
   virtual bool GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton);
 
 private:
   friend class DrawTargetCG;
   CGFontRef mFont;
   CTFontRef mCTFont; // only created if CTFontDrawGlyphs is available, otherwise null
 
   typedef void (CTFontDrawGlyphsFuncT)(CTFontRef,
--- a/gfx/harfbuzz/src/Makefile.am
+++ b/gfx/harfbuzz/src/Makefile.am
@@ -23,16 +23,18 @@ HBSOURCES =  \
 	hb-buffer-deserialize-json.hh \
 	hb-buffer-deserialize-text.hh \
 	hb-buffer-private.hh \
 	hb-buffer-serialize.cc \
 	hb-buffer.cc \
 	hb-cache-private.hh \
 	hb-common.cc \
 	hb-fallback-shape.cc \
+	hb-face-private.hh \
+	hb-face.cc \
 	hb-font-private.hh \
 	hb-font.cc \
 	hb-mutex-private.hh \
 	hb-object-private.hh \
 	hb-open-file-private.hh \
 	hb-open-type-private.hh \
 	hb-ot-head-table.hh \
 	hb-ot-hhea-table.hh \
@@ -56,16 +58,18 @@ HBSOURCES =  \
 	hb-utf-private.hh \
 	hb-warning.cc \
 	$(NULL)
 HBHEADERS = \
 	hb.h \
 	hb-blob.h \
 	hb-buffer.h \
 	hb-common.h \
+	hb-deprecated.h \
+	hb-face.h \
 	hb-font.h \
 	hb-set.h \
 	hb-shape.h \
 	hb-shape-plan.h \
 	hb-unicode.h \
 	$(NULL)
 HBNODISTHEADERS = \
 	hb-version.h \
@@ -74,16 +78,17 @@ HBNODISTHEADERS = \
 if HAVE_OT
 HBSOURCES += \
 	hb-ot-layout.cc \
 	hb-ot-layout-common-private.hh \
 	hb-ot-layout-gdef-table.hh \
 	hb-ot-layout-gpos-table.hh \
 	hb-ot-layout-gsubgpos-private.hh \
 	hb-ot-layout-gsub-table.hh \
+	hb-ot-layout-jstf-table.hh \
 	hb-ot-layout-private.hh \
 	hb-ot-map.cc \
 	hb-ot-map-private.hh \
 	hb-ot-shape.cc \
 	hb-ot-shape-complex-arabic.cc \
 	hb-ot-shape-complex-arabic-fallback.hh \
 	hb-ot-shape-complex-arabic-table.hh \
 	hb-ot-shape-complex-default.cc \
@@ -117,38 +122,16 @@ endif
 
 if HAVE_GLIB
 HBCFLAGS += $(GLIB_CFLAGS)
 HBLIBS   += $(GLIB_LIBS)
 HBSOURCES += hb-glib.cc
 HBHEADERS += hb-glib.h
 endif
 
-if HAVE_GOBJECT
-HBCFLAGS += $(GOBJECT_CFLAGS)
-HBLIBS   += $(GOBJECT_LIBS)
-HBSOURCES += hb-gobject-structs.cc
-nodist_HBSOURCES = hb-gobject-enums.cc
-HBHEADERS += hb-gobject.h
-BUILT_SOURCES += hb-gobject-enums.cc
-EXTRA_DIST += hb-gobject-enums.cc.tmpl
-DISTCLEANFILES += hb-gobject-enums.cc
-
-hb-gobject-enums.cc: hb-gobject-enums.cc.tmpl $(HBHEADERS)
-	$(AM_V_GEN) $(GLIB_MKENUMS) --template $^ > "$@.tmp" && \
-	mv "$@.tmp" "$@" || ( $(RM) "@.tmp" && false )
-endif
-
-if HAVE_ICU
-HBCFLAGS += $(ICU_CFLAGS)
-HBLIBS   += $(ICU_LIBS)
-HBSOURCES += hb-icu.cc
-HBHEADERS += hb-icu.h
-endif
-
 if HAVE_FREETYPE
 HBCFLAGS += $(FREETYPE_CFLAGS)
 HBLIBS   += $(FREETYPE_LIBS)
 HBSOURCES += hb-ft.cc
 HBHEADERS += hb-ft.h
 endif
 
 if HAVE_GRAPHITE2
@@ -167,68 +150,107 @@ endif
 
 if HAVE_CORETEXT
 HBCFLAGS += $(CORETEXT_CFLAGS)
 HBLIBS   += $(CORETEXT_LIBS)
 HBSOURCES += hb-coretext.cc
 HBHEADERS += hb-coretext.h
 endif
 
-if HAVE_HB_OLD
-SUBDIRS += hb-old
-HBCFLAGS += -I$(srcdir)/hb-old
-HBLIBS   += hb-old/libhb-old.la
-HBSOURCES += hb-old.cc
-endif
-DIST_SUBDIRS += hb-old
-
-if HAVE_ICU_LE
-SUBDIRS += hb-icu-le
-HBCFLAGS += -I$(srcdir)/hb-icu-le
-HBLIBS   += hb-icu-le/libhb-icu-le.la
-HBSOURCES += hb-icu-le.cc
-endif
-DIST_SUBDIRS += hb-icu-le
-
 if HAVE_UCDN
 SUBDIRS += hb-ucdn
 HBCFLAGS += -I$(srcdir)/hb-ucdn
 HBLIBS   += hb-ucdn/libhb-ucdn.la
 HBSOURCES += hb-ucdn.cc
 endif
 DIST_SUBDIRS += hb-ucdn
 
 
 # Put the library together
 
 if OS_WIN32
 export_symbols = -export-symbols harfbuzz.def
 harfbuzz_def_dependency = harfbuzz.def
 libharfbuzz_la_LINK = $(CXXLINK) $(libharfbuzz_la_LDFLAGS)
 else
-if HAVE_ICU
-libharfbuzz_la_LINK = $(CXXLINK) $(libharfbuzz_la_LDFLAGS)
-else
 # Use a C linker for GCC, not C++; Don't link to libstdc++
 if HAVE_GCC
 libharfbuzz_la_LINK = $(LINK) $(libharfbuzz_la_LDFLAGS)
 else
 libharfbuzz_la_LINK = $(CXXLINK) $(libharfbuzz_la_LDFLAGS)
 endif
 endif
-endif
 
 libharfbuzz_la_SOURCES = $(HBSOURCES) $(HBHEADERS) $(HBNODISTHEADERS)
-nodist_libharfbuzz_la_SOURCES = $(nodist_HBSOURCES)
 libharfbuzz_la_CPPFLAGS = $(HBCFLAGS)
 libharfbuzz_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(HB_LIBTOOL_VERSION_INFO) $(export_symbols) -no-undefined
 libharfbuzz_la_LIBADD = $(HBLIBS)
 EXTRA_libharfbuzz_la_DEPENDENCIES = $(harfbuzz_def_dependency)
 pkginclude_HEADERS = $(HBHEADERS)
 nodist_pkginclude_HEADERS = $(HBNODISTHEADERS)
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = harfbuzz.pc
+EXTRA_DIST += harfbuzz.pc.in
+
+if HAVE_ICU
+lib_LTLIBRARIES += libharfbuzz-icu.la
+libharfbuzz_icu_la_SOURCES = hb-icu.cc
+libharfbuzz_icu_la_CPPFLAGS = $(ICU_CFLAGS)
+libharfbuzz_icu_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(HB_LIBTOOL_VERSION_INFO) -no-undefined
+libharfbuzz_icu_la_LIBADD = $(ICU_LIBS) libharfbuzz.la
+pkginclude_HEADERS += hb-icu.h
+pkgconfig_DATA += harfbuzz-icu.pc
+endif
+EXTRA_DIST += harfbuzz-icu.pc.in
+
+if HAVE_GOBJECT
+lib_LTLIBRARIES += libharfbuzz-gobject.la
+libharfbuzz_gobject_la_SOURCES = hb-gobject-structs.cc
+nodist_libharfbuzz_gobject_la_SOURCES = hb-gobject-enums.cc
+libharfbuzz_gobject_la_CPPFLAGS = $(GOBJECT_CFLAGS)
+libharfbuzz_gobject_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(HB_LIBTOOL_VERSION_INFO) -no-undefined
+libharfbuzz_gobject_la_LIBADD = $(GOBJECT_LIBS) libharfbuzz.la
+pkginclude_HEADERS += hb-gobject.h hb-gobject-structs.h
+nodist_pkginclude_HEADERS += hb-gobject-enums.h
+pkgconfig_DATA += harfbuzz-gobject.pc
+
+BUILT_SOURCES += \
+	hb-gobject-enums.cc \
+	hb-gobject-enums.h \
+	$(NULL)
+DISTCLEANFILES += \
+	hb-gobject-enums.cc \
+	hb-gobject-enums.h \
+	$(NULL)
+hb-gobject-enums.%: hb-gobject-enums.%.tmpl $(HBHEADERS)
+	$(AM_V_GEN) $(GLIB_MKENUMS) \
+		--identifier-prefix hb_ --symbol-prefix hb_gobject \
+		--template $^ | \
+	sed 's/_t_get_type/_get_type/g; s/_T (/ (/g' > "$@.tmp" && \
+	mv "$@.tmp" "$@" || ( $(RM) "@.tmp" && false )
+endif
+EXTRA_DIST += \
+	harfbuzz-gobject.pc.in \
+	hb-gobject-enums.cc.tmpl \
+	hb-gobject-enums.h.tmpl \
+	$(NULL)
+
+
+%.pc: %.pc.in $(top_builddir)/config.status
+	$(AM_V_GEN) \
+	$(SED)	-e 's@%prefix%@$(prefix)@g' \
+		-e 's@%exec_prefix%@$(exec_prefix)@g' \
+		-e 's@%libdir%@$(libdir)@g' \
+		-e 's@%includedir%@$(includedir)@g' \
+		-e 's@%VERSION%@$(VERSION)@g' \
+	"$<" \
+	> "$@.tmp" && mv "$@.tmp" "$@" || ( $(RM) "$@.tmp"; false )
+
+CLEANFILES += $(pkgconfig_DATA)
+
 
 CLEANFILES += harfbuzz.def
 harfbuzz.def: $(HBHEADERS) $(HBNODISTHEADERS)
 	$(AM_V_GEN) (echo EXPORTS; \
 	(cat $^ || echo 'hb_ERROR ()' ) | \
 	$(EGREP) '^hb_.* \(' | \
 	sed -e 's/ (.*//' | \
 	LANG=C sort; \
@@ -236,17 +258,16 @@ harfbuzz.def: $(HBHEADERS) $(HBNODISTHEA
 	) >"$@.tmp"
 	@ ! grep -q hb_ERROR "$@.tmp" && mv "$@.tmp" "$@" || ($(RM) "$@"; false)
 
 
 GENERATORS = \
 	gen-arabic-table.py \
 	gen-indic-table.py \
 	$(NULL)
-
 EXTRA_DIST += $(GENERATORS)
 
 unicode-tables: arabic-table indic-table
 
 indic-table: gen-indic-table.py IndicSyllabicCategory.txt IndicMatraCategory.txt Blocks.txt
 	$(AM_V_GEN) $(builddir)/$^ > hb-ot-shape-complex-indic-table.cc.tmp && \
 	mv hb-ot-shape-complex-indic-table.cc.tmp $(srcdir)/hb-ot-shape-complex-indic-table.cc || \
 	($(RM) hb-ot-shape-complex-indic-table.cc.tmp; false)
@@ -268,17 +289,17 @@ BUILT_SOURCES += \
 	$(NULL)
 EXTRA_DIST += \
 	hb-buffer-deserialize-json.rl \
 	hb-buffer-deserialize-text.rl \
 	hb-ot-shape-complex-indic-machine.rl \
 	hb-ot-shape-complex-myanmar-machine.rl \
 	hb-ot-shape-complex-sea-machine.rl \
 	$(NULL)
-%.hh: %.rl
+.rl.hh:
 	$(AM_V_GEN)$(top_srcdir)/missing --run ragel -e -F1 -o "$@.tmp" "$<" && \
 	mv "$@.tmp" "$@" || ( $(RM) "$@.tmp" && false )
 
 noinst_PROGRAMS = \
 	main \
 	test \
 	test-buffer-serialize \
 	test-size-params \
@@ -303,54 +324,68 @@ test_size_params_CPPFLAGS = $(HBCFLAGS)
 test_size_params_LDADD = libharfbuzz.la $(HBLIBS)
 
 test_buffer_serialize_SOURCES = test-buffer-serialize.cc
 test_buffer_serialize_CPPFLAGS = $(HBCFLAGS)
 test_buffer_serialize_LDADD = libharfbuzz.la $(HBLIBS)
 
 dist_check_SCRIPTS = \
 	check-c-linkage-decls.sh \
+	check-defs.sh \
 	check-header-guards.sh \
 	check-includes.sh \
+	check-libstdc++.sh \
+	check-static-inits.sh \
 	check-symbols.sh \
 	$(NULL)
 
-if HAVE_ICU
-else
-dist_check_SCRIPTS += check-libstdc++.sh
-endif
-
-if HAVE_ICU_LE
-else
-dist_check_SCRIPTS += check-static-inits.sh
-endif
-
 TESTS = $(dist_check_SCRIPTS)
 TESTS_ENVIRONMENT = \
 	srcdir="$(srcdir)" \
 	MAKE="$(MAKE) $(AM_MAKEFLAGS)" \
 	HBSOURCES="$(HBSOURCES)" \
 	HBHEADERS="$(HBHEADERS) $(HBNODISTHEADERS)" \
 	$(NULL)
 
-#-include $(INTROSPECTION_MAKEFILE)
-#INTROSPECTION_GIRS = hb-1.0.gir
-#INTROSPECTION_SCANNER_ARGS = -I$(srcdir) -n hb --identifier-prefix=hb_
-#INTROSPECTION_COMPILER_ARGS = --includedir=$(srcdir)
-#
-#if HAVE_INTROSPECTION
-#
-#hb-1.0.gir: libharfbuzz.la
-#hb_1_0_gir_INCLUDES = GObject-2.0
-#hb_1_0_gir_CFLAGS = $(INCLUDES) $(HBCFLAGS) -DHB_H -DHB_H_IN -DHB_OT_H -DHB_OT_H_IN
-#hb_1_0_gir_LIBS = libharfbuzz.la
-#hb_1_0_gir_FILES = $(HBHEADERS) $(HBNODISTHEADERS)
-#
-#girdir = $(datadir)/gir-1.0
-#gir_DATA = $(INTROSPECTION_GIRS)
-#
-#typelibdir = $(libdir)/girepository-1.0
-#typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib)
-#
-#CLEANFILES += $(gir_DATA) $(typelib_DATA)
-#endif
+if HAVE_INTROSPECTION
+
+-include $(INTROSPECTION_MAKEFILE)
+INTROSPECTION_GIRS = HarfBuzz-$(HB_VERSION_MAJOR).0.gir # What does the 0 mean anyway?!
+INTROSPECTION_SCANNER_ARGS = -I$(srcdir) -n hb --identifier-prefix=hb_ --warn-all
+INTROSPECTION_COMPILER_ARGS = --includedir=$(srcdir)
+
+HarfBuzz-0.0.gir: libharfbuzz.la libharfbuzz-gobject.la
+HarfBuzz_0_0_gir_INCLUDES = GObject-2.0
+HarfBuzz_0_0_gir_CFLAGS = \
+	$(INCLUDES) \
+	$(HBCFLAGS) \
+	-DHB_H \
+	-DHB_H_IN \
+	-DHB_OT_H \
+	-DHB_OT_H_IN \
+	-DHB_GOBJECT_H \
+	-DHB_GOBJECT_H_IN \
+	$(NULL)
+HarfBuzz_0_0_gir_LIBS = \
+	libharfbuzz.la \
+	libharfbuzz-gobject.la \
+	$(NULL)
+HarfBuzz_0_0_gir_FILES = \
+	$(HBHEADERS) \
+	$(HBNODISTHEADERS) \
+	$(HBSOURCES) \
+	hb-gobject-enums.cc \
+	hb-gobject-enums.h \
+	hb-gobject-structs.cc \
+	hb-gobject-structs.h \
+	$(NULL)
+
+girdir = $(datadir)/gir-1.0
+gir_DATA = $(INTROSPECTION_GIRS)
+
+typelibdir = $(libdir)/girepository-1.0
+typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib)
+
+CLEANFILES += $(gir_DATA) $(typelib_DATA)
+
+endif
 
 -include $(top_srcdir)/git.mk
--- a/gfx/harfbuzz/src/check-c-linkage-decls.sh
+++ b/gfx/harfbuzz/src/check-c-linkage-decls.sh
@@ -1,18 +1,18 @@
 #!/bin/sh
 
 LC_ALL=C
 export LC_ALL
 
 test -z "$srcdir" && srcdir=.
 stat=0
 
-test "x$HBHEADERS" = x && HBHEADERS=`find . -maxdepth 1 -name 'hb*.h'`
-test "x$HBSOURCES" = x && HBSOURCES=`find . -maxdepth 1 -name 'hb*.h'`
+test "x$HBHEADERS" = x && HBHEADERS=`cd "$srcdir"; find . -maxdepth 1 -name 'hb*.h'`
+test "x$HBSOURCES" = x && HBSOURCES=`cd "$srcdir"; find . -maxdepth 1 -name 'hb*.h'`
 
 
 for x in $HBHEADERS; do
 	test -f $srcdir/$x && x=$srcdir/$x
 	if ! grep -q HB_BEGIN_DECLS "$x" || ! grep -q HB_END_DECLS "$x"; then
 		echo "Ouch, file $x does not have HB_BEGIN_DECLS / HB_END_DECLS, but it should"
 		stat=1
 	fi
--- a/gfx/harfbuzz/src/check-header-guards.sh
+++ b/gfx/harfbuzz/src/check-header-guards.sh
@@ -1,18 +1,18 @@
 #!/bin/sh
 
 LC_ALL=C
 export LC_ALL
 
 test -z "$srcdir" && srcdir=.
 stat=0
 
-test "x$HBHEADERS" = x && HBHEADERS=`find . -maxdepth 1 -name 'hb*.h'`
-test "x$HBSOURCES" = x && HBSOURCES=`find . -maxdepth 1 -name 'hb-*.cc' -or -name 'hb-*.hh'`
+test "x$HBHEADERS" = x && HBHEADERS=`cd "$srcdir"; find . -maxdepth 1 -name 'hb*.h'`
+test "x$HBSOURCES" = x && HBSOURCES=`cd "$srcdir"; find . -maxdepth 1 -name 'hb-*.cc' -or -name 'hb-*.hh'`
 
 
 for x in $HBHEADERS $HBSOURCES; do
 	test -f "$srcdir/$x" && x="$srcdir/$x"
 	echo "$x" | grep '[^h]$' -q && continue;
 	xx=`echo "$x" | sed 's@.*/@@'`
 	tag=`echo "$xx" | tr 'a-z.-' 'A-Z_'`
 	lines=`grep "\<$tag\>" "$x" | wc -l | sed 's/[ 	]*//g'`
--- a/gfx/harfbuzz/src/check-includes.sh
+++ b/gfx/harfbuzz/src/check-includes.sh
@@ -1,42 +1,44 @@
 #!/bin/sh
 
 LC_ALL=C
 export LC_ALL
 
 test -z "$srcdir" && srcdir=.
 stat=0
 
-test "x$HBHEADERS" = x && HBHEADERS=`find . -maxdepth 1 -name 'hb*.h'`
-test "x$HBSOURCES" = x && HBSOURCES=`find . -maxdepth 1 -name 'hb-*.cc' -or -name 'hb-*.hh'`
-
-
-cd "$srcdir"
+test "x$HBHEADERS" = x && HBHEADERS=`cd "$srcdir"; find . -maxdepth 1 -name 'hb*.h'`
+test "x$HBSOURCES" = x && HBSOURCES=`cd "$srcdir"; find . -maxdepth 1 -name 'hb-*.cc' -or -name 'hb-*.hh'`
 
 
 echo 'Checking that public header files #include "hb-common.h" or "hb.h" first (or none)'
 
 for x in $HBHEADERS; do
+	test -f "$srcdir/$x" && x="$srcdir/$x"
 	grep '#.*\<include\>' "$x" /dev/null | head -n 1
 done |
 grep -v '"hb-common[.]h"' |
 grep -v '"hb[.]h"' |
 grep -v 'hb-common[.]h:' |
 grep -v 'hb[.]h:' |
 grep . >&2 && stat=1
 
 
 echo 'Checking that source files #include "hb-*private.hh" first (or none)'
 
 for x in $HBSOURCES; do
+	test -f "$srcdir/$x" && x="$srcdir/$x"
 	grep '#.*\<include\>' "$x" /dev/null | head -n 1
 done |
 grep -v '"hb-.*private[.]hh"' |
 grep -v 'hb-private[.]hh:' |
 grep . >&2 && stat=1
 
 
 echo 'Checking that there is no #include <hb.*.h>'
-grep '#.*\<include\>.*<.*hb' $HBHEADERS $HBSOURCES >&2 && stat=1
+for x in $HBHEADERS $HBSOURCES; do
+	test -f "$srcdir/$x" && x="$srcdir/$x"
+	grep '#.*\<include\>.*<.*hb' "$x" /dev/null >&2 && stat=1
+done
 
 
 exit $stat
--- a/gfx/harfbuzz/src/check-symbols.sh
+++ b/gfx/harfbuzz/src/check-symbols.sh
@@ -1,50 +1,37 @@
 #!/bin/sh
 
 LC_ALL=C
 export LC_ALL
 
 test -z "$srcdir" && srcdir=.
-test -z "$MAKE" && MAKE=make
 stat=0
 
+
 if which nm 2>/dev/null >/dev/null; then
 	:
 else
 	echo "check-symbols.sh: 'nm' not found; skipping test"
 	exit 77
 fi
 
-defs="harfbuzz.def"
-$MAKE $defs > /dev/null
+echo "Checking that we are not exposing internal symbols"
 tested=false
-for def in $defs; do
-	lib=`echo "$def" | sed 's/[.]def$//;s@.*/@@'`
-	so=.libs/lib${lib}.so
-
+for so in `ls .libs/lib*.so .libs/lib*.dylib 2>/dev/null` ; do
+	
 	EXPORTED_SYMBOLS="`nm "$so" | grep ' [BCDGINRSTVW] ' | grep -v ' _fini\>\| _init\>\| _fdata\>\| _ftext\>\| __bss_start\>\| __bss_start__\>\| __bss_end__\>\| _edata\>\| _end\>\| _bss_end__\>\| __end__\>' | cut -d' ' -f3`"
-
-	if test -f "$so"; then
+	prefix=`basename "$so" | sed 's/libharfbuzz/hb/; s/-/_/g; s/[.].*//'`
 
-		echo "Checking that $so has the same symbol list as $def"
-		{
-			echo EXPORTS
-			echo "$EXPORTED_SYMBOLS"
-			# cheat: copy the last line from the def file!
-			tail -n1 "$def"
-		} | diff "$def" - >&2 || stat=1
+	echo "Processing $so"
+	if echo "$EXPORTED_SYMBOLS" | grep -v "^${prefix}_"; then
+		echo "Ouch, internal symbols exposed"
+		stat=1
+	fi
 
-		echo "Checking that we are not exposing internal symbols"
-		if echo "$EXPORTED_SYMBOLS" | grep -v 'hb_'; then
-			echo "Ouch, internal symbols exposed"
-			stat=1
-		fi
-
-		tested=true
-	fi
+	tested=true
 done
 if ! $tested; then
-	echo "check-exported-symbols.sh: libharfbuzz shared library not found; skipping test"
+	echo "check-symbols.sh: no shared library found; skipping test"
 	exit 77
 fi
 
 exit $stat
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/harfbuzz-gobject.pc.in
@@ -0,0 +1,12 @@
+prefix=%prefix%
+exec_prefix=%exec_prefix%
+libdir=%libdir%
+includedir=%includedir%
+
+Name: harfbuzz
+Description: HarfBuzz text shaping library GObject integration
+Version: %VERSION%
+
+Requires: harfbuzz gobject-2.0 glib-2.0
+Libs: -L${libdir} -lharfbuzz-gobject
+Cflags: -I${includedir}/harfbuzz
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/harfbuzz-icu.pc.in
@@ -0,0 +1,13 @@
+prefix=%prefix%
+exec_prefix=%exec_prefix%
+libdir=%libdir%
+includedir=%includedir%
+
+Name: harfbuzz
+Description: HarfBuzz text shaping library ICU integration
+Version: %VERSION%
+
+Requires: harfbuzz
+Requires.private: icu-uc
+Libs: -L${libdir} -lharfbuzz-icu
+Cflags: -I${includedir}/harfbuzz
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/harfbuzz.pc.in
@@ -0,0 +1,11 @@
+prefix=%prefix%
+exec_prefix=%exec_prefix%
+libdir=%libdir%
+includedir=%includedir%
+
+Name: harfbuzz
+Description: HarfBuzz text shaping library
+Version: %VERSION%
+
+Libs: -L${libdir} -lharfbuzz
+Cflags: -I${includedir}/harfbuzz
--- a/gfx/harfbuzz/src/hb-blob.cc
+++ b/gfx/harfbuzz/src/hb-blob.cc
@@ -71,16 +71,32 @@ static void
 {
   if (blob->destroy) {
     blob->destroy (blob->user_data);
     blob->user_data = NULL;
     blob->destroy = NULL;
   }
 }
 
+/**
+ * hb_blob_create: (Xconstructor)
+ * @data: (array length=length) (closure user_data) (destroy destroy) (scope notified) (transfer none): Pointer to blob data.
+ * @length: Length of @data in bytes.
+ * @mode: Memory mode for @data.
+ * @user_data: Data parameter to pass to @destroy.
+ * @destroy: Callback to call when @data is not needed anymore.
+ *
+ * Creates a new "blob" object wrapping @data.  The @mode parameter is used
+ * to negotiate ownership and lifecycle of @data.
+ *
+ * Return value: New blob, or the empty blob if something failed or if @length is
+ * zero.  Destroy with hb_blob_destroy().
+ *
+ * Since: 1.0
+ **/
 hb_blob_t *
 hb_blob_create (const char        *data,
 		unsigned int       length,
 		hb_memory_mode_t   mode,
 		void              *user_data,
 		hb_destroy_func_t  destroy)
 {
   hb_blob_t *blob;
@@ -104,16 +120,36 @@ hb_blob_create (const char        *data,
       hb_blob_destroy (blob);
       return hb_blob_get_empty ();
     }
   }
 
   return blob;
 }
 
+/**
+ * hb_blob_create_sub_blob:
+ * @parent: Parent blob.
+ * @offset: Start offset of sub-blob within @parent, in bytes.
+ * @length: Length of sub-blob.
+ *
+ * Returns a blob that represents a range of bytes in @parent.  The new
+ * blob is always created with %HB_MEMORY_MODE_READONLY, meaning that it
+ * will never modify data in the parent blob.  The parent data is not
+ * expected to be modified, and will result in undefined behavior if it
+ * is.
+ *
+ * Makes @parent immutable.
+ *
+ * Return value: New blob, or the empty blob if something failed or if
+ * @length is zero or @offset is beyond the end of @parent's data.  Destroy
+ * with hb_blob_destroy().
+ *
+ * Since: 1.0
+ **/
 hb_blob_t *
 hb_blob_create_sub_blob (hb_blob_t    *parent,
 			 unsigned int  offset,
 			 unsigned int  length)
 {
   hb_blob_t *blob;
 
   if (!length || offset >= parent->length)
@@ -125,16 +161,27 @@ hb_blob_create_sub_blob (hb_blob_t    *p
 			 MIN (length, parent->length - offset),
 			 HB_MEMORY_MODE_READONLY,
 			 hb_blob_reference (parent),
 			 (hb_destroy_func_t) hb_blob_destroy);
 
   return blob;
 }
 
+/**
+ * hb_blob_get_empty:
+ *
+ * Returns the singleton empty blob.
+ *
+ * See TODO:link object types for more information.
+ *
+ * Return value: (transfer full): the empty blob.
+ *
+ * Since: 1.0
+ **/
 hb_blob_t *
 hb_blob_get_empty (void)
 {
   static const hb_blob_t _hb_blob_nil = {
     HB_OBJECT_HEADER_STATIC,
 
     true, /* immutable */
 
@@ -144,81 +191,183 @@ hb_blob_get_empty (void)
 
     NULL, /* user_data */
     NULL  /* destroy */
   };
 
   return const_cast<hb_blob_t *> (&_hb_blob_nil);
 }
 
+/**
+ * hb_blob_reference: (skip)
+ * @blob: a blob.
+ *
+ * Increases the reference count on @blob.
+ *
+ * See TODO:link object types for more information.
+ *
+ * Return value: @blob.
+ *
+ * Since: 1.0
+ **/
 hb_blob_t *
 hb_blob_reference (hb_blob_t *blob)
 {
   return hb_object_reference (blob);
 }
 
+/**
+ * hb_blob_destroy: (skip)
+ * @blob: a blob.
+ *
+ * Descreases the reference count on @blob, and if it reaches zero, destroys
+ * @blob, freeing all memory, possibly calling the destroy-callback the blob
+ * was created for if it has not been called already.
+ *
+ * See TODO:link object types for more information.
+ *
+ * Since: 1.0
+ **/
 void
 hb_blob_destroy (hb_blob_t *blob)
 {
   if (!hb_object_destroy (blob)) return;
 
   _hb_blob_destroy_user_data (blob);
 
   free (blob);
 }
 
+/**
+ * hb_blob_set_user_data: (skip)
+ * @blob: a blob.
+ * @key: key for data to set.
+ * @data: data to set.
+ * @destroy: callback to call when @data is not needed anymore.
+ * @replace: whether to replace an existing data with the same key.
+ *
+ * Return value: 
+ *
+ * Since: 1.0
+ **/
 hb_bool_t
 hb_blob_set_user_data (hb_blob_t          *blob,
 		       hb_user_data_key_t *key,
 		       void *              data,
 		       hb_destroy_func_t   destroy,
 		       hb_bool_t           replace)
 {
   return hb_object_set_user_data (blob, key, data, destroy, replace);
 }
 
+/**
+ * hb_blob_get_user_data: (skip)
+ * @blob: a blob.
+ * @key: key for data to get.
+ *
+ * 
+ *
+ * Return value: (transfer none): 
+ *
+ * Since: 1.0
+ **/
 void *
 hb_blob_get_user_data (hb_blob_t          *blob,
 		       hb_user_data_key_t *key)
 {
   return hb_object_get_user_data (blob, key);
 }
 
 
+/**
+ * hb_blob_make_immutable:
+ * @blob: a blob.
+ *
+ * 
+ *
+ * Since: 1.0
+ **/
 void
 hb_blob_make_immutable (hb_blob_t *blob)
 {
   if (hb_object_is_inert (blob))
     return;
 
   blob->immutable = true;
 }
 
+/**
+ * hb_blob_is_immutable:
+ * @blob: a blob.
+ *
+ * 
+ *
+ * Return value: TODO
+ *
+ * Since: 1.0
+ **/
 hb_bool_t
 hb_blob_is_immutable (hb_blob_t *blob)
 {
   return blob->immutable;
 }
 
 
+/**
+ * hb_blob_get_length:
+ * @blob: a blob.
+ *
+ * 
+ *
+ * Return value: the length of blob data in bytes.
+ *
+ * Since: 1.0
+ **/
 unsigned int
 hb_blob_get_length (hb_blob_t *blob)
 {
   return blob->length;
 }
 
+/**
+ * hb_blob_get_data:
+ * @blob: a blob.
+ * @length: (out):
+ *
+ * 
+ *
+ * Returns: (transfer none) (array length=length): 
+ *
+ * Since: 1.0
+ **/
 const char *
 hb_blob_get_data (hb_blob_t *blob, unsigned int *length)
 {
   if (length)
     *length = blob->length;
 
   return blob->data;
 }
 
+/**
+ * hb_blob_get_data_writable:
+ * @blob: a blob.
+ * @length: (out): output length of the writable data.
+ *
+ * Tries to make blob data writable (possibly copying it) and
+ * return pointer to data.
+ *
+ * Fails if blob has been made immutable, or if memory allocation
+ * fails.
+ *
+ * Returns: (transfer none) (array length=length): Writable blob data,
+ * or %NULL if failed.
+ *
+ * Since: 1.0
+ **/
 char *
 hb_blob_get_data_writable (hb_blob_t *blob, unsigned int *length)
 {
   if (!_try_writable (blob)) {
     if (length)
       *length = 0;
 
     return NULL;
@@ -319,10 +468,8 @@ static bool
   _hb_blob_destroy_user_data (blob);
   blob->mode = HB_MEMORY_MODE_WRITABLE;
   blob->data = new_data;
   blob->user_data = new_data;
   blob->destroy = free;
 
   return true;
 }
-
-
--- a/gfx/harfbuzz/src/hb-buffer-private.hh
+++ b/gfx/harfbuzz/src/hb-buffer-private.hh
@@ -75,16 +75,18 @@ struct hb_buffer_t {
   inline hb_glyph_info_t cur (unsigned int i = 0) const { return info[idx + i]; }
 
   inline hb_glyph_position_t &cur_pos (unsigned int i = 0) { return pos[idx + i]; }
   inline hb_glyph_position_t cur_pos (unsigned int i = 0) const { return pos[idx + i]; }
 
   inline hb_glyph_info_t &prev (void) { return out_info[out_len - 1]; }
   inline hb_glyph_info_t prev (void) const { return info[out_len - 1]; }
 
+  inline bool has_separate_output (void) const { return info != out_info; }
+
   unsigned int serial;
 
   /* These reflect current allocations of the bytes in glyph_info_t's var1 and var2. */
   uint8_t allocated_var_bytes[8];
   const char *allocated_var_owner[8];
 
   /* Text before / after the main buffer contents.
    * Always in Unicode, and ordered outward.
--- a/gfx/harfbuzz/src/hb-buffer-serialize.cc
+++ b/gfx/harfbuzz/src/hb-buffer-serialize.cc
@@ -28,29 +28,59 @@
 
 
 static const char *serialize_formats[] = {
   "text",
   "json",
   NULL
 };
 
+/**
+ * hb_buffer_serialize_list_formats:
+ *
+ * 
+ *
+ * Return value: (transfer none):
+ *
+ * Since: 1.0
+ **/
 const char **
 hb_buffer_serialize_list_formats (void)
 {
   return serialize_formats;
 }
 
+/**
+ * hb_buffer_serialize_format_from_string:
+ * @str: 
+ * @len: 
+ *
+ * 
+ *
+ * Return value: 
+ *
+ * Since: 1.0
+ **/
 hb_buffer_serialize_format_t
 hb_buffer_serialize_format_from_string (const char *str, int len)
 {
   /* Upper-case it. */
   return (hb_buffer_serialize_format_t) (hb_tag_from_string (str, len) & ~0x20202020);
 }
 
+/**
+ * hb_buffer_serialize_format_to_string:
+ * @format: 
+ *
+ * 
+ *
+ * Return value: 
+ *
+ * Since: 1.0
+ **/
 const char *
 hb_buffer_serialize_format_to_string (hb_buffer_serialize_format_t format)
 {
   switch (format)
   {
     case HB_BUFFER_SERIALIZE_FORMAT_TEXT:	return serialize_formats[0];
     case HB_BUFFER_SERIALIZE_FORMAT_JSON:	return serialize_formats[1];
     default:
@@ -95,20 +125,20 @@ static unsigned int
       for (char *q = g; *q; q++) {
         if (*q == '"')
 	  *p++ = '\\';
 	*p++ = *q;
       }
       *p++ = '"';
     }
     else
-      p += snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint);
+      p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint));
 
     if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) {
-      p += snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"cl\":%u", info[i].cluster);
+      p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"cl\":%u", info[i].cluster));
     }
 
     if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS))
     {
       p += snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"dx\":%d,\"dy\":%d",
 		     pos[i].x_offset, pos[i].y_offset);
       p += snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"ax\":%d,\"ay\":%d",
 		     pos[i].x_advance, pos[i].y_advance);
@@ -156,31 +186,31 @@ static unsigned int
       *p++ = '|';
 
     if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES))
     {
       hb_font_glyph_to_string (font, info[i].codepoint, p, 128);
       p += strlen (p);
     }
     else
-      p += snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint);
+      p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint));
 
     if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) {
-      p += snprintf (p, ARRAY_LENGTH (b) - (p - b), "=%u", info[i].cluster);
+      p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "=%u", info[i].cluster));
     }
 
     if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS))
     {
       if (pos[i].x_offset || pos[i].y_offset)
-	p += snprintf (p, ARRAY_LENGTH (b) - (p - b), "@%d,%d", pos[i].x_offset, pos[i].y_offset);
+	p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "@%d,%d", pos[i].x_offset, pos[i].y_offset));
 
       *p++ = '+';
-      p += snprintf (p, ARRAY_LENGTH (b) - (p - b), "%d", pos[i].x_advance);
+      p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%d", pos[i].x_advance));
       if (pos->y_advance)
-	p += snprintf (p, ARRAY_LENGTH (b) - (p - b), ",%d", pos[i].y_advance);
+	p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",%d", pos[i].y_advance));
     }
 
     if (buf_size > (p - b))
     {
       unsigned int l = p - b;
       memcpy (buf, b, l);
       buf += l;
       buf_size -= l;
@@ -189,16 +219,34 @@ static unsigned int
     } else
       return i - start;
   }
 
   return end - start;
 }
 
 /* Returns number of items, starting at start, that were serialized. */
+/**
+ * hb_buffer_serialize_glyphs:
+ * @buffer: a buffer.
+ * @start: 
+ * @end: 
+ * @buf: (array length=buf_size):
+ * @buf_size: 
+ * @buf_consumed: (out):
+ * @font: 
+ * @format: 
+ * @flags: 
+ *
+ * 
+ *
+ * Return value: 
+ *
+ * Since: 1.0
+ **/
 unsigned int
 hb_buffer_serialize_glyphs (hb_buffer_t *buffer,
 			    unsigned int start,
 			    unsigned int end,
 			    char *buf,
 			    unsigned int buf_size,
 			    unsigned int *buf_consumed, /* May be NULL */
 			    hb_font_t *font, /* May be NULL */
@@ -281,16 +329,31 @@ parse_int (const char *pp, const char *e
 
   *pv = v;
   return true;
 }
 
 #include "hb-buffer-deserialize-json.hh"
 #include "hb-buffer-deserialize-text.hh"
 
+/**
+ * hb_buffer_deserialize_glyphs:
+ * @buffer: a buffer.
+ * @buf: (array length=buf_len):
+ * @buf_len: 
+ * @end_ptr: (out):
+ * @font: 
+ * @format: 
+ *
+ * 
+ *
+ * Return value: 
+ *
+ * Since: 1.0
+ **/
 hb_bool_t
 hb_buffer_deserialize_glyphs (hb_buffer_t *buffer,
 			      const char *buf,
 			      int buf_len, /* -1 means nul-terminated */
 			      const char **end_ptr, /* May be NULL */
 			      hb_font_t *font, /* May be NULL */
 			      hb_buffer_serialize_format_t format)
 {
--- a/gfx/harfbuzz/src/hb-buffer.cc
+++ b/gfx/harfbuzz/src/hb-buffer.cc
@@ -171,17 +171,17 @@ hb_buffer_t::reset (void)
 void
 hb_buffer_t::clear (void)
 {
   if (unlikely (hb_object_is_inert (this)))
     return;
 
   hb_segment_properties_t default_props = HB_SEGMENT_PROPERTIES_DEFAULT;
   props = default_props;
-  flags = HB_BUFFER_FLAGS_DEFAULT;
+  flags = HB_BUFFER_FLAG_DEFAULT;
 
   content_type = HB_BUFFER_CONTENT_TYPE_INVALID;
   in_error = false;
   have_output = false;
   have_positions = false;
 
   idx = 0;
   len = 0;
@@ -544,49 +544,49 @@ dump_var_allocation (const hb_buffer_t *
 	     "Current var allocation: %s",
 	     buf);
 }
 
 void hb_buffer_t::allocate_var (unsigned int byte_i, unsigned int count, const char *owner)
 {
   assert (byte_i < 8 && byte_i + count <= 8);
 
-  if (DEBUG (BUFFER))
+  if (DEBUG_ENABLED (BUFFER))
     dump_var_allocation (this);
   DEBUG_MSG (BUFFER, this,
 	     "Allocating var bytes %d..%d for %s",
 	     byte_i, byte_i + count - 1, owner);
 
   for (unsigned int i = byte_i; i < byte_i + count; i++) {
     assert (!allocated_var_bytes[i]);
     allocated_var_bytes[i]++;
     allocated_var_owner[i] = owner;
   }
 }
 
 void hb_buffer_t::deallocate_var (unsigned int byte_i, unsigned int count, const char *owner)
 {
-  if (DEBUG (BUFFER))
+  if (DEBUG_ENABLED (BUFFER))
     dump_var_allocation (this);
 
   DEBUG_MSG (BUFFER, this,
 	     "Deallocating var bytes %d..%d for %s",
 	     byte_i, byte_i + count - 1, owner);
 
   assert (byte_i < 8 && byte_i + count <= 8);
   for (unsigned int i = byte_i; i < byte_i + count; i++) {
     assert (allocated_var_bytes[i]);
     assert (0 == strcmp (allocated_var_owner[i], owner));
     allocated_var_bytes[i]--;
   }
 }
 
 void hb_buffer_t::assert_var (unsigned int byte_i, unsigned int count, const char *owner)
 {
-  if (DEBUG (BUFFER))
+  if (DEBUG_ENABLED (BUFFER))
     dump_var_allocation (this);
 
   DEBUG_MSG (BUFFER, this,
 	     "Asserting var bytes %d..%d for %s",
 	     byte_i, byte_i + count - 1, owner);
 
   assert (byte_i < 8 && byte_i + count <= 8);
   for (unsigned int i = byte_i; i < byte_i + count; i++) {
@@ -598,240 +598,491 @@ void hb_buffer_t::assert_var (unsigned i
 void hb_buffer_t::deallocate_var_all (void)
 {
   memset (allocated_var_bytes, 0, sizeof (allocated_var_bytes));
   memset (allocated_var_owner, 0, sizeof (allocated_var_owner));
 }
 
 /* Public API */
 
+/**
+ * hb_buffer_create: (Xconstructor)
+ *
+ * 
+ *
+ * Return value: (transfer full)
+ *
+ * Since: 1.0
+ **/
 hb_buffer_t *
 hb_buffer_create (void)
 {
   hb_buffer_t *buffer;
 
   if (!(buffer = hb_object_create<hb_buffer_t> ()))
     return hb_buffer_get_empty ();
 
   buffer->reset ();
 
   return buffer;
 }
 
+/**
+ * hb_buffer_get_empty:
+ *
+ * 
+ *
+ * Return value: (transfer full):
+ *
+ * Since: 1.0
+ **/
 hb_buffer_t *
 hb_buffer_get_empty (void)
 {
   static const hb_buffer_t _hb_buffer_nil = {
     HB_OBJECT_HEADER_STATIC,
 
     const_cast<hb_unicode_funcs_t *> (&_hb_unicode_funcs_nil),
     HB_SEGMENT_PROPERTIES_DEFAULT,
-    HB_BUFFER_FLAGS_DEFAULT,
+    HB_BUFFER_FLAG_DEFAULT,
 
     HB_BUFFER_CONTENT_TYPE_INVALID,
     true, /* in_error */
     true, /* have_output */
     true  /* have_positions */
 
     /* Zero is good enough for everything else. */
   };
 
   return const_cast<hb_buffer_t *> (&_hb_buffer_nil);
 }
 
+/**
+ * hb_buffer_reference: (skip)
+ * @buffer: a buffer.
+ *
+ * 
+ *
+ * Return value: (transfer full):
+ *
+ * Since: 1.0
+ **/
 hb_buffer_t *
 hb_buffer_reference (hb_buffer_t *buffer)
 {
   return hb_object_reference (buffer);
 }
 
+/**
+ * hb_buffer_destroy: (skip)
+ * @buffer: a buffer.
+ *
+ * 
+ *
+ * Since: 1.0
+ **/
 void
 hb_buffer_destroy (hb_buffer_t *buffer)
 {
   if (!hb_object_destroy (buffer)) return;
 
   hb_unicode_funcs_destroy (buffer->unicode);
 
   free (buffer->info);
   free (buffer->pos);
 
   free (buffer);
 }
 
+/**
+ * hb_buffer_set_user_data: (skip)
+ * @buffer: a buffer.
+ * @key: 
+ * @data: 
+ * @destroy: 
+ * @replace: 
+ *
+ * 
+ *
+ * Return value: 
+ *
+ * Since: 1.0
+ **/
 hb_bool_t
 hb_buffer_set_user_data (hb_buffer_t        *buffer,
 			 hb_user_data_key_t *key,
 			 void *              data,
 			 hb_destroy_func_t   destroy,
 			 hb_bool_t           replace)
 {
   return hb_object_set_user_data (buffer, key, data, destroy, replace);
 }
 
+/**
+ * hb_buffer_get_user_data: (skip)
+ * @buffer: a buffer.
+ * @key: 
+ *
+ * 
+ *
+ * Return value: 
+ *
+ * Since: 1.0
+ **/
 void *
 hb_buffer_get_user_data (hb_buffer_t        *buffer,
 			 hb_user_data_key_t *key)
 {
   return hb_object_get_user_data (buffer, key);
 }
 
 
+/**
+ * hb_buffer_set_content_type:
+ * @buffer: a buffer.
+ * @content_type: 
+ *
+ * 
+ *
+ * Since: 1.0
+ **/
 void
 hb_buffer_set_content_type (hb_buffer_t              *buffer,
 			    hb_buffer_content_type_t  content_type)
 {
   buffer->content_type = content_type;
 }
 
+/**
+ * hb_buffer_get_content_type:
+ * @buffer: a buffer.
+ *
+ * 
+ *
+ * Return value: 
+ *
+ * Since: 1.0
+ **/
 hb_buffer_content_type_t
 hb_buffer_get_content_type (hb_buffer_t *buffer)
 {
   return buffer->content_type;
 }
 
 
+/**
+ * hb_buffer_set_unicode_funcs:
+ * @buffer: a buffer.
+ * @unicode_funcs: 
+ *
+ * 
+ *
+ * Since: 1.0
+ **/
 void
 hb_buffer_set_unicode_funcs (hb_buffer_t        *buffer,
-			     hb_unicode_funcs_t *unicode)
+			     hb_unicode_funcs_t *unicode_funcs)
 {
   if (unlikely (hb_object_is_inert (buffer)))
     return;
 
-  if (!unicode)
-    unicode = hb_unicode_funcs_get_default ();
+  if (!unicode_funcs)
+    unicode_funcs = hb_unicode_funcs_get_default ();
 
 
-  hb_unicode_funcs_reference (unicode);
+  hb_unicode_funcs_reference (unicode_funcs);
   hb_unicode_funcs_destroy (buffer->unicode);
-  buffer->unicode = unicode;
+  buffer->unicode = unicode_funcs;
 }
 
+/**
+ * hb_buffer_get_unicode_funcs:
+ * @buffer: a buffer.
+ *
+ * 
+ *
+ * Return value: 
+ *
+ * Since: 1.0
+ **/
 hb_unicode_funcs_t *
 hb_buffer_get_unicode_funcs (hb_buffer_t        *buffer)
 {
   return buffer->unicode;
 }
 
+/**
+ * hb_buffer_set_direction:
+ * @buffer: a buffer.
+ * @direction: 
+ *
+ * 
+ *
+ * Since: 1.0
+ **/
 void
 hb_buffer_set_direction (hb_buffer_t    *buffer,
 			 hb_direction_t  direction)
 
 {
   if (unlikely (hb_object_is_inert (buffer)))
     return;
 
   buffer->props.direction = direction;
 }
 
+/**
+ * hb_buffer_get_direction:
+ * @buffer: a buffer.
+ *
+ * 
+ *
+ * Return value: 
+ *
+ * Since: 1.0
+ **/
 hb_direction_t
 hb_buffer_get_direction (hb_buffer_t    *buffer)
 {
   return buffer->props.direction;
 }
 
+/**
+ * hb_buffer_set_script:
+ * @buffer: a buffer.
+ * @script: 
+ *
+ * 
+ *
+ * Since: 1.0
+ **/
 void
 hb_buffer_set_script (hb_buffer_t *buffer,
 		      hb_script_t  script)
 {
   if (unlikely (hb_object_is_inert (buffer)))
     return;
 
   buffer->props.script = script;
 }
 
+/**
+ * hb_buffer_get_script:
+ * @buffer: a buffer.
+ *
+ * 
+ *
+ * Return value: 
+ *
+ * Since: 1.0
+ **/
 hb_script_t
 hb_buffer_get_script (hb_buffer_t *buffer)
 {
   return buffer->props.script;
 }
 
+/**
+ * hb_buffer_set_language:
+ * @buffer: a buffer.
+ * @language: 
+ *
+ * 
+ *
+ * Since: 1.0
+ **/
 void
 hb_buffer_set_language (hb_buffer_t   *buffer,
 			hb_language_t  language)
 {
   if (unlikely (hb_object_is_inert (buffer)))
     return;
 
   buffer->props.language = language;
 }
 
+/**
+ * hb_buffer_get_language:
+ * @buffer: a buffer.
+ *
+ * 
+ *
+ * Return value: 
+ *
+ * Since: 1.0
+ **/
 hb_language_t
 hb_buffer_get_language (hb_buffer_t *buffer)
 {
   return buffer->props.language;
 }
 
+/**
+ * hb_buffer_set_segment_properties:
+ * @buffer: a buffer.
+ * @props: 
+ *
+ * 
+ *
+ * Since: 1.0
+ **/
 void
 hb_buffer_set_segment_properties (hb_buffer_t *buffer,
 				  const hb_segment_properties_t *props)
 {
   if (unlikely (hb_object_is_inert (buffer)))
     return;
 
   buffer->props = *props;
 }
 
+/**
+ * hb_buffer_get_segment_properties:
+ * @buffer: a buffer.
+ * @props: 
+ *
+ * 
+ *
+ * Since: 1.0
+ **/
 void
 hb_buffer_get_segment_properties (hb_buffer_t *buffer,
 				  hb_segment_properties_t *props)
 {
   *props = buffer->props;
 }
 
 
+/**
+ * hb_buffer_set_flags:
+ * @buffer: a buffer.
+ * @flags: 
+ *
+ * 
+ *
+ * Since: 1.0
+ **/
 void
 hb_buffer_set_flags (hb_buffer_t       *buffer,
 		     hb_buffer_flags_t  flags)
 {
   if (unlikely (hb_object_is_inert (buffer)))
     return;
 
   buffer->flags = flags;
 }
 
+/**
+ * hb_buffer_get_flags:
+ * @buffer: a buffer.
+ *
+ * 
+ *
+ * Return value: 
+ *
+ * Since: 1.0
+ **/
 hb_buffer_flags_t
 hb_buffer_get_flags (hb_buffer_t *buffer)
 {
   return buffer->flags;
 }
 
 
+/**
+ * hb_buffer_reset:
+ * @buffer: a buffer.
+ *
+ * 
+ *
+ * Since: 1.0
+ **/
 void
 hb_buffer_reset (hb_buffer_t *buffer)
 {
   buffer->reset ();
 }
 
+/**
+ * hb_buffer_clear_contents:
+ * @buffer: a buffer.
+ *
+ * 
+ *
+ * Since: 1.0
+ **/
 void
 hb_buffer_clear_contents (hb_buffer_t *buffer)
 {
   buffer->clear ();
 }
 
+/**
+ * hb_buffer_pre_allocate:
+ * @buffer: a buffer.
+ * @size: 
+ *
+ * 
+ *
+ * Return value: 
+ *
+ * Since: 1.0
+ **/
 hb_bool_t
 hb_buffer_pre_allocate (hb_buffer_t *buffer, unsigned int size)
 {
   return buffer->ensure (size);
 }
 
+/**
+ * hb_buffer_allocation_successful:
+ * @buffer: a buffer.
+ *
+ * 
+ *
+ * Return value: 
+ *
+ * Since: 1.0
+ **/
 hb_bool_t
 hb_buffer_allocation_successful (hb_buffer_t  *buffer)
 {
   return !buffer->in_error;
 }
 
+/**
+ * hb_buffer_add:
+ * @buffer: a buffer.
+ * @codepoint: 
+ * @cluster: 
+ *
+ * 
+ *
+ * Since: 1.0
+ **/
 void
 hb_buffer_add (hb_buffer_t    *buffer,
 	       hb_codepoint_t  codepoint,
 	       unsigned int    cluster)
 {
   buffer->add (codepoint, cluster);
   buffer->clear_context (1);
 }
 
+/**
+ * hb_buffer_set_length:
+ * @buffer: a buffer.
+ * @length: 
+ *
+ * 
+ *
+ * Return value: 
+ *
+ * Since: 1.0
+ **/
 hb_bool_t
 hb_buffer_set_length (hb_buffer_t  *buffer,
 		      unsigned int  length)
 {
   if (unlikely (hb_object_is_inert (buffer)))
     return length == 0;
 
   if (!buffer->ensure (length))
@@ -848,59 +1099,133 @@ hb_buffer_set_length (hb_buffer_t  *buff
 
   if (!length)
     buffer->clear_context (0);
   buffer->clear_context (1);
 
   return true;
 }
 
+/**
+ * hb_buffer_get_length:
+ * @buffer: a buffer.
+ *
+ * Returns the number of items in the buffer.
+ *
+ * Return value: buffer length.
+ *
+ * Since: 1.0
+ **/
 unsigned int
 hb_buffer_get_length (hb_buffer_t *buffer)
 {
   return buffer->len;
 }
 
-/* Return value valid as long as buffer not modified */
+/**
+ * hb_buffer_get_glyph_infos:
+ * @buffer: a buffer.
+ * @length: (out): output array length.
+ *
+ * Returns buffer glyph information array.  Returned pointer
+ * is valid as long as buffer contents are not modified.
+ *
+ * Return value: (transfer none) (array length=length): buffer glyph information array.
+ *
+ * Since: 1.0
+ **/
 hb_glyph_info_t *
 hb_buffer_get_glyph_infos (hb_buffer_t  *buffer,
                            unsigned int *length)
 {
   if (length)
     *length = buffer->len;
 
   return (hb_glyph_info_t *) buffer->info;
 }
 
-/* Return value valid as long as buffer not modified */
+/**
+ * hb_buffer_get_glyph_positions:
+ * @buffer: a buffer.
+ * @length: (out): output length.
+ *
+ * Returns buffer glyph position array.  Returned pointer
+ * is valid as long as buffer contents are not modified.
+ *
+ * Return value: (transfer none) (array length=length): buffer glyph position array.
+ *
+ * Since: 1.0
+ **/
 hb_glyph_position_t *
 hb_buffer_get_glyph_positions (hb_buffer_t  *buffer,
                                unsigned int *length)
 {
   if (!buffer->have_positions)
     buffer->clear_positions ();
 
   if (length)
     *length = buffer->len;
 
   return (hb_glyph_position_t *) buffer->pos;
 }
 
+/**
+ * hb_buffer_reverse:
+ * @buffer: a buffer.
+ *
+ * Reverses buffer contents.
+ *
+ * Since: 1.0
+ **/
 void
 hb_buffer_reverse (hb_buffer_t *buffer)
 {
   buffer->reverse ();
 }
 
+/**
+ * hb_buffer_reverse_clusters:
+ * @buffer: a buffer.
+ *
+ * Reverses buffer clusters.  That is, the buffer contents are
+ * reversed, then each cluster (consecutive items having the
+ * same cluster number) are reversed again.
+ *
+ * Since: 1.0
+ **/
 void
 hb_buffer_reverse_clusters (hb_buffer_t *buffer)
 {
   buffer->reverse_clusters ();
 }
 
+/**
+ * hb_buffer_guess_segment_properties:
+ * @buffer: a buffer.
+ *
+ * Sets unset buffer segment properties based on buffer Unicode
+ * contents.  If buffer is not empty, it must have content type
+ * %HB_BUFFER_CONTENT_TYPE_UNICODE.
+ *
+ * If buffer script is not set (ie. is %HB_SCRIPT_INVALID), it
+ * will be set to the Unicode script of the first character in
+ * the buffer that has a script other than %HB_SCRIPT_COMMON,
+ * %HB_SCRIPT_INHERITED, and %HB_SCRIPT_UNKNOWN.
+ *
+ * Next, if buffer direction is not set (ie. is %HB_DIRECTION_INVALID),
+ * it will be set to the natural horizontal direction of the
+ * buffer script as returned by hb_script_get_horizontal_direction().
+ *
+ * Finally, if buffer language is not set (ie. is %HB_LANGUAGE_INVALID),
+ * it will be set to the process's default language as returned by
+ * hb_language_get_default().  This may change in the future by
+ * taking buffer script into consideration when choosing a language.
+ *
+ * Since: 1.0
+ **/
 void
 hb_buffer_guess_segment_properties (hb_buffer_t *buffer)
 {
   buffer->guess_segment_properties ();
 }
 
 template <typename T>
 static inline void
@@ -963,36 +1288,72 @@ hb_buffer_add_utf (hb_buffer_t  *buffer,
     hb_codepoint_t u;
     next = hb_utf_next (next, end, &u);
     buffer->context[1][buffer->context_len[1]++] = u;
   }
 
   buffer->content_type = HB_BUFFER_CONTENT_TYPE_UNICODE;
 }
 
+/**
+ * hb_buffer_add_utf8:
+ * @buffer: a buffer.
+ * @text: (array length=text_length):
+ * @text_length: 
+ * @item_offset: 
+ * @item_length: 
+ *
+ * 
+ *
+ * Since: 1.0
+ **/
 void
 hb_buffer_add_utf8 (hb_buffer_t  *buffer,
 		    const char   *text,
 		    int           text_length,
 		    unsigned int  item_offset,
 		    int