Merge m-c to inbound.
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 05 Sep 2012 17:52:28 -0400
changeset 107940 6268138eb19d0b482f694ff65d1cf07d994ed2c6
parent 107939 a101c20b729dd0d6cb8ea33f3acbbbb09ca7f3ca (current diff)
parent 107840 627f6297acea92a8d3bab0989cb3242df02b92a6 (diff)
child 107941 f9922b42205abb3191f5012ba0196f9ef837f393
push idunknown
push userunknown
push dateunknown
milestone18.0a1
Merge m-c to inbound.
browser/base/content/browser.js
dom/base/nsGlobalWindow.cpp
dom/devicestorage/test/Makefile.in
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -86,17 +86,17 @@
     <command id="cmd_fullZoomToggle"  oncommand="ZoomManager.toggleZoom();"/>
     <command id="Browser:OpenLocation" oncommand="openLocation();"/>
 
     <command id="Tools:Search" oncommand="BrowserSearch.webSearch();"/>
     <command id="Tools:Downloads" oncommand="BrowserDownloadsUI();"/>
     <command id="Tools:DevToolbar" oncommand="DeveloperToolbar.toggle();" disabled="true" hidden="true"/>
     <command id="Tools:DevToolbarFocus" oncommand="DeveloperToolbar.focus();" disabled="true"/>
     <command id="Tools:WebConsole" oncommand="HUDConsoleUI.toggleHUD();"/>
-    <command id="Tools:Inspect" oncommand="InspectorUI.toggleInspectorUI();" disabled="true" hidden="true"/>
+    <command id="Tools:Inspect" oncommand="InspectorUI.toggleInspectorUI();"/>
     <command id="Tools:Debugger" oncommand="DebuggerUI.toggleDebugger();" disabled="true" hidden="true"/>
     <command id="Tools:RemoteDebugger" oncommand="DebuggerUI.toggleRemoteDebugger();" disabled="true" hidden="true"/>
     <command id="Tools:ChromeDebugger" oncommand="DebuggerUI.toggleChromeDebugger();" disabled="true" hidden="true"/>
     <command id="Tools:Scratchpad" oncommand="Scratchpad.openScratchpad();" disabled="true" hidden="true"/>
     <command id="Tools:StyleEditor" oncommand="StyleEditor.toggle();" disabled="true" hidden="true"/>
     <command id="Tools:ResponsiveUI" oncommand="ResponsiveUI.toggle();" disabled="true" hidden="true"/>
     <command id="Tools:Addons" oncommand="BrowserOpenAddonsMgr();"/>
     <command id="Tools:ErrorConsole" oncommand="toJavaScriptConsole()" disabled="true" hidden="true"/>
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -600,34 +600,34 @@ html|*#gcli-output-frame,
 /* We don't show the Style Editor button in the developer toolbar for now.
    See bug 771203 */
 #developer-toolbar-styleeditor {
   display: none;
 }
 
 /* Responsive Mode */
 
-vbox[anonid=browserContainer][responsivemode] {
+.browserContainer[responsivemode] {
   overflow: auto;
 }
 
 .devtools-responsiveui-toolbar:-moz-locale-dir(rtl) {
   -moz-box-pack: end;
 }
 
-stack[anonid=browserStack][responsivemode] {
+.browserStack[responsivemode] {
   transition-duration: 200ms;
   transition-timing-function: linear;
 }
 
-stack[anonid=browserStack][responsivemode] {
+.browserStack[responsivemode] {
   transition-property: min-width, max-width, min-height, max-height;
 }
 
-stack[anonid=browserStack][responsivemode][notransition] {
+.browserStack[responsivemode][notransition] {
   transition: none;
 }
 
 chatbox {
   -moz-binding: url("chrome://browser/content/socialchat.xml#chatbox");
 }
 
 chatbar {
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1429,24 +1429,16 @@ var gBrowserInit = {
       document.getElementById("Tools:DevToolbarFocus").removeAttribute("disabled");
 
       // Show the toolbar if it was previously visible
       if (gPrefService.getBoolPref("devtools.toolbar.visible")) {
         DeveloperToolbar.show(false);
       }
     }
 
-    // Enable Inspector?
-    let enabled = gPrefService.getBoolPref("devtools.inspector.enabled");
-    if (enabled) {
-      let cmd = document.getElementById("Tools:Inspect");
-      cmd.removeAttribute("disabled");
-      cmd.removeAttribute("hidden");
-    }
-
     // Enable Debugger?
     let enabled = gPrefService.getBoolPref("devtools.debugger.enabled");
     if (enabled) {
       let cmd = document.getElementById("Tools:Debugger");
       cmd.removeAttribute("disabled");
       cmd.removeAttribute("hidden");
     }
 
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -21,22 +21,24 @@
 
     <content>
       <xul:stringbundle anonid="tbstringbundle" src="chrome://browser/locale/tabbrowser.properties"/>
       <xul:tabbox anonid="tabbox" class="tabbrowser-tabbox"
                   flex="1" eventnode="document" xbl:inherits="handleCtrlPageUpDown"
                   onselect="if (event.target.localName == 'tabpanels') this.parentNode.updateCurrentBrowser();">
         <xul:tabpanels flex="1" class="plain" selectedIndex="0" anonid="panelcontainer">
           <xul:notificationbox flex="1">
-            <xul:vbox flex="1" anonid="browserContainer">
-              <xul:stack flex="1" anonid="browserStack">
-                <xul:browser type="content-primary" message="true" disablehistory="true"
-                             xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu,autocompletepopup"/>
-              </xul:stack>
-            </xul:vbox>
+            <xul:hbox flex="1" class="browserSidebarContainer">
+              <xul:vbox flex="1" class="browserContainer">
+                <xul:stack flex="1" class="browserStack">
+                  <xul:browser anonid="initialBrowser" type="content-primary" message="true" disablehistory="true"
+                               xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu,autocompletepopup"/>
+                </xul:stack>
+              </xul:vbox>
+            </xul:hbox>
           </xul:notificationbox>
         </xul:tabpanels>
       </xul:tabbox>
       <children/>
     </content>
     <implementation implements="nsIDOMEventListener">
 
       <property name="tabContextMenu" readonly="true"
@@ -290,16 +292,25 @@
         ]]>
         </body>
       </method>
 
       <method name="getNotificationBox">
         <parameter name="aBrowser"/>
         <body>
           <![CDATA[
+            return this.getSidebarContainer(aBrowser).parentNode;
+          ]]>
+        </body>
+      </method>
+
+      <method name="getSidebarContainer">
+        <parameter name="aBrowser"/>
+        <body>
+          <![CDATA[
             return this.getBrowserContainer(aBrowser).parentNode;
           ]]>
         </body>
       </method>
 
       <method name="getBrowserContainer">
         <parameter name="aBrowser"/>
         <body>
@@ -1287,34 +1298,42 @@
             if (this.hasAttribute("autocompletepopup"))
               b.setAttribute("autocompletepopup", this.getAttribute("autocompletepopup"));
             b.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
 
             // Create the browserStack container
             var stack = document.createElementNS(
                                     "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
                                     "stack");
-            stack.setAttribute("anonid", "browserStack");
+            stack.className = "browserStack";
             stack.appendChild(b);
             stack.setAttribute("flex", "1");
 
             // Create the browserContainer
-            var box = document.createElementNS(
+            var browserContainer = document.createElementNS(
                                     "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
                                     "vbox");
-            box.setAttribute("anonid", "browserContainer");
-            box.appendChild(stack);
-            box.setAttribute("flex", "1");
+            browserContainer.className = "browserContainer";
+            browserContainer.appendChild(stack);
+            browserContainer.setAttribute("flex", "1");
+
+            // Create the sidebar container
+            var browserSidebarContainer = document.createElementNS(
+                                      "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
+                                      "hbox");
+            browserSidebarContainer.className = "browserSidebarContainer";
+            browserSidebarContainer.appendChild(browserContainer);
+            browserSidebarContainer.setAttribute("flex", "1");
 
             // Add the Message and the Browser to the box
             var notificationbox = document.createElementNS(
                                     "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
                                     "notificationbox");
             notificationbox.setAttribute("flex", "1");
-            notificationbox.appendChild(box);
+            notificationbox.appendChild(browserSidebarContainer);
 
             var position = this.tabs.length - 1;
             var uniqueId = "panel" + Date.now() + position;
             notificationbox.id = uniqueId;
             t.linkedPanel = uniqueId;
             t.linkedBrowser = b;
             t._tPos = position;
             if (t.previousSibling.selected)
@@ -1749,17 +1768,17 @@
             // browser removal. So we remove the browser and the panel in two
             // steps.
 
             var panel = this.getNotificationBox(browser);
 
             // This will unload the document. An unload handler could remove
             // dependant tabs, so it's important that the tabbrowser is now in
             // a consistent state (tab removed, tab positions updated, etc.).
-            panel.removeChild(this.getBrowserContainer(browser));
+            browser.parentNode.removeChild(browser);
 
             // Release the browser in case something is erroneously holding a
             // reference to the tab after its removal.
             aTab.linkedBrowser = null;
 
             // As the browser is removed, the removal of a dependent document can
             // cause the whole window to close. So at this point, it's possible
             // that the binding is destructed.
@@ -2554,17 +2573,17 @@
               }
               break;
           }
         ]]></body>
       </method>
 
       <constructor>
         <![CDATA[
-          this.mCurrentBrowser = this.mPanelContainer.firstChild.firstChild.firstChild.firstChild;
+          this.mCurrentBrowser = document.getAnonymousElementByAttribute(this, "anonid", "initialBrowser");
           this.mCurrentTab = this.tabContainer.firstChild;
           document.addEventListener("keypress", this, false);
           window.addEventListener("sizemodechange", this, false);
 
           var uniqueId = "panel" + Date.now();
           this.mPanelContainer.childNodes[0].id = uniqueId;
           this.mCurrentTab.linkedPanel = uniqueId;
           this.mCurrentTab._tPos = 0;
--- a/browser/base/content/test/browser_bug462673.js
+++ b/browser/base/content/test/browser_bug462673.js
@@ -10,17 +10,17 @@ var runs = [
     var newBrowser = newTab.linkedBrowser;
     tabbrowser.removeTab(tab);
     ok(!win.closed, "Window stays open");
     if (!win.closed) {
       is(tabbrowser.tabContainer.childElementCount, 1, "Window has one tab");
       is(tabbrowser.browsers.length, 1, "Window has one browser");
       is(tabbrowser.selectedTab, newTab, "Remaining tab is selected");
       is(tabbrowser.selectedBrowser, newBrowser, "Browser for remaining tab is selected");
-      is(tabbrowser.mTabBox.selectedPanel, newBrowser.parentNode.parentNode.parentNode, "Panel for remaining tab is selected");
+      is(tabbrowser.mTabBox.selectedPanel, newBrowser.parentNode.parentNode.parentNode.parentNode, "Panel for remaining tab is selected");
     }
   }
 ];
 
 function test() {
   waitForExplicitFinish();
   runOneTest();
 }
--- a/browser/devtools/highlighter/highlighter.jsm
+++ b/browser/devtools/highlighter/highlighter.jsm
@@ -143,28 +143,28 @@ Highlighter.prototype = {
 
     // The controlsBox will host the different interactive
     // elements of the highlighter (buttons, toolbars, ...).
     let controlsBox = this.chromeDoc.createElement("box");
     controlsBox.id = "highlighter-controls";
     this.highlighterContainer.appendChild(outlineContainer);
     this.highlighterContainer.appendChild(controlsBox);
 
-    stack.appendChild(this.highlighterContainer);
-
-    this.showOutline();
+    // Insert the highlighter right after the browser
+    stack.insertBefore(this.highlighterContainer, stack.childNodes[1]);
 
     this.buildInfobar(controlsBox);
 
     this.transitionDisabler = null;
     this.pageEventsMuter = null;
 
-    this.computeZoomFactor();
     this.unlock();
-    this.hide();
+
+    this.hidden = true;
+    this.show();
   },
 
   /**
    * Destroy the nodes. Remove listeners.
    */
   destroy: function Highlighter_destroy()
   {
     this.detachMouseListeners();
@@ -215,16 +215,20 @@ Highlighter.prototype = {
     }
 
     if (oldNode !== this.node) {
       this.updateInfobar();
     }
 
     this.invalidateSize(!!aScroll);
 
+    if (this._highlighting) {
+      this.showOutline();
+    }
+
     if (oldNode !== this.node) {
       this.emitEvent("nodeselected");
     }
   },
 
   /**
    * Notify that a pseudo-class lock was toggled on the highlighted element
    *
--- a/browser/devtools/highlighter/inspector.jsm
+++ b/browser/devtools/highlighter/inspector.jsm
@@ -891,16 +891,20 @@ InspectorUI.prototype = {
   {
     if (!this.inspecting) {
       return;
     }
 
     this.inspectCommand.setAttribute("checked", "false");
 
     this.inspecting = false;
+
+    if (this.closing)
+      return;
+
     if (this.highlighter.getNode()) {
       this.select(this.highlighter.getNode(), true, !aPreventScroll);
     } else {
       this.select(null, true, true);
     }
 
     this.highlighter.lock();
     this._notifySelected();
--- a/browser/devtools/responsivedesign/responsivedesign.jsm
+++ b/browser/devtools/responsivedesign/responsivedesign.jsm
@@ -88,17 +88,17 @@ let presets = [
 function ResponsiveUI(aWindow, aTab)
 {
   this.mainWindow = aWindow;
   this.tab = aTab;
   this.tabContainer = aWindow.gBrowser.tabContainer;
   this.browser = aTab.linkedBrowser;
   this.chromeDoc = aWindow.document;
   this.container = aWindow.gBrowser.getBrowserContainer(this.browser);
-  this.stack = this.container.querySelector("[anonid=browserStack]");
+  this.stack = this.container.querySelector(".browserStack");
 
   // Try to load presets from prefs
   if (Services.prefs.prefHasUserValue("devtools.responsiveUI.presets")) {
     try {
       presets = JSON.parse(Services.prefs.getCharPref("devtools.responsiveUI.presets"));
     } catch(e) {
       // User pref is malformated.
       Cu.reportError("Could not parse pref `devtools.responsiveUI.presets`: " + e);
@@ -277,22 +277,22 @@ ResponsiveUI.prototype = {
    */
    unCheckMenus: function RUI_unCheckMenus() {
      this.chromeDoc.getElementById("Tools:ResponsiveUI").setAttribute("checked", "false");
    },
 
   /**
    * Build the toolbar and the resizers.
    *
-   * <vbox anonid="browserContainer"> From tabbrowser.xml
+   * <vbox class="browserContainer"> From tabbrowser.xml
    *  <toolbar class="devtools-toolbar devtools-responsiveui-toolbar">
    *    <menulist class="devtools-menulist"/> // presets
    *    <toolbarbutton tabindex="0" class="devtools-toolbarbutton" label="rotate"/> // rotate
    *  </toolbar>
-   *  <stack anonid="browserStack"> From tabbrowser.xml
+   *  <stack class="browserStack"> From tabbrowser.xml
    *    <browser/>
    *    <box class="devtools-responsiveui-resizehandle" bottom="0" right="0"/>
    *    <box class="devtools-responsiveui-resizebar" top="0" right="0"/>
    *  </stack>
    * </vbox>
    */
   buildUI: function RUI_buildUI() {
     // Toolbar
--- a/browser/devtools/tilt/test/head.js
+++ b/browser/devtools/tilt/test/head.js
@@ -56,24 +56,21 @@ const BEFORE_DESTROYED = Tilt.NOTIFICATI
 const DESTROYED = Tilt.NOTIFICATIONS.DESTROYED;
 const SHOWN = Tilt.NOTIFICATIONS.SHOWN;
 const HIDDEN = Tilt.NOTIFICATIONS.HIDDEN;
 const HIGHLIGHTING = Tilt.NOTIFICATIONS.HIGHLIGHTING;
 const UNHIGHLIGHTING = Tilt.NOTIFICATIONS.UNHIGHLIGHTING;
 const NODE_REMOVED = Tilt.NOTIFICATIONS.NODE_REMOVED;
 
 const TILT_ENABLED = Services.prefs.getBoolPref("devtools.tilt.enabled");
-const INSP_ENABLED = Services.prefs.getBoolPref("devtools.inspector.enabled");
 
 
 function isTiltEnabled() {
-  let enabled = TILT_ENABLED && INSP_ENABLED;
-
-  info("Apparently, Tilt is" + (enabled ? "" : " not") + " enabled.");
-  return enabled;
+  info("Apparently, Tilt is" + (TILT_ENABLED ? "" : " not") + " enabled.");
+  return TILT_ENABLED;
 }
 
 function isWebGLSupported() {
   let supported = !TiltGL.isWebGLForceEnabled() &&
                    TiltGL.isWebGLSupported() &&
                    TiltGL.create3DContext(createCanvas());
 
   info("Apparently, WebGL is" + (supported ? "" : " not") + " supported.");
--- a/browser/themes/gnomestripe/browser.css
+++ b/browser/themes/gnomestripe/browser.css
@@ -2482,23 +2482,23 @@ html|*#gcli-output-frame {
 }
 
 .gcli-in-closebrace {
   color: hsl(0,0%,80%);
 }
 
 /* Responsive Mode */
 
-vbox[anonid=browserContainer][responsivemode] {
+.browserContainer[responsivemode] {
   background: #2a3643 url("chrome://browser/skin/devtools/responsive-background.png");
   box-shadow: 0 0 7px black inset;
   padding: 0 20px 20px 20px;
 }
 
-stack[anonid=browserStack][responsivemode] {
+.browserStack[responsivemode] {
   box-shadow: 0 0 7px black;
 }
 
 .devtools-responsiveui-toolbar {
   background: transparent;
   margin: 10px 0;
   padding: 0;
   box-shadow: none;
--- a/browser/themes/pinstripe/browser.css
+++ b/browser/themes/pinstripe/browser.css
@@ -3201,23 +3201,23 @@ html|*#gcli-output-frame {
 }
 
 .gcli-in-closebrace {
   color: hsl(0,0%,80%);
 }
 
 /* Responsive Mode */
 
-vbox[anonid=browserContainer][responsivemode] {
+.browserContainer[responsivemode] {
   background: #2a3643 url("chrome://browser/skin/devtools/responsive-background.png");
   box-shadow: 0 0 7px black inset;
   padding: 0 20px 20px 20px;
 }
 
-stack[anonid=browserStack][responsivemode] {
+.browserStack[responsivemode] {
   box-shadow: 0 0 7px black;
 }
 
 .devtools-responsiveui-toolbar {
   background: transparent;
   margin: 10px 0;
   padding: 0;
   box-shadow: none;
--- a/browser/themes/winstripe/browser.css
+++ b/browser/themes/winstripe/browser.css
@@ -3162,23 +3162,23 @@ html|*#gcli-output-frame {
 }
 
 .gcli-in-closebrace {
   color: hsl(0,0%,80%);
 }
 
 /* Responsive Mode */
 
-vbox[anonid=browserContainer][responsivemode] {
+.browserContainer[responsivemode] {
   background: #2a3643 url("chrome://browser/skin/devtools/responsive-background.png");
   box-shadow: 0 0 7px black inset;
   padding: 0 20px 20px 20px;
 }
 
-stack[anonid=browserStack][responsivemode] {
+.browserStack[responsivemode] {
   box-shadow: 0 0 7px black;
 }
 
 .devtools-responsiveui-toolbar {
   background: transparent;
   margin: 10px 0;
   padding: 0;
   box-shadow: none;
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -6795,19 +6795,21 @@ nsGlobalWindow::LeaveModalState(nsIDOMWi
         do_QueryInterface(topWin->GetExtantDocument());
       mSuspendedDoc->UnsuppressEventHandlingAndFireEvents(currentDoc == mSuspendedDoc);
       mSuspendedDoc = nullptr;
     }
   }
 
   if (aCallerWin) {
     nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryInterface(aCallerWin));
-    nsIScriptContext *scx = sgo->GetContext();
-    if (scx)
-      scx->LeaveModalState();
+    if (sgo) {
+      nsIScriptContext *scx = sgo->GetContext();
+      if (scx)
+        scx->LeaveModalState();
+    }
   }
 
   if (mContext) {
     mContext->LeaveModalState();
   }
 
   // Remember the time of the last dialog quit.
   nsGlobalWindow *inner = topWin->GetCurrentInnerWindowInternal();
--- a/dom/devicestorage/DeviceStorage.h
+++ b/dom/devicestorage/DeviceStorage.h
@@ -45,16 +45,19 @@ private:
 
   nsresult EnumerateInternal(const JS::Value & aName,
                              const JS::Value & aOptions,
                              JSContext* aCx,
                              uint8_t aArgc, 
                              bool aEditable, 
                              nsIDOMDeviceStorageCursor** aRetval);
 
+  static bool IsMimeTypeCorrectForStorageType(nsAString& aType,
+					      nsIDOMBlob* aBlob);
+
   nsString mStorageType;
   nsCOMPtr<nsIFile> mRootDirectory;
 
   nsCOMPtr<nsIPrincipal> mPrincipal;
 
   bool mIsWatchingFile;
   bool mAllowedToWatchFile;
 
--- a/dom/devicestorage/DeviceStorageRequestParent.cpp
+++ b/dom/devicestorage/DeviceStorageRequestParent.cpp
@@ -308,17 +308,17 @@ DeviceStorageRequestParent::StatFileEven
 
 nsresult
 DeviceStorageRequestParent::StatFileEvent::CancelableRun()
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
   nsCOMPtr<nsIRunnable> r;
   uint64_t diskUsage = 0;
-  DeviceStorageFile::DirectoryDiskUsage(mFile->mFile, &diskUsage);
+  DeviceStorageFile::DirectoryDiskUsage(mFile->mFile, &diskUsage, mFile->mStorageType);
   int64_t freeSpace = 0;
   nsresult rv = mFile->mFile->GetDiskSpaceAvailable(&freeSpace);
   if (NS_FAILED(rv)) {
     freeSpace = 0;
   }
   
   r = new PostStatResultEvent(mParent, freeSpace, diskUsage);
   NS_DispatchToMainThread(r);
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -34,29 +34,27 @@
 #include "nsCRT.h"
 #include "mozilla/Services.h"
 #include "nsIObserverService.h"
 #include "GeneratedEvents.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "nsIMIMEService.h"
 #include "nsCExternalHandlerService.h"
 
+#include "nsIStringBundle.h"
+
 // Microsoft's API Name hackery sucks
 #undef CreateEvent
 
 #ifdef MOZ_WIDGET_GONK
 #include "nsIVolume.h"
 #include "nsIVolumeService.h"
 #endif
 
-#define DEBUG_ISTYPE 1
-
-#ifdef DEBUG_ISTYPE
-#include "nsIConsoleService.h"
-#endif
+#define DEVICESTORAGE_PROPERTIES "chrome://global/content/devicestorage.properties"
 
 using namespace mozilla::dom;
 using namespace mozilla::dom::devicestorage;
 
 #include "nsDirectoryServiceDefs.h"
 
 class IOEventComplete : public nsRunnable
 {
@@ -79,17 +77,19 @@ public:
     return NS_OK;
   }
 
 private:
   nsRefPtr<DeviceStorageFile> mFile;
   nsCString mType;
 };
 
-DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType, nsIFile* aFile, const nsAString& aPath)
+DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType,
+				     nsIFile* aFile,
+				     const nsAString& aPath)
   : mPath(aPath)
   , mStorageType(aStorageType)
   , mEditable(false)
 {
   NS_ASSERTION(aFile, "Must not create a DeviceStorageFile with a null nsIFile");
   // always take a clone
   nsCOMPtr<nsIFile> file;
   aFile->Clone(getter_AddRefs(mFile));
@@ -146,82 +146,50 @@ DeviceStorageFile::IsSafePath()
         PL_strcmp(token, "..") == 0 ) {
       return false;
     }
   }
   return true;
 }
 
 bool
-DeviceStorageFile::IsType(nsAString& aType)
+DeviceStorageFile::IsType(nsIFile* aFile, const nsAString& aStorageType)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  // in testing, we ignore filtering for the testing types
-  if (mozilla::Preferences::GetBool("device.storage.testing", false) &&
-      (aType.Equals(NS_LITERAL_STRING("testing")) ||
-       aType.Equals(NS_LITERAL_STRING("testing-other")))) {
-    return true;
+  // String bundles are cached by the bundle service.
+  nsCOMPtr<nsIStringBundleService> stringService = mozilla::services::GetStringBundleService();
+  if (!stringService) {
+    return false;
   }
 
-#ifdef DEBUG_ISTYPE
-  nsCOMPtr<nsIConsoleService> svc = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
-  char buffer[1024];
-  nsCString path;
-  mFile->GetNativePath(path);
-
-  PRIntervalTime iStart = PR_IntervalNow();
-#endif
-
-  nsAutoCString mimeType;
-  nsCOMPtr<nsIMIMEService> mimeService = do_GetService(NS_MIMESERVICE_CONTRACTID);
-  if (!mimeService) {
+  nsCOMPtr<nsIStringBundle> filterBundle;
+  if (NS_FAILED(stringService->CreateBundle(DEVICESTORAGE_PROPERTIES,
+					    getter_AddRefs(filterBundle)))) {
     return false;
   }
 
-  nsresult rv = mimeService->GetTypeFromFile(mFile, mimeType);
-  if (NS_FAILED(rv)) {
-#ifdef DEBUG_ISTYPE
-    sprintf(buffer, "GetTypeFromFile failed for %s (took: %dms)\n",
-	    path.get(),
-	    PR_IntervalToMilliseconds(PR_IntervalNow() - iStart));
+  nsString path;
+  aFile->GetPath(path);
 
-    nsString data;
-    CopyASCIItoUTF16(buffer, data);
-    svc->LogStringMessage(data.get());
-    printf("%s\n", buffer);
-#endif
+  int32_t dotIdx = path.RFindChar(PRUnichar('.'));
+  if (dotIdx == kNotFound) {
     return false;
   }
 
-#ifdef DEBUG_ISTYPE
-  sprintf(buffer, "IsType of %s is %s (took: %dms)\n",
-	  path.get(),
-	  mimeType.get(),
-	  PR_IntervalToMilliseconds(PR_IntervalNow() - iStart));
+  nsAutoString extensionMatch;
+  extensionMatch.AssignASCII("*");
+  extensionMatch.Append(Substring(path, dotIdx));
+  extensionMatch.AppendASCII(";");
 
-  nsString data;
-  CopyASCIItoUTF16(buffer, data);
-  svc->LogStringMessage(data.get());
-  printf("%s\n", buffer);
-#endif
-
-  if (aType.Equals(NS_LITERAL_STRING("pictures"))) {
-    return StringBeginsWith(mimeType, NS_LITERAL_CSTRING("image/"));
+  nsString extensionListStr;
+  if (NS_FAILED(filterBundle->GetStringFromName(aStorageType.BeginReading(),
+						getter_Copies(extensionListStr)))) {
+    return false;
   }
 
-  if (aType.Equals(NS_LITERAL_STRING("videos"))) {
-    return StringBeginsWith(mimeType, NS_LITERAL_CSTRING("video/"));
-  }
-
-  if (aType.Equals(NS_LITERAL_STRING("music"))) {
-    return StringBeginsWith(mimeType, NS_LITERAL_CSTRING("audio/"));
-  }
-
-  return false;
+  return FindInReadable(extensionMatch, extensionListStr);
 }
 
 void
 DeviceStorageFile::NormalizeFilePath() {
 #if defined(XP_WIN)
   PRUnichar* cur = mPath.BeginWriting();
   PRUnichar* end = mPath.EndWriting();
   for (; cur < end; ++cur) {
@@ -345,17 +313,17 @@ DeviceStorageFile::Remove()
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
   bool check;
   nsresult rv = mFile->Exists(&check);
   if (NS_FAILED(rv)) {
     return rv;
   }
-  
+
   if (!check) {
     return NS_OK;
   }
 
   rv = mFile->Remove(true);
   if (NS_FAILED(rv)) {
     return rv;
   }
@@ -430,17 +398,17 @@ DeviceStorageFile::collectFilesInternal(
       nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, f);
       dsf->SetPath(newPath);
       aFiles.AppendElement(dsf);
     }
   }
 }
 
 void
-DeviceStorageFile::DirectoryDiskUsage(nsIFile* aFile, uint64_t* aSoFar)
+DeviceStorageFile::DirectoryDiskUsage(nsIFile* aFile, uint64_t* aSoFar, const nsAString& aStorageType)
 {
   if (!aFile) {
     return;
   }
 
   nsresult rv;
   nsCOMPtr<nsISimpleEnumerator> e;
   rv = aFile->GetDirectoryEntries(getter_AddRefs(e));
@@ -466,22 +434,28 @@ DeviceStorageFile::DirectoryDiskUsage(ns
       continue;
     }
 
     bool isLink;
     rv = f->IsSymlink(&isLink);
     if (NS_FAILED(rv)) {
       continue;
     }
+
     if (isLink) {
       // for now, lets just totally ignore symlinks.
       NS_WARNING("DirectoryDiskUsage ignores symlinks");
     } else if (isDir) {
-      DirectoryDiskUsage(f, aSoFar);
+      DirectoryDiskUsage(f, aSoFar, aStorageType);
     } else if (isFile) {
+
+      if (!DeviceStorageFile::IsType(f, aStorageType)) {
+	continue;
+      }
+
       int64_t size;
       rv = f->GetFileSize(&size);
       if (NS_SUCCEEDED(rv)) {
 	*aSoFar += size;
       }
     }
   }
 }
@@ -542,53 +516,54 @@ nsDOMDeviceStorage::SetRootDirectoryForT
   // Picture directory
   if (aType.Equals(NS_LITERAL_STRING("pictures"))) {
 #ifdef MOZ_WIDGET_GONK
     NS_NewLocalFile(NS_LITERAL_STRING("/sdcard"), false, getter_AddRefs(f));
 #elif defined (MOZ_WIDGET_COCOA)
     dirService->Get(NS_OSX_PICTURE_DOCUMENTS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
 #elif defined (XP_UNIX)
     dirService->Get(NS_UNIX_XDG_PICTURES_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
+#elif defined (XP_WIN)
+    dirService->Get(NS_WIN_PERSONAL_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
 #endif
   }
 
   // Video directory
   else if (aType.Equals(NS_LITERAL_STRING("videos"))) {
 #ifdef MOZ_WIDGET_GONK
     NS_NewLocalFile(NS_LITERAL_STRING("/sdcard"), false, getter_AddRefs(f));
 #elif defined (MOZ_WIDGET_COCOA)
     dirService->Get(NS_OSX_MOVIE_DOCUMENTS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
 #elif defined (XP_UNIX)
     dirService->Get(NS_UNIX_XDG_VIDEOS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
+#elif defined (XP_WIN)
+    dirService->Get(NS_WIN_PERSONAL_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
 #endif
   }
 
   // Music directory
   else if (aType.Equals(NS_LITERAL_STRING("music"))) {
 #ifdef MOZ_WIDGET_GONK
     NS_NewLocalFile(NS_LITERAL_STRING("/sdcard"), false, getter_AddRefs(f));
 #elif defined (MOZ_WIDGET_COCOA)
     dirService->Get(NS_OSX_MUSIC_DOCUMENTS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
 #elif defined (XP_UNIX)
     dirService->Get(NS_UNIX_XDG_MUSIC_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
+#elif defined (XP_WIN)
+    dirService->Get(NS_WIN_PERSONAL_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
 #endif
   }
 
-  // in testing, we have access to a few more directory locations
-  if (mozilla::Preferences::GetBool("device.storage.testing", false)) {
-
-    // testing directory
-    if (aType.Equals(NS_LITERAL_STRING("testing")) ||
-	aType.Equals(NS_LITERAL_STRING("testing-other"))) {
-      dirService->Get(NS_OS_TEMP_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
-      if (f) {
-        f->AppendRelativeNativePath(NS_LITERAL_CSTRING("device-storage-testing"));
-        f->Create(nsIFile::DIRECTORY_TYPE, 0777);
-        f->Normalize();
-      }
+  // in testing, we default all device storage types to a temp directory
+  if (f && mozilla::Preferences::GetBool("device.storage.testing", false)) {
+    dirService->Get(NS_OS_TEMP_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
+    if (f) {
+      f->AppendRelativeNativePath(NS_LITERAL_CSTRING("device-storage-testing"));
+      f->Create(nsIFile::DIRECTORY_TYPE, 0777);
+      f->Normalize();
     }
   }
 
 #ifdef MOZ_WIDGET_GONK
   RegisterForSDCardChanges(this);
 #endif
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
@@ -803,17 +778,17 @@ ContinueCursorEvent::Run() {
 
   nsDOMDeviceStorageCursor* cursor = static_cast<nsDOMDeviceStorageCursor*>(mRequest.get());
   nsString cursorStorageType;
   cursor->GetStorageType(cursorStorageType);
 
   while (cursor->mFiles.Length() > 0) {
     nsRefPtr<DeviceStorageFile> file = cursor->mFiles[0];
     cursor->mFiles.RemoveElementAt(0);
-    if (!file->IsType(cursorStorageType)) {
+    if (!DeviceStorageFile::IsType(file->mFile, cursorStorageType)) {
       continue;
     }
     val = nsIFileToJsval(cursor->GetOwner(), file);
     cursor->mOkToCallContinue = true;
     break;
   }
 
   mRequest->FireSuccess(val);
@@ -1217,17 +1192,17 @@ public:
 
   ~StatFileEvent() {}
 
   NS_IMETHOD Run()
   {
     NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
     nsCOMPtr<nsIRunnable> r;
     uint64_t diskUsage = 0;
-    DeviceStorageFile::DirectoryDiskUsage(mFile->mFile, &diskUsage);
+    DeviceStorageFile::DirectoryDiskUsage(mFile->mFile, &diskUsage, mFile->mStorageType);
     int64_t freeSpace = 0;
     nsresult rv = mFile->mFile->GetDiskSpaceAvailable(&freeSpace);
     if (NS_FAILED(rv)) {
       freeSpace = 0;
     }
 
     r = new PostStatResultEvent(mRequest, freeSpace, diskUsage);
     NS_DispatchToMainThread(r);
@@ -1572,27 +1547,75 @@ nsDOMDeviceStorage::CreateDeviceStorages
                                             nsDOMDeviceStorage** aStore)
 {
   nsRefPtr<nsDOMDeviceStorage> storage = new nsDOMDeviceStorage();
   if (NS_SUCCEEDED(storage->Init(aWin, aType))) {
     NS_ADDREF(*aStore = storage);
   }
 }
 
+bool
+nsDOMDeviceStorage::IsMimeTypeCorrectForStorageType(nsAString& aType, nsIDOMBlob* aBlob)
+{
+  NS_ASSERTION(aBlob, "Calling IsMimeTypeCorrectForStorageType without a blob");
+
+  nsString mimeType;
+  if (NS_FAILED(aBlob->GetType(mimeType))) {
+    return false;
+  }
+
+  if (aType.Equals(NS_LITERAL_STRING("pictures"))) {
+    return StringBeginsWith(mimeType, NS_LITERAL_STRING("image/"));
+  }
+
+  if (aType.Equals(NS_LITERAL_STRING("videos"))) {
+    return StringBeginsWith(mimeType, NS_LITERAL_STRING("video/"));
+  }
+
+  if (aType.Equals(NS_LITERAL_STRING("music"))) {
+    return StringBeginsWith(mimeType, NS_LITERAL_STRING("audio/"));
+  }
+
+  return false;
+}
+
 NS_IMETHODIMP
 nsDOMDeviceStorage::Add(nsIDOMBlob *aBlob, nsIDOMDOMRequest * *_retval)
 {
+  if (!aBlob) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIMIMEService> mimeSvc = do_GetService(NS_MIMESERVICE_CONTRACTID);
+  if (!mimeSvc) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // if mimeType isn't set, we will not get a correct
+  // extension, and AddNamed() will fail.  This will post an
+  // onerror to the requestee.
+  nsString mimeType;
+  aBlob->GetType(mimeType);
+
+  nsCString extension;
+  mimeSvc->GetPrimaryExtension(NS_LossyConvertUTF16toASCII(mimeType), EmptyCString(), extension);
+  // if extension is null here, we will ignore it for now.
+  // AddNamed() will check the file path and fail.  This
+  // will post an onerror to the requestee.
+
   // possible race here w/ unique filename
   char buffer[128];
-  NS_MakeRandomString(buffer, 128);
+  NS_MakeRandomString(buffer, ArrayLength(buffer));
 
-  nsString path;
-  path.AssignWithConversion(nsDependentCString(buffer));
+  nsAutoCString path;
+  path.Assign(nsDependentCString(buffer));
+  path.Append(".");
+  path.Append(extension);
 
-  return AddNamed(aBlob, path, _retval);
+  return AddNamed(aBlob, NS_ConvertASCIItoUTF16(path), _retval);
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorage::AddNamed(nsIDOMBlob *aBlob,
                              const nsAString & aPath,
                              nsIDOMDOMRequest * *_retval)
 {
   // if the blob is null here, bail
@@ -1605,24 +1628,27 @@ nsDOMDeviceStorage::AddNamed(nsIDOMBlob 
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
   NS_ADDREF(*_retval = request);
 
   nsCOMPtr<nsIRunnable> r;
 
   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDirectory, aPath);
-
-  if (!dsf->IsSafePath()) {
+  if (!DeviceStorageFile::IsType(dsf->mFile, mStorageType) || !IsMimeTypeCorrectForStorageType(mStorageType, aBlob)) {
+    r = new PostErrorEvent(request, POST_ERROR_EVENT_ILLEGAL_TYPE, dsf);
+  }
+  else if (!dsf->IsSafePath()) {
     r = new PostErrorEvent(request, POST_ERROR_EVENT_ILLEGAL_FILE_NAME, dsf);
   }
   else {
     r = new DeviceStorageRequest(DeviceStorageRequest::DEVICE_STORAGE_REQUEST_WRITE,
-                                 win, mPrincipal, dsf, request, aBlob);
+				 win, mPrincipal, dsf, request, aBlob);
   }
+
   NS_DispatchToMainThread(r);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorage::Get(const JS::Value & aPath,
                         JSContext* aCx,
                         nsIDOMDOMRequest * *_retval)
@@ -1662,17 +1688,16 @@ nsDOMDeviceStorage::GetInternal(const JS
                            POST_ERROR_EVENT_NON_STRING_TYPE_UNSUPPORTED,
                            dsf);
     NS_DispatchToMainThread(r);
     return NS_OK;
   }
 
   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDirectory, path);
   dsf->SetEditable(aEditable);
-
   if (!dsf->IsSafePath()) {
     r = new PostErrorEvent(request, POST_ERROR_EVENT_ILLEGAL_FILE_NAME, dsf);
   } else {
     r = new DeviceStorageRequest(DeviceStorageRequest::DEVICE_STORAGE_REQUEST_READ,
                                  win, mPrincipal, dsf, request);
   }
   NS_DispatchToMainThread(r);
   return NS_OK;
@@ -2128,9 +2153,9 @@ nsDOMDeviceStorage::GetContextForEventHa
 }
 
 JSContext *
 nsDOMDeviceStorage::GetJSContextForEventHandlers()
 {
   return nsDOMEventTargetHelper::GetJSContextForEventHandlers();
 }
 
-NS_IMPL_EVENT_HANDLER(nsDOMDeviceStorage, change)
\ No newline at end of file
+NS_IMPL_EVENT_HANDLER(nsDOMDeviceStorage, change)
--- a/dom/devicestorage/nsDeviceStorage.h
+++ b/dom/devicestorage/nsDeviceStorage.h
@@ -28,16 +28,17 @@ class nsPIDOMWindow;
 #include "mozilla/Mutex.h"
 #include "DeviceStorage.h"
 
 
 #define POST_ERROR_EVENT_FILE_DOES_NOT_EXIST         "File location doesn't exists"
 #define POST_ERROR_EVENT_FILE_NOT_ENUMERABLE         "File location is not enumerable"
 #define POST_ERROR_EVENT_PERMISSION_DENIED           "Permission Denied"
 #define POST_ERROR_EVENT_ILLEGAL_FILE_NAME           "Illegal file name"
+#define POST_ERROR_EVENT_ILLEGAL_TYPE                "Illegal content type"
 #define POST_ERROR_EVENT_UNKNOWN                     "Unknown"
 #define POST_ERROR_EVENT_NON_STRING_TYPE_UNSUPPORTED "Non-string type unsupported"
 #define POST_ERROR_EVENT_NOT_IMPLEMENTED             "Not implemented"
 
 using namespace mozilla::dom;
 
 class DeviceStorageFile MOZ_FINAL
   : public nsISupports {
@@ -52,25 +53,25 @@ public:
   void SetPath(const nsAString& aPath);
   void SetEditable(bool aEditable);
 
   NS_DECL_ISUPPORTS
 
   // we want to make sure that the names of file can't reach
   // outside of the type of storage the user asked for.
   bool IsSafePath();
-  bool IsType(nsAString& aType);
 
   nsresult Remove();
   nsresult Write(nsIInputStream* aInputStream);
   nsresult Write(InfallibleTArray<uint8_t>& bits);
   void CollectFiles(nsTArray<nsRefPtr<DeviceStorageFile> > &aFiles, uint64_t aSince = 0);
   void collectFilesInternal(nsTArray<nsRefPtr<DeviceStorageFile> > &aFiles, uint64_t aSince, nsAString& aRootPath);
 
-  static void DirectoryDiskUsage(nsIFile* aFile, uint64_t* aSoFar);
+  static bool IsType(nsIFile* aFile, const nsAString& aStorageType);
+  static void DirectoryDiskUsage(nsIFile* aFile, uint64_t* aSoFar, const nsAString& aStorageType);
 
 private:
   void NormalizeFilePath();
   void AppendRelativePath();
 };
 
 class ContinueCursorEvent MOZ_FINAL: public nsRunnable
 {
--- a/dom/devicestorage/test/Makefile.in
+++ b/dom/devicestorage/test/Makefile.in
@@ -5,22 +5,27 @@
 DEPTH		= @DEPTH@
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir	= @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
+#  man, our mime database sucks hard.  followup bug # 788273
+#		test_add.html \
+
 MOCHITEST_FILES	= \
 		test_sanity.html \
+		test_addCorrectType.html \
 		test_basic.html \
 		test_enumerate.html \
 		test_enumerateMultipleContinue.html \
 		test_overwrite.html \
+		test_diskSpace.html \
 		test_dotdot.html \
 		test_enumerateOptions.html \
 		test_lastModificationFilter.html \
 		test_stat.html \
 		test_watch.html \
 		test_watchOther.html \
 		devicestorage_common.js \
 		$(NULL)
--- a/dom/devicestorage/test/devicestorage_common.js
+++ b/dom/devicestorage/test/devicestorage_common.js
@@ -49,18 +49,18 @@ function getRandomBuffer() {
   var buffer = new ArrayBuffer(size);
   var view = new Uint8Array(buffer);
   for (var i = 0; i < size; i++) {
     view[i] = parseInt(Math.random() * 255);
   }
   return buffer;
 }
 
-function createRandomBlob() {
-  return blob = new Blob([getRandomBuffer()], {type: 'binary/random'});
+function createRandomBlob(mime) {
+  return blob = new Blob([getRandomBuffer()], {type: mime});
 }
 
 function randomFilename(l) {
   var set = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZ";
   var result = "";
   for (var i=0; i<l; i++) {
     var r = Math.floor(set.length * Math.random());
     result += set.substring(r, r + 1);
new file mode 100644
--- /dev/null
+++ b/dom/devicestorage/test/test_add.html
@@ -0,0 +1,68 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=786922
+-->
+<head>
+  <title>Test for basic sanity of the device storage API </title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="devicestorage_common.js"></script>
+
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=786922">Mozilla Bug 786922</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+
+devicestorage_setup();
+
+function add(storage, mime) {
+  dump("adding: " + mime + "\n");
+  return navigator.getDeviceStorage(storage).add(createRandomBlob(mime));
+}
+
+var tests = [
+  function () { return add("pictures", "image/png")},
+  function () { return add("videos",   "video/webm")},
+  function () { return add("music",    "audio/wav")},
+];
+
+function fail(e) {
+  ok(false, "onerror was called");
+  devicestorage_cleanup();
+}
+
+function next(e) {
+
+  if (e != undefined)
+    ok(true, "addError was called");
+  
+  var f = tests.pop();
+
+  if (f == undefined) {
+    devicestorage_cleanup();
+    return;
+  }
+
+  request = f();
+  request.onsuccess = next;
+  request.onerror = fail;
+}
+
+next();
+
+</script>
+</pre>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/dom/devicestorage/test/test_addCorrectType.html
@@ -0,0 +1,72 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=786922
+-->
+<head>
+  <title>Test for basic sanity of the device storage API </title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="devicestorage_common.js"></script>
+
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=786922">Mozilla Bug 786922</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+
+devicestorage_setup();
+
+function addNamed(storage, mime, fileExtension) {
+  dump("adding: " + mime + " " + fileExtension + "\n");
+  return navigator.getDeviceStorage(storage).addNamed(createRandomBlob(mime), randomFilename(40) + "." + fileExtension);
+}
+
+// These tests must all fail
+var tests = [
+  function () { return addNamed("pictures", "kyle/smash", ".png")},
+  function () { return addNamed("pictures", "image/png",  ".poo")},
+  function () { return addNamed("music",    "kyle/smash", ".mp3")},
+  function () { return addNamed("music",    "music/mp3",  ".poo")},
+  function () { return addNamed("videos",   "kyle/smash", ".ogv")},
+  function () { return addNamed("videos",   "video/ogv",  ".poo")},
+];
+
+function fail(e) {
+  ok(false, "addSuccess was called");
+  devicestorage_cleanup();
+}
+
+function next(e) {
+
+  if (e != undefined)
+    ok(true, "addError was called");
+  
+  var f = tests.pop();
+
+  if (f == undefined) {
+    devicestorage_cleanup();
+    return;
+  }
+
+  request = f();
+  request.onsuccess = fail;
+  request.onerror = next;
+}
+
+next();
+
+</script>
+</pre>
+</body>
+</html>
+
--- a/dom/devicestorage/test/test_basic.html
+++ b/dom/devicestorage/test/test_basic.html
@@ -28,19 +28,19 @@ function unload() {
 
   delete gFileReader;
   gFileReader = null;
 }
 
 
 devicestorage_setup();
 
-var gFileName = "devicestorage/hi";
+var gFileName = "devicestorage/hi.png";
 var gData = "My name is Doug Turner.  My IRC nick is DougT.  I like Maple cookies."
-var gDataBlob = new Blob([gData], {type: 'text/plain'});
+var gDataBlob = new Blob([gData], {type: 'image/png'});
 var gFileReader = new FileReader();
 
 function getAfterDeleteSuccess(e) {
   ok(false, "file was deleted not successfully");
   devicestorage_cleanup();
 }
 
 function getAfterDeleteError(e) {
@@ -48,30 +48,30 @@ function getAfterDeleteError(e) {
   devicestorage_cleanup();
 }
 
 function deleteSuccess(e) {
 
   ok(e.target.result == gFileName, "File name should match");
   dump(e.target.result + "\n")
 
-  var storage = navigator.getDeviceStorage("testing");
+  var storage = navigator.getDeviceStorage("pictures");
   request = storage.get(e.target.result);
   request.onsuccess = getAfterDeleteSuccess;
   request.onerror = getAfterDeleteError;
 
 }
 
 function deleteError(e) {
   ok(false, "deleteError was called : " + e.target.error.name);
   devicestorage_cleanup();
 }
 
 function getSuccess(e) {
-  var storage = navigator.getDeviceStorage("testing");
+  var storage = navigator.getDeviceStorage("pictures");
   ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
 
   ok(e.target.result.name == gFileName, "File name should match");
 
   var name = e.target.result.name;
 
   gFileReader.readAsArrayBuffer(gDataBlob);
   gFileReader.onload = function(e) {
@@ -96,35 +96,35 @@ function getError(e) {
   ok(false, "getError was called : " + e.target.error.name);
   devicestorage_cleanup();
 }
 
 function addSuccess(e) {
 
   ok(e.target.result == gFileName, "File name should match");
 
-  var storage = navigator.getDeviceStorage("testing");
+  var storage = navigator.getDeviceStorage("pictures");
   request = storage.get(gFileName);
   request.onsuccess = getSuccess;
   request.onerror = getError;
 
   ok(true, "addSuccess was called");
 }
 
 function addError(e) {
   ok(false, "addError was called : " + e.target.error.name);
   devicestorage_cleanup();
 }
 
 ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
 
-var storage = navigator.getDeviceStorage("testing");
+var storage = navigator.getDeviceStorage("pictures");
 ok(storage, "Should have gotten a storage");
 
-request = storage.addNamed(gDataBlob, "devicestorage/hi");
+request = storage.addNamed(gDataBlob, "devicestorage/hi.png");
 ok(request, "Should have a non-null request");
 
 request.onsuccess = addSuccess;
 request.onerror = addError;
 
 </script>
 </pre>
 </body>
new file mode 100644
--- /dev/null
+++ b/dom/devicestorage/test/test_diskSpace.html
@@ -0,0 +1,101 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html> <!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=717103
+-->
+<head>
+  <title>Test for the device storage API </title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="devicestorage_common.js"></script>
+
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717103">Mozilla Bug 717103</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+devicestorage_setup();
+
+
+var freeBytes = -1;
+var stats = 0;
+
+function stat(s, file_list_length) {
+  if (freeBytes == -1) {
+    freeBytes = s.target.result.freeBytes;
+  }
+
+  ok(freeBytes == s.target.result.freeBytes, "Free bytes should be the same");
+  ok(file_list_length * 1024 == s.target.result.totalBytes, "space taken up by files should match")
+
+  stats = stats + 1;
+
+  if (stats == 2) {
+    devicestorage_cleanup();
+  }
+}
+
+function addSuccess(e) {
+  added = added - 1;
+
+  if (added == 0) {
+    request = pictures.stat();
+    request.onsuccess = function(s) {stat(s, picture_files.length)};
+
+    request = videos.stat();
+    request.onsuccess = function(s) {stat(s, video_files.length)};
+
+    request = music.stat();
+    request.onsuccess = function(s) {stat(s, music_files.length)};
+  }
+}
+
+function addError(e) {
+  ok(false, "addError was called : " + e.target.error.name);
+  devicestorage_cleanup();
+}
+
+ok(true, "hi");
+
+var pictures = navigator.getDeviceStorage("pictures");
+var picture_files = [ "a.png", "b.png", "c.png", "d.png", "e.png" ];
+
+var videos = navigator.getDeviceStorage("videos");
+var video_files = [ "a.ogv", "b.ogv" ];
+
+var music = navigator.getDeviceStorage("music");
+var music_files = [ "a.mp3", "b.mp3", "c.mp3" ];
+
+var added = picture_files.length + video_files.length + music_files.length;
+
+for (var i=0; i < picture_files.length; i++) {
+ request = pictures.addNamed(createRandomBlob('image/png'), picture_files[i]);
+ request.onsuccess = addSuccess;
+ request.onerror = addError;
+}
+
+for (var i=0; i < video_files.length; i++) {
+ request = videos.addNamed(createRandomBlob('video/ogv'), video_files[i]);
+ request.onsuccess = addSuccess;
+ request.onerror = addError;
+}
+
+for (var i=0; i < music_files.length; i++) {
+ request = music.addNamed(createRandomBlob('audio/mp3'), music_files[i]);
+ request.onsuccess = addSuccess;
+ request.onerror = addError;
+}
+
+</script>
+</pre>
+</body>
+</html>
+
--- a/dom/devicestorage/test/test_dotdot.html
+++ b/dom/devicestorage/test/test_dotdot.html
@@ -20,27 +20,27 @@ https://bugzilla.mozilla.org/show_bug.cg
   
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 devicestorage_setup();
 
 function testingStorage() {
-  return navigator.getDeviceStorage("testing");
+  return navigator.getDeviceStorage("pictures");
 }
 
 var tests = [
-  function () { return testingStorage().addNamed(createRandomBlob(), gFileName); },
+  function () { return testingStorage().addNamed(createRandomBlob('image/png'), gFileName); },
   function () { return testingStorage().delete(gFileName); },
   function () { return testingStorage().get(gFileName); },
   function () { var r = testingStorage().enumerate("../"); return r; }
 ];
 
-var gFileName = "../owned";
+var gFileName = "../owned.png";
 
 function fail(e) {
   ok(false, "addSuccess was called");
   dump(request);
   devicestorage_cleanup();
 }
 
 function next(e) {
--- a/dom/devicestorage/test/test_enumerate.html
+++ b/dom/devicestorage/test/test_enumerate.html
@@ -62,27 +62,27 @@ function addSuccess(e) {
   }
 }
 
 function addError(e) {
   ok(false, "addError was called : " + e.target.error.name);
   devicestorage_cleanup();
 }
 
-var storage = navigator.getDeviceStorage("testing");
+var storage = navigator.getDeviceStorage("pictures");
 ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
-var prefix = "devicestorage/" + randomFilename(12)
+var prefix = "devicestorage/" + randomFilename(12) + ".png"
 
-var files = [ "a", "b", "c", "d/a", "d/b", "d/c", "d/d", "The/quick/brown/fox/jumps/over/the/lazy/dog"]
+var files = [ "a.png", "b.png", "c.png", "d/a.png", "d/b.png", "d/c.png", "d/d.png", "The/quick/brown/fox/jumps/over/the/lazy/dog.png"]
 var addedSoFar = 0;
 
 
 for (var i=0; i<files.length; i++) {
 
- request = storage.addNamed(createRandomBlob(), prefix + '/' + files[i]);
+ request = storage.addNamed(createRandomBlob('image/png'), prefix + '/' + files[i]);
 
  ok(request, "Should have a non-null request");
  request.onsuccess = addSuccess;
  request.onerror = addError;
 }
 
 </script>
 </pre>
--- a/dom/devicestorage/test/test_enumerateMultipleContinue.html
+++ b/dom/devicestorage/test/test_enumerateMultipleContinue.html
@@ -25,17 +25,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 devicestorage_setup();
 
 function enumerateSuccess(e) {
 }
 
 function enumerateFailure(e) {
 }
 
-var cursor = navigator.getDeviceStorage("testing").enumerate();
+var cursor = navigator.getDeviceStorage("pictures").enumerate();
 cursor.onsuccess = enumerateSuccess;
 cursor.onerror = enumerateFailure;
 
 try {
  cursor.continue();
 }
 catch (e) {
   ok(true, "Calling continue before enumerateSuccess fires should throw");
--- a/dom/devicestorage/test/test_enumerateNoParam.html
+++ b/dom/devicestorage/test/test_enumerateNoParam.html
@@ -66,27 +66,27 @@ function addSuccess(e) {
   }
 }
 
 function addError(e) {
   ok(false, "addError was called : " + e.target.error.name);
   devicestorage_cleanup();
 }
 
-var storage = navigator.getDeviceStorage("testing");
+var storage = navigator.getDeviceStorage("pictures");
 ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
 var prefix = "devicestorage/" + randomFilename(12)
 
-var files = [ "a", "b", "c" ]
+var files = [ "a.png", "b.png", "c.png" ]
 var addedSoFar = 0;
 
 
 for (var i=0; i<files.length; i++) {
 
- request = storage.addNamed(createRandomBlob(), prefix + '/' + files[i]);
+ request = storage.addNamed(createRandomBlob('image/png'), prefix + '/' + files[i]);
 
  ok(request, "Should have a non-null request");
  request.onsuccess = addSuccess;
  request.onerror = addError;
 }
 
 </script>
 </pre>
--- a/dom/devicestorage/test/test_enumerateOptions.html
+++ b/dom/devicestorage/test/test_enumerateOptions.html
@@ -20,17 +20,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 devicestorage_setup()
 
-storage = navigator.getDeviceStorage("testing");
+storage = navigator.getDeviceStorage("pictures");
 
 
 throws = false;
 try {
 var cursor = storage.enumerate();
 } catch(e) {throws = true}
 ok(!throws, "enumerate no parameter");
 
--- a/dom/devicestorage/test/test_lastModificationFilter.html
+++ b/dom/devicestorage/test/test_lastModificationFilter.html
@@ -39,17 +39,17 @@ function verifyAndDelete(prefix, files, 
   var index = files.indexOf(filename);
   ok(index > -1, "filename should be in the enumeration : " + e.target.result.name);
   if (index == -1)
     return;
 
   files.remove(index);
 
   // clean up
-  var storage = navigator.getDeviceStorage("testing");
+  var storage = navigator.getDeviceStorage("pictures");
   var cleanup = storage.delete(prefix + "/" + filename);
   cleanup.onsuccess = function(e) {}
 }
 
 function addFiles(prefix, files, date, callback) {
 
   const Cc = SpecialPowers.wrap(Components).classes;
   const Ci = Components.interfaces;
@@ -71,25 +71,25 @@ function addFiles(prefix, files, date, c
   callback();
 }
 
 
 devicestorage_setup();
 
 var prefix = "devicestorage/" + randomFilename(12)
 
-var oldFiles = ["a", "b", "c"];
-var newFiles = ["d", "e", "f"];
+var oldFiles = ["a.png", "b.png", "c.png"];
+var newFiles = ["d.png", "e.png", "f.png"];
 
 // 157795200 is a long long time ago.
 addFiles(prefix, oldFiles, 157795200, addNewFiles);
 
 function enumerateNew() {
 
-  var storage = navigator.getDeviceStorage("testing");
+  var storage = navigator.getDeviceStorage("pictures");
   ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
 
 // 836031600 is a long time ago
   var cursor = storage.enumerate(prefix, {"since": new Date(836031600)});
   cursor.onsuccess = function(e) {
     verifyAndDelete(prefix, newFiles, e);
     if (e.target.result) {
       e.target.continue();
--- a/dom/devicestorage/test/test_overwrite.html
+++ b/dom/devicestorage/test/test_overwrite.html
@@ -18,17 +18,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717103">Mozilla Bug 717103</a>
 <p id="display"></p>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-var filename = "devicestorage/aaaa"
+var filename = "devicestorage/aaaa.png"
 
 devicestorage_setup();
 
 
 function deleteSuccess(e) {
   devicestorage_cleanup();
 }
 
@@ -40,47 +40,47 @@ function deleteError(e) {
 function addOverwritingSuccess(e) {
   ok(false, "addOverwritingSuccess was called.");
   devicestorage_cleanup();
 }
 
 function addOverwritingError(e) {
   ok(true, "Adding to the same location should fail");
 
-  var storage = navigator.getDeviceStorage("testing");
+  var storage = navigator.getDeviceStorage("pictures");
   request = storage.delete(filename)
   request.onsuccess = deleteSuccess;
   request.onerror = deleteError;
 }
 
 function addSuccess(e) {
   ok(true, "addSuccess was called");
 
-  var storage = navigator.getDeviceStorage("testing");
+  var storage = navigator.getDeviceStorage("pictures");
   ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
 
-  request = storage.addNamed(createRandomBlob(), filename);
+  request = storage.addNamed(createRandomBlob('image/png'), filename);
   ok(request, "Should have a non-null request");
 
   request.onsuccess = addOverwritingSuccess;
   request.onerror = addOverwritingError;
 }
 
 function addError(e) {
   // test file is already exists.  clean it up and try again..
-  var storage = navigator.getDeviceStorage("testing");
+  var storage = navigator.getDeviceStorage("pictures");
   request = storage.delete(filename)
   request.onsuccess = runtest;
 }
 
 function runtest() {
-  var storage = navigator.getDeviceStorage("testing");
+  var storage = navigator.getDeviceStorage("pictures");
   ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
 
-  request = storage.addNamed(createRandomBlob(), filename);
+  request = storage.addNamed(createRandomBlob('image/png'), filename);
   ok(request, "Should have a non-null request");
 
   request.onsuccess = addSuccess;
   request.onerror = addError;
 }
 
 runtest();
 
--- a/dom/devicestorage/test/test_sanity.html
+++ b/dom/devicestorage/test/test_sanity.html
@@ -33,18 +33,24 @@ var throws = false;
 try {
  storage = navigator.getDeviceStorage();
 } catch(e) {throws = true}
 ok(throws, "getDeviceStorage takes one arg");
 
 storage = navigator.getDeviceStorage("kilimanjaro");
 ok(!storage, "kilimanjaro - Should not have this type of storage");
 
-storage = navigator.getDeviceStorage("testing");
-ok(storage, "testing - Should have getDeviceStorage");
+storage = navigator.getDeviceStorage("pictures");
+ok(storage, "pictures - Should have getDeviceStorage");
+
+storage = navigator.getDeviceStorage("music");
+ok(storage, "music - Should have getDeviceStorage");
+
+storage = navigator.getDeviceStorage("videos");
+ok(storage, "videos - Should have getDeviceStorage");
 
 var cursor = storage.enumerate();
 ok(cursor, "Should have a non-null cursor");
 
 devicestorage_cleanup();
 
 </script>
 </pre>
--- a/dom/devicestorage/test/test_stat.html
+++ b/dom/devicestorage/test/test_stat.html
@@ -30,33 +30,33 @@ function statSuccess(e) {
   devicestorage_cleanup();
 }
 
 function statError(e) {
   ok(false, "statError was called");
   devicestorage_cleanup();
 }
 
-var storage = navigator.getDeviceStorage("testing");
+var storage = navigator.getDeviceStorage("pictures");
 ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
 
 function addError(e) {
   ok(false, "addError was called : " + e.target.error.name);
   devicestorage_cleanup();
 }
 
 function addSuccess(e) {
   request = storage.stat();
   ok(request, "Should have a non-null request");
 
   request.onsuccess = statSuccess;
   request.onerror = statError;
 }
 
-request = storage.addNamed(createRandomBlob(), "a/b");
+request = storage.addNamed(createRandomBlob('image/png'), "a/b.png");
 request.onsuccess = addSuccess;
 request.onerror = addError;
 
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/devicestorage/test/test_watch.html
+++ b/dom/devicestorage/test/test_watch.html
@@ -19,17 +19,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 devicestorage_setup();
 
-var gFileName = randomFilename(20);
+var gFileName = randomFilename(20) + ".png"
 
 function addSuccess(e) {
 }
 
 function addError(e) {
   ok(false, "addError was called : " + e.target.error.name);
   devicestorage_cleanup();
 }
@@ -44,21 +44,21 @@ function onChange(e) {
     devicestorage_cleanup();
   }
   else {
     // we may see other file changes during the test, and
     // that is completely ok
   }
 }
 
-var storage = navigator.getDeviceStorage("testing");
+var storage = navigator.getDeviceStorage("pictures");
 ok(storage, "Should have storage");
 storage.addEventListener("change", onChange);
 
-request = storage.addNamed(createRandomBlob(), gFileName);
+request = storage.addNamed(createRandomBlob('image/png'), gFileName);
 ok(request, "Should have a non-null request");
 
 request.onsuccess = addSuccess;
 request.onerror = addError;
 
 </script>
 </pre>
 </body>
--- a/dom/devicestorage/test/test_watchOther.html
+++ b/dom/devicestorage/test/test_watchOther.html
@@ -19,17 +19,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 devicestorage_setup();
 
-var gFileName = randomFilename(20);
+var gFileName = randomFilename(20) + ".png"
 
 function addSuccess(e) {
 }
 
 function addError(e) {
   ok(false, "addError was called : " + e.target.error.name);
   devicestorage_cleanup();
 }
@@ -49,25 +49,25 @@ function onChange(e) {
   }
 }
 
 function onChangeFail(e) {
   dump("onChangeFail: " + e.path + " " + e.reason + "\n");
   ok(false, "We should never see any changes");
 }
 
-var storage = navigator.getDeviceStorage("testing");
+var storage = navigator.getDeviceStorage("pictures");
 ok(storage, "Should have storage");
 storage.addEventListener("change", onChange);
 
-var storageOther = navigator.getDeviceStorage("testing-other");
+var storageOther = navigator.getDeviceStorage("music");
 ok(storageOther, "Should have storage");
 storageOther.addEventListener("change", onChangeFail);
 
-request = storage.addNamed(createRandomBlob(), gFileName);
+request = storage.addNamed(createRandomBlob('image/png'), gFileName);
 ok(request, "Should have a non-null request");
 
 request.onsuccess = addSuccess;
 request.onerror = addError;
 
 </script>
 </pre>
 </body>
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -1887,18 +1887,17 @@ WorkerPrivateParent<Derived>::WorkerPriv
                                      WorkerPrivate* aParent,
                                      JSContext* aParentJSContext,
                                      const nsAString& aScriptURL,
                                      bool aIsChromeWorker,
                                      const nsACString& aDomain,
                                      nsCOMPtr<nsPIDOMWindow>& aWindow,
                                      nsCOMPtr<nsIScriptContext>& aScriptContext,
                                      nsCOMPtr<nsIURI>& aBaseURI,
-                                     nsCOMPtr<nsIPrincipal>& aPrincipal,
-                                     nsCOMPtr<nsIDocument>& aDocument)
+                                     nsCOMPtr<nsIPrincipal>& aPrincipal)
 : EventTarget(aParent ? aCx : NULL), mMutex("WorkerPrivateParent Mutex"),
   mCondVar(mMutex, "WorkerPrivateParent CondVar"),
   mJSObject(aObject), mParent(aParent), mParentJSContext(aParentJSContext),
   mScriptURL(aScriptURL), mDomain(aDomain), mBusyCount(0),
   mParentStatus(Pending), mJSContextOptions(0), mJSRuntimeHeapSize(0),
   mGCZeal(0), mJSObjectRooted(false), mParentSuspended(false),
   mIsChromeWorker(aIsChromeWorker), mPrincipalIsSystem(false),
   mMainThreadObjectsForgotten(false)
@@ -1909,17 +1908,16 @@ WorkerPrivateParent<Derived>::WorkerPriv
     NS_ASSERTION(aWindow->IsInnerWindow(), "Should have inner window here!");
   }
 
   mWindow.swap(aWindow);
   mScriptContext.swap(aScriptContext);
   mScriptNotify = do_QueryInterface(mScriptContext);
   mBaseURI.swap(aBaseURI);
   mPrincipal.swap(aPrincipal);
-  mDocument.swap(aDocument);
 
   if (aParent) {
     aParent->AssertIsOnWorkerThread();
 
     NS_ASSERTION(JS_GetOptions(aCx) == aParent->GetJSContextOptions(),
                  "Options mismatch!");
     mJSContextOptions = aParent->GetJSContextOptions();
 
@@ -2216,17 +2214,16 @@ WorkerPrivateParent<Derived>::ForgetMain
   aDoomed.SetCapacity(7);
 
   SwapToISupportsArray(mWindow, aDoomed);
   SwapToISupportsArray(mScriptContext, aDoomed);
   SwapToISupportsArray(mScriptNotify, aDoomed);
   SwapToISupportsArray(mBaseURI, aDoomed);
   SwapToISupportsArray(mScriptURI, aDoomed);
   SwapToISupportsArray(mPrincipal, aDoomed);
-  SwapToISupportsArray(mDocument, aDoomed);
 
   mMainThreadObjectsForgotten = true;
 }
 
 template <class Derived>
 bool
 WorkerPrivateParent<Derived>::PostMessage(JSContext* aCx, jsval aMessage)
 {
@@ -2273,17 +2270,18 @@ WorkerPrivateParent<Derived>::PostMessag
   return runnable->Dispatch(aCx);
 }
 
 template <class Derived>
 uint64_t
 WorkerPrivateParent<Derived>::GetInnerWindowId()
 {
   AssertIsOnMainThread();
-  return mDocument ? mDocument->InnerWindowID() : 0;
+  NS_ASSERTION(!mWindow || mWindow->IsInnerWindow(), "Outer window?");
+  return mWindow ? mWindow->WindowID() : 0;
 }
 
 template <class Derived>
 void
 WorkerPrivateParent<Derived>::UpdateJSContextOptions(JSContext* aCx,
                                                      uint32_t aOptions)
 {
   AssertIsOnParentThread();
@@ -2449,22 +2447,21 @@ WorkerPrivateParent<Derived>::ParentJSCo
 WorkerPrivate::WorkerPrivate(JSContext* aCx, JSObject* aObject,
                              WorkerPrivate* aParent,
                              JSContext* aParentJSContext,
                              const nsAString& aScriptURL, bool aIsChromeWorker,
                              const nsACString& aDomain,
                              nsCOMPtr<nsPIDOMWindow>& aWindow,
                              nsCOMPtr<nsIScriptContext>& aParentScriptContext,
                              nsCOMPtr<nsIURI>& aBaseURI,
-                             nsCOMPtr<nsIPrincipal>& aPrincipal,
-                             nsCOMPtr<nsIDocument>& aDocument)
+                             nsCOMPtr<nsIPrincipal>& aPrincipal)
 : WorkerPrivateParent<WorkerPrivate>(aCx, aObject, aParent, aParentJSContext,
                                      aScriptURL, aIsChromeWorker, aDomain,
                                      aWindow, aParentScriptContext, aBaseURI,
-                                     aPrincipal, aDocument),
+                                     aPrincipal),
   mJSContext(nullptr), mErrorHandlerRecursionCount(0), mNextTimeoutId(1),
   mStatus(Pending), mSuspended(false), mTimerRunning(false),
   mRunningExpiredTimeouts(false), mCloseHandlerStarted(false),
   mCloseHandlerFinished(false), mMemoryReporterRunning(false)
 {
   MOZ_COUNT_CTOR(mozilla::dom::workers::WorkerPrivate);
 }
 
@@ -2643,17 +2640,17 @@ WorkerPrivate::Create(JSContext* aCx, JS
     return nullptr;
   }
 
   nsDependentString scriptURL(urlChars, urlLength);
 
   nsRefPtr<WorkerPrivate> worker =
     new WorkerPrivate(aCx, aObj, aParent, parentContext, scriptURL,
                       aIsChromeWorker, domain, window, scriptContext, baseURI,
-                      principal, document);
+                      principal);
 
   worker->SetIsDOMBinding();
   worker->SetWrapper(aObj);
 
   nsRefPtr<CompileScriptRunnable> compiler = new CompileScriptRunnable(worker);
   if (!compiler->Dispatch(aCx)) {
     return nullptr;
   }
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -6,16 +6,17 @@
 #ifndef mozilla_dom_workers_workerprivate_h__
 #define mozilla_dom_workers_workerprivate_h__
 
 #include "Workers.h"
 
 #include "nsIRunnable.h"
 #include "nsIThread.h"
 #include "nsIThreadInternal.h"
+#include "nsPIDOMWindow.h"
 
 #include "jsapi.h"
 #include "mozilla/CondVar.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/TimeStamp.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsEventQueue.h"
@@ -176,17 +177,16 @@ private:
 
   // Main-thread things.
   nsCOMPtr<nsPIDOMWindow> mWindow;
   nsCOMPtr<nsIScriptContext> mScriptContext;
   nsCOMPtr<nsIXPCScriptNotify> mScriptNotify;
   nsCOMPtr<nsIURI> mBaseURI;
   nsCOMPtr<nsIURI> mScriptURI;
   nsCOMPtr<nsIPrincipal> mPrincipal;
-  nsCOMPtr<nsIDocument> mDocument;
 
   // Only used for top level workers.
   nsTArray<nsRefPtr<WorkerRunnable> > mQueuedRunnables;
 
   uint64_t mBusyCount;
   Status mParentStatus;
   uint32_t mJSContextOptions;
   uint32_t mJSRuntimeHeapSize;
@@ -199,18 +199,17 @@ private:
 
 protected:
   WorkerPrivateParent(JSContext* aCx, JSObject* aObject, WorkerPrivate* aParent,
                       JSContext* aParentJSContext, const nsAString& aScriptURL,
                       bool aIsChromeWorker, const nsACString& aDomain,
                       nsCOMPtr<nsPIDOMWindow>& aWindow,
                       nsCOMPtr<nsIScriptContext>& aScriptContext,
                       nsCOMPtr<nsIURI>& aBaseURI,
-                      nsCOMPtr<nsIPrincipal>& aPrincipal,
-                      nsCOMPtr<nsIDocument>& aDocument);
+                      nsCOMPtr<nsIPrincipal>& aPrincipal);
 
   ~WorkerPrivateParent();
 
 private:
   Derived*
   ParentAsWorkerPrivate() const
   {
     return static_cast<Derived*>(const_cast<WorkerPrivateParent*>(this));
@@ -421,24 +420,17 @@ public:
   {
     return mPrincipalIsSystem;
   }
 
   nsIDocument*
   GetDocument() const
   {
     AssertIsOnMainThread();
-    return mDocument;
-  }
-
-  void
-  SetDocument(nsIDocument* aDocument)
-  {
-    AssertIsOnMainThread();
-    mDocument = aDocument;
+    return mWindow ? mWindow->GetExtantDoc() : nullptr;
   }
 
   nsPIDOMWindow*
   GetWindow()
   {
     AssertIsOnMainThread();
     return mWindow;
   }
@@ -722,18 +714,17 @@ public:
   GetCrossThreadDispatcher();
 
 private:
   WorkerPrivate(JSContext* aCx, JSObject* aObject, WorkerPrivate* aParent,
                 JSContext* aParentJSContext, const nsAString& aScriptURL,
                 bool aIsChromeWorker, const nsACString& aDomain,
                 nsCOMPtr<nsPIDOMWindow>& aWindow,
                 nsCOMPtr<nsIScriptContext>& aScriptContext,
-                nsCOMPtr<nsIURI>& aBaseURI, nsCOMPtr<nsIPrincipal>& aPrincipal,
-                nsCOMPtr<nsIDocument>& aDocument);
+                nsCOMPtr<nsIURI>& aBaseURI, nsCOMPtr<nsIPrincipal>& aPrincipal);
 
   bool
   Dispatch(WorkerRunnable* aEvent, EventQueue* aQueue);
 
   bool
   DispatchToSyncQueue(WorkerSyncRunnable* aEvent);
 
   void
--- a/layout/generic/nsSimplePageSequence.cpp
+++ b/layout/generic/nsSimplePageSequence.cpp
@@ -491,16 +491,19 @@ nsSimplePageSequenceFrame::StartPrint(ns
   }
 
   return rv;
 }
 
 void
 GetPrintCanvasElementsInFrame(nsIFrame* aFrame, nsTArray<nsRefPtr<nsHTMLCanvasElement> >* aArr)
 {
+  if (!aFrame) {
+    return;
+  }
   for (nsIFrame::ChildListIterator childLists(aFrame);
     !childLists.IsDone(); childLists.Next()) {
 
     nsFrameList children = childLists.CurrentList();
     for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) {
       nsIFrame* child = e.get();
 
       // Check if child is a nsHTMLCanvasFrame.
--- a/mobile/android/base/PromptService.java
+++ b/mobile/android/base/PromptService.java
@@ -185,17 +185,17 @@ public class PromptService implements On
         final LayerView layerView = GeckoApp.mAppContext.getLayerView();
         layerView.post(new Runnable() {
             public void run() {
                 // treat actions that show a dialog as if preventDefault by content to prevent panning
                 layerView.abortPanning();
             }
         });
 
-        AlertDialog.Builder builder = new AlertDialog.Builder(GeckoApp.mAppContext);
+        final AlertDialog.Builder builder = new AlertDialog.Builder(GeckoApp.mAppContext);
         if (!aTitle.equals("")) {
             builder.setTitle(aTitle);
         }
 
         if (!aText.equals("")) {
             builder.setMessage(aText);
         }
 
@@ -251,19 +251,24 @@ public class PromptService implements On
         }
         if (length > 1) {
             builder.setNeutralButton(aButtons[1].label, this);
         }
         if (length > 2) {
             builder.setNegativeButton(aButtons[2].label, this);
         }
 
-        mDialog = builder.create();
-        mDialog.setOnCancelListener(this);
-        mDialog.show();
+        // The AlertDialog must be created on the UI thread, not the GeckoBackgroundThread.
+        GeckoAppShell.getMainHandler().post(new Runnable() {
+            public void run() {
+                mDialog = builder.create();
+                mDialog.setOnCancelListener(PromptService.this);
+                mDialog.show();
+            }
+        });
     }
 
     public void onClick(DialogInterface aDialog, int aWhich) {
         JSONObject ret = new JSONObject();
         try {
             int button = -1;
             ListView list = mDialog.getListView();
             if (list != null || mSelected != null) {
--- a/testing/marionette/client/marionette/tests/unit/test_navigation.py
+++ b/testing/marionette/client/marionette/tests/unit/test_navigation.py
@@ -1,14 +1,15 @@
 # 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/.
 
-import os
 from marionette_test import MarionetteTestCase
+from errors import MarionetteException
+from errors import TimeoutException
 
 class TestNavigate(MarionetteTestCase):
     def test_navigate(self):
         self.assertTrue(self.marionette.execute_script("window.location.href = 'about:blank'; return true;"))
         self.assertEqual("about:blank", self.marionette.execute_script("return window.location.href;"))
         test_html = self.marionette.absolute_url("test.html")
         self.marionette.navigate(test_html)
         self.assertNotEqual("about:blank", self.marionette.execute_script("return window.location.href;"))
@@ -55,8 +56,30 @@ class TestNavigate(MarionetteTestCase):
         self.assertEqual("Marionette Test", self.marionette.title)
         self.assertTrue(self.marionette.execute_script("var elem = window.document.createElement('div'); elem.id = 'someDiv';" +
                                         "window.document.body.appendChild(elem); return true;"))
         self.assertFalse(self.marionette.execute_script("return window.document.getElementById('someDiv') == undefined;"))
         self.marionette.refresh()
         self.assertEqual("Marionette Test", self.marionette.title)
         self.assertTrue(self.marionette.execute_script("return window.document.getElementById('someDiv') == undefined;"))
 
+    def test_navigate_frame(self):
+        self.marionette.navigate(self.marionette.absolute_url("test_iframe.html"))
+        self.marionette.switch_to_frame(0)
+        self.marionette.navigate(self.marionette.absolute_url("empty.html"))
+        self.assertTrue('empty.html' in self.marionette.get_url())
+        self.marionette.switch_to_frame()
+        self.assertTrue('test_iframe.html' in self.marionette.get_url())
+
+    def test_shouldnt_error_if_nonexistent_url_used(self):
+        try:
+            self.marionette.navigate("http://localhost:12345")
+            self.fail("Should have thrown a MarionetteException")
+        except TimeoutException: 
+            self.fail("The socket shouldn't have timed out when navigating to a non-existent URL")
+        except MarionetteException:
+            pass
+        except Exception as inst:
+            import traceback
+            print traceback.format_exc()
+            self.fail("Should have thrown a MarionetteException instead of %s" % type(inst))
+
+
--- a/testing/marionette/marionette-actors.js
+++ b/testing/marionette/marionette-actors.js
@@ -296,17 +296,29 @@ MarionetteDriverActor.prototype = {
    * @param boolean newSession
    *        True if this is the first time we're talking to this browser
    */
   startBrowser: function MDA_startBrowser(win, newSession) {
     this.mainFrame = win;
     this.curFrame = null;
     this.addBrowser(win);
     this.curBrowser.newSession = newSession;
-    this.curBrowser.startSession(newSession);
+    this.curBrowser.startSession(newSession, win, this.whenBrowserStarted.bind(this));
+  },
+
+  /**
+   * Callback invoked after a new session has been started in a browser.
+   * Loads the Marionette frame script into the browser if needed.
+   *
+   * @param nsIDOMWindow win
+   *        Window whose browser we need to access
+   * @param boolean newSession
+   *        True if this is the first time we're talking to this browser
+   */
+  whenBrowserStarted: function MDA_whenBrowserStarted(win, newSession) {
     try {
       if (!Services.prefs.getBoolPref("marionette.contentListener") || !newSession) {
         this.curBrowser.loadFrameScript("chrome://marionette/content/marionette-listener.js", win);
       }
     }
     catch (e) {
       //there may not always be a content process
       logger.info("could not load listener into content for page: " + win.location.href);
@@ -366,17 +378,17 @@ MarionetteDriverActor.prototype = {
     }
 
     if (!Services.prefs.getBoolPref("marionette.contentListener")) {
       waitForWindow.call(this);
     }
     else if ((appName == "B2G") && (this.curBrowser == null)) {
       //if there is a content listener, then we just wake it up
       this.addBrowser(this.getCurrentWindow());
-      this.curBrowser.startSession(false);
+      this.curBrowser.startSession(false, this.getCurrentWindow(), this.whenBrowserStarted);
       this.messageManager.broadcastAsyncMessage("Marionette:restart", {});
     }
     else {
       this.sendError("Session already running", 500, null);
     }
   },
 
   getSessionCapabilities: function MDA_getSessionCapabilities(){
@@ -1553,31 +1565,37 @@ BrowserObj.prototype = {
    * In a desktop environment, if newTab is true, it will start 
    * a new 'about:blank' tab and change focus to this tab.
    *
    * This will also set the active messagemanager for this object
    *
    * @param boolean newTab
    *        If true, create new tab
    */
-  startSession: function BO_startSession(newTab) {
+  startSession: function BO_startSession(newTab, win, callback) {
     if (appName == "B2G") {
-      return;
+      callback(win, newTab);
     }
-    if (newTab) {
+    else if (newTab) {
       this.addTab(this.startPage);
-      //if we have a new tab, make it the selected tab and give it focus
+      //if we have a new tab, make it the selected tab
       this.browser.selectedTab = this.tab;
       let newTabBrowser = this.browser.getBrowserForTab(this.tab);
+      // wait for tab to be loaded
+      newTabBrowser.addEventListener("load", function onLoad() {
+        newTabBrowser.removeEventListener("load", onLoad, true);
+        callback(win, newTab);
+      }, true);
     }
     else {
       //set this.tab to the currently focused tab
       if (this.browser != undefined && this.browser.selectedTab != undefined) {
         this.tab = this.browser.selectedTab;
       }
+      callback(win, newTab);
     }
   },
 
   /**
    * Closes current tab
    */
   closeTab: function BO_closeTab() {
     if (this.tab != null && (appName != "B2G")) {
--- a/testing/marionette/marionette-listener.js
+++ b/testing/marionette/marionette-listener.js
@@ -492,27 +492,34 @@ function setSearchTimeout(msg) {
   sendOk();
 }
 
 /**
  * Navigate to URI. Handles the case where we navigate within an iframe.
  * All other navigation is handled by the server (in chrome space).
  */
 function goUrl(msg) {
-  curWindow.location = msg.json.value;
-  //TODO: replace this with DOMContentLoaded event listening when Bug 720714 is resolved
-  let checkTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-  function checkLoad() { 
-    if (curWindow.document.readyState == "complete") { 
+  addEventListener("DOMContentLoaded", function onDOMContentLoaded(event) {
+    // Prevent DOMContentLoaded events from frames from invoking this code,
+    // unless the event is coming from the frame associated with the current
+    // window (i.e., someone has used switch_to_frame).
+    if (!event.originalTarget.defaultView.frameElement || 
+        event.originalTarget.defaultView.frameElement == curWindow.frameElement) {
+      removeEventListener("DOMContentLoaded", onDOMContentLoaded, false);
+
+      let errorRegex = /about:.+(error)|(blocked)\?/;
+      if (curWindow.document.readyState == "interactive" && errorRegex.exec(curWindow.document.baseURI)) {
+        sendError("Error loading page", 13, null);
+        return;
+      }
+
       sendOk();
-      return;
-    } 
-    checkTimer.initWithCallback(checkLoad, 100, Ci.nsITimer.TYPE_ONE_SHOT);
-  }
-  checkTimer.initWithCallback(checkLoad, 100, Ci.nsITimer.TYPE_ONE_SHOT);
+    }
+  }, false);
+  curWindow.location = msg.json.value;
 }
 
 /**
  * Get the current URI
  */
 function getUrl(msg) {
   sendResponse({value: curWindow.location.href});
 }
new file mode 100644
--- /dev/null
+++ b/toolkit/content/devicestorage.properties
@@ -0,0 +1,3 @@
+pictures=*.jpe; *.jpg; *.jpeg; *.gif; *.png; *.bmp;
+music=*.mp3; *.ogg; *.m4a; *.m4b; *.m4p; *.m4v; *.m4r; *.3gp; *.mp4; *.aac;
+videos=*.avi; *.divx; *.flv; *.m4v; *.mkv; *.mov; *.mp4; *.mpeg; *.mpg; *.ogm; *.ogv; *.ogx; *.rm; *.rmvb; *.smil; *.webm; *.wmv; *.xvid
--- a/toolkit/content/jar.mn
+++ b/toolkit/content/jar.mn
@@ -23,16 +23,17 @@ toolkit.jar:
 +  content/global/charsetOverlay.js           (charsetOverlay.js)
 +  content/global/charsetOverlay.xul          (charsetOverlay.xul)
 *  content/global/contentAreaUtils.js         (contentAreaUtils.js)
    content/global/customizeCharset.js         (customizeCharset.js)
    content/global/customizeCharset.xul        (customizeCharset.xul)
    content/global/customizeToolbar.css        (customizeToolbar.css)
 *  content/global/customizeToolbar.js         (customizeToolbar.js)
    content/global/customizeToolbar.xul        (customizeToolbar.xul)
+   content/global/devicestorage.properties    (devicestorage.properties)
    content/global/editMenuOverlay.js          (editMenuOverlay.js)
 *+ content/global/editMenuOverlay.xul         (editMenuOverlay.xul)
    content/global/finddialog.js               (finddialog.js)
 *+ content/global/finddialog.xul              (finddialog.xul)
    content/global/findUtils.js                (findUtils.js)
    content/global/filepicker.properties       (filepicker.properties)
 *+ content/global/globalOverlay.js            (globalOverlay.js)
 +  content/global/mozilla.xhtml               (mozilla.xhtml)
--- a/toolkit/mozapps/extensions/AddonUpdateChecker.jsm
+++ b/toolkit/mozapps/extensions/AddonUpdateChecker.jsm
@@ -152,17 +152,17 @@ RDFSerializer.prototype = {
           items.push(aIndent + "<em:" + prop + ">" +
                      this.escapeEntities(target.Value) + "</em:" + prop + ">\n");
         }
         else if (target instanceof Ci.nsIRDFInt) {
           items.push(aIndent + "<em:" + prop + " NC:parseType=\"Integer\">" +
                      target.Value + "</em:" + prop + ">\n");
         }
         else {
-          throw new Error("Cannot serialize unknown literal type");
+          throw Components.Exception("Cannot serialize unknown literal type");
         }
       }
     }
     items.sort();
     result += items.join("");
     return result;
   },
 
@@ -179,17 +179,17 @@ RDFSerializer.prototype = {
    *         The current level of indent for pretty-printing. If undefined no
    *         indent will be added
    * @return a string containing the serialized resource.
    * @throws if the RDF data contains multiple references to the same resource.
    */
   serializeResource: function RDFS_serializeResource(aDs, aResource, aIndent) {
     if (this.resources.indexOf(aResource) != -1 ) {
       // We cannot output multiple references to the same resource.
-      throw new Error("Cannot serialize multiple references to " + aResource.Value);
+      throw Components.Exception("Cannot serialize multiple references to " + aResource.Value);
     }
     if (aIndent === undefined)
       aIndent = "";
 
     this.resources.push(aResource);
     var container = null;
     var type = "Description";
     if (this.cUtils.IsSeq(aDs, aResource)) {
@@ -251,17 +251,17 @@ function parseRDFManifest(aId, aType, aU
 
   function getProperty(aDs, aSource, aProperty) {
     return getValue(aDs.GetTarget(aSource, EM_R(aProperty), true));
   }
 
   function getRequiredProperty(aDs, aSource, aProperty) {
     let value = getProperty(aDs, aSource, aProperty);
     if (!value)
-      throw new Error("Update manifest is missing a required " + aProperty + " property.");
+      throw Components.Exception("Update manifest is missing a required " + aProperty + " property.");
     return value;
   }
 
   let rdfParser = Cc["@mozilla.org/rdf/xml-parser;1"].
                   createInstance(Ci.nsIRDFXMLParser);
   let ds = Cc["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"].
            createInstance(Ci.nsIRDFDataSource);
   rdfParser.parseString(ds, aRequest.channel.URI, aRequest.responseText);
@@ -279,58 +279,60 @@ function parseRDFManifest(aId, aType, aU
   }
 
   let extensionRes  = gRDF.GetResource(item);
 
   // If we have an update key then the update manifest must be signed
   if (aUpdateKey) {
     let signature = getProperty(ds, extensionRes, "signature");
     if (!signature)
-      throw new Error("Update manifest for " + aId + " does not contain a required signature");
+      throw Components.Exception("Update manifest for " + aId + " does not contain a required signature");
     let serializer = new RDFSerializer();
     let updateString = null;
 
     try {
       updateString = serializer.serializeResource(ds, extensionRes);
     }
     catch (e) {
-      throw new Error("Failed to generate signed string for " + aId + ". Serializer threw " + e);
+      throw Components.Exception("Failed to generate signed string for " + aId + ". Serializer threw " + e,
+                                 e.result);
     }
 
     let result = false;
 
     try {
       let verifier = Cc["@mozilla.org/security/datasignatureverifier;1"].
                      getService(Ci.nsIDataSignatureVerifier);
       result = verifier.verifyData(updateString, signature, aUpdateKey);
     }
     catch (e) {
-      throw new Error("The signature or updateKey for " + aId + " is malformed");
+      throw Components.Exception("The signature or updateKey for " + aId + " is malformed." +
+                                 "Verifier threw " + e, e.result);
     }
 
     if (!result)
-      throw new Error("The signature for " + aId + " was not created by the add-on's updateKey");
+      throw Components.Exception("The signature for " + aId + " was not created by the add-on's updateKey");
   }
 
   let updates = ds.GetTarget(extensionRes, EM_R("updates"), true);
 
   // A missing updates property doesn't count as a failure, just as no avialable
   // update information
   if (!updates) {
     WARN("Update manifest for " + aId + " did not contain an updates property");
     return [];
   }
 
   if (!(updates instanceof Ci.nsIRDFResource))
-    throw new Error("Missing updates property for " + extensionRes.Value);
+    throw Components.Exception("Missing updates property for " + extensionRes.Value);
 
   let cu = Cc["@mozilla.org/rdf/container-utils;1"].
            getService(Ci.nsIRDFContainerUtils);
   if (!cu.IsContainer(ds, updates))
-    throw new Error("Updates property was not an RDF container");
+    throw Components.Exception("Updates property was not an RDF container");
 
   let results = [];
   let ctr = Cc["@mozilla.org/rdf/container;1"].
             createInstance(Ci.nsIRDFContainer);
   ctr.Init(ds, updates);
   let items = ctr.GetElements();
   while (items.hasMoreElements()) {
     let item = items.getNext().QueryInterface(Ci.nsIRDFResource);
--- a/toolkit/mozapps/extensions/content/extensions-content.js
+++ b/toolkit/mozapps/extensions/content/extensions-content.js
@@ -3,19 +3,20 @@
 # 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";
 
 (function(){
 
-let Cc = Components.classes;
-let Ci = Components.interfaces;
-let Cu = Components.utils;
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+const Cr = Components.results;
 
 const MSG_INSTALL_ENABLED  = "WebInstallerIsInstallEnabled";
 const MSG_INSTALL_ADDONS   = "WebInstallerInstallAddonsFromWebpage";
 const MSG_INSTALL_CALLBACK = "WebInstallerInstallCallback";
 const MSG_JAR_FLUSH        = "AddonJarFlush";
 
 var gIoService = Components.classes["@mozilla.org/network/io-service;1"]
                            .getService(Components.interfaces.nsIIOService);
@@ -60,40 +61,41 @@ function createInstallTrigger(window) {
       return this.enabled();
     },
 
     /**
      * @see amIInstallTriggerInstaller.idl
      */
     install: function(aArgs, aCallback) {
       if (!aArgs || typeof aArgs != "object")
-        throw new Error("Incorrect arguments passed to InstallTrigger.install()");
+        throw Components.Exception("Incorrect arguments passed to InstallTrigger.install()",
+                                   Cr.NS_ERROR_INVALID_ARGS);
 
       var params = {
         installerId: this.installerId,
         mimetype: "application/x-xpinstall",
         referer: this.url.spec,
         uris: [],
         hashes: [],
         names: [],
         icons: [],
       };
 
       for (var name in aArgs) {
         var item = aArgs[name];
         if (typeof item === 'string') {
           item = { URL: item };
         } else if (!("URL" in item) || item.URL === undefined) {
-          throw new Error("Missing URL property for '" + name + "'");
+          throw Components.Exception("Missing URL property for '" + name + "'");
         }
 
         // Resolve and validate urls
         var url = this.resolveURL(item.URL);
         if (!this.checkLoadURIFromScript(url))
-          throw new Error("insufficient permissions to install: " + url);
+          throw Components.Exception("Insufficient permissions to install: " + url);
 
         var iconUrl = null;
         if ("IconURL" in item && item.IconURL !== undefined) {
           iconUrl = this.resolveURL(item.IconURL);
           if (!this.checkLoadURIFromScript(iconUrl)) {
             iconUrl = null; // If page can't load the icon, just ignore it
           }
         }
--- a/toolkit/mozapps/extensions/content/extensions.js
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -2,16 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
+const Cr = Components.results;
 
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/PluralForm.jsm");
 Cu.import("resource://gre/modules/DownloadUtils.jsm");
 Cu.import("resource://gre/modules/AddonManager.jsm");
 Cu.import("resource://gre/modules/AddonRepository.jsm");
@@ -216,27 +217,27 @@ var FakeHistory = {
   },
 
   get canGoForward() {
     return (this.pos + 1) < this.states.length;
   },
 
   back: function FakeHistory_back() {
     if (this.pos == 0)
-      throw new Error("Cannot go back from this point");
+      throw Components.Exception("Cannot go back from this point");
 
     this.pos--;
     gViewController.updateState(this.states[this.pos]);
     gViewController.updateCommand("cmd_back");
     gViewController.updateCommand("cmd_forward");
   },
 
   forward: function FakeHistory_forward() {
     if ((this.pos + 1) >= this.states.length)
-      throw new Error("Cannot go forward from this point");
+      throw Components.Exception("Cannot go forward from this point");
 
     this.pos++;
     gViewController.updateState(this.states[this.pos]);
     gViewController.updateCommand("cmd_back");
     gViewController.updateCommand("cmd_forward");
   },
 
   pushState: function FakeHistory_pushState(aState) {
@@ -246,17 +247,17 @@ var FakeHistory = {
   },
 
   replaceState: function FakeHistory_replaceState(aState) {
     this.states[this.pos] = aState;
   },
 
   popState: function FakeHistory_popState() {
     if (this.pos == 0)
-      throw new Error("Cannot popState from this view");
+      throw Components.Exception("Cannot popState from this view");
 
     this.states.splice(this.pos, this.states.length);
     this.pos--;
 
     gViewController.updateState(this.states[this.pos]);
     gViewController.updateCommand("cmd_back");
     gViewController.updateCommand("cmd_forward");
   }
@@ -575,21 +576,21 @@ var gViewController = {
     this.initialViewSelected = true;
     notifyInitialized();
   },
 
   loadViewInternal: function gVC_loadViewInternal(aViewId, aPreviousView, aState) {
     var view = this.parseViewId(aViewId);
 
     if (!view.type || !(view.type in this.viewObjects))
-      throw new Error("Invalid view: " + view.type);
+      throw Components.Exception("Invalid view: " + view.type);
 
     var viewObj = this.viewObjects[view.type];
     if (!viewObj.node)
-      throw new Error("Root node doesn't exist for '" + view.type + "' view");
+      throw Components.Exception("Root node doesn't exist for '" + view.type + "' view");
 
     if (this.currentViewObj && aViewId != aPreviousView) {
       try {
         let canHide = this.currentViewObj.hide();
         if (canHide === false)
           return;
         this.viewPort.selectedPanel.removeAttribute("loading");
       } catch (e) {
@@ -2330,17 +2331,17 @@ var gListView = {
         if (item)
           item.showInDetailView();
       }
     }, false);
   },
 
   show: function gListView_show(aType, aRequest) {
     if (!(aType in AddonManager.addonTypes))
-      throw new Error("Attempting to show unknown type " + aType);
+      throw Components.Exception("Attempting to show unknown type " + aType, Cr.NS_ERROR_INVALID_ARG);
 
     this._type = aType;
     this.node.setAttribute("type", aType);
     this.showEmptyNotice(false);
 
     while (this._listBox.itemCount > 0)
       this._listBox.removeItemAt(0);
 
--- a/toolkit/mozapps/extensions/content/extensions.xml
+++ b/toolkit/mozapps/extensions/content/extensions.xml
@@ -77,17 +77,17 @@
       <property name="showRating">
         <getter><![CDATA[
           if (this.hasAttribute("showrating"))
             return this.getAttribute("showrating");
           return "average";
         ]]></getter>
         <setter><![CDATA[
           if (val != "average" || val != "user")
-            throw new Error("Invalid value");
+            throw Components.Exception("Invalid value", Components.results.NS_ERROR_ILLEGAL_VALUE);
           this.setAttribute("showrating", val);
           this._updateStars();
         ]]></setter>
       </property>
 
       <method name="_updateStars">
         <body><![CDATA[
           var stars = this.stars;
--- a/toolkit/mozapps/extensions/content/setting.xml
+++ b/toolkit/mozapps/extensions/content/setting.xml
@@ -34,17 +34,17 @@
 
         QueryInterface: function(aIID) {
           const Ci = Components.interfaces;
           if (aIID.equals(Ci.nsIObserver) ||
               aIID.equals(Ci.nsISupportsWeakReference) ||
               aIID.equals(Ci.nsISupports))
             return this;
 
-          throw Components.results.NS_ERROR_NO_INTERFACE;
+          throw Components.Exception("No interface", Components.results.NS_ERROR_NO_INTERFACE);
         },
 
         observe: function(aSubject, aTopic, aPrefName) {
           if (aTopic != "nsPref:changed")
             return;
 
           if (this._self.pref == aPrefName)
             this._self.preferenceChanged();
@@ -73,26 +73,28 @@
           ]]>
         </body>
       </method>
 
       <method name="valueFromPreference">
         <body>
         <![CDATA[
           // Should be code to set the from the preference input.value
-          throw "No valueFromPreference implementation";
+          throw Components.Exception("No valueFromPreference implementation",
+                                     Components.results.NS_ERROR_NOT_IMPLEMENTED);
         ]]>
         </body>
       </method>
 
       <method name="valueToPreference">
         <body>
         <![CDATA[
           // Should be code to set the input.value from the preference
-          throw "No valueToPreference implementation";
+          throw Components.Exception("No valueToPreference implementation",
+                                     Components.results.NS_ERROR_NOT_IMPLEMENTED);
         ]]>
         </body>
       </method>
 
       <method name="inputChanged">
         <body>
         <![CDATA[
           if (this.usePref && !this._updatingInput) {
--- a/toolkit/mozapps/extensions/test/browser/browser_openDialog.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_openDialog.js
@@ -51,23 +51,25 @@ let CustomChromeProtocol = {
 
     unregister: function CCP_register() {
       this.registrar.unregisterFactory(CustomChromeProtocol.classID, this);
     },
 
     // nsIFactory
     createInstance: function BNPH_createInstance(aOuter, aIID) {
       if (aOuter) {
-        throw Components.results.NS_ERROR_NO_AGGREGATION;
+        throw Components.Exception("Class does not allow aggregation",
+                                   Components.results.NS_ERROR_NO_AGGREGATION);
       }
       return CustomChromeProtocol.QueryInterface(aIID);
     },
 
     lockFactory: function BNPH_lockFactory(aLock) {
-      throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+      throw Components.Exception("Function lockFactory is not implemented",
+                                 Components.results.NS_ERROR_NOT_IMPLEMENTED);
     },
 
     QueryInterface: XPCOMUtils.generateQI([
       Ci.nsIFactory
     ])
   }
 }
 
--- a/toolkit/mozapps/extensions/test/browser/head.js
+++ b/toolkit/mozapps/extensions/test/browser/head.js
@@ -500,17 +500,17 @@ CertOverrideListener.prototype = {
   },
 
   QueryInterface: function(aIID) {
     if (aIID.equals(Ci.nsIBadCertListener2) ||
         aIID.equals(Ci.nsIInterfaceRequestor) ||
         aIID.equals(Ci.nsISupports))
       return this;
 
-    throw Components.results.NS_ERROR_NO_INTERFACE;
+    throw Components.Exception("No interface", Components.results.NS_ERROR_NO_INTERFACE);
   },
 
   notifyCertProblem: function (socketInfo, sslStatus, targetHost) {
     var cert = sslStatus.QueryInterface(Components.interfaces.nsISSLStatus)
                         .serverCert;
     var cos = Cc["@mozilla.org/security/certoverride;1"].
               getService(Ci.nsICertOverrideService);
     cos.rememberValidityOverride(this.host, -1, cert, this.bits, false);
@@ -1039,30 +1039,30 @@ MockAddon.prototype = {
   },
 
   findUpdates: function(aListener, aReason, aAppVersion, aPlatformVersion) {
     // Tests can implement this if they need to
   },
 
   uninstall: function() {
     if (this.pendingOperations & AddonManager.PENDING_UNINSTALL)
-      throw new Error("Add-on is already pending uninstall");
+      throw Components.Exception("Add-on is already pending uninstall");
 
     var needsRestart = !!(this.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL);
     this.pendingOperations |= AddonManager.PENDING_UNINSTALL;
     AddonManagerPrivate.callAddonListeners("onUninstalling", this, needsRestart);
     if (!needsRestart) {
       this.pendingOperations -= AddonManager.PENDING_UNINSTALL;
       this._provider.removeAddon(this);
     }
   },
 
   cancelUninstall: function() {
     if (!(this.pendingOperations & AddonManager.PENDING_UNINSTALL))
-      throw new Error("Add-on is not pending uninstall");
+      throw Components.Exception("Add-on is not pending uninstall");
 
     this.pendingOperations -= AddonManager.PENDING_UNINSTALL;
     AddonManagerPrivate.callAddonListeners("onOperationCancelled", this);
   },
 
   _updateActiveState: function(currentActive, newActive) {
     if (currentActive == newActive)
       return;
--- a/toolkit/mozapps/extensions/test/xpcshell/test_bug470377_2.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug470377_2.js
@@ -1,18 +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/.
  */
 
 // Disables security checking our updates which haven't been signed
 Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
 
-AddonManager.checkCompatibility = false;
-
 var ADDONS = [
   "test_bug470377_1",
   "test_bug470377_2",
   "test_bug470377_3",
   "test_bug470377_4",
   "test_bug470377_5",
 ];
 
@@ -23,16 +21,17 @@ function run_test() {
   do_test_pending();
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
 
   server = new HttpServer();
   server.registerDirectory("/", do_get_file("data/test_bug470377"));
   server.start(4444);
 
   startupManager();
+  AddonManager.checkCompatibility = false;
 
   installAllFiles([do_get_addon(a) for each (a in ADDONS)], function() {
     restartManager();
 
     AddonManager.getAddonsByIDs(["bug470377_1@tests.mozilla.org",
                                  "bug470377_2@tests.mozilla.org",
                                  "bug470377_3@tests.mozilla.org",
                                  "bug470377_4@tests.mozilla.org",
--- a/toolkit/mozapps/extensions/test/xpcshell/test_bug470377_4.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug470377_4.js
@@ -35,19 +35,19 @@ function run_test() {
   dest.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755);
   source = do_get_file("data/test_bug470377/install_5.rdf");
   source.copyTo(dest, "install.rdf");
 
   run_test_1();
 }
 
 function run_test_1() {
+  startupManager();
   AddonManager.checkCompatibility = false;
-
-  startupManager();
+  restartManager();
 
   AddonManager.getAddonsByIDs(["bug470377_1@tests.mozilla.org",
                                "bug470377_2@tests.mozilla.org",
                                "bug470377_3@tests.mozilla.org",
                                "bug470377_4@tests.mozilla.org",
                                "bug470377_5@tests.mozilla.org"],
                                function([a1, a2, a3, a4, a5]) {
     do_check_neq(a1, null);
--- a/toolkit/mozapps/extensions/test/xpcshell/test_bug521905.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug521905.js
@@ -3,29 +3,30 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 const ADDON = "test_bug521905";
 const ID = "bug521905@tests.mozilla.org";
 
 // Disables security checking our updates which haven't been signed
 Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
-AddonManager.checkCompatibility = false;
 
 function run_test() {
   // This test is only relevant on builds where the version is included in the
   // checkCompatibility preference name
   if (isNightlyChannel()) {
     return;
   }
 
   do_test_pending();
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2.0pre", "2");
 
   startupManager();
+  AddonManager.checkCompatibility = false;
+
   installAllFiles([do_get_addon(ADDON)], function() {
     restartManager();
 
     AddonManager.getAddonByID(ID, function(addon) {
       do_check_neq(addon, null);
       do_check_true(addon.isActive);
 
       run_test_1();