Merge fx-team to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 24 Apr 2014 13:39:46 -0400
changeset 198505 3b166b8add936dd501593c6177d67bdeb15c46e8
parent 198477 7fdae4f893df21ecde663b72e19a436e8d72c9e1 (current diff)
parent 198504 9a6ce164613574831046489a26a2f85defefa39d (diff)
child 198506 5ecd532a167ef0fe53cfd86075cebaaa6dc987e9
child 198513 f31baef145d4b5d0369bb59d6b284605c05032f9
child 198553 891fc0052015ba36d976dca083ceec5a5af35aa2
child 198603 09e329985ca22d60286cf58cae3f5abdb423cf62
push id3624
push userasasaki@mozilla.com
push dateMon, 09 Jun 2014 21:49:01 +0000
treeherdermozilla-beta@b1a5da15899a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone31.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge fx-team to m-c.
b2g/app/b2g.js
b2g/chrome/content/settings.js
browser/devtools/devtools-clhandler.js
modules/libpref/src/init/all.js
--- a/browser/components/customizableui/src/CustomizeMode.jsm
+++ b/browser/components/customizableui/src/CustomizeMode.jsm
@@ -156,18 +156,17 @@ CustomizeMode.prototype = {
         }.bind(this);
         Services.obs.addObserver(delayedStartupObserver, "browser-delayed-startup-finished", false);
         yield delayedStartupDeferred.promise;
       }
 
       let toolbarVisibilityBtn = document.getElementById(kToolbarVisibilityBtn);
       let togglableToolbars = window.getTogglableToolbars();
       let bookmarksToolbar = document.getElementById("PersonalToolbar");
-      if (togglableToolbars.length == 0 ||
-          (togglableToolbars.length == 1 && togglableToolbars[0] == bookmarksToolbar)) {
+      if (togglableToolbars.length == 0) {
         toolbarVisibilityBtn.setAttribute("hidden", "true");
       } else {
         toolbarVisibilityBtn.removeAttribute("hidden");
       }
 
       // Disable lightweight themes while in customization mode since
       // they don't have large enough images to pad the full browser window.
       if (this.document.documentElement._lightweightTheme)
--- a/browser/devtools/layoutview/test/browser_layoutview.js
+++ b/browser/devtools/layoutview/test/browser_layoutview.js
@@ -92,8 +92,24 @@ function*() {
 
   yield waitForUpdate();
 
   for (let i = 0; i < res2.length; i++) {
     let elt = viewdoc.querySelector(res2[i].selector);
     is(elt.textContent, res2[i].value, res2[i].selector + " has the right value after style update.");
   }
 });
+
+addTest("Test that long labels on left/right are rotated 90 degrees",
+function*() {
+  let viewdoc = view.document;
+  const LONG_TEXT_ROTATE_LIMIT = 3;
+
+  for (let i = 0; i < res1.length; i++) {
+    let elt = viewdoc.querySelector(res1[i].selector);
+    let isLong = elt.textContent.length > LONG_TEXT_ROTATE_LIMIT;
+    let classList = elt.parentNode.classList
+    let canBeRotated = classList.contains("left") || classList.contains("right");
+    let isRotated = classList.contains("rotate");
+
+    is(canBeRotated && isLong, isRotated, res1[i].selector + " correctly rotated.");
+  }
+});
--- a/browser/devtools/layoutview/view.css
+++ b/browser/devtools/layoutview/view.css
@@ -169,16 +169,24 @@ body {
 .margin.right {
   right: 0;
 }
 
 .margin.left {
   left: 0;
 }
 
+.rotate.left:not(.editing) {
+  transform: rotate(-90deg);
+}
+
+.rotate.right:not(.editing) {
+  transform: rotate(90deg);
+}
+
 .tooltip {
   position: absolute;
   bottom: 0;
   right: 2px;
   pointer-events: none;
 }
 
 body.dim > #header > #element-position,
--- a/browser/devtools/layoutview/view.js
+++ b/browser/devtools/layoutview/view.js
@@ -15,16 +15,17 @@ Cu.import("resource://gre/modules/Task.j
 Cu.import("resource://gre/modules/devtools/Loader.jsm");
 Cu.import("resource://gre/modules/devtools/Console.jsm");
 
 const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
 const {InplaceEditor, editableItem} = devtools.require("devtools/shared/inplace-editor");
 const {parseDeclarations} = devtools.require("devtools/styleinspector/css-parsing-utils");
 
 const NUMERIC = /^-?[\d\.]+$/;
+const LONG_TEXT_ROTATE_LIMIT = 3;
 
 /**
  * An instance of EditingSession tracks changes that have been made during the
  * modification of box model values. All of these changes can be reverted by
  * calling revert.
  *
  * @param doc    A DOM document that can be used to test style rules.
  * @param rules  An array of the style rules defined for the node being edited.
@@ -218,20 +219,24 @@ LayoutView.prototype = {
    */
   initEditor: function LV_initEditor(element, event, dimension) {
     let { property, realProperty } = dimension;
     if (!realProperty)
       realProperty = property;
     let session = new EditingSession(document, this.elementRules);
     let initialValue = session.getProperty(realProperty);
 
-    new InplaceEditor({
+    let editor = new InplaceEditor({
       element: element,
       initial: initialValue,
 
+      start: (editor) => {
+        editor.elt.parentNode.classList.add("editing");
+      },
+
       change: (value) => {
         if (NUMERIC.test(value))
           value += "px";
         let properties = [
           { name: property, value: value }
         ]
 
         if (property.substring(0, 7) == "border-") {
@@ -240,16 +245,17 @@ LayoutView.prototype = {
           if (!style || style == "none" || style == "hidden")
             properties.push({ name: bprop, value: "solid" });
         }
 
         session.setProperties(properties);
       },
 
       done: (value, commit) => {
+        editor.elt.parentNode.classList.remove("editing");
         if (!commit)
           session.revert();
       }
     }, event);
   },
 
   /**
    * Is the layoutview visible in the sidebar?
@@ -382,16 +388,17 @@ LayoutView.prototype = {
       for (let i in this.map) {
         let selector = this.map[i].selector;
         let span = this.doc.querySelector(selector);
         if (span.textContent.length > 0 &&
             span.textContent == this.map[i].value) {
           continue;
         }
         span.textContent = this.map[i].value;
+        this.manageOverflowingText(span);
       }
 
       width -= this.map.borderLeft.value + this.map.borderRight.value +
                this.map.paddingLeft.value + this.map.paddingRight.value;
 
       height -= this.map.borderTop.value + this.map.borderBottom.value +
                 this.map.paddingTop.value + this.map.paddingBottom.value;
 
@@ -415,16 +422,25 @@ LayoutView.prototype = {
     toolbox.highlighterUtils.highlightNodeFront(nodeFront, options);
   },
 
   hideBoxModel: function() {
     let toolbox = this.inspector.toolbox;
 
     toolbox.highlighterUtils.unhighlight();
   },
+
+  manageOverflowingText: function(span) {
+    let classList = span.parentNode.classList;
+
+    if (classList.contains("left") || classList.contains("right")) {
+      let force = span.textContent.length > LONG_TEXT_ROTATE_LIMIT;
+      classList.toggle("rotate", force);
+    }
+  }
 };
 
 let elts;
 let tooltip;
 
 let onmouseover = function(e) {
   let region = e.target.getAttribute("data-box");
 
--- a/browser/devtools/scratchpad/test/browser_scratchpad_recent_files.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_recent_files.js
@@ -188,23 +188,23 @@ function testClearedAll()
 
   finishTest();
 }
 
 function createAndLoadTemporaryFile(aFile, aFileName, aFileContent)
 {
   // Create a temporary file.
   aFile = FileUtils.getFile("TmpD", [aFileName]);
-  aFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
+  aFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
 
   // Write the temporary file.
   let fout = Cc["@mozilla.org/network/file-output-stream;1"].
              createInstance(Ci.nsIFileOutputStream);
   fout.init(aFile.QueryInterface(Ci.nsILocalFile), 0x02 | 0x08 | 0x20,
-            0644, fout.DEFER_OPEN);
+            0o644, fout.DEFER_OPEN);
 
   gScratchpad.setFilename(aFile.path);
   gScratchpad.importFromFile(aFile.QueryInterface(Ci.nsILocalFile),  true,
                             fileImported);
   gScratchpad.saveFile(fileSaved);
 
   return aFile;
 }
--- a/browser/devtools/scratchpad/test/browser_scratchpad_reset_undo.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_reset_undo.js
@@ -40,31 +40,31 @@ function test()
 }
 
 function runTests()
 {
   gScratchpad = gScratchpadWindow.Scratchpad;
 
   // Create a temporary file.
   gFileA = FileUtils.getFile("TmpD", ["fileAForBug684546.tmp"]);
-  gFileA.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
+  gFileA.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
 
   gFileB = FileUtils.getFile("TmpD", ["fileBForBug684546.tmp"]);
-  gFileB.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
+  gFileB.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
 
   // Write the temporary file.
   let foutA = Cc["@mozilla.org/network/file-output-stream;1"].
              createInstance(Ci.nsIFileOutputStream);
   foutA.init(gFileA.QueryInterface(Ci.nsILocalFile), 0x02 | 0x08 | 0x20,
-            0644, foutA.DEFER_OPEN);
+            0o644, foutA.DEFER_OPEN);
 
   let foutB = Cc["@mozilla.org/network/file-output-stream;1"].
              createInstance(Ci.nsIFileOutputStream);
   foutB.init(gFileB.QueryInterface(Ci.nsILocalFile), 0x02 | 0x08 | 0x20,
-            0644, foutB.DEFER_OPEN);
+            0o644, foutB.DEFER_OPEN);
 
   let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
                   createInstance(Ci.nsIScriptableUnicodeConverter);
   converter.charset = "UTF-8";
   let fileContentStreamA = converter.convertToInputStream(gFileAContent);
   let fileContentStreamB = converter.convertToInputStream(gFileBContent);
 
   NetUtil.asyncCopy(fileContentStreamA, foutA, tempFileSaved);
--- a/browser/devtools/scratchpad/test/browser_scratchpad_revert_to_saved.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_revert_to_saved.js
@@ -93,23 +93,23 @@ function testAfterSecondRevert() {
   gFile = gScratchpad = menu = null;
   finish();
 }
 
 function createAndLoadTemporaryFile()
 {
   // Create a temporary file.
   gFile = FileUtils.getFile("TmpD", [gFileName]);
-  gFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
+  gFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
 
   // Write the temporary file.
   let fout = Cc["@mozilla.org/network/file-output-stream;1"].
              createInstance(Ci.nsIFileOutputStream);
   fout.init(gFile.QueryInterface(Ci.nsILocalFile), 0x02 | 0x08 | 0x20,
-            0644, fout.DEFER_OPEN);
+            0o644, fout.DEFER_OPEN);
 
   let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
                   createInstance(Ci.nsIScriptableUnicodeConverter);
   converter.charset = "UTF-8";
   let fileContentStream = converter.convertToInputStream(gFileContent);
 
   NetUtil.asyncCopy(fileContentStream, fout, tempFileSaved);
 }
--- a/mobile/android/base/home/HomeConfig.java
+++ b/mobile/android/base/home/HomeConfig.java
@@ -1331,16 +1331,18 @@ public final class HomeConfig {
             ThreadUtils.assertOnThread(mOriginalThread);
 
             if (!mConfigOrder.contains(panelId)) {
                 return false;
             }
 
             mConfigOrder.remove(panelId);
             mConfigOrder.add(destIndex, panelId);
+            mHasChanged = true;
+
             return true;
         }
 
         /**
          * Replaces an existing panel with a new {@code PanelConfig} instance.
          *
          * @return true if the item has been updated.
          */
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -4489,17 +4489,17 @@ Tab.prototype = {
           ViewportHandler.updateMetadata(this, true);
 
           // Note that if we draw without a display-port, things can go wrong. By the
           // time we execute this, it's almost certain a display-port has been set via
           // the MozScrolledAreaChanged event. If that didn't happen, the updateMetadata
           // call above does so at the end of the updateViewportSize function. As long
           // as that is happening, we don't need to do it again here.
 
-          if (contentDocument.mozSyntheticDocument) {
+          if (!this.restoredSessionZoom() && contentDocument.mozSyntheticDocument) {
             // for images, scale to fit width. this needs to happen *after* the call
             // to updateMetadata above, because that call sets the CSS viewport which
             // will affect the page size (i.e. contentDocument.body.scroll*) that we
             // use in this calculation. also we call sendViewportUpdate after changing
             // the resolution so that the display port gets recalculated appropriately.
             let fitZoom = Math.min(gScreenWidth / contentDocument.body.scrollWidth,
                                    gScreenHeight / contentDocument.body.scrollHeight);
             this.setResolution(fitZoom, false);
--- a/modules/libpref/public/nsIPrefBranch.idl
+++ b/modules/libpref/public/nsIPrefBranch.idl
@@ -65,17 +65,18 @@ interface nsIPrefBranch : nsISupports
   boolean getBoolPref(in string aPrefName);
 
   /**
    * Called to set the state of an individual boolean preference.
    *
    * @param aPrefName The boolean preference to set the state of.
    * @param aValue    The boolean value to set the preference to.
    *
-   * @throws Error if setting failed or the value is the wrong type.
+   * @throws Error if setting failed or the preference has a default
+             value of a type other than boolean.
    *
    * @see getBoolPref
    */
   void setBoolPref(in string aPrefName, in boolean aValue);
 
   /**
    * Called to get the state of an individual floating-point preference.
    * "Floating point" preferences are really string preferences that
@@ -101,17 +102,18 @@ interface nsIPrefBranch : nsISupports
   string getCharPref(in string aPrefName);
 
   /**
    * Called to set the state of an individual string preference.
    *
    * @param aPrefName The string preference to set.
    * @param aValue    The string value to set the preference to.
    *
-   * @throws Error if setting failed or the value is the wrong type.
+   * @throws Error if setting failed or the preference has a default
+             value of a type other than string.
    *
    * @see getCharPref
    */
   void setCharPref(in string aPrefName, in string aValue);
 
   /**
    * Called to get the state of an individual integer preference.
    *
@@ -124,17 +126,18 @@ interface nsIPrefBranch : nsISupports
   long getIntPref(in string aPrefName);
 
   /**
    * Called to set the state of an individual integer preference.
    *
    * @param aPrefName The integer preference to set the value of.
    * @param aValue    The integer value to set the preference to.
    *
-   * @throws Error if setting failed or the value is the wrong type.
+   * @throws Error if setting failed or the preference has a default
+             value of a type other than integer.
    *
    * @see getIntPref
    */
   void setIntPref(in string aPrefName, in long aValue);
 
   /**
    * Called to get the state of an individual complex preference. A complex
    * preference is a preference which represents an XPCOM object that can not
--- a/modules/libpref/src/prefapi.cpp
+++ b/modules/libpref/src/prefapi.cpp
@@ -701,29 +701,34 @@ static bool pref_ValueChanged(PrefValue 
     }
     else if (type & PREF_INT)
         changed = oldValue.intVal != newValue.intVal;
     else if (type & PREF_BOOL)
         changed = oldValue.boolVal != newValue.boolVal;
     return changed;
 }
 
-static void pref_SetValue(PrefValue* oldValue, PrefValue newValue, PrefType type)
+/*
+ * Overwrite the type and value of an existing preference. Caller must
+ * ensure that they are not changing the type of a preference that has
+ * a default value.
+ */
+static void pref_SetValue(PrefValue* existingValue, uint16_t *existingFlags,
+                          PrefValue newValue, PrefType newType)
 {
-    switch (type & PREF_VALUETYPE_MASK)
-    {
-        case PREF_STRING:
-            PR_ASSERT(newValue.stringVal);
-            if (oldValue->stringVal)
-                PL_strfree(oldValue->stringVal);
-            oldValue->stringVal = newValue.stringVal ? PL_strdup(newValue.stringVal) : nullptr;
-            break;
-
-        default:
-            *oldValue = newValue;
+    if ((*existingFlags & PREF_STRING) && existingValue->stringVal) {
+        PL_strfree(existingValue->stringVal);
+    }
+    *existingFlags = (*existingFlags & ~PREF_VALUETYPE_MASK) | newType;
+    if (newType & PREF_STRING) {
+        PR_ASSERT(newValue.stringVal);
+        existingValue->stringVal = newValue.stringVal ? PL_strdup(newValue.stringVal) : nullptr;
+    }
+    else {
+        *existingValue = newValue;
     }
     gDirty = true;
 }
 
 PrefHashEntry* pref_HashTableLookup(const void *key)
 {
 #ifndef MOZ_B2G
     MOZ_ASSERT(NS_IsMainThread());
@@ -747,66 +752,67 @@ nsresult pref_HashPref(const char *key, 
     if (!gHashTable.ops)
         return NS_ERROR_OUT_OF_MEMORY;
 
     PrefHashEntry* pref = static_cast<PrefHashEntry*>(PL_DHashTableOperate(&gHashTable, key, PL_DHASH_ADD));
 
     if (!pref)
         return NS_ERROR_OUT_OF_MEMORY;
 
-    // new entry, better intialize
+    // new entry, better initialize
     if (!pref->key) {
 
         // initialize the pref entry
         pref->flags = type;
         pref->key = ArenaStrDup(key, &gPrefNameArena);
         memset(&pref->defaultPref, 0, sizeof(pref->defaultPref));
         memset(&pref->userPref, 0, sizeof(pref->userPref));
     }
-    else if ((((PrefType)(pref->flags)) & PREF_VALUETYPE_MASK) !=
-                 (type & PREF_VALUETYPE_MASK))
+    else if ((pref->flags & PREF_HAS_DEFAULT) && PREF_TYPE(pref) != type)
     {
-        NS_WARNING(nsPrintfCString("Trying to set pref %s to with the wrong type!", key).get());
+        NS_WARNING(nsPrintfCString("Trying to overwrite value of default pref %s with the wrong type!", key).get());
         return NS_ERROR_UNEXPECTED;
     }
 
     bool valueChanged = false;
     if (flags & kPrefSetDefault)
     {
         if (!PREF_IS_LOCKED(pref))
         {       /* ?? change of semantics? */
             if (pref_ValueChanged(pref->defaultPref, value, type) ||
                 !(pref->flags & PREF_HAS_DEFAULT))
             {
-                pref_SetValue(&pref->defaultPref, value, type);
+                pref_SetValue(&pref->defaultPref, &pref->flags, value, type);
                 pref->flags |= PREF_HAS_DEFAULT;
                 if (!PREF_HAS_USER_VALUE(pref))
                     valueChanged = true;
             }
         }
     }
     else
     {
         /* If new value is same as the default value, then un-set the user value.
            Otherwise, set the user value only if it has changed */
-        if (!pref_ValueChanged(pref->defaultPref, value, type) &&
-            (pref->flags & PREF_HAS_DEFAULT) &&
+        if ((pref->flags & PREF_HAS_DEFAULT) &&
+            !pref_ValueChanged(pref->defaultPref, value, type) &&
             !(flags & kPrefForceSet))
         {
             if (PREF_HAS_USER_VALUE(pref))
             {
+                /* XXX should we free a user-set string value if there is one? */
                 pref->flags &= ~PREF_USERSET;
                 if (!PREF_IS_LOCKED(pref))
                     valueChanged = true;
             }
         }
-        else if ( !PREF_HAS_USER_VALUE(pref) ||
-                   pref_ValueChanged(pref->userPref, value, type) )
+        else if (!PREF_HAS_USER_VALUE(pref) ||
+                 PREF_TYPE(pref) != type ||
+                 pref_ValueChanged(pref->userPref, value, type) )
         {
-            pref_SetValue(&pref->userPref, value, type);
+            pref_SetValue(&pref->userPref, &pref->flags, value, type);
             pref->flags |= PREF_USERSET;
             if (!PREF_IS_LOCKED(pref))
                 valueChanged = true;
         }
     }
 
     nsresult rv = NS_OK;
     if (valueChanged) {
new file mode 100644
--- /dev/null
+++ b/modules/libpref/test/unit/test_changeType.js
@@ -0,0 +1,63 @@
+/* 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/. */
+
+/* Tests for changing the type of a preference (bug 985998) */
+
+const PREF_INVALID = 0;
+const PREF_BOOL    = 128;
+const PREF_INT     = 64;
+const PREF_STRING  = 32;
+
+function run_test() {
+
+  var ps = Cc["@mozilla.org/preferences-service;1"].
+            getService(Ci.nsIPrefService);
+
+  let defaultBranch = ps.getDefaultBranch("");
+  let userBranch = ps.getBranch("");
+
+  //**************************************************************************//
+  // Can't change the type of prefs that have default values
+
+  defaultBranch.setBoolPref("TypeTest.existing.bool", true);
+  defaultBranch.setIntPref("TypeTest.existing.int", 23);
+  defaultBranch.setCharPref("TypeTest.existing.char", "hey");
+
+  // The user branch reads back the expected default
+  do_check_eq(userBranch.getBoolPref("TypeTest.existing.bool"), true);
+  do_check_eq(userBranch.getIntPref("TypeTest.existing.int"), 23);
+  do_check_eq(userBranch.getCharPref("TypeTest.existing.char"), "hey");
+
+  // All the combinations of attempted type changes
+  do_check_throws(function() {
+    userBranch.setCharPref("TypeTest.existing.bool", "boo"); }, Cr.NS_ERROR_UNEXPECTED);
+  do_check_throws(function() {
+    userBranch.setIntPref("TypeTest.existing.bool", 5); }, Cr.NS_ERROR_UNEXPECTED);
+  do_check_throws(function() {
+    userBranch.setCharPref("TypeTest.existing.int", "boo"); }, Cr.NS_ERROR_UNEXPECTED);
+  do_check_throws(function() {
+    userBranch.setBoolPref("TypeTest.existing.int", true); }, Cr.NS_ERROR_UNEXPECTED);
+  do_check_throws(function() {
+    userBranch.setBoolPref("TypeTest.existing.char", true); }, Cr.NS_ERROR_UNEXPECTED);
+  do_check_throws(function() {
+    userBranch.setIntPref("TypeTest.existing.char", 6); }, Cr.NS_ERROR_UNEXPECTED);
+
+
+  //**************************************************************************//
+  // Prefs that don't have default values can mutate
+  let pref = "TypeTest.user";
+  userBranch.setBoolPref(pref, true);
+  userBranch.setCharPref(pref, "yay");
+  do_check_eq(userBranch.getCharPref(pref), "yay");
+  userBranch.setIntPref(pref, 7);
+  do_check_eq(userBranch.getIntPref(pref), 7);
+  userBranch.setBoolPref(pref, false);
+  do_check_eq(userBranch.getBoolPref(pref), false);
+  userBranch.setIntPref(pref, 8);
+  do_check_eq(userBranch.getIntPref(pref), 8);
+  userBranch.setCharPref(pref, "whee");
+  do_check_eq(userBranch.getCharPref(pref), "whee");
+  userBranch.setBoolPref(pref, true);
+  do_check_eq(userBranch.getBoolPref(pref), true);
+}
--- a/modules/libpref/test/unit/xpcshell.ini
+++ b/modules/libpref/test/unit/xpcshell.ini
@@ -5,10 +5,11 @@ support-files =
   data/testPref.js
   extdata/testExt.js
 
 [test_warnings.js]
 [test_bug345529.js]
 [test_bug506224.js]
 [test_bug577950.js]
 [test_bug790374.js]
+[test_changeType.js]
 [test_extprefs.js]
 [test_libPrefs.js]
--- a/storage/src/StorageBaseStatementInternal.cpp
+++ b/storage/src/StorageBaseStatementInternal.cpp
@@ -189,18 +189,18 @@ StorageBaseStatementInternal::ExecuteAsy
   // actual logic is very simple, we now roll our own.
   nsTArray<StatementData> stmts(1);
   StatementData data;
   nsresult rv = getAsynchronousStatementData(data);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(stmts.AppendElement(data), NS_ERROR_OUT_OF_MEMORY);
 
   // Dispatch to the background
-  return AsyncExecuteStatements::execute(stmts, mDBConnection, aCallback,
-                                         _stmt);
+  return AsyncExecuteStatements::execute(stmts, mDBConnection,
+                                         mNativeConnection, aCallback, _stmt);
 }
 
 NS_IMETHODIMP
 StorageBaseStatementInternal::EscapeStringForLIKE(
   const nsAString &aValue,
   const char16_t aEscapeChar,
   nsAString &_escapedString
 )
--- a/storage/src/StorageBaseStatementInternal.h
+++ b/storage/src/StorageBaseStatementInternal.h
@@ -6,16 +6,17 @@
 
 #ifndef mozilla_storage_StorageBaseStatementInternal_h_
 #define mozilla_storage_StorageBaseStatementInternal_h_
 
 #include "nsISupports.h"
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 
+struct sqlite3;
 struct sqlite3_stmt;
 class mozIStorageError;
 class mozIStorageBindingParamsArray;
 class mozIStorageBindingParams;
 class mozIStorageStatementCallback;
 class mozIStoragePendingStatement;
 
 namespace mozilla {
@@ -92,16 +93,17 @@ public:
   virtual already_AddRefed<mozIStorageBindingParams> newBindingParams(
     mozIStorageBindingParamsArray *aOwner
   ) = 0;
 
 protected: // mix-in bits are protected
   StorageBaseStatementInternal();
 
   nsRefPtr<Connection> mDBConnection;
+  sqlite3 *mNativeConnection;
 
   /**
    * Our asynchronous statement.
    *
    * For Statement this is populated by the first invocation to
    * getAsyncStatement.
    *
    * For AsyncStatement, this is null at creation time and initialized by the
--- a/storage/src/mozStorageAsyncStatement.cpp
+++ b/storage/src/mozStorageAsyncStatement.cpp
@@ -126,23 +126,25 @@ static AsyncStatementClassInfo sAsyncSta
 AsyncStatement::AsyncStatement()
 : StorageBaseStatementInternal()
 , mFinalized(false)
 {
 }
 
 nsresult
 AsyncStatement::initialize(Connection *aDBConnection,
+                           sqlite3 *aNativeConnection,
                            const nsACString &aSQLStatement)
 {
-  NS_ASSERTION(aDBConnection, "No database connection given!");
-  NS_ASSERTION(aDBConnection->GetNativeConnection(),
-               "We should never be called with a null sqlite3 database!");
+  MOZ_ASSERT(aDBConnection, "No database connection given!");
+  MOZ_ASSERT(!aDBConnection->isClosed(), "Database connection should be valid");
+  MOZ_ASSERT(aNativeConnection, "No native connection given!");
 
   mDBConnection = aDBConnection;
+  mNativeConnection = aNativeConnection;
   mSQLString = aSQLStatement;
 
   PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Inited async statement '%s' (0x%p)",
                                       mSQLString.get()));
 
 #ifdef DEBUG
   // We want to try and test for LIKE and that consumers are using
   // escapeStringForLIKE instead of just trusting user input.  The idea to
@@ -295,21 +297,22 @@ AsyncStatement::getAsyncStatement(sqlite
   // Make sure we are never called on the connection's owning thread.
   bool onOpenedThread = false;
   (void)mDBConnection->threadOpenedOn->IsOnCurrentThread(&onOpenedThread);
   NS_ASSERTION(!onOpenedThread,
                "We should only be called on the async thread!");
 #endif
 
   if (!mAsyncStatement) {
-    int rc = mDBConnection->prepareStatement(mSQLString, &mAsyncStatement);
+    int rc = mDBConnection->prepareStatement(mNativeConnection, mSQLString,
+                                             &mAsyncStatement);
     if (rc != SQLITE_OK) {
       PR_LOG(gStorageLog, PR_LOG_ERROR,
              ("Sqlite statement prepare error: %d '%s'", rc,
-              ::sqlite3_errmsg(mDBConnection->GetNativeConnection())));
+              ::sqlite3_errmsg(mNativeConnection)));
       PR_LOG(gStorageLog, PR_LOG_ERROR,
              ("Statement was: '%s'", mSQLString.get()));
       *_stmt = nullptr;
       return rc;
     }
     PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Initialized statement '%s' (0x%p)",
                                         mSQLString.get(),
                                         mAsyncStatement));
--- a/storage/src/mozStorageAsyncStatement.h
+++ b/storage/src/mozStorageAsyncStatement.h
@@ -40,20 +40,23 @@ public:
   AsyncStatement();
 
   /**
    * Initializes the object on aDBConnection by preparing the SQL statement
    * given by aSQLStatement.
    *
    * @param aDBConnection
    *        The Connection object this statement is associated with.
+   * @param aNativeConnection
+   *        The native Sqlite connection this statement is associated with.
    * @param aSQLStatement
    *        The SQL statement to prepare that this object will represent.
    */
   nsresult initialize(Connection *aDBConnection,
+                      sqlite3 *aNativeConnection,
                       const nsACString &aSQLStatement);
 
   /**
    * Obtains and transfers ownership of the array of parameters that are bound
    * to this statment.  This can be null.
    */
   inline already_AddRefed<BindingParamsArray> bindingParamsArray()
   {
--- a/storage/src/mozStorageAsyncStatementExecution.cpp
+++ b/storage/src/mozStorageAsyncStatementExecution.cpp
@@ -158,22 +158,24 @@ private:
 
 ////////////////////////////////////////////////////////////////////////////////
 //// AsyncExecuteStatements
 
 /* static */
 nsresult
 AsyncExecuteStatements::execute(StatementDataArray &aStatements,
                                 Connection *aConnection,
+                                sqlite3 *aNativeConnection,
                                 mozIStorageStatementCallback *aCallback,
                                 mozIStoragePendingStatement **_stmt)
 {
   // Create our event to run in the background
   nsRefPtr<AsyncExecuteStatements> event =
-    new AsyncExecuteStatements(aStatements, aConnection, aCallback);
+    new AsyncExecuteStatements(aStatements, aConnection, aNativeConnection,
+                               aCallback);
   NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
 
   // Dispatch it to the background
   nsIEventTarget *target = aConnection->getAsyncExecutionTarget();
 
   // If we don't have a valid target, this is a bug somewhere else. In the past,
   // this assert found cases where a Run method would schedule a new statement
   // without checking if asyncClose had been called. The caller must prevent
@@ -189,34 +191,41 @@ AsyncExecuteStatements::execute(Statemen
 
   // Return it as the pending statement object and track it.
   NS_ADDREF(*_stmt = event);
   return NS_OK;
 }
 
 AsyncExecuteStatements::AsyncExecuteStatements(StatementDataArray &aStatements,
                                                Connection *aConnection,
+                                               sqlite3 *aNativeConnection,
                                                mozIStorageStatementCallback *aCallback)
 : mConnection(aConnection)
-, mTransactionManager(nullptr)
+, mNativeConnection(aNativeConnection)
+, mHasTransaction(false)
 , mCallback(aCallback)
 , mCallingThread(::do_GetCurrentThread())
 , mMaxWait(TimeDuration::FromMilliseconds(MAX_MILLISECONDS_BETWEEN_RESULTS))
 , mIntervalStart(TimeStamp::Now())
 , mState(PENDING)
 , mCancelRequested(false)
 , mMutex(aConnection->sharedAsyncExecutionMutex)
 , mDBMutex(aConnection->sharedDBMutex)
   , mRequestStartDate(TimeStamp::Now())
 {
   (void)mStatements.SwapElements(aStatements);
   NS_ASSERTION(mStatements.Length(), "We weren't given any statements!");
   NS_IF_ADDREF(mCallback);
 }
 
+AsyncExecuteStatements::~AsyncExecuteStatements()
+{
+  MOZ_ASSERT(!mHasTransaction, "There should be no transaction at this point");
+}
+
 bool
 AsyncExecuteStatements::shouldNotify()
 {
 #ifdef DEBUG
   mMutex.AssertNotCurrentThreadOwns();
 
   bool onCallingThread = false;
   (void)mCallingThread->IsOnCurrentThread(&onCallingThread);
@@ -328,17 +337,17 @@ bool
 AsyncExecuteStatements::executeStatement(sqlite3_stmt *aStatement)
 {
   mMutex.AssertNotCurrentThreadOwns();
   Telemetry::AutoTimer<Telemetry::MOZ_STORAGE_ASYNC_REQUESTS_MS> finallySendExecutionDuration(mRequestStartDate);
   while (true) {
     // lock the sqlite mutex so sqlite3_errmsg cannot change
     SQLiteMutexAutoLock lockedScope(mDBMutex);
 
-    int rc = mConnection->stepStatement(aStatement);
+    int rc = mConnection->stepStatement(mNativeConnection, aStatement);
     // Stop if we have no more results.
     if (rc == SQLITE_DONE)
     {
       Telemetry::Accumulate(Telemetry::MOZ_STORAGE_ASYNC_REQUESTS_SUCCESS, true);
       return false;
     }
 
     // If we got results, we can return now.
@@ -359,18 +368,19 @@ AsyncExecuteStatements::executeStatement
     }
 
     // Set an error state.
     mState = ERROR;
     Telemetry::Accumulate(Telemetry::MOZ_STORAGE_ASYNC_REQUESTS_SUCCESS, false);
 
     // Construct the error message before giving up the mutex (which we cannot
     // hold during the call to notifyError).
-    sqlite3 *db = mConnection->GetNativeConnection();
-    nsCOMPtr<mozIStorageError> errorObj(new Error(rc, ::sqlite3_errmsg(db)));
+    nsCOMPtr<mozIStorageError> errorObj(
+      new Error(rc, ::sqlite3_errmsg(mNativeConnection))
+    );
     // We cannot hold the DB mutex while calling notifyError.
     SQLiteMutexAutoUnlock unlockedScope(mDBMutex);
     (void)notifyError(errorObj);
 
     // Finally, indicate that we should stop processing.
     return false;
   }
 }
@@ -428,30 +438,29 @@ AsyncExecuteStatements::notifyComplete()
 
   // Release references to the statement data as soon as possible. If this
   // is the last reference, statements will be finalized immediately on the
   // async thread, hence avoiding several bounces between threads and possible
   // race conditions with AsyncClose().
   mStatements.Clear();
 
   // Handle our transaction, if we have one
-  if (mTransactionManager) {
+  if (mHasTransaction) {
     if (mState == COMPLETED) {
-      nsresult rv = mTransactionManager->Commit();
+      nsresult rv = mConnection->commitTransactionInternal(mNativeConnection);
       if (NS_FAILED(rv)) {
         mState = ERROR;
         (void)notifyError(mozIStorageError::ERROR,
                           "Transaction failed to commit");
       }
     }
     else {
-      (void)mTransactionManager->Rollback();
+      NS_WARN_IF(NS_FAILED(mConnection->rollbackTransactionInternal(mNativeConnection)));
     }
-    delete mTransactionManager;
-    mTransactionManager = nullptr;
+    mHasTransaction = false;
   }
 
   // Always generate a completion notification; it is what guarantees that our
   // destruction does not happen here on the async thread.
   nsRefPtr<CompletionNotifier> completionEvent =
     new CompletionNotifier(mCallback, mState);
 
   // We no longer own mCallback (the CompletionNotifier takes ownership).
@@ -558,48 +567,56 @@ AsyncExecuteStatements::Cancel()
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// nsIRunnable
 
 NS_IMETHODIMP
 AsyncExecuteStatements::Run()
 {
+  MOZ_ASSERT(!mConnection->isClosed());
+
   // Do not run if we have been canceled.
   {
     MutexAutoLock lockedScope(mMutex);
     if (mCancelRequested)
       mState = CANCELED;
   }
   if (mState == CANCELED)
     return notifyComplete();
 
   if (statementsNeedTransaction()) {
     Connection* rawConnection = static_cast<Connection*>(mConnection.get());
-    mTransactionManager = new mozStorageAsyncTransaction(rawConnection, false,
-                                                         mozIStorageConnection::TRANSACTION_IMMEDIATE);
+    if (NS_SUCCEEDED(mConnection->beginTransactionInternal(mNativeConnection,
+                                                           mozIStorageConnection::TRANSACTION_IMMEDIATE))) {
+      mHasTransaction = true;
+    }
+#ifdef DEBUG
+    else {
+      NS_WARNING("Unable to create a transaction for async execution.");
+    }
+#endif
   }
 
   // Execute each statement, giving the callback results if it returns any.
   for (uint32_t i = 0; i < mStatements.Length(); i++) {
     bool finished = (i == (mStatements.Length() - 1));
 
     sqlite3_stmt *stmt;
     { // lock the sqlite mutex so sqlite3_errmsg cannot change
       SQLiteMutexAutoLock lockedScope(mDBMutex);
 
       int rc = mStatements[i].getSqliteStatement(&stmt);
       if (rc != SQLITE_OK) {
         // Set our error state.
         mState = ERROR;
 
         // Build the error object; can't call notifyError with the lock held
-        sqlite3 *db = mConnection->GetNativeConnection();
         nsCOMPtr<mozIStorageError> errorObj(
-          new Error(rc, ::sqlite3_errmsg(db))
+          new Error(rc, ::sqlite3_errmsg(mNativeConnection))
         );
         {
           // We cannot hold the DB mutex and call notifyError.
           SQLiteMutexAutoUnlock unlockedScope(mDBMutex);
           (void)notifyError(errorObj);
         }
         break;
       }
--- a/storage/src/mozStorageAsyncStatementExecution.h
+++ b/storage/src/mozStorageAsyncStatementExecution.h
@@ -24,24 +24,16 @@ struct sqlite3_stmt;
 
 namespace mozilla {
 namespace storage {
 
 class Connection;
 class ResultSet;
 class StatementData;
 
-/**
- * An instance of the mozStorageTransaction<> family dedicated
- * to concrete class |Connection|.
- */
-typedef mozStorageTransactionBase<mozilla::storage::Connection,
-                                  nsRefPtr<mozilla::storage::Connection> >
-    mozStorageAsyncTransaction;
-
 class AsyncExecuteStatements MOZ_FINAL : public nsIRunnable
                                        , public mozIStoragePendingStatement
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIRUNNABLE
   NS_DECL_MOZISTORAGEPENDINGSTATEMENT
 
@@ -61,23 +53,26 @@ public:
    * Executes a statement in the background, and passes results back to the
    * caller.
    *
    * @param aStatements
    *        The statements to execute and possibly bind in the background.
    *        Ownership is transfered from the caller.
    * @param aConnection
    *        The connection that created the statements to execute.
+   * @param aNativeConnection
+   *        The native Sqlite connection that created the statements to execute.
    * @param aCallback
    *        The callback that is notified of results, completion, and errors.
    * @param _stmt
    *        The handle to control the execution of the statements.
    */
   static nsresult execute(StatementDataArray &aStatements,
                           Connection *aConnection,
+                          sqlite3 *aNativeConnection,
                           mozIStorageStatementCallback *aCallback,
                           mozIStoragePendingStatement **_stmt);
 
   /**
    * Indicates when events on the calling thread should run or not.  Certain
    * events posted back to the calling thread should call this see if they
    * should run or not.
    *
@@ -85,17 +80,19 @@ public:
    *
    * @returns true if the event should notify still, false otherwise.
    */
   bool shouldNotify();
 
 private:
   AsyncExecuteStatements(StatementDataArray &aStatements,
                          Connection *aConnection,
+                         sqlite3 *aNativeConnection,
                          mozIStorageStatementCallback *aCallback);
+  ~AsyncExecuteStatements();
 
   /**
    * Binds and then executes a given statement until completion, an error
    * occurs, or we are canceled.  If aLastStatement is true, we should set
    * mState accordingly.
    *
    * @pre mMutex is not held
    *
@@ -182,17 +179,18 @@ private:
    * transaction.
    *
    * @return true if an explicit transaction is needed, false otherwise.
    */
   bool statementsNeedTransaction();
 
   StatementDataArray mStatements;
   nsRefPtr<Connection> mConnection;
-  mozStorageAsyncTransaction *mTransactionManager;
+  sqlite3 *mNativeConnection;
+  bool mHasTransaction;
   mozIStorageStatementCallback *mCallback;
   nsCOMPtr<nsIThread> mCallingThread;
   nsRefPtr<ResultSet> mResultSet;
 
   /**
    * The maximum amount of time we want to wait between results.  Defined by
    * MAX_MILLISECONDS_BETWEEN_RESULTS and set at construction.
    */
--- a/storage/src/mozStorageConnection.cpp
+++ b/storage/src/mozStorageConnection.cpp
@@ -328,35 +328,37 @@ WaitForUnlockNotify(sqlite3* aDatabase)
 //// Local Classes
 
 namespace {
 
 class AsyncCloseConnection MOZ_FINAL: public nsRunnable
 {
 public:
   AsyncCloseConnection(Connection *aConnection,
+                       sqlite3 *aNativeConnection,
                        nsIRunnable *aCallbackEvent,
                        already_AddRefed<nsIThread> aAsyncExecutionThread)
   : mConnection(aConnection)
+  , mNativeConnection(aNativeConnection)
   , mCallbackEvent(aCallbackEvent)
   , mAsyncExecutionThread(aAsyncExecutionThread)
   {
   }
 
   NS_METHOD Run()
   {
 #ifdef DEBUG
     // This code is executed on the background thread
     bool onAsyncThread = false;
     (void)mAsyncExecutionThread->IsOnCurrentThread(&onAsyncThread);
     MOZ_ASSERT(onAsyncThread);
 #endif // DEBUG
 
     // Internal close.
-    (void)mConnection->internalClose();
+    (void)mConnection->internalClose(mNativeConnection);
 
     // Callback
     if (mCallbackEvent) {
       nsCOMPtr<nsIThread> thread;
       (void)NS_GetMainThread(getter_AddRefs(thread));
       (void)thread->Dispatch(mCallbackEvent, NS_DISPATCH_NORMAL);
     }
 
@@ -371,16 +373,17 @@ public:
     mConnection.swap(rawConnection);
     (void)NS_ProxyRelease(thread,
                           NS_ISUPPORTS_CAST(mozIStorageConnection *,
                                             rawConnection));
     (void)NS_ProxyRelease(thread, mCallbackEvent);
   }
 private:
   nsRefPtr<Connection> mConnection;
+  sqlite3 *mNativeConnection;
   nsCOMPtr<nsIRunnable> mCallbackEvent;
   nsCOMPtr<nsIThread> mAsyncExecutionThread;
 };
 
 /**
  * An event used to initialize the clone of a connection.
  *
  * Must be executed on the clone's async execution thread.
@@ -465,16 +468,17 @@ private:
 Connection::Connection(Service *aService,
                        int aFlags,
                        bool aAsyncOnly)
 : sharedAsyncExecutionMutex("Connection::sharedAsyncExecutionMutex")
 , sharedDBMutex("Connection::sharedDBMutex")
 , threadOpenedOn(do_GetCurrentThread())
 , mDBConn(nullptr)
 , mAsyncExecutionThreadShuttingDown(false)
+, mConnectionClosed(false)
 , mTransactionInProgress(false)
 , mProgressHandler(nullptr)
 , mFlags(aFlags)
 , mStorageService(aService)
 , mAsyncOnly(aAsyncOnly)
 {
   mStorageService->registerConnection(this);
 }
@@ -514,16 +518,28 @@ NS_IMETHODIMP_(MozExternalRefCountType) 
     NS_ASSERT_OWNINGTHREAD(Connection);
 #endif
     delete (this);
     return 0;
   }
   return count;
 }
 
+int32_t
+Connection::getSqliteRuntimeStatus(int32_t aStatusOption, int32_t* aMaxValue)
+{
+  MOZ_ASSERT(mDBConn, "A connection must exist at this point");
+  int curr = 0, max = 0;
+  DebugOnly<int> rc = ::sqlite3_db_status(mDBConn, aStatusOption, &curr, &max, 0);
+  MOZ_ASSERT(NS_SUCCEEDED(convertResultCode(rc)));
+  if (aMaxValue)
+    *aMaxValue = max;
+  return curr;
+}
+
 nsIEventTarget *
 Connection::getAsyncExecutionTarget()
 {
   MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
 
   // If we are shutting down the asynchronous thread, don't hand out any more
   // references to the thread.
   if (mAsyncExecutionThreadShuttingDown)
@@ -649,17 +665,17 @@ Connection::initializeInternal(nsIFile* 
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Setting the cache_size forces the database open, verifying if it is valid
   // or corrupt.  So this is executed regardless it being actually needed.
   // The cache_size is calculated from the actual page_size, to save memory.
   nsAutoCString cacheSizeQuery(MOZ_STORAGE_UNIQUIFY_QUERY_STR
                                "PRAGMA cache_size = ");
   cacheSizeQuery.AppendInt(-MAX_CACHE_SIZE_KIBIBYTES);
-  int srv = executeSql(cacheSizeQuery.get());
+  int srv = executeSql(mDBConn, cacheSizeQuery.get());
   if (srv != SQLITE_OK) {
     ::sqlite3_close(mDBConn);
     mDBConn = nullptr;
     return convertResultCode(srv);
   }
 
   // Register our built-in SQL functions.
   srv = registerFunctions(mDBConn);
@@ -727,21 +743,21 @@ Connection::databaseElementExists(enum D
       query.Append("table");
       break;
   }
   query.Append("' AND name ='");
   query.Append(element);
   query.Append("'");
 
   sqlite3_stmt *stmt;
-  int srv = prepareStatement(query, &stmt);
+  int srv = prepareStatement(mDBConn, query, &stmt);
   if (srv != SQLITE_OK)
     return convertResultCode(srv);
 
-  srv = stepStatement(stmt);
+  srv = stepStatement(mDBConn, stmt);
   // we just care about the return value from step
   (void)::sqlite3_finalize(stmt);
 
   if (srv == SQLITE_ROW) {
     *_exists = true;
     return NS_OK;
   }
   if (srv == SQLITE_DONE) {
@@ -796,70 +812,87 @@ Connection::setClosedState()
   // Flag that we are shutting down the async thread, so that
   // getAsyncExecutionTarget knows not to expose/create the async thread.
   {
     MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
     NS_ENSURE_FALSE(mAsyncExecutionThreadShuttingDown, NS_ERROR_UNEXPECTED);
     mAsyncExecutionThreadShuttingDown = true;
   }
 
+  // Set the property to null before closing the connection, otherwise the other
+  // functions in the module may try to use the connection after it is closed.
+  mDBConn = nullptr;
+
   return NS_OK;
 }
 
 bool
-Connection::isClosing(bool aResultOnClosed) {
+Connection::connectionReady()
+{
+  return mDBConn != nullptr;
+}
+
+bool
+Connection::isClosing()
+{
+  bool shuttingDown = false;
+  {
+    MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
+    shuttingDown = mAsyncExecutionThreadShuttingDown;
+  }
+  return shuttingDown && !isClosed();
+}
+
+bool
+Connection::isClosed()
+{
   MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
-  return mAsyncExecutionThreadShuttingDown &&
-    (aResultOnClosed || ConnectionReady());
+  return mConnectionClosed;
 }
 
 nsresult
-Connection::internalClose()
+Connection::internalClose(sqlite3 *aNativeConnection)
 {
+  // Sanity checks to make sure we are in the proper state before calling this.
+  MOZ_ASSERT(aNativeConnection, "Database connection is invalid!");
+  MOZ_ASSERT(!isClosed());
+
 #ifdef DEBUG
-  // Sanity checks to make sure we are in the proper state before calling this.
-  NS_ASSERTION(mDBConn, "Database connection is already null!");
-
   { // Make sure we have marked our async thread as shutting down.
     MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
     NS_ASSERTION(mAsyncExecutionThreadShuttingDown,
                  "Did not call setClosedState!");
   }
-
-  bool onOpeningThread = false;
-  (void)threadOpenedOn->IsOnCurrentThread(&onOpeningThread);
 #endif // DEBUG
 
 #ifdef PR_LOGGING
   nsAutoCString leafName(":memory");
   if (mDatabaseFile)
       (void)mDatabaseFile->GetNativeLeafName(leafName);
   PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Closing connection to '%s'",
                                       leafName.get()));
 #endif
 
-  // Set the property to null before closing the connection, otherwise the other
-  // functions in the module may try to use the connection after it is closed.
-  sqlite3 *dbConn = mDBConn;
-  mDBConn = nullptr;
-
   // At this stage, we may still have statements that need to be
   // finalized. Attempt to close the database connection. This will
   // always disconnect any virtual tables and cleanly finalize their
   // internal statements. Once this is done, closing may fail due to
   // unfinalized client statements, in which case we need to finalize
   // these statements and close again.
-
-  int srv = sqlite3_close(dbConn);
+  {
+    MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
+    mConnectionClosed = true;
+  }
+  int srv = sqlite3_close(aNativeConnection);
 
   if (srv == SQLITE_BUSY) {
     // We still have non-finalized statements. Finalize them.
 
     sqlite3_stmt *stmt = nullptr;
-    while ((stmt = ::sqlite3_next_stmt(dbConn, stmt))) {
+    while ((stmt = ::sqlite3_next_stmt(aNativeConnection, stmt))) {
       PR_LOG(gStorageLog, PR_LOG_NOTICE,
              ("Auto-finalizing SQL statement '%s' (%x)",
               ::sqlite3_sql(stmt),
               stmt));
 
 #ifdef DEBUG
       char *msg = ::PR_smprintf("SQL statement '%s' (%x) should have been finalized before closing the connection",
                                 ::sqlite3_sql(stmt),
@@ -884,17 +917,17 @@ Connection::internalClose()
       // or not.
       if (srv == SQLITE_OK) {
         stmt = nullptr;
       }
     }
 
     // Now that all statements have been finalized, we
     // should be able to close.
-    srv = ::sqlite3_close(dbConn);
+    srv = ::sqlite3_close(aNativeConnection);
 
   }
 
   if (srv != SQLITE_OK) {
     MOZ_ASSERT(srv == SQLITE_OK,
                "sqlite3_close failed. There are probably outstanding statements that are listed above!");
   }
 
@@ -907,45 +940,43 @@ Connection::getFilename()
   nsCString leafname(":memory:");
   if (mDatabaseFile) {
     (void)mDatabaseFile->GetNativeLeafName(leafname);
   }
   return leafname;
 }
 
 int
-Connection::stepStatement(sqlite3_stmt *aStatement)
+Connection::stepStatement(sqlite3 *aNativeConnection, sqlite3_stmt *aStatement)
 {
   MOZ_ASSERT(aStatement);
   bool checkedMainThread = false;
   TimeStamp startTime = TimeStamp::Now();
 
-  // mDBConn may be null if the executing statement has been created and cached
-  // after a call to asyncClose() but before the connection has been nullified
-  // by internalClose().  In such a case closing the connection fails due to
-  // the existence of prepared statements, but mDBConn is set to null
-  // regardless. This usually happens when other tasks using cached statements
-  // are asynchronously scheduled for execution and any of them ends up after
-  // asyncClose. See bug 728653 for details.
-  if (!mDBConn)
+  // The connection may have been closed if the executing statement has been
+  // created and cached after a call to asyncClose() but before the actual
+  // sqlite3_close().  This usually happens when other tasks using cached
+  // statements are asynchronously scheduled for execution and any of them ends
+  // up after asyncClose. See bug 728653 for details.
+  if (isClosed())
     return SQLITE_MISUSE;
 
-  (void)::sqlite3_extended_result_codes(mDBConn, 1);
+  (void)::sqlite3_extended_result_codes(aNativeConnection, 1);
 
   int srv;
   while ((srv = ::sqlite3_step(aStatement)) == SQLITE_LOCKED_SHAREDCACHE) {
     if (!checkedMainThread) {
       checkedMainThread = true;
       if (::NS_IsMainThread()) {
         NS_WARNING("We won't allow blocking on the main thread!");
         break;
       }
     }
 
-    srv = WaitForUnlockNotify(mDBConn);
+    srv = WaitForUnlockNotify(aNativeConnection);
     if (srv != SQLITE_OK) {
       break;
     }
 
     ::sqlite3_reset(aStatement);
   }
 
   // Report very slow SQL statements to Telemetry
@@ -954,84 +985,90 @@ Connection::stepStatement(sqlite3_stmt *
     NS_IsMainThread() ? Telemetry::kSlowSQLThresholdForMainThread
                       : Telemetry::kSlowSQLThresholdForHelperThreads;
   if (duration.ToMilliseconds() >= threshold) {
     nsDependentCString statementString(::sqlite3_sql(aStatement));
     Telemetry::RecordSlowSQLStatement(statementString, getFilename(),
                                       duration.ToMilliseconds());
   }
 
-  (void)::sqlite3_extended_result_codes(mDBConn, 0);
+  (void)::sqlite3_extended_result_codes(aNativeConnection, 0);
   // Drop off the extended result bits of the result code.
   return srv & 0xFF;
 }
 
 int
-Connection::prepareStatement(const nsCString &aSQL,
+Connection::prepareStatement(sqlite3 *aNativeConnection, const nsCString &aSQL,
                              sqlite3_stmt **_stmt)
 {
+  // We should not even try to prepare statements after the connection has
+  // been closed.
+  if (isClosed())
+    return SQLITE_MISUSE;
+
   bool checkedMainThread = false;
 
-  (void)::sqlite3_extended_result_codes(mDBConn, 1);
+  (void)::sqlite3_extended_result_codes(aNativeConnection, 1);
 
   int srv;
-  while((srv = ::sqlite3_prepare_v2(mDBConn,
+  while((srv = ::sqlite3_prepare_v2(aNativeConnection,
                                     aSQL.get(),
                                     -1,
                                     _stmt,
                                     nullptr)) == SQLITE_LOCKED_SHAREDCACHE) {
     if (!checkedMainThread) {
       checkedMainThread = true;
       if (::NS_IsMainThread()) {
         NS_WARNING("We won't allow blocking on the main thread!");
         break;
       }
     }
 
-    srv = WaitForUnlockNotify(mDBConn);
+    srv = WaitForUnlockNotify(aNativeConnection);
     if (srv != SQLITE_OK) {
       break;
     }
   }
 
   if (srv != SQLITE_OK) {
     nsCString warnMsg;
     warnMsg.AppendLiteral("The SQL statement '");
     warnMsg.Append(aSQL);
     warnMsg.AppendLiteral("' could not be compiled due to an error: ");
-    warnMsg.Append(::sqlite3_errmsg(mDBConn));
+    warnMsg.Append(::sqlite3_errmsg(aNativeConnection));
 
 #ifdef DEBUG
     NS_WARNING(warnMsg.get());
 #endif
     PR_LOG(gStorageLog, PR_LOG_ERROR, ("%s", warnMsg.get()));
   }
 
-  (void)::sqlite3_extended_result_codes(mDBConn, 0);
+  (void)::sqlite3_extended_result_codes(aNativeConnection, 0);
   // Drop off the extended result bits of the result code.
   int rc = srv & 0xFF;
   // sqlite will return OK on a comment only string and set _stmt to nullptr.
   // The callers of this function are used to only checking the return value,
   // so it is safer to return an error code.
   if (rc == SQLITE_OK && *_stmt == nullptr) {
     return SQLITE_MISUSE;
   }
 
   return rc;
 }
 
 
 int
-Connection::executeSql(const char *aSqlString)
+Connection::executeSql(sqlite3 *aNativeConnection, const char *aSqlString)
 {
-  if (!mDBConn)
+  if (isClosed())
     return SQLITE_MISUSE;
 
   TimeStamp startTime = TimeStamp::Now();
-  int srv = ::sqlite3_exec(mDBConn, aSqlString, nullptr, nullptr, nullptr);
+  int srv = ::sqlite3_exec(aNativeConnection, aSqlString, nullptr, nullptr,
+                           nullptr);
 
   // Report very slow SQL statements to Telemetry
   TimeDuration duration = TimeStamp::Now() - startTime;
   const uint32_t threshold =
     NS_IsMainThread() ? Telemetry::kSlowSQLThresholdForMainThread
                       : Telemetry::kSlowSQLThresholdForHelperThreads;
   if (duration.ToMilliseconds() >= threshold) {
     nsDependentCString statementString(aSqlString);
@@ -1071,49 +1108,56 @@ Connection::Close()
     // If this fails, the mDBConn will be left open, resulting in a leak.
     // Ideally we'd schedule some code to destroy the mDBConn once all its
     // async statements have finished executing;  see bug 704030.
     MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
     bool asyncCloseWasCalled = !mAsyncExecutionThread;
     NS_ENSURE_TRUE(asyncCloseWasCalled, NS_ERROR_UNEXPECTED);
   }
 
+  // setClosedState nullifies our connection pointer, so we take a raw pointer
+  // off it, to pass it through the close procedure.
+  sqlite3 *nativeConn = mDBConn;
   nsresult rv = setClosedState();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  return internalClose();
+  return internalClose(nativeConn);
 }
 
 NS_IMETHODIMP
 Connection::AsyncClose(mozIStorageCompletionCallback *aCallback)
 {
   if (!NS_IsMainThread()) {
     return NS_ERROR_NOT_SAME_THREAD;
   }
   if (!mDBConn)
     return NS_ERROR_NOT_INITIALIZED;
 
   nsIEventTarget *asyncThread = getAsyncExecutionTarget();
   NS_ENSURE_TRUE(asyncThread, NS_ERROR_NOT_INITIALIZED);
 
+  // setClosedState nullifies our connection pointer, so we take a raw pointer
+  // off it, to pass it through the close procedure.
+  sqlite3 *nativeConn = mDBConn;
   nsresult rv = setClosedState();
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Create our callback event if we were given a callback.
   nsCOMPtr<nsIRunnable> completeEvent;
   if (aCallback) {
     completeEvent = newCompletionEvent(aCallback);
   }
 
   // Create and dispatch our close event to the background thread.
   nsCOMPtr<nsIRunnable> closeEvent;
   {
     // We need to lock because we're modifying mAsyncExecutionThread
     MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
     closeEvent = new AsyncCloseConnection(this,
+                                          nativeConn,
                                           completeEvent,
                                           mAsyncExecutionThread.forget());
   }
 
   rv = asyncThread->Dispatch(closeEvent, NS_DISPATCH_NORMAL);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
@@ -1235,17 +1279,17 @@ Connection::GetDefaultPageSize(int32_t *
 {
   *_defaultPageSize = Service::getDefaultPageSize();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 Connection::GetConnectionReady(bool *_ready)
 {
-  *_ready = ConnectionReady();
+  *_ready = connectionReady();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 Connection::GetDatabaseFile(nsIFile **_dbFile)
 {
   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
 
@@ -1330,17 +1374,17 @@ Connection::CreateStatement(const nsACSt
                             mozIStorageStatement **_stmt)
 {
   NS_ENSURE_ARG_POINTER(_stmt);
   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
 
   nsRefPtr<Statement> statement(new Statement());
   NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY);
 
-  nsresult rv = statement->initialize(this, aSQLStatement);
+  nsresult rv = statement->initialize(this, mDBConn, aSQLStatement);
   NS_ENSURE_SUCCESS(rv, rv);
 
   Statement *rawPtr;
   statement.forget(&rawPtr);
   *_stmt = rawPtr;
   return NS_OK;
 }
 
@@ -1349,31 +1393,31 @@ Connection::CreateAsyncStatement(const n
                                  mozIStorageAsyncStatement **_stmt)
 {
   NS_ENSURE_ARG_POINTER(_stmt);
   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
 
   nsRefPtr<AsyncStatement> statement(new AsyncStatement());
   NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY);
 
-  nsresult rv = statement->initialize(this, aSQLStatement);
+  nsresult rv = statement->initialize(this, mDBConn, aSQLStatement);
   NS_ENSURE_SUCCESS(rv, rv);
 
   AsyncStatement *rawPtr;
   statement.forget(&rawPtr);
   *_stmt = rawPtr;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 Connection::ExecuteSimpleSQL(const nsACString &aSQLStatement)
 {
   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
 
-  int srv = executeSql(PromiseFlatCString(aSQLStatement).get());
+  int srv = executeSql(mDBConn, PromiseFlatCString(aSQLStatement).get());
   return convertResultCode(srv);
 }
 
 NS_IMETHODIMP
 Connection::ExecuteAsync(mozIStorageBaseStatement **aStatements,
                          uint32_t aNumStatements,
                          mozIStorageStatementCallback *aCallback,
                          mozIStoragePendingStatement **_handle)
@@ -1391,17 +1435,18 @@ Connection::ExecuteAsync(mozIStorageBase
     NS_ASSERTION(stmt->getOwner() == this,
                  "Statement must be from this database connection!");
 
     // Now append it to our array.
     NS_ENSURE_TRUE(stmts.AppendElement(data), NS_ERROR_OUT_OF_MEMORY);
   }
 
   // Dispatch to the background
-  return AsyncExecuteStatements::execute(stmts, this, aCallback, _handle);
+  return AsyncExecuteStatements::execute(stmts, this, mDBConn, aCallback,
+                                         _handle);
 }
 
 NS_IMETHODIMP
 Connection::ExecuteSimpleSQLAsync(const nsACString &aSQLStatement,
                                   mozIStorageStatementCallback *aCallback,
                                   mozIStoragePendingStatement **_handle)
 {
   if (!NS_IsMainThread()) {
@@ -1454,81 +1499,101 @@ Connection::BeginTransaction()
   return BeginTransactionAs(mozIStorageConnection::TRANSACTION_DEFERRED);
 }
 
 NS_IMETHODIMP
 Connection::BeginTransactionAs(int32_t aTransactionType)
 {
   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
 
+  return beginTransactionInternal(mDBConn, aTransactionType);
+}
+
+nsresult
+Connection::beginTransactionInternal(sqlite3 *aNativeConnection,
+                                     int32_t aTransactionType)
+{
   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
   if (mTransactionInProgress)
     return NS_ERROR_FAILURE;
   nsresult rv;
   switch(aTransactionType) {
     case TRANSACTION_DEFERRED:
-      rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("BEGIN DEFERRED"));
+      rv = convertResultCode(executeSql(aNativeConnection, "BEGIN DEFERRED"));
       break;
     case TRANSACTION_IMMEDIATE:
-      rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("BEGIN IMMEDIATE"));
+      rv = convertResultCode(executeSql(aNativeConnection, "BEGIN IMMEDIATE"));
       break;
     case TRANSACTION_EXCLUSIVE:
-      rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("BEGIN EXCLUSIVE"));
+      rv = convertResultCode(executeSql(aNativeConnection, "BEGIN EXCLUSIVE"));
       break;
     default:
       return NS_ERROR_ILLEGAL_VALUE;
   }
   if (NS_SUCCEEDED(rv))
     mTransactionInProgress = true;
   return rv;
 }
 
 NS_IMETHODIMP
 Connection::CommitTransaction()
 {
   if (!mDBConn)
     return NS_ERROR_NOT_INITIALIZED;
 
+  return commitTransactionInternal(mDBConn);
+}
+
+nsresult
+Connection::commitTransactionInternal(sqlite3 *aNativeConnection)
+{
   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
   if (!mTransactionInProgress)
     return NS_ERROR_UNEXPECTED;
-
-  nsresult rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("COMMIT TRANSACTION"));
+  nsresult rv =
+    convertResultCode(executeSql(aNativeConnection, "COMMIT TRANSACTION"));
   if (NS_SUCCEEDED(rv))
     mTransactionInProgress = false;
   return rv;
 }
 
 NS_IMETHODIMP
 Connection::RollbackTransaction()
 {
   if (!mDBConn)
     return NS_ERROR_NOT_INITIALIZED;
 
+  return rollbackTransactionInternal(mDBConn);
+}
+
+nsresult
+Connection::rollbackTransactionInternal(sqlite3 *aNativeConnection)
+{
   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
   if (!mTransactionInProgress)
     return NS_ERROR_UNEXPECTED;
 
-  nsresult rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("ROLLBACK TRANSACTION"));
+  nsresult rv =
+    convertResultCode(executeSql(aNativeConnection, "ROLLBACK TRANSACTION"));
   if (NS_SUCCEEDED(rv))
     mTransactionInProgress = false;
   return rv;
 }
 
 NS_IMETHODIMP
 Connection::CreateTable(const char *aTableName,
                         const char *aTableSchema)
 {
   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
 
   char *buf = ::PR_smprintf("CREATE TABLE %s (%s)", aTableName, aTableSchema);
   if (!buf)
     return NS_ERROR_OUT_OF_MEMORY;
 
-  int srv = executeSql(buf);
+  int srv = executeSql(mDBConn, buf);
   ::PR_smprintf_free(buf);
 
   return convertResultCode(srv);
 }
 
 NS_IMETHODIMP
 Connection::CreateFunction(const nsACString &aFunctionName,
                            int32_t aNumArguments,
--- a/storage/src/mozStorageConnection.h
+++ b/storage/src/mozStorageConnection.h
@@ -91,34 +91,58 @@ public:
    * Creates the connection to the database.
    *
    * @param aFileURL
    *        The nsIFileURL of the location of the database to open, or create if it
    *        does not exist.
    */
   nsresult initialize(nsIFileURL *aFileURL);
 
-  // fetch the native handle
-  sqlite3 *GetNativeConnection() { return mDBConn; }
-  operator sqlite3 *() const { return mDBConn; }
+  /**
+   * Fetches runtime status information for this connection.
+   *
+   * @param aStatusOption One of the SQLITE_DBSTATUS options defined at
+   *        http://www.sqlite.org/c3ref/c_dbstatus_options.html
+   * @param [optional] aMaxValue if provided, will be set to the highest
+   *        istantaneous value.
+   * @return the current value for the specified option.
+   */
+  int32_t getSqliteRuntimeStatus(int32_t aStatusOption,
+                                 int32_t* aMaxValue=nullptr);
+  /**
+   * Registers/unregisters a commit hook callback.
+   *
+   * @param aCallbackFn a callback function to be invoked on transactions
+   *        commit.  Pass nullptr to unregister the current callback.
+   * @param [optional] aData if provided, will be passed to the callback.
+   * @see http://sqlite.org/c3ref/commit_hook.html
+   */
+  void setCommitHook(int (*aCallbackFn)(void *) , void *aData=nullptr) {
+    MOZ_ASSERT(mDBConn, "A connection must exist at this point");
+    ::sqlite3_commit_hook(mDBConn, aCallbackFn, aData);
+  };
 
   /**
    * Lazily creates and returns a background execution thread.  In the future,
    * the thread may be re-claimed if left idle, so you should call this
    * method just before you dispatch and not save the reference.
    *
    * @returns an event target suitable for asynchronous statement execution.
    */
   nsIEventTarget *getAsyncExecutionTarget();
 
   /**
    * Mutex used by asynchronous statements to protect state.  The mutex is
    * declared on the connection object because there is no contention between
-   * asynchronous statements (they are serialized on mAsyncExecutionThread).  It
-   * also protects mPendingStatements.
+   * asynchronous statements (they are serialized on mAsyncExecutionThread).
+   * Currently protects:
+   *  - Connection.mAsyncExecutionThreadShuttingDown
+   *  - Connection.mAsyncExecutionThread
+   *  - Connection.mConnectionClosed
+   *  - AsyncExecuteStatements.mCancelRequested
    */
   Mutex sharedAsyncExecutionMutex;
 
   /**
    * Wraps the mutex that SQLite gives us from sqlite3_db_mutex.  This is public
    * because we already expose the sqlite3* native connection and proper
    * operation of the deadlock detector requires everyone to use the same single
    * SQLiteMutex instance for correctness.
@@ -129,59 +153,72 @@ public:
    * References the thread this database was opened on.  This MUST be thread it is
    * closed on.
    */
   const nsCOMPtr<nsIThread> threadOpenedOn;
 
   /**
    * Closes the SQLite database, and warns about any non-finalized statements.
    */
-  nsresult internalClose();
+  nsresult internalClose(sqlite3 *aDBConn);
 
   /**
    * Obtains the filename of the connection.  Useful for logging.
    */
   nsCString getFilename();
 
   /**
    * Creates an sqlite3 prepared statement object from an SQL string.
    *
+   * @param aNativeConnection
+   *        The underlying Sqlite connection to prepare the statement with.
    * @param aSQL
    *        The SQL statement string to compile.
    * @param _stmt
    *        New sqlite3_stmt object.
    * @return the result from sqlite3_prepare_v2.
    */
-  int prepareStatement(const nsCString &aSQL, sqlite3_stmt **_stmt);
+  int prepareStatement(sqlite3* aNativeConnection,
+                       const nsCString &aSQL, sqlite3_stmt **_stmt);
 
   /**
    * Performs a sqlite3_step on aStatement, while properly handling SQLITE_LOCKED
    * when not on the main thread by waiting until we are notified.
    *
+   * @param aNativeConnection
+   *        The underlying Sqlite connection to step the statement with.
    * @param aStatement
    *        A pointer to a sqlite3_stmt object.
    * @return the result from sqlite3_step.
    */
-  int stepStatement(sqlite3_stmt* aStatement);
-
-  bool ConnectionReady() {
-    return mDBConn != nullptr;
-  }
+  int stepStatement(sqlite3* aNativeConnection, sqlite3_stmt* aStatement);
 
   /**
-   * True if this connection is currently shutting down.
+   * Raw connection transaction management.
    *
-   * In particular, if |isClosing(true)| returns |true|, any sqlite3 statement
-   * belonging to this connection must be discarded as its memory has already
-   * been released to sqlite3.
-   *
-   * @param aResultOnceClosed
-   *        The value to return if closing has completed.
+   * @see BeginTransactionAs, CommitTransaction, RollbackTransaction.
    */
-  bool isClosing(bool aResultOnceClosed = false);
+  nsresult beginTransactionInternal(sqlite3 *aNativeConnection,
+                                    int32_t aTransactionType=TRANSACTION_DEFERRED);
+  nsresult commitTransactionInternal(sqlite3 *aNativeConnection);
+  nsresult rollbackTransactionInternal(sqlite3 *aNativeConnection);
+
+  bool connectionReady();
+
+  /**
+   * True if this connection is shutting down but not yet closed.
+   */
+  bool isClosing();
+
+  /**
+   * True if the underlying connection is closed.
+   * Any sqlite resources may be lost when this returns true, so nothing should
+   * try to use them.
+   */
+  bool isClosed();
 
   nsresult initializeClone(Connection *aClone, bool aReadOnly);
 
 private:
   ~Connection();
   nsresult initializeInternal(nsIFile *aDatabaseFile);
 
   /**
@@ -190,21 +227,23 @@ private:
    *
    * @note mDBConn is set to nullptr in this method.
    */
   nsresult setClosedState();
 
   /**
    * Helper for calls to sqlite3_exec. Reports long delays to Telemetry.
    *
+   * @param aNativeConnection
+   *        The underlying Sqlite connection to execute the query with.
    * @param aSqlString
    *        SQL string to execute
    * @return the result from sqlite3_exec.
    */
-  int executeSql(const char *aSqlString);
+  int executeSql(sqlite3 *aNativeConnection, const char *aSqlString);
 
   /**
    * Describes a certain primitive type in the database.
    *
    * Possible Values Are:
    *  INDEX - To check for the existence of an index
    *  TABLE - To check for the existence of a table
    */
@@ -251,35 +290,44 @@ private:
    * If false, we guarantee both that the underlying sqlite3 database
    * connection is still open and that getAsyncExecutionTarget() can
    * return a thread. Once true, either the sqlite3 database
    * connection is being shutdown or it has been
    * shutdown. Additionally, once true, getAsyncExecutionTarget()
    * returns null.
    *
    * This variable should be accessed while holding the
-   * mAsyncExecutionMutex.
+   * sharedAsyncExecutionMutex.
    */
   bool mAsyncExecutionThreadShuttingDown;
 
   /**
+   * Set to true just prior to calling sqlite3_close on the
+   * connection.
+   *
+   * This variable should be accessed while holding the
+   * sharedAsyncExecutionMutex.
+   */
+  bool mConnectionClosed;
+
+  /**
    * Tracks if we have a transaction in progress or not.  Access protected by
-   * mDBMutex.
+   * sharedDBMutex.
    */
   bool mTransactionInProgress;
 
   /**
    * Stores the mapping of a given function by name to its instance.  Access is
-   * protected by mDBMutex.
+   * protected by sharedDBMutex.
    */
   nsDataHashtable<nsCStringHashKey, FunctionInfo> mFunctions;
 
   /**
    * Stores the registered progress handler for the database connection.  Access
-   * is protected by mDBMutex.
+   * is protected by sharedDBMutex.
    */
   nsCOMPtr<mozIStorageProgressHandler> mProgressHandler;
 
   /**
    * Stores the flags we passed to sqlite3_open_v2.
    */
   const int mFlags;
 
--- a/storage/src/mozStorageService.cpp
+++ b/storage/src/mozStorageService.cpp
@@ -89,38 +89,34 @@ StorageSQLiteDistinguishedAmount()
  * @param aOption
  *        The SQLite constant for getting the measurement.
  * @param aTotal
  *        The accumulator for the measurement.
  */
 nsresult
 ReportConn(nsIHandleReportCallback *aHandleReport,
            nsISupports *aData,
-           sqlite3 *aConn,
+           Connection *aConn,
            const nsACString &aPathHead,
            const nsACString &aKind,
            const nsACString &aDesc,
-           int aOption,
+           int32_t aOption,
            size_t *aTotal)
 {
   nsCString path(aPathHead);
   path.Append(aKind);
   path.AppendLiteral("-used");
 
-  int curr = 0, max = 0;
-  int rc = ::sqlite3_db_status(aConn, aOption, &curr, &max, 0);
-  nsresult rv = convertResultCode(rc);
+  int32_t val = aConn->getSqliteRuntimeStatus(aOption);
+  nsresult rv = aHandleReport->Callback(EmptyCString(), path,
+                                        nsIMemoryReporter::KIND_HEAP,
+                                        nsIMemoryReporter::UNITS_BYTES,
+                                        int64_t(val), aDesc, aData);
   NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = aHandleReport->Callback(EmptyCString(), path,
-                               nsIMemoryReporter::KIND_HEAP,
-                               nsIMemoryReporter::UNITS_BYTES, int64_t(curr),
-                               aDesc, aData);
-  NS_ENSURE_SUCCESS(rv, rv);
-  *aTotal += curr;
+  *aTotal += val;
 
   return NS_OK;
 }
 
 // Warning: To get a Connection's measurements requires holding its lock.
 // There may be a delay getting the lock if another thread is accessing the
 // Connection.  This isn't very nice if CollectReports is called from the main
 // thread!  But at the time of writing this function is only called when
@@ -150,33 +146,33 @@ Service::CollectReports(nsIHandleReportC
       pathHead.Append(conn->getFilename());
       pathHead.AppendLiteral("/");
 
       SQLiteMutexAutoLock lockedScope(conn->sharedDBMutex);
 
       NS_NAMED_LITERAL_CSTRING(stmtDesc,
         "Memory (approximate) used by all prepared statements used by "
         "connections to this database.");
-      rv = ReportConn(aHandleReport, aData, *conn.get(), pathHead,
+      rv = ReportConn(aHandleReport, aData, conn, pathHead,
                       NS_LITERAL_CSTRING("stmt"), stmtDesc,
                       SQLITE_DBSTATUS_STMT_USED, &totalConnSize);
       NS_ENSURE_SUCCESS(rv, rv);
 
       NS_NAMED_LITERAL_CSTRING(cacheDesc,
         "Memory (approximate) used by all pager caches used by connections "
         "to this database.");
-      rv = ReportConn(aHandleReport, aData, *conn.get(), pathHead,
+      rv = ReportConn(aHandleReport, aData, conn, pathHead,
                       NS_LITERAL_CSTRING("cache"), cacheDesc,
                       SQLITE_DBSTATUS_CACHE_USED, &totalConnSize);
       NS_ENSURE_SUCCESS(rv, rv);
 
       NS_NAMED_LITERAL_CSTRING(schemaDesc,
         "Memory (approximate) used to store the schema for all databases "
         "associated with connections to this database.");
-      rv = ReportConn(aHandleReport, aData, *conn.get(), pathHead,
+      rv = ReportConn(aHandleReport, aData, conn, pathHead,
                       NS_LITERAL_CSTRING("schema"), schemaDesc,
                       SQLITE_DBSTATUS_SCHEMA_USED, &totalConnSize);
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
 #ifdef MOZ_DMD
     if (::sqlite3_memory_used() != int64_t(gSqliteMemoryUsed)) {
       NS_WARNING("memory consumption reported by SQLite doesn't match "
@@ -345,17 +341,17 @@ Service::getConnections(/* inout */ nsTA
 void
 Service::minimizeMemory()
 {
   nsTArray<nsRefPtr<Connection> > connections;
   getConnections(connections);
 
   for (uint32_t i = 0; i < connections.Length(); i++) {
     nsRefPtr<Connection> conn = connections[i];
-    if (conn->ConnectionReady()) {
+    if (conn->connectionReady()) {
       NS_NAMED_LITERAL_CSTRING(shrinkPragma, "PRAGMA shrink_memory");
       nsCOMPtr<mozIStorageConnection> syncConn = do_QueryInterface(
         NS_ISUPPORTS_CAST(mozIStorageAsyncConnection*, conn));
       DebugOnly<nsresult> rv;
 
       if (!syncConn) {
         nsCOMPtr<mozIStoragePendingStatement> ps;
         rv = connections[i]->ExecuteSimpleSQLAsync(shrinkPragma, nullptr,
@@ -913,35 +909,32 @@ Service::Observe(nsISupports *, const ch
 
     bool anyOpen = false;
     do {
       nsTArray<nsRefPtr<Connection> > connections;
       getConnections(connections);
       anyOpen = false;
       for (uint32_t i = 0; i < connections.Length(); i++) {
         nsRefPtr<Connection> &conn = connections[i];
-
-        // While it would be nice to close all connections, we only
-        // check async ones for now.
         if (conn->isClosing()) {
           anyOpen = true;
           break;
         }
       }
       if (anyOpen) {
         nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
         NS_ProcessNextEvent(thread);
       }
     } while (anyOpen);
 
     if (gShutdownChecks == SCM_CRASH) {
       nsTArray<nsRefPtr<Connection> > connections;
       getConnections(connections);
       for (uint32_t i = 0, n = connections.Length(); i < n; i++) {
-        if (connections[i]->ConnectionReady()) {
+        if (!connections[i]->isClosed()) {
           MOZ_CRASH();
         }
       }
     }
   }
 
   return NS_OK;
 }
--- a/storage/src/mozStorageStatement.cpp
+++ b/storage/src/mozStorageStatement.cpp
@@ -130,40 +130,42 @@ Statement::Statement()
 , mDBStatement(nullptr)
 , mColumnNames()
 , mExecuting(false)
 {
 }
 
 nsresult
 Statement::initialize(Connection *aDBConnection,
+                      sqlite3 *aNativeConnection,
                       const nsACString &aSQLStatement)
 {
-  NS_ASSERTION(aDBConnection, "No database connection given!");
-  NS_ASSERTION(!mDBStatement, "Statement already initialized!");
+  MOZ_ASSERT(aDBConnection, "No database connection given!");
+  MOZ_ASSERT(!aDBConnection->isClosed(), "Database connection should be valid");
+  MOZ_ASSERT(!mDBStatement, "Statement already initialized!");
+  MOZ_ASSERT(aNativeConnection, "No native connection given!");
 
-  DebugOnly<sqlite3 *> db = aDBConnection->GetNativeConnection();
-  NS_ASSERTION(db, "We should never be called with a null sqlite3 database!");
-
-  int srv = aDBConnection->prepareStatement(PromiseFlatCString(aSQLStatement),
+  int srv = aDBConnection->prepareStatement(aNativeConnection,
+                                            PromiseFlatCString(aSQLStatement),
                                             &mDBStatement);
   if (srv != SQLITE_OK) {
       PR_LOG(gStorageLog, PR_LOG_ERROR,
              ("Sqlite statement prepare error: %d '%s'", srv,
-              ::sqlite3_errmsg(db)));
+              ::sqlite3_errmsg(aNativeConnection)));
       PR_LOG(gStorageLog, PR_LOG_ERROR,
              ("Statement was: '%s'", PromiseFlatCString(aSQLStatement).get()));
       return NS_ERROR_FAILURE;
     }
 
   PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Initialized statement '%s' (0x%p)",
                                       PromiseFlatCString(aSQLStatement).get(),
                                       mDBStatement));
 
   mDBConnection = aDBConnection;
+  mNativeConnection = aNativeConnection;
   mParamCount = ::sqlite3_bind_parameter_count(mDBStatement);
   mResultColumnCount = ::sqlite3_column_count(mDBStatement);
   mColumnNames.Clear();
 
   for (uint32_t i = 0; i < mResultColumnCount; i++) {
       const char *name = ::sqlite3_column_name(mDBStatement, i);
       (void)mColumnNames.AppendElement(nsDependentCString(name));
   }
@@ -279,17 +281,18 @@ int
 Statement::getAsyncStatement(sqlite3_stmt **_stmt)
 {
   // If we have no statement, we shouldn't be calling this method!
   NS_ASSERTION(mDBStatement != nullptr, "We have no statement to clone!");
 
   // If we do not yet have a cached async statement, clone our statement now.
   if (!mAsyncStatement) {
     nsDependentCString sql(::sqlite3_sql(mDBStatement));
-    int rc = mDBConnection->prepareStatement(sql, &mAsyncStatement);
+    int rc = mDBConnection->prepareStatement(mNativeConnection, sql,
+                                             &mAsyncStatement);
     if (rc != SQLITE_OK) {
       *_stmt = nullptr;
       return rc;
     }
 
     PR_LOG(gStorageLog, PR_LOG_NOTICE,
            ("Cloned statement 0x%p to 0x%p", mDBStatement, mAsyncStatement));
   }
@@ -330,17 +333,17 @@ MIXIN_IMPL_STORAGEBASESTATEMENTINTERNAL(
 
 NS_IMETHODIMP
 Statement::Clone(mozIStorageStatement **_statement)
 {
   nsRefPtr<Statement> statement(new Statement());
   NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY);
 
   nsAutoCString sql(::sqlite3_sql(mDBStatement));
-  nsresult rv = statement->initialize(mDBConnection, sql);
+  nsresult rv = statement->initialize(mDBConnection, mNativeConnection, sql);
   NS_ENSURE_SUCCESS(rv, rv);
 
   statement.forget(_statement);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 Statement::Finalize()
@@ -351,17 +354,17 @@ Statement::Finalize()
 nsresult
 Statement::internalFinalize(bool aDestructing)
 {
   if (!mDBStatement)
     return NS_OK;
 
   int srv = SQLITE_OK;
 
-  if (!mDBConnection->isClosing(true)) {
+  if (!mDBConnection->isClosed()) {
     //
     // The connection is still open. While statement finalization and
     // closing may, in some cases, take place in two distinct threads,
     // we have a guarantee that the connection will remain open until
     // this method terminates:
     //
     // a. The connection will be closed synchronously. In this case,
     // there is no race condition, as everything takes place on the
@@ -627,17 +630,17 @@ Statement::ExecuteStep(bool *_moreResult
       int32_t srv;
       (void)error->GetResult(&srv);
       return convertResultCode(srv);
     }
 
     // We have bound, so now we can clear our array.
     mParamsArray = nullptr;
   }
-  int srv = mDBConnection->stepStatement(mDBStatement);
+  int srv = mDBConnection->stepStatement(mNativeConnection, mDBStatement);
 
 #ifdef PR_LOGGING
   if (srv != SQLITE_ROW && srv != SQLITE_DONE) {
       nsAutoCString errStr;
       (void)mDBConnection->GetLastErrorString(errStr);
       PR_LOG(gStorageLog, PR_LOG_DEBUG,
              ("Statement::ExecuteStep error: %s", errStr.get()));
   }
--- a/storage/src/mozStorageStatement.h
+++ b/storage/src/mozStorageStatement.h
@@ -42,20 +42,23 @@ public:
   Statement();
 
   /**
    * Initializes the object on aDBConnection by preparing the SQL statement
    * given by aSQLStatement.
    *
    * @param aDBConnection
    *        The Connection object this statement is associated with.
+   * @param aNativeConnection
+   *        The native Sqlite connection this statement is associated with.
    * @param aSQLStatement
    *        The SQL statement to prepare that this object will represent.
    */
   nsresult initialize(Connection *aDBConnection,
+                      sqlite3* aNativeConnection,
                       const nsACString &aSQLStatement);
 
 
   /**
    * Obtains the native statement pointer.
    */
   inline sqlite3_stmt *nativeStatement() { return mDBStatement; }
 
--- a/storage/src/mozStorageStatementData.h
+++ b/storage/src/mozStorageStatementData.h
@@ -67,25 +67,16 @@ public:
     }
     *_stmt = mStatement;
     return SQLITE_OK;
   }
 
   operator BindingParamsArray *() const { return mParamsArray; }
 
   /**
-   * Provide the ability to coerce back to a sqlite3 * connection for purposes 
-   * of getting an error message out of it.
-   */
-  operator sqlite3 *() const
-  {
-    return mStatementOwner->getOwner()->GetNativeConnection();
-  }
-
-  /**
    * NULLs out our sqlite3_stmt (it is held by the owner) after reseting it and
    * clear all bindings to it.  This is expected to occur on the async thread.
    */
   inline void reset()
   {
     NS_PRECONDITION(mStatementOwner, "Must have a statement owner!");
 #ifdef DEBUG
     {
--- a/storage/test/test_asyncStatementExecution_transaction.cpp
+++ b/storage/test/test_asyncStatementExecution_transaction.cpp
@@ -43,29 +43,29 @@ int commit_hook(void *aArg)
 void
 check_transaction(mozIStorageConnection *aDB,
                   mozIStorageBaseStatement **aStmts,
                   uint32_t aStmtsLen,
                   bool aTransactionExpected)
 {
   // -- install a transaction commit hook.
   int commit = 0;
-  ::sqlite3_commit_hook(*static_cast<Connection *>(aDB), commit_hook, &commit);
+  static_cast<Connection *>(aDB)->setCommitHook(commit_hook, &commit);
 
   nsRefPtr<AsyncStatementSpinner> asyncSpin(new AsyncStatementSpinner());
   nsCOMPtr<mozIStoragePendingStatement> asyncPend;
   do_check_success(aDB->ExecuteAsync(aStmts, aStmtsLen, asyncSpin,
                                      getter_AddRefs(asyncPend)));
   do_check_true(asyncPend);
 
   // -- complete the execution
   asyncSpin->SpinUntilCompleted();
 
   // -- uninstall the transaction commit hook.
-  ::sqlite3_commit_hook(*static_cast<Connection *>(aDB), nullptr, nullptr);
+  static_cast<Connection *>(aDB)->setCommitHook(nullptr);
 
   // -- check transaction
   do_check_eq(aTransactionExpected, !!commit);
 
   // -- check that only one transaction was created.
   if (aTransactionExpected) {
     do_check_eq(1, commit);
   }
--- a/testing/marionette/marionette-server.js
+++ b/testing/marionette/marionette-server.js
@@ -30,20 +30,16 @@ specialpowers.specialPowersObserver = ne
 specialpowers.specialPowersObserver.init();
 
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
 
 Services.prefs.setBoolPref("marionette.contentListener", false);
 let appName = Services.appinfo.name;
 
-// dumpn needed/used by dbg-transport.js
-this.dumpn = function dumpn(str) {
-  logger.trace(str);
-}
 let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 let DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils.js");
 this.DevToolsUtils = DevToolsUtils;
 loader.loadSubScript("resource://gre/modules/devtools/server/transport.js");
 
 let bypassOffline = false;
 let qemu = "0";
 let device = null;
--- a/toolkit/components/places/Database.cpp
+++ b/toolkit/components/places/Database.cpp
@@ -210,56 +210,48 @@ SetJournalMode(nsCOMPtr<mozIStorageConne
     }
     // This is an unknown journal.
     MOZ_ASSERT(true);
   }
 
   return JOURNAL_DELETE;
 }
 
-class BlockingConnectionCloseCallback MOZ_FINAL : public mozIStorageCompletionCallback {
+class ConnectionCloseCallback MOZ_FINAL : public mozIStorageCompletionCallback {
   bool mDone;
 
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_MOZISTORAGECOMPLETIONCALLBACK
-  BlockingConnectionCloseCallback();
-  void Spin();
+  ConnectionCloseCallback();
 };
 
 NS_IMETHODIMP
-BlockingConnectionCloseCallback::Complete(nsresult, nsISupports*)
+ConnectionCloseCallback::Complete(nsresult, nsISupports*)
 {
   mDone = true;
   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   MOZ_ASSERT(os);
   if (!os)
     return NS_OK;
   DebugOnly<nsresult> rv = os->NotifyObservers(nullptr,
                                                TOPIC_PLACES_CONNECTION_CLOSED,
                                                nullptr);
   MOZ_ASSERT(NS_SUCCEEDED(rv));
   return NS_OK;
 }
 
-BlockingConnectionCloseCallback::BlockingConnectionCloseCallback()
+ConnectionCloseCallback::ConnectionCloseCallback()
   : mDone(false)
 {
   MOZ_ASSERT(NS_IsMainThread());
 }
 
-void BlockingConnectionCloseCallback::Spin() {
-  nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
-  while (!mDone) {
-    NS_ProcessNextEvent(thread);
-  }
-}
-
 NS_IMPL_ISUPPORTS1(
-  BlockingConnectionCloseCallback
+  ConnectionCloseCallback
 , mozIStorageCompletionCallback
 )
 
 nsresult
 CreateRoot(nsCOMPtr<mozIStorageConnection>& aDBConn,
            const nsCString& aRootName,
            const nsXPIDLString& titleString)
 {
@@ -1934,22 +1926,21 @@ Database::Shutdown()
   mMainThreadAsyncStatements.FinalizeStatements();
 
   nsRefPtr< FinalizeStatementCacheProxy<mozIStorageStatement> > event =
     new FinalizeStatementCacheProxy<mozIStorageStatement>(
           mAsyncThreadStatements, NS_ISUPPORTS_CAST(nsIObserver*, this)
         );
   DispatchToAsyncThread(event);
 
-  nsRefPtr<BlockingConnectionCloseCallback> closeListener =
-    new BlockingConnectionCloseCallback();
+  mClosed = true;
+
+  nsRefPtr<ConnectionCloseCallback> closeListener =
+    new ConnectionCloseCallback();
   (void)mMainConn->AsyncClose(closeListener);
-  closeListener->Spin();
-
-  mClosed = true;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// nsIObserver
 
 NS_IMETHODIMP
 Database::Observe(nsISupports *aSubject,
                   const char *aTopic,
--- a/toolkit/devtools/DevToolsUtils.js
+++ b/toolkit/devtools/DevToolsUtils.js
@@ -5,17 +5,17 @@
 "use strict";
 
 /* General utilities used throughout devtools. */
 
 // hasChrome is provided as a global by the loader. It is true if we are running
 // on the main thread, and false if we are running on a worker thread.
 var { Ci, Cu } = require("chrome");
 var Services = require("Services");
-var setTimeout = Cu.import("resource://gre/modules/Timer.jsm", {}).setTimeout;
+var { setTimeout } = require("Timer");
 
 /**
  * Turn the error |aError| into a string, without fail.
  */
 exports.safeErrorString = function safeErrorString(aError) {
   try {
     let errorString = aError.toString();
     if (typeof errorString == "string") {
--- a/toolkit/devtools/Loader.jsm
+++ b/toolkit/devtools/Loader.jsm
@@ -18,16 +18,17 @@ Cu.evalInSandbox(
   "Components.utils.import('resource://gre/modules/jsdebugger.jsm');" +
   "addDebuggerToGlobal(this);",
   sandbox
 );
 let Debugger = sandbox.Debugger;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
+let Timer = Cu.import("resource://gre/modules/Timer.jsm", {});
 
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", "resource://gre/modules/FileUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "console", "resource://gre/modules/devtools/Console.jsm");
 
 let SourceMap = {};
 Cu.import("resource://gre/modules/devtools/SourceMap.jsm", SourceMap);
@@ -58,16 +59,17 @@ let loaderGlobals = {
 // Used when the tools should be loaded from the Firefox package itself (the default)
 function BuiltinProvider() {}
 BuiltinProvider.prototype = {
   load: function() {
     this.loader = new loader.Loader({
       modules: {
         "Debugger": Debugger,
         "Services": Object.create(Services),
+        "Timer": Object.create(Timer),
         "toolkit/loader": loader,
         "source-map": SourceMap,
       },
       paths: {
         // When you add a line to this mapping, don't forget to make a
         // corresponding addition to the SrcdirProvider mapping below as well.
         "": "resource://gre/modules/commonjs/",
         "main": "resource:///modules/devtools/main.js",
@@ -136,16 +138,17 @@ SrcdirProvider.prototype = {
     let contentObserverURI = this.fileURI(OS.Path.join(toolkitDir), "content-observer.js");
     let gcliURI = this.fileURI(OS.Path.join(toolkitDir, "gcli", "source", "lib", "gcli"));
     let acornURI = this.fileURI(OS.Path.join(toolkitDir, "acorn"));
     let acornWalkURI = OS.Path.join(acornURI, "walk.js");
     this.loader = new loader.Loader({
       modules: {
         "Debugger": Debugger,
         "Services": Object.create(Services),
+        "Timer": Object.create(Timer),
         "toolkit/loader": loader,
         "source-map": SourceMap,
       },
       paths: {
         "": "resource://gre/modules/commonjs/",
         "main": mainURI,
         "devtools": devtoolsURI,
         "devtools/toolkit": toolkitURI,
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/server/actors/common.js
@@ -0,0 +1,168 @@
+/* -*- tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ft=javascript 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/. */
+
+"use strict";
+
+/**
+ * Methods shared between RootActor and BrowserTabActor.
+ */
+
+/**
+ * Populate |this._extraActors| as specified by |aFactories|, reusing whatever
+ * actors are already there. Add all actors in the final extra actors table to
+ * |aPool|.
+ *
+ * The root actor and the tab actor use this to instantiate actors that other
+ * parts of the browser have specified with DebuggerServer.addTabActor antd
+ * DebuggerServer.addGlobalActor.
+ *
+ * @param aFactories
+ *     An object whose own property names are the names of properties to add to
+ *     some reply packet (say, a tab actor grip or the "listTabs" response
+ *     form), and whose own property values are actor constructor functions, as
+ *     documented for addTabActor and addGlobalActor.
+ *
+ * @param this
+ *     The BrowserRootActor or BrowserTabActor with which the new actors will
+ *     be associated. It should support whatever API the |aFactories|
+ *     constructor functions might be interested in, as it is passed to them.
+ *     For the sake of CommonCreateExtraActors itself, it should have at least
+ *     the following properties:
+ *
+ *     - _extraActors
+ *        An object whose own property names are factory table (and packet)
+ *        property names, and whose values are no-argument actor constructors,
+ *        of the sort that one can add to an ActorPool.
+ *
+ *     - conn
+ *        The DebuggerServerConnection in which the new actors will participate.
+ *
+ *     - actorID
+ *        The actor's name, for use as the new actors' parentID.
+ */
+exports.createExtraActors = function createExtraActors(aFactories, aPool) {
+  // Walk over global actors added by extensions.
+  for (let name in aFactories) {
+    let actor = this._extraActors[name];
+    if (!actor) {
+      actor = aFactories[name].bind(null, this.conn, this);
+      actor.prototype = aFactories[name].prototype;
+      actor.parentID = this.actorID;
+      this._extraActors[name] = actor;
+    }
+    aPool.addActor(actor);
+  }
+}
+
+/**
+ * Append the extra actors in |this._extraActors|, constructed by a prior call
+ * to CommonCreateExtraActors, to |aObject|.
+ *
+ * @param aObject
+ *     The object to which the extra actors should be added, under the
+ *     property names given in the |aFactories| table passed to
+ *     CommonCreateExtraActors.
+ *
+ * @param this
+ *     The BrowserRootActor or BrowserTabActor whose |_extraActors| table we
+ *     should use; see above.
+ */
+exports.appendExtraActors = function appendExtraActors(aObject) {
+  for (let name in this._extraActors) {
+    let actor = this._extraActors[name];
+    aObject[name] = actor.actorID;
+  }
+}
+
+/**
+ * Construct an ActorPool.
+ *
+ * ActorPools are actorID -> actor mapping and storage.  These are
+ * used to accumulate and quickly dispose of groups of actors that
+ * share a lifetime.
+ */
+function ActorPool(aConnection)
+{
+  this.conn = aConnection;
+  this._cleanups = {};
+  this._actors = {};
+}
+
+ActorPool.prototype = {
+  /**
+   * Add an actor to the actor pool.  If the actor doesn't have an ID,
+   * allocate one from the connection.
+   *
+   * @param aActor object
+   *        The actor implementation.  If the object has a
+   *        'disconnect' property, it will be called when the actor
+   *        pool is cleaned up.
+   */
+  addActor: function AP_addActor(aActor) {
+    aActor.conn = this.conn;
+    if (!aActor.actorID) {
+      let prefix = aActor.actorPrefix;
+      if (typeof aActor == "function") {
+        // typeName is a convention used with protocol.js-based actors
+        prefix = aActor.prototype.actorPrefix || aActor.prototype.typeName;
+      }
+      aActor.actorID = this.conn.allocID(prefix || undefined);
+    }
+
+    if (aActor.registeredPool) {
+      aActor.registeredPool.removeActor(aActor);
+    }
+    aActor.registeredPool = this;
+
+    this._actors[aActor.actorID] = aActor;
+    if (aActor.disconnect) {
+      this._cleanups[aActor.actorID] = aActor;
+    }
+  },
+
+  get: function AP_get(aActorID) {
+    return this._actors[aActorID];
+  },
+
+  has: function AP_has(aActorID) {
+    return aActorID in this._actors;
+  },
+
+  /**
+   * Returns true if the pool is empty.
+   */
+  isEmpty: function AP_isEmpty() {
+    return Object.keys(this._actors).length == 0;
+  },
+
+  /**
+   * Remove an actor from the actor pool.
+   */
+  removeActor: function AP_remove(aActor) {
+    delete this._actors[aActor.actorID];
+    delete this._cleanups[aActor.actorID];
+  },
+
+  /**
+   * Match the api expected by the protocol library.
+   */
+  unmanage: function(aActor) {
+    return this.removeActor(aActor);
+  },
+
+  /**
+   * Run all actor cleanups.
+   */
+  cleanup: function AP_cleanup() {
+    for each (let actor in this._cleanups) {
+      actor.disconnect();
+    }
+    this._cleanups = {};
+  }
+}
+
+exports.ActorPool = ActorPool;
+
--- a/toolkit/devtools/server/actors/device.js
+++ b/toolkit/devtools/server/actors/device.js
@@ -3,19 +3,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const {Cc, Ci, Cu, CC} = require("chrome");
 const Services = require("Services");
 const protocol = require("devtools/server/protocol");
 const {method, RetVal} = protocol;
 const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
 const {LongStringActor} = require("devtools/server/actors/string");
+const {DebuggerServer} = require("devtools/server/main");
 
 Cu.import("resource://gre/modules/PermissionsTable.jsm")
-Cu.import('resource://gre/modules/devtools/dbg-server.jsm');
 
 const APP_MAP = {
   '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}': 'firefox',
   '{3550f703-e582-4d05-9a08-453d09bdfdc6}': 'thunderbird',
   '{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}': 'seamonkey',
   '{718e30fb-e89b-41dd-9da7-e25a45638b28}': 'sunbird',
   '{3c2e2abc-06d4-11e1-ac3b-374f68613e61}': 'b2g',
   '{aa3c5121-dab2-40e2-81ca-7ea25febc110}': 'mobile/android',
--- a/toolkit/devtools/server/actors/preference.js
+++ b/toolkit/devtools/server/actors/preference.js
@@ -3,17 +3,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const {Cc, Ci, Cu, CC} = require("chrome");
 const protocol = require("devtools/server/protocol");
 const {Arg, method, RetVal} = protocol;
 const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
 
 Cu.import("resource://gre/modules/Services.jsm");
-Cu.import('resource://gre/modules/devtools/dbg-server.jsm');
 
 exports.register = function(handle) {
   handle.addGlobalActor(PreferenceActor, "preferenceActor");
 };
 
 exports.unregister = function(handle) {
 };
 
--- a/toolkit/devtools/server/actors/root.js
+++ b/toolkit/devtools/server/actors/root.js
@@ -1,88 +1,20 @@
 /* -*- tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ft=javascript 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/. */
 
 "use strict";
 
-/* Root actor for the remote debugging protocol. */
-
-/**
- * Methods shared between RootActor and BrowserTabActor.
- */
+let devtools_ = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
+let { createExtraActors, appendExtraActors } = devtools_.require("devtools/server/actors/common");
 
-/**
- * Populate |this._extraActors| as specified by |aFactories|, reusing whatever
- * actors are already there. Add all actors in the final extra actors table to
- * |aPool|.
- *
- * The root actor and the tab actor use this to instantiate actors that other
- * parts of the browser have specified with DebuggerServer.addTabActor antd
- * DebuggerServer.addGlobalActor.
- *
- * @param aFactories
- *     An object whose own property names are the names of properties to add to
- *     some reply packet (say, a tab actor grip or the "listTabs" response
- *     form), and whose own property values are actor constructor functions, as
- *     documented for addTabActor and addGlobalActor.
- *
- * @param this
- *     The BrowserRootActor or BrowserTabActor with which the new actors will
- *     be associated. It should support whatever API the |aFactories|
- *     constructor functions might be interested in, as it is passed to them.
- *     For the sake of CommonCreateExtraActors itself, it should have at least
- *     the following properties:
- *
- *     - _extraActors
- *        An object whose own property names are factory table (and packet)
- *        property names, and whose values are no-argument actor constructors,
- *        of the sort that one can add to an ActorPool.
- *
- *     - conn
- *        The DebuggerServerConnection in which the new actors will participate.
- *
- *     - actorID
- *        The actor's name, for use as the new actors' parentID.
- */
-function CommonCreateExtraActors(aFactories, aPool) {
-  // Walk over global actors added by extensions.
-  for (let name in aFactories) {
-    let actor = this._extraActors[name];
-    if (!actor) {
-      actor = aFactories[name].bind(null, this.conn, this);
-      actor.prototype = aFactories[name].prototype;
-      actor.parentID = this.actorID;
-      this._extraActors[name] = actor;
-    }
-    aPool.addActor(actor);
-  }
-}
-
-/**
- * Append the extra actors in |this._extraActors|, constructed by a prior call
- * to CommonCreateExtraActors, to |aObject|.
- *
- * @param aObject
- *     The object to which the extra actors should be added, under the
- *     property names given in the |aFactories| table passed to
- *     CommonCreateExtraActors.
- *
- * @param this
- *     The BrowserRootActor or BrowserTabActor whose |_extraActors| table we
- *     should use; see above.
- */
-function CommonAppendExtraActors(aObject) {
-  for (let name in this._extraActors) {
-    let actor = this._extraActors[name];
-    aObject[name] = actor.actorID;
-  }
-}
+/* Root actor for the remote debugging protocol. */
 
 /**
  * Create a remote debugging protocol root actor.
  *
  * @param aConnection
  *     The DebuggerServerConnection whose root actor we are constructing.
  *
  * @param aParameters
@@ -359,18 +291,18 @@ RootActor.prototype = {
     return JSON.parse(JSON.stringify(aRequest));
   },
 
   onProtocolDescription: function (aRequest) {
     return protocol.dumpProtocolSpec()
   },
 
   /* Support for DebuggerServer.addGlobalActor. */
-  _createExtraActors: CommonCreateExtraActors,
-  _appendExtraActors: CommonAppendExtraActors,
+  _createExtraActors: createExtraActors,
+  _appendExtraActors: appendExtraActors,
 
   /* ThreadActor hooks. */
 
   /**
    * Prepare to enter a nested event loop by disabling debuggee events.
    */
   preNest: function() {
     // Disable events in all open windows.
--- a/toolkit/devtools/server/actors/script.js
+++ b/toolkit/devtools/server/actors/script.js
@@ -1,13 +1,14 @@
 /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; js-indent-level: 2; -*- */
 /* vim: set ft=javascript 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/. */
+
 "use strict";
 
 let B2G_ID = "{3c2e2abc-06d4-11e1-ac3b-374f68613e61}";
 
 let TYPED_ARRAY_CLASSES = ["Uint8Array", "Uint8ClampedArray", "Uint16Array",
       "Uint32Array", "Int8Array", "Int16Array", "Int32Array", "Float32Array",
       "Float64Array"];
 
@@ -549,17 +550,17 @@ ThreadActor.prototype = {
   get prettyPrintWorker() {
     if (!this._prettyPrintWorker) {
       this._prettyPrintWorker = new ChromeWorker(
         "resource://gre/modules/devtools/server/actors/pretty-print-worker.js");
 
       this._prettyPrintWorker.addEventListener(
         "error", this._onPrettyPrintError, false);
 
-      if (wantLogging) {
+      if (dumpn.wantLogging) {
         this._prettyPrintWorker.addEventListener("message", this._onPrettyPrintMsg, false);
 
         const postMsg = this._prettyPrintWorker.postMessage;
         this._prettyPrintWorker.postMessage = data => {
           dumpn("Sending message to prettyPrintWorker: "
                 + JSON.stringify(data, null, 2) + "\n");
           return postMsg.call(this._prettyPrintWorker, data);
         };
--- a/toolkit/devtools/server/actors/string.js
+++ b/toolkit/devtools/server/actors/string.js
@@ -1,16 +1,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/. */
 
 "use strict";
 
 let {Cu} = require("chrome");
-let {DebuggerServer} = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
+let {DebuggerServer} = require("devtools/server/main");
 
 let {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
 let {Class} = require("sdk/core/heritage");
 
 let protocol = require("devtools/server/protocol");
 let {method, Arg, Option, RetVal} = protocol;
 
 exports.LongStringActor = protocol.ActorClass({
--- a/toolkit/devtools/server/actors/tracer.js
+++ b/toolkit/devtools/server/actors/tracer.js
@@ -1,16 +1,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/. */
 
 "use strict";
 
 const { Cu } = require("chrome");
-const { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
+const { DebuggerServer } = require("devtools/server/main");
 const { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
 
 Cu.import("resource://gre/modules/jsdebugger.jsm");
 addDebuggerToGlobal(this);
 
 // TODO bug 943125: remove this polyfill and use Debugger.Frame.prototype.depth
 // once it is implemented.
 if (!Object.getOwnPropertyDescriptor(Debugger.Frame.prototype, "depth")) {
--- a/toolkit/devtools/server/actors/webbrowser.js
+++ b/toolkit/devtools/server/actors/webbrowser.js
@@ -1,27 +1,37 @@
 /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ft=javascript 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/. */
 
 "use strict";
 
-let {Cu} = require("chrome");
+let {Ci,Cu} = require("chrome");
+let {createExtraActors, appendExtraActors} = require("devtools/server/actors/common");
+let DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
+
 let {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
 XPCOMUtils.defineLazyModuleGetter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
 
 // Assumptions on events module:
 // events needs to be dispatched synchronously,
 // by calling the listeners in the order or registration.
 XPCOMUtils.defineLazyGetter(this, "events", () => {
   return devtools.require("sdk/event/core");
 });
 
+// Also depends on following symbols, shared by common scope with main.js:
+// DebuggerServer, CommonCreateExtraActors, CommonAppendExtraActors, ActorPool,
+// ThreadActor
+
 /**
  * Browser-specific actors.
  */
 
 /**
  * Yield all windows of type |aWindowType|, from the oldest window to the
  * youngest, using nsIWindowMediator::getEnumerator. We're usually
  * interested in "navigator:browser" windows.
@@ -656,18 +666,18 @@ TabActor.prototype = {
       this.conn.send({ from: this.actorID,
                        type: "tabDetached" });
     }
 
     this._exited = true;
   },
 
   /* Support for DebuggerServer.addTabActor. */
-  _createExtraActors: CommonCreateExtraActors,
-  _appendExtraActors: CommonAppendExtraActors,
+  _createExtraActors: createExtraActors,
+  _appendExtraActors: appendExtraActors,
 
   /**
    * Does the actual work of attching to a tab.
    */
   _attach: function BTA_attach() {
     if (this._attached) {
       return;
     }
@@ -1035,19 +1045,25 @@ TabActor.prototype = {
    * the page.
    *
    * @param nsIDOMWindow aWindow
    *        The window object you want to check.
    * @return boolean
    *         True if the window.console object is native, or false otherwise.
    */
   hasNativeConsoleAPI: function BTA_hasNativeConsoleAPI(aWindow) {
-    // Do not expose WebConsoleActor function directly as it is always
-    // loaded after the BrowserTabActor
-    return WebConsoleActor.prototype.hasNativeConsoleAPI(aWindow);
+    let isNative = false;
+    try {
+      // We are very explicitly examining the "console" property of
+      // the non-Xrayed object here.
+      let console = aWindow.wrappedJSObject.console;
+      isNative = console instanceof aWindow.Console;
+    }
+    catch (ex) { }
+    return isNative;
   }
 };
 
 /**
  * The request types this actor can handle.
  */
 TabActor.prototype.requestTypes = {
   "attach": TabActor.prototype.onAttach,
--- a/toolkit/devtools/server/actors/webconsole.js
+++ b/toolkit/devtools/server/actors/webconsole.js
@@ -1,48 +1,54 @@
 /* -*- js2-basic-offset: 2; indent-tabs-mode: nil; -*- */
 /* 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/. */
 
 "use strict";
 
-let Cc = Components.classes;
-let Ci = Components.interfaces;
-let Cu = Components.utils;
+let {Cc, Ci, Cu} = require("chrome");
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
-let devtools = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
+let { DebuggerServer, ActorPool } = require("devtools/server/main");
+// Symbols from script.js
+let { ThreadActor, EnvironmentActor, ObjectActor, LongStringActor } = DebuggerServer;
+
+Cu.import("resource://gre/modules/jsdebugger.jsm");
+addDebuggerToGlobal(this);
 
 XPCOMUtils.defineLazyModuleGetter(this, "Services",
                                   "resource://gre/modules/Services.jsm");
 XPCOMUtils.defineLazyGetter(this, "NetworkMonitor", () => {
-  return devtools.require("devtools/toolkit/webconsole/network-monitor")
+  return require("devtools/toolkit/webconsole/network-monitor")
          .NetworkMonitor;
 });
 XPCOMUtils.defineLazyGetter(this, "NetworkMonitorChild", () => {
-  return devtools.require("devtools/toolkit/webconsole/network-monitor")
+  return require("devtools/toolkit/webconsole/network-monitor")
          .NetworkMonitorChild;
 });
 XPCOMUtils.defineLazyGetter(this, "ConsoleProgressListener", () => {
-  return devtools.require("devtools/toolkit/webconsole/network-monitor")
+  return require("devtools/toolkit/webconsole/network-monitor")
          .ConsoleProgressListener;
 });
+XPCOMUtils.defineLazyGetter(this, "events", () => {
+  return require("sdk/event/core");
+});
 
 for (let name of ["WebConsoleUtils", "ConsoleServiceListener",
                   "ConsoleAPIListener", "JSTermHelpers", "JSPropertyProvider",
                   "ConsoleReflowListener"]) {
   Object.defineProperty(this, name, {
     get: function(prop) {
       if (prop == "WebConsoleUtils") {
         prop = "Utils";
       }
-      return devtools.require("devtools/toolkit/webconsole/utils")[prop];
+      return require("devtools/toolkit/webconsole/utils")[prop];
     }.bind(null, name),
     configurable: true,
     enumerable: true
   });
 }
 
 
 /**
@@ -1826,11 +1832,17 @@ NetworkEventActor.prototype.requestTypes
   "getRequestCookies": NetworkEventActor.prototype.onGetRequestCookies,
   "getRequestPostData": NetworkEventActor.prototype.onGetRequestPostData,
   "getResponseHeaders": NetworkEventActor.prototype.onGetResponseHeaders,
   "getResponseCookies": NetworkEventActor.prototype.onGetResponseCookies,
   "getResponseContent": NetworkEventActor.prototype.onGetResponseContent,
   "getEventTimings": NetworkEventActor.prototype.onGetEventTimings,
 };
 
-DebuggerServer.addTabActor(WebConsoleActor, "consoleActor");
-DebuggerServer.addGlobalActor(WebConsoleActor, "consoleActor");
+exports.register = function(handle) {
+  handle.addGlobalActor(WebConsoleActor, "consoleActor");
+  handle.addTabActor(WebConsoleActor, "consoleActor");
+};
 
+exports.unregister = function(handle) {
+  handle.removeGlobalActor(WebConsoleActor, "consoleActor");
+  handle.removeTabActor(WebConsoleActor, "consoleActor");
+};
--- a/toolkit/devtools/server/main.js
+++ b/toolkit/devtools/server/main.js
@@ -7,17 +7,19 @@
 "use strict";
 /**
  * Toolkit glue for the remote debugging protocol, loaded into the
  * debugging global.
  */
 let { Ci, Cc, CC, Cu, Cr } = require("chrome");
 let Debugger = require("Debugger");
 let Services = require("Services");
-let DevToolsUtils = require("devtools/toolkit/DevToolsUtils.js");
+let { ActorPool } = require("devtools/server/actors/common");
+let { DebuggerTransport, LocalDebuggerTransport, ChildDebuggerTransport } = require("devtools/server/transport");
+let DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 let { dumpn, dbg_assert } = DevToolsUtils;
 let Services = require("Services");
 let EventEmitter = require("devtools/toolkit/event-emitter");
 
 // Until all Debugger server code is converted to SDK modules,
 // imports Components.* alias from chrome module.
 var { Ci, Cc, CC, Cu, Cr } = require("chrome");
 // On B2G, `this` != Global scope, so `Ci` won't be binded on `this`
@@ -25,31 +27,33 @@ var { Ci, Cc, CC, Cu, Cr } = require("ch
 // Ci,... won't be defined for sub scripts.
 this.Ci = Ci;
 this.Cc = Cc;
 this.CC = CC;
 this.Cu = Cu;
 this.Cr = Cr;
 this.Debugger = Debugger;
 this.Services = Services;
+this.ActorPool = ActorPool;
 this.DevToolsUtils = DevToolsUtils;
 this.dumpn = dumpn;
 this.dbg_assert = dbg_assert;
 
 // Overload `Components` to prevent SDK loader exception on Components
 // object usage
 Object.defineProperty(this, "Components", {
   get: function () require("chrome").components
 });
 
 const DBG_STRINGS_URI = "chrome://global/locale/devtools/debugger.properties";
 
 const nsFile = CC("@mozilla.org/file/local;1", "nsIFile", "initWithPath");
 Cu.import("resource://gre/modules/reflect.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
 dumpn.wantLogging = Services.prefs.getBoolPref("devtools.debugger.log");
 
 Cu.import("resource://gre/modules/devtools/deprecated-sync-thenables.js");
 
 function loadSubScript(aURL)
 {
   try {
     let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
@@ -76,18 +80,16 @@ Cu.import("resource://gre/modules/devtoo
 
 XPCOMUtils.defineLazyModuleGetter(this, "console",
                                   "resource://gre/modules/devtools/Console.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "NetworkMonitorManager", () => {
   return require("devtools/toolkit/webconsole/network-monitor").NetworkMonitorManager;
 });
 
-loadSubScript.call(this, "resource://gre/modules/devtools/server/transport.js");
-
 // XPCOM constructors
 const ServerSocket = CC("@mozilla.org/network/server-socket;1",
                         "nsIServerSocket",
                         "initSpecialConnection");
 const UnixDomainServerSocket = CC("@mozilla.org/network/server-socket;1",
                                   "nsIServerSocket",
                                   "initWithFilename");
 
@@ -354,34 +356,34 @@ var DebuggerServer = {
 
   /**
    * Install tab actors in documents loaded in content childs
    */
   addChildActors: function () {
     // In case of apps being loaded in parent process, DebuggerServer is already
     // initialized and browser actors are already loaded,
     // but childtab.js hasn't been loaded yet.
-    if (!("WebConsoleActor" in this)) {
+    if (!DebuggerServer.tabActorFactories.hasOwnProperty("consoleActor")) {
       this.addTabActors();
     }
     // But webbrowser.js and childtab.js aren't loaded from shell.js.
     if (!("BrowserTabActor" in this)) {
       this.addActors("resource://gre/modules/devtools/server/actors/webbrowser.js");
     }
     if (!("ContentActor" in this)) {
       this.addActors("resource://gre/modules/devtools/server/actors/childtab.js");
     }
   },
 
   /**
    * Install tab actors.
    */
   addTabActors: function() {
     this.addActors("resource://gre/modules/devtools/server/actors/script.js");
-    this.addActors("resource://gre/modules/devtools/server/actors/webconsole.js");
+    this.registerModule("devtools/server/actors/webconsole");
     this.registerModule("devtools/server/actors/inspector");
     this.registerModule("devtools/server/actors/call-watcher");
     this.registerModule("devtools/server/actors/canvas");
     this.registerModule("devtools/server/actors/webgl");
     this.registerModule("devtools/server/actors/webaudio");
     this.registerModule("devtools/server/actors/stylesheets");
     this.registerModule("devtools/server/actors/styleeditor");
     this.registerModule("devtools/server/actors/storage");
@@ -817,108 +819,20 @@ var DebuggerServer = {
 EventEmitter.decorate(DebuggerServer);
 
 if (this.exports) {
   exports.DebuggerServer = DebuggerServer;
 }
 // Needed on B2G (See header note)
 this.DebuggerServer = DebuggerServer;
 
-/**
- * Construct an ActorPool.
- *
- * ActorPools are actorID -> actor mapping and storage.  These are
- * used to accumulate and quickly dispose of groups of actors that
- * share a lifetime.
- */
-function ActorPool(aConnection)
-{
-  this.conn = aConnection;
-  this._cleanups = {};
-  this._actors = {};
-}
-
+// Export ActorPool for requirers of main.js
 if (this.exports) {
   exports.ActorPool = ActorPool;
 }
-// Needed on B2G (See header note)
-this.ActorPool = ActorPool;
-
-ActorPool.prototype = {
-  /**
-   * Add an actor to the actor pool.  If the actor doesn't have an ID,
-   * allocate one from the connection.
-   *
-   * @param aActor object
-   *        The actor implementation.  If the object has a
-   *        'disconnect' property, it will be called when the actor
-   *        pool is cleaned up.
-   */
-  addActor: function AP_addActor(aActor) {
-    aActor.conn = this.conn;
-    if (!aActor.actorID) {
-      let prefix = aActor.actorPrefix;
-      if (typeof aActor == "function") {
-        // typeName is a convention used with protocol.js-based actors
-        prefix = aActor.prototype.actorPrefix || aActor.prototype.typeName;
-      }
-      aActor.actorID = this.conn.allocID(prefix || undefined);
-    }
-
-    if (aActor.registeredPool) {
-      aActor.registeredPool.removeActor(aActor);
-    }
-    aActor.registeredPool = this;
-
-    this._actors[aActor.actorID] = aActor;
-    if (aActor.disconnect) {
-      this._cleanups[aActor.actorID] = aActor;
-    }
-  },
-
-  get: function AP_get(aActorID) {
-    return this._actors[aActorID];
-  },
-
-  has: function AP_has(aActorID) {
-    return aActorID in this._actors;
-  },
-
-  /**
-   * Returns true if the pool is empty.
-   */
-  isEmpty: function AP_isEmpty() {
-    return Object.keys(this._actors).length == 0;
-  },
-
-  /**
-   * Remove an actor from the actor pool.
-   */
-  removeActor: function AP_remove(aActor) {
-    delete this._actors[aActor.actorID];
-    delete this._cleanups[aActor.actorID];
-  },
-
-  /**
-   * Match the api expected by the protocol library.
-   */
-  unmanage: function(aActor) {
-    return this.removeActor(aActor);
-  },
-
-  /**
-   * Run all actor cleanups.
-   */
-  cleanup: function AP_cleanup() {
-    for each (let actor in this._cleanups) {
-      actor.disconnect();
-    }
-    this._cleanups = {};
-  }
-}
 
 /**
  * Creates a DebuggerServerConnection.
  *
  * Represents a connection to this debugging global from a client.
  * Manages a set of actors and actor pools, allocates actor ids, and
  * handles incoming requests.
  *
--- a/toolkit/devtools/server/tests/unit/head_dbg.js
+++ b/toolkit/devtools/server/tests/unit/head_dbg.js
@@ -3,18 +3,19 @@
 
 "use strict";
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+const Services = devtools.require("Services");
+const { ActorPool, createExtraActors, appendExtraActors } = devtools.require("devtools/server/actors/common");
 const DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils.js");
-const Services = devtools.require("Services");
 const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
 
 // Always log packets when running tests. runxpcshelltests.py will throw
 // the output away anyway, unless you give it the --verbose flag.
 Services.prefs.setBoolPref("devtools.debugger.log", true);
 // Enable remote debugging for the relevant tests.
 Services.prefs.setBoolPref("devtools.debugger.remote-enabled", true);
 
--- a/toolkit/devtools/server/tests/unit/testactors.js
+++ b/toolkit/devtools/server/tests/unit/testactors.js
@@ -102,16 +102,16 @@ TestTabActor.prototype = {
   onDetach: function(aRequest) {
     if (!this._attached) {
       return { "error":"wrongState" };
     }
     return { type: "detached" };
   },
 
   /* Support for DebuggerServer.addTabActor. */
-  _createExtraActors: CommonCreateExtraActors,
-  _appendExtraActors: CommonAppendExtraActors
+  _createExtraActors: createExtraActors,
+  _appendExtraActors: appendExtraActors
 };
 
 TestTabActor.prototype.requestTypes = {
   "attach": TestTabActor.prototype.onAttach,
   "detach": TestTabActor.prototype.onDetach
 };
--- a/toolkit/devtools/server/transport.js
+++ b/toolkit/devtools/server/transport.js
@@ -1,18 +1,38 @@
 /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ft=javascript 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/. */
 
+// TODO: Get rid of this code once the marionette server loads transport.js as
+// an SDK module (see bug 1000814)
+(function (factory) { // Module boilerplate
+  if (this.module && module.id.indexOf("transport") >= 0) { // require
+    factory(require, exports);
+  } else { // loadSubScript
+    if (this.require) {
+      factory(require, this);
+    } else {
+      const Cu = Components.utils;
+      const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+      factory(devtools.require, this);
+    }
+  }
+}).call(this, function (require, exports) {
+
 "use strict";
-Components.utils.import("resource://gre/modules/NetUtil.jsm");
 
-let wantLogging = Services.prefs.getBoolPref("devtools.debugger.log");
+const { Cc, Ci, Cr, Cu } = require("chrome");
+const Services = require("Services");
+const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
+const { dumpn } = DevToolsUtils;
+
+Cu.import("resource://gre/modules/NetUtil.jsm");
 
 /**
  * An adapter that handles data transfers between the debugger client and
  * server. It can work with both nsIPipe and nsIServerSocket transports so
  * long as the properly created input and output streams are specified.
  * (However, for intra-process connections, LocalDebuggerTransport, below,
  * is more efficient than using an nsIPipe pair with DebuggerTransport.)
  *
@@ -38,17 +58,17 @@ let wantLogging = Services.prefs.getBool
  * - onClosed(status) - called when the connection is closed. |Status| is
  *   an nsresult, of the sort passed to nsIRequestObserver.
  *
  * Data is transferred as a JSON packet serialized into a string, with the
  * string length prepended to the packet, followed by a colon
  * ([length]:[packet]). The contents of the JSON packet are specified in
  * the Remote Debugging Protocol specification.
  */
-this.DebuggerTransport = function DebuggerTransport(aInput, aOutput)
+function DebuggerTransport(aInput, aOutput)
 {
   this._input = aInput;
   this._output = aOutput;
 
   this._converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
     .createInstance(Ci.nsIScriptableUnicodeConverter);
   this._converter.charset = "UTF-8";
 
@@ -93,17 +113,17 @@ DebuggerTransport.prototype = {
     }
   },
 
   onOutputStreamReady:
   DevToolsUtils.makeInfallible(function DT_onOutputStreamReady(aStream) {
     let written = 0;
     try {
       written = aStream.write(this._outgoing, this._outgoing.length);
-    } catch(e if e.result == Components.results.NS_BASE_STREAM_CLOSED) {
+    } catch(e if e.result == Cr.NS_BASE_STREAM_CLOSED) {
       dumpn("Connection closed.");
       this.close();
       return;
     }
     this._outgoing = this._outgoing.slice(written);
     this._flushOutgoing();
   }, "DebuggerTransport.prototype.onOutputStreamReady"),
 
@@ -185,45 +205,46 @@ DebuggerTransport.prototype = {
       let msg = "Error parsing incoming packet: " + packet + " (" + e + " - " + e.stack + ")";
       if (Cu.reportError) {
         Cu.reportError(msg);
       }
       dump(msg + "\n");
       return true;
     }
 
-    if (wantLogging) {
+    if (dumpn.wantLogging) {
       dumpn("Got: " + JSON.stringify(parsed, null, 2));
     }
     let self = this;
     Services.tm.currentThread.dispatch(DevToolsUtils.makeInfallible(function() {
       // Ensure the hooks are still around by the time this runs (they will go
       // away when the transport is closed).
       if (self.hooks) {
         self.hooks.onPacket(parsed);
       }
     }, "DebuggerTransport instance's this.hooks.onPacket"), 0);
 
     return true;
   }
 }
 
+exports.DebuggerTransport = DebuggerTransport;
 
 /**
  * An adapter that handles data transfers between the debugger client and
  * server when they both run in the same process. It presents the same API as
  * DebuggerTransport, but instead of transmitting serialized messages across a
  * connection it merely calls the packet dispatcher of the other side.
  *
  * @param aOther LocalDebuggerTransport
  *        The other endpoint for this debugger connection.
  *
  * @see DebuggerTransport
  */
-this.LocalDebuggerTransport = function LocalDebuggerTransport(aOther)
+function LocalDebuggerTransport(aOther)
 {
   this.other = aOther;
   this.hooks = null;
 
   /*
    * A packet number, shared between this and this.other. This isn't used
    * by the protocol at all, but it makes the packet traces a lot easier to
    * follow.
@@ -233,30 +254,30 @@ this.LocalDebuggerTransport = function L
 
 LocalDebuggerTransport.prototype = {
   /**
    * Transmit a message by directly calling the onPacket handler of the other
    * endpoint.
    */
   send: function LDT_send(aPacket) {
     let serial = this._serial.count++;
-    if (wantLogging) {
+    if (dumpn.wantLogging) {
       /* Check 'from' first, as 'echo' packets have both. */
       if (aPacket.from) {
         dumpn("Packet " + serial + " sent from " + uneval(aPacket.from));
       } else if (aPacket.to) {
         dumpn("Packet " + serial + " sent to " + uneval(aPacket.to));
       }
     }
     this._deepFreeze(aPacket);
     let other = this.other;
     if (other) {
       Services.tm.currentThread.dispatch(DevToolsUtils.makeInfallible(function() {
         // Avoid the cost of JSON.stringify() when logging is disabled.
-        if (wantLogging) {
+        if (dumpn.wantLogging) {
           dumpn("Received packet " + serial + ": " + JSON.stringify(aPacket, null, 2));
         }
         if (other.hooks) {
           other.hooks.onPacket(aPacket);
         }
       }, "LocalDebuggerTransport instance's this.other.hooks.onPacket"), 0);
     }
   },
@@ -271,17 +292,17 @@ LocalDebuggerTransport.prototype = {
       let other = this.other;
       this.other = null;
       other.close();
     }
     if (this.hooks) {
       try {
         this.hooks.onClosed();
       } catch(ex) {
-        Components.utils.reportError(ex);
+        Cu.reportError(ex);
       }
       this.hooks = null;
     }
   },
 
   /**
    * An empty method for emulating the DebuggerTransport API.
    */
@@ -300,32 +321,34 @@ LocalDebuggerTransport.prototype = {
       if (aObject.hasOwnProperty(prop) && typeof aObject === "object" &&
           !Object.isFrozen(aObject)) {
         this._deepFreeze(o[prop]);
       }
     }
   }
 };
 
+exports.LocalDebuggerTransport = LocalDebuggerTransport;
+
 /**
  * A transport for the debugging protocol that uses nsIMessageSenders to
  * exchange packets with servers running in child processes.
  *
  * In the parent process, |aSender| should be the nsIMessageSender for the
  * child process. In a child process, |aSender| should be the child process
  * message manager, which sends packets to the parent.
  *
  * aPrefix is a string included in the message names, to distinguish
  * multiple servers running in the same child process.
  *
  * This transport exchanges messages named 'debug:<prefix>:packet', where
  * <prefix> is |aPrefix|, whose data is the protocol packet.
  */
 function ChildDebuggerTransport(aSender, aPrefix) {
-  this._sender = aSender.QueryInterface(Components.interfaces.nsIMessageSender);
+  this._sender = aSender.QueryInterface(Ci.nsIMessageSender);
   this._messageName = "debug:" + aPrefix + ":packet";
 }
 
 /*
  * To avoid confusion, we use 'message' to mean something that
  * nsIMessageSender conveys, and 'packet' to mean a remote debugging
  * protocol packet.
  */
@@ -346,8 +369,12 @@ ChildDebuggerTransport.prototype = {
   receiveMessage: function ({data}) {
     this.hooks.onPacket(data);
   },
 
   send: function (packet) {
     this._sender.sendAsyncMessage(this._messageName, packet);
   }
 };
+
+exports.ChildDebuggerTransport = ChildDebuggerTransport;
+
+});
--- a/toolkit/webapps/NativeApp.jsm
+++ b/toolkit/webapps/NativeApp.jsm
@@ -178,17 +178,17 @@ CommonNativeApp.prototype = {
    */
   _getIcon: function(aTmpDir) {
     try {
       // If the icon is in the zip package, we should modify the url
       // to point to the zip file (we can't use the app protocol yet
       // because the app isn't installed yet).
       if (this.iconURI.scheme == "app") {
         let zipUrl = OS.Path.toFileURI(OS.Path.join(aTmpDir,
-                                                    "application.zip"));
+                                                    this.zipFile));
 
         let filePath = this.iconURI.QueryInterface(Ci.nsIURL).filePath;
 
         this.iconURI = Services.io.newURI("jar:" + zipUrl + "!" + filePath,
                                           null, null);
       }