Bug 832883 - Move IDBKeyRange to WebIDL and define indexedDB/IDBKeyRange in all the spots. r=khuey,bent (initial work done by Ms2ger)
authorJan Varga <jan.varga@gmail.com>
Sat, 28 Sep 2013 13:25:46 +0200
changeset 157778 81f8a3c52c23cc1c12f6a4564a5f4f2b330dbe5f
parent 157777 8918bc282c8ae3e37b52af44124245616d65e181
child 157779 92f573a2f75d7f3e9312522db8fd85be7ac0d13c
push id4537
push userlsblakk@mozilla.com
push dateMon, 28 Oct 2013 22:18:47 +0000
treeherdermozilla-aurora@60c6fd67470e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey, bent
bugs832883
milestone27.0a1
Bug 832883 - Move IDBKeyRange to WebIDL and define indexedDB/IDBKeyRange in all the spots. r=khuey,bent (initial work done by Ms2ger)
addon-sdk/source/lib/sdk/indexed-db.js
addon-sdk/source/lib/toolkit/loader.js
dom/activities/src/ActivitiesService.jsm
dom/alarm/AlarmDB.jsm
dom/alarm/AlarmService.jsm
dom/base/IndexedDBHelper.jsm
dom/base/nsDOMClassInfo.cpp
dom/base/nsDOMClassInfoClasses.h
dom/bindings/Bindings.conf
dom/contacts/fallback/ContactDB.jsm
dom/contacts/fallback/ContactService.jsm
dom/contacts/tests/test_contacts_upgrade.html
dom/indexedDB/IDBFactory.cpp
dom/indexedDB/IDBIndex.cpp
dom/indexedDB/IDBKeyRange.cpp
dom/indexedDB/IDBKeyRange.h
dom/indexedDB/IndexedDatabaseManager.cpp
dom/indexedDB/IndexedDatabaseManager.h
dom/indexedDB/moz.build
dom/indexedDB/nsIIDBKeyRange.idl
dom/indexedDB/nsIIndexedDatabaseManager.idl
dom/indexedDB/test/Makefile.in
dom/indexedDB/test/browserHelpers.js
dom/indexedDB/test/chromeHelpers.js
dom/indexedDB/test/event_propagation_iframe.html
dom/indexedDB/test/exceptions_in_events_iframe.html
dom/indexedDB/test/extensions/Makefile.in
dom/indexedDB/test/extensions/bootstrap.js
dom/indexedDB/test/extensions/install.rdf
dom/indexedDB/test/extensions/moz.build
dom/indexedDB/test/moz.build
dom/indexedDB/test/test_globalObjects.html
dom/indexedDB/test/test_globalObjects.xul
dom/indexedDB/test/unit/GlobalObjectsChild.js
dom/indexedDB/test/unit/GlobalObjectsComponent.js
dom/indexedDB/test/unit/GlobalObjectsComponent.manifest
dom/indexedDB/test/unit/GlobalObjectsModule.jsm
dom/indexedDB/test/unit/GlobalObjectsSandbox.js
dom/indexedDB/test/unit/Makefile.in
dom/indexedDB/test/unit/head.js
dom/indexedDB/test/unit/test_globalObjects.js
dom/indexedDB/test/unit/test_globalObjects_ipc.js
dom/indexedDB/test/unit/xpcshell.ini
dom/mobilemessage/src/gonk/MobileMessageDatabaseService.js
dom/mobilemessage/tests/test_smsdatabaseservice.xul
dom/network/src/NetworkStatsDB.jsm
dom/network/src/NetworkStatsService.jsm
dom/network/tests/unit_stats/test_networkstats_db.js
dom/push/src/PushService.jsm
dom/settings/SettingsDB.jsm
dom/settings/SettingsManager.js
dom/settings/SettingsService.js
dom/tests/unit/test_geolocation_provider.js
dom/tests/unit/test_geolocation_timeout.js
dom/tests/unit/xpcshell.ini
dom/webidl/IDBKeyRange.webidl
dom/webidl/moz.build
js/xpconnect/src/Sandbox.cpp
js/xpconnect/src/dom_quickstubs.qsconf
js/xpconnect/src/nsXPConnect.cpp
js/xpconnect/src/xpcprivate.h
toolkit/mozapps/extensions/XPIProvider.jsm
--- 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/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/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/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/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"
--- 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/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/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 \
@@ -110,16 +111,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/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 \
--- 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;
+}
--- 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]
--- 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/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/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"
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/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/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -25,25 +25,27 @@
 #include "nsPrincipal.h"
 #include "nsXMLHttpRequest.h"
 #include "WrapperFactory.h"
 #include "xpcprivate.h"
 #include "XPCQuickStubs.h"
 #include "XPCWrapper.h"
 #include "XrayWrapper.h"
 #include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
 #include "mozilla/dom/TextDecoderBinding.h"
 #include "mozilla/dom/TextEncoderBinding.h"
 
 using namespace mozilla;
 using namespace JS;
 using namespace js;
 using namespace xpc;
 
 using mozilla::dom::DestroyProtoAndIfaceCache;
+using mozilla::dom::indexedDB::IndexedDatabaseManager;
 
 NS_IMPL_ISUPPORTS3(SandboxPrivate,
                    nsIScriptObjectPrincipal,
                    nsIGlobalObject,
                    nsISupportsWeakReference)
 
 const char kScriptSecurityManagerContractID[] = NS_SCRIPTSECURITYMANAGER_CONTRACTID;
 
@@ -884,17 +886,19 @@ xpc::GlobalProperties::Parse(JSContext *
         ok = JS_GetElement(cx, obj, i, &nameValue);
         NS_ENSURE_TRUE(ok, false);
         if (!nameValue.isString()) {
             JS_ReportError(cx, "Property names must be strings");
             return false;
         }
         JSAutoByteString name(cx, nameValue.toString());
         NS_ENSURE_TRUE(name, false);
-        if (!strcmp(name.ptr(), "XMLHttpRequest")) {
+        if (!strcmp(name.ptr(), "indexedDB")) {
+            indexedDB = true;
+        } else if (!strcmp(name.ptr(), "XMLHttpRequest")) {
             XMLHttpRequest = true;
         } else if (!strcmp(name.ptr(), "TextEncoder")) {
             TextEncoder = true;
         } else if (!strcmp(name.ptr(), "TextDecoder")) {
             TextDecoder = true;
         } else if (!strcmp(name.ptr(), "atob")) {
             atob = true;
         } else if (!strcmp(name.ptr(), "btoa")) {
@@ -905,16 +909,21 @@ xpc::GlobalProperties::Parse(JSContext *
         }
     }
     return true;
 }
 
 bool
 xpc::GlobalProperties::Define(JSContext *cx, JS::HandleObject obj)
 {
+    if (indexedDB && AccessCheck::isChrome(obj) &&
+        (!IndexedDatabaseManager::DefineConstructors(cx, obj) ||
+         !IndexedDatabaseManager::DefineIndexedDBGetter(cx, obj)))
+        return false;
+
     if (XMLHttpRequest &&
         !JS_DefineFunction(cx, obj, "XMLHttpRequest", CreateXMLHttpRequest, 0, JSFUN_CONSTRUCTOR))
         return false;
 
     if (TextEncoder &&
         !dom::TextEncoderBinding::GetConstructorObject(cx, obj))
         return false;
 
--- a/js/xpconnect/src/dom_quickstubs.qsconf
+++ b/js/xpconnect/src/dom_quickstubs.qsconf
@@ -78,17 +78,16 @@ members = [
     'nsIBoxObject.x',
     'nsIBoxObject.y',
     'nsIBoxObject.screenX',
     'nsIBoxObject.screenY',
     'nsIBoxObject.width',
     'nsIBoxObject.height',
 
     # dom/indexedDB
-    'nsIIDBKeyRange.*',
     'nsIIndexedDatabaseManager.*',
 
     # dom/file
     'nsIDOMLockedFile.*',
 
     # dom/quota
     'nsIQuotaManager.*',
     'nsIQuotaRequest.*',
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -25,32 +25,35 @@
 #ifdef MOZ_JSDEBUGGER
 #include "jsdIDebuggerService.h"
 #endif
 
 #include "XPCQuickStubs.h"
 
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/Exceptions.h"
+#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
 #include "mozilla/dom/TextDecoderBinding.h"
 #include "mozilla/dom/TextEncoderBinding.h"
 #include "mozilla/dom/DOMErrorBinding.h"
 
 #include "nsDOMMutationObserver.h"
 #include "nsICycleCollectorListener.h"
 #include "nsThread.h"
 #include "mozilla/XPTInterfaceInfoManager.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace xpc;
 using namespace JS;
 
+using mozilla::dom::indexedDB::IndexedDatabaseManager;
+
 NS_IMPL_ISUPPORTS5(nsXPConnect,
                    nsIXPConnect,
                    nsISupportsWeakReference,
                    nsIThreadObserver,
                    nsIJSRuntimeService,
                    nsIJSEngineTelemetryStats)
 
 nsXPConnect* nsXPConnect::gSelf = nullptr;
@@ -529,16 +532,22 @@ nsXPConnect::InitClassesWithNewWrappedGl
     // XXX Please do not add any additional classes here without the approval of
     //     the XPConnect module owner.
     if (!TextDecoderBinding::GetConstructorObject(aJSContext, global) ||
         !TextEncoderBinding::GetConstructorObject(aJSContext, global) ||
         !DOMErrorBinding::GetConstructorObject(aJSContext, global)) {
         return UnexpectedFailure(NS_ERROR_FAILURE);
     }
 
+    if (nsContentUtils::IsSystemPrincipal(aPrincipal) &&
+        !IndexedDatabaseManager::DefineIndexedDBLazyGetter(aJSContext,
+                                                           global)) {
+        return UnexpectedFailure(NS_ERROR_FAILURE);
+    }
+
     wrappedGlobal.forget(_retval);
     return NS_OK;
 }
 
 static nsresult
 NativeInterface2JSObject(HandleObject aScope,
                          nsISupports *aCOMObj,
                          nsWrapperCache *aCache,
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -3585,16 +3585,17 @@ NewFunctionForwarder(JSContext *cx, JS::
 // Old fashioned xpc error reporter. Try to use JS_ReportError instead.
 nsresult
 ThrowAndFail(nsresult errNum, JSContext *cx, bool *retval);
 
 struct GlobalProperties {
     GlobalProperties() { mozilla::PodZero(this); }
     bool Parse(JSContext *cx, JS::HandleObject obj);
     bool Define(JSContext *cx, JS::HandleObject obj);
+    bool indexedDB;
     bool XMLHttpRequest;
     bool TextDecoder;
     bool TextEncoder;
     bool atob;
     bool btoa;
 };
 
 // Infallible.
--- a/toolkit/mozapps/extensions/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/XPIProvider.jsm
@@ -2,16 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
+const Cu = Components.utils;
 
 this.EXPORTED_SYMBOLS = [];
 
 Components.utils.import("resource://gre/modules/Services.jsm");
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/AddonManager.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "AddonRepository",
@@ -3897,25 +3898,27 @@ var XPIProvider = {
     }
 
     LOG("Loading bootstrap scope from " + aFile.path);
 
     let principal = Cc["@mozilla.org/systemprincipal;1"].
                     createInstance(Ci.nsIPrincipal);
 
     if (!aFile.exists()) {
-      this.bootstrapScopes[aId] = new Components.utils.Sandbox(principal,
-                                                               {sandboxName: aFile.path});
+      this.bootstrapScopes[aId] =
+        new Cu.Sandbox(principal, {sandboxName: aFile.path,
+                                   wantGlobalProperties: ["indexedDB"]});
       ERROR("Attempted to load bootstrap scope from missing directory " + aFile.path);
       return;
     }
 
     let uri = getURIForResourceInFile(aFile, "bootstrap.js").spec;
-    this.bootstrapScopes[aId] = new Components.utils.Sandbox(principal,
-                                                             {sandboxName: uri});
+    this.bootstrapScopes[aId] =
+      new Cu.Sandbox(principal, {sandboxName: uri,
+                                 wantGlobalProperties: ["indexedDB"]});
 
     let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
                  createInstance(Ci.mozIJSSubScriptLoader);
 
     // Add a mapping for XPIProvider.mapURIToAddonID
     this._addURIMapping(aId, aFile);
 
     try {