Merge mozilla-central and fx-team
authorEd Morley <emorley@mozilla.com>
Fri, 03 Aug 2012 15:52:55 +0100
changeset 103192 163ba68c71cfbd4ee7771ff0be36cca2a53ef136
parent 103191 c3b7cfd70d378d78e84cfcb256f3b6962eef3970 (current diff)
parent 103164 62d4f0efe48520fc9052c35a0bbafb131c3ee2fe (diff)
child 103193 083607a91532e06e109a3d79598308d575136e54
push id18
push usershu@rfrn.org
push dateMon, 06 Aug 2012 22:42:45 +0000
milestone17.0a1
Merge mozilla-central and fx-team
build/automation.py.in
build/automationutils.py
dom/base/nsGlobalWindow.cpp
toolkit/components/maintenanceservice/prefetch.cpp
toolkit/components/maintenanceservice/prefetch.h
--- a/accessible/src/atk/ApplicationAccessibleWrap.cpp
+++ b/accessible/src/atk/ApplicationAccessibleWrap.cpp
@@ -567,17 +567,17 @@ toplevel_event_watcher(GSignalInvocation
       }
 
     }
   }
 
   return TRUE;
 }
 
-bool
+void
 ApplicationAccessibleWrap::Init()
 {
     if (ShouldA11yBeEnabled()) {
         // load and initialize gail library
         nsresult rv = LoadGtkModule(sGail);
         if (NS_SUCCEEDED(rv)) {
             (*sGail.init)();
         }
@@ -610,17 +610,17 @@ ApplicationAccessibleWrap::Init()
               reinterpret_cast<gpointer>(nsIAccessibleEvent::EVENT_SHOW), NULL);
           sToplevel_hide_hook =
             g_signal_add_emission_hook(g_signal_lookup("hide", GTK_TYPE_WINDOW),
               0, toplevel_event_watcher,
               reinterpret_cast<gpointer>(nsIAccessibleEvent::EVENT_HIDE), NULL);
         }
     }
 
-    return ApplicationAccessible::Init();
+    ApplicationAccessible::Init();
 }
 
 void
 ApplicationAccessibleWrap::Unload()
 {
     if (sToplevel_event_hook_added) {
       sToplevel_event_hook_added = false;
       g_signal_remove_emission_hook(g_signal_lookup("show", GTK_TYPE_WINDOW),
--- a/accessible/src/atk/ApplicationAccessibleWrap.h
+++ b/accessible/src/atk/ApplicationAccessibleWrap.h
@@ -18,17 +18,17 @@ public:
   static void Unload();
   static void PreCreate();
 
 public:
   ApplicationAccessibleWrap();
   virtual ~ApplicationAccessibleWrap();
 
   // nsAccessNode
-  virtual bool Init();
+  virtual void Init();
 
   // Accessible
   virtual mozilla::a11y::ENameValueFlag Name(nsString& aName);
   virtual bool AppendChild(Accessible* aChild);
   virtual bool RemoveChild(Accessible* aChild);
 
   /**
    * Return the atk object for app root accessible.
--- a/accessible/src/base/nsAccDocManager.cpp
+++ b/accessible/src/base/nsAccDocManager.cpp
@@ -383,20 +383,17 @@ nsAccDocManager::CreateDocOrRootAccessib
   nsRefPtr<DocAccessible> docAcc = isRootDoc ?
     new RootAccessibleWrap(aDocument, rootElm, presShell) :
     new DocAccessibleWrap(aDocument, rootElm, presShell);
 
   // Cache the document accessible into document cache.
   mDocAccessibleCache.Put(aDocument, docAcc);
 
   // Initialize the document accessible.
-  if (!docAcc->Init()) {
-    docAcc->Shutdown();
-    return nullptr;
-  }
+  docAcc->Init();
   docAcc->SetRoleMapEntry(aria::GetRoleMap(aDocument));
 
   // Bind the document to the tree.
   if (isRootDoc) {
     Accessible* appAcc = nsAccessNode::GetApplicationAccessible();
     if (!appAcc->AppendChild(docAcc)) {
       docAcc->Shutdown();
       return nullptr;
--- a/accessible/src/base/nsAccessNode.cpp
+++ b/accessible/src/base/nsAccessNode.cpp
@@ -91,22 +91,17 @@ nsAccessNode::GetApplicationAccessible()
   if (!gApplicationAccessible) {
     ApplicationAccessibleWrap::PreCreate();
 
     gApplicationAccessible = new ApplicationAccessibleWrap();
 
     // Addref on create. Will Release in ShutdownXPAccessibility()
     NS_ADDREF(gApplicationAccessible);
 
-    nsresult rv = gApplicationAccessible->Init();
-    if (NS_FAILED(rv)) {
-      gApplicationAccessible->Shutdown();
-      NS_RELEASE(gApplicationAccessible);
-      return nullptr;
-    }
+    gApplicationAccessible->Init();
   }
 
   return gApplicationAccessible;
 }
 
 void nsAccessNode::ShutdownXPAccessibility()
 {
   // Called by nsAccessibilityService::Shutdown()
--- a/accessible/src/base/nsTextEquivUtils.cpp
+++ b/accessible/src/base/nsTextEquivUtils.cpp
@@ -6,16 +6,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsTextEquivUtils.h"
 
 #include "Accessible-inl.h"
 #include "AccIterator.h"
 #include "nsAccessibilityService.h"
 #include "nsAccUtils.h"
+#include "nsStyleStructInlines.h"
 
 #include "nsIDOMXULLabeledControlEl.h"
 
 #include "nsArrayUtils.h"
 
 using namespace mozilla::a11y;
 
 #define NS_OK_NO_NAME_CLAUSE_HANDLED \
@@ -121,17 +122,17 @@ nsTextEquivUtils::AppendTextEquivFromTex
     nsIContent *parentContent = aContent->GetParent();
     if (parentContent) {
       nsIFrame *frame = parentContent->GetPrimaryFrame();
       if (frame) {
         // If this text is inside a block level frame (as opposed to span
         // level), we need to add spaces around that block's text, so we don't
         // get words jammed together in final name.
         const nsStyleDisplay* display = frame->GetStyleDisplay();
-        if (display->IsBlockOutside() ||
+        if (display->IsBlockOutsideStyle() ||
             display->mDisplay == NS_STYLE_DISPLAY_TABLE_CELL) {
           isHTMLBlock = true;
           if (!aString->IsEmpty()) {
             aString->Append(PRUnichar(' '));
           }
         }
       }
     }
--- a/accessible/src/generic/Accessible.cpp
+++ b/accessible/src/generic/Accessible.cpp
@@ -190,20 +190,19 @@ Accessible::~Accessible()
 }
 
 void
 Accessible::SetRoleMapEntry(nsRoleMapEntry* aRoleMapEntry)
 {
   mRoleMapEntry = aRoleMapEntry;
 }
 
-bool
+void
 Accessible::Init()
 {
-  return true;
 }
 
 NS_IMETHODIMP
 Accessible::GetDocument(nsIAccessibleDocument** aDocument)
 {
   NS_ENSURE_ARG_POINTER(aDocument);
 
   NS_IF_ADDREF(*aDocument = Document());
--- a/accessible/src/generic/Accessible.h
+++ b/accessible/src/generic/Accessible.h
@@ -124,17 +124,17 @@ public:
   virtual void Shutdown();
 
   //////////////////////////////////////////////////////////////////////////////
   // Public methods
 
   /**
    * Initialize the accessible.
    */
-  virtual bool Init();
+  virtual void Init();
 
   /**
    * Get the description of this accessible.
    */
   virtual void Description(nsString& aDescription);
 
   /**
    * Get the value of this accessible.
--- a/accessible/src/generic/ApplicationAccessible.cpp
+++ b/accessible/src/generic/ApplicationAccessible.cpp
@@ -263,21 +263,20 @@ ApplicationAccessible::GetPlatformVersio
 
   AppendUTF8toUTF16(cversion, aVersion);
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccessNode public methods
 
-bool
+void
 ApplicationAccessible::Init()
 {
   mAppInfo = do_GetService("@mozilla.org/xre/app-info;1");
-  return true;
 }
 
 void
 ApplicationAccessible::Shutdown()
 {
   mAppInfo = nullptr;
 }
 
--- a/accessible/src/generic/ApplicationAccessible.h
+++ b/accessible/src/generic/ApplicationAccessible.h
@@ -56,17 +56,17 @@ public:
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString &aName);
   NS_IMETHOD GetActionDescription(PRUint8 aIndex, nsAString &aDescription);
   NS_IMETHOD DoAction(PRUint8 aIndex);
 
   // nsIAccessibleApplication
   NS_DECL_NSIACCESSIBLEAPPLICATION
 
   // nsAccessNode
-  virtual bool Init();
+  virtual void Init();
   virtual void Shutdown();
   virtual bool IsPrimaryForNode() const;
 
   // Accessible
   virtual GroupPos GroupPosition();
   virtual ENameValueFlag Name(nsString& aName);
   virtual void ApplyARIAState(PRUint64* aState) const;
   virtual void Description(nsString& aDescription);
--- a/accessible/src/generic/DocAccessible.cpp
+++ b/accessible/src/generic/DocAccessible.cpp
@@ -599,37 +599,34 @@ DocAccessible::GetAccessible(nsINode* aN
 #endif
 
   return accessible;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccessNode
 
-bool
+void
 DocAccessible::Init()
 {
 #ifdef DEBUG
   if (logging::IsEnabled(logging::eDocCreate))
     logging::DocCreate("document initialize", mDocument, this);
 #endif
 
   // Initialize notification controller.
   mNotificationController = new NotificationController(this, mPresShell);
-  if (!mNotificationController)
-    return false;
 
   // Mark the document accessible as loaded if its DOM document was loaded at
   // this point (this can happen because a11y is started late or DOM document
   // having no container was loaded.
   if (mDocument->GetReadyStateEnum() == nsIDocument::READYSTATE_COMPLETE)
     mLoadState |= eDOMLoaded;
 
   AddEventListeners();
-  return true;
 }
 
 void
 DocAccessible::Shutdown()
 {
   if (!mPresShell) // already shutdown
     return;
 
@@ -1392,22 +1389,17 @@ DocAccessible::BindToDocument(Accessible
   // Put into DOM node cache.
   if (aAccessible->IsPrimaryForNode())
     mNodeToAccessibleMap.Put(aAccessible->GetNode(), aAccessible);
 
   // Put into unique ID cache.
   mAccessibleCache.Put(aAccessible->UniqueID(), aAccessible);
 
   // Initialize the accessible.
-  if (!aAccessible->Init()) {
-    NS_ERROR("Failed to initialize an accessible!");
-
-    UnbindFromDocument(aAccessible);
-    return false;
-  }
+  aAccessible->Init();
 
   aAccessible->SetRoleMapEntry(aRoleMapEntry);
   if (aAccessible->IsElement())
     AddDependentIDsFor(aAccessible);
 
   return true;
 }
 
--- a/accessible/src/generic/DocAccessible.h
+++ b/accessible/src/generic/DocAccessible.h
@@ -66,17 +66,17 @@ public:
   // nsIScrollPositionListener
   virtual void ScrollPositionWillChange(nscoord aX, nscoord aY) {}
   virtual void ScrollPositionDidChange(nscoord aX, nscoord aY);
 
   // nsIDocumentObserver
   NS_DECL_NSIDOCUMENTOBSERVER
 
   // nsAccessNode
-  virtual bool Init();
+  virtual void Init();
   virtual void Shutdown();
   virtual nsIFrame* GetFrame() const;
   virtual nsINode* GetNode() const { return mDocument; }
   virtual nsIDocument* GetDocumentNode() const { return mDocument; }
 
   // Accessible
   virtual mozilla::a11y::ENameValueFlag Name(nsString& aName);
   virtual void Description(nsString& aDescription);
--- a/accessible/src/xul/XULTreeAccessible.cpp
+++ b/accessible/src/xul/XULTreeAccessible.cpp
@@ -1145,24 +1145,21 @@ XULTreeItemAccessible::Name(nsString& aN
 
   GetCellName(mColumn, aName);
   return eNameOK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULTreeItemAccessible: nsAccessNode implementation
 
-bool
+void
 XULTreeItemAccessible::Init()
 {
-  if (!XULTreeItemAccessibleBase::Init())
-    return false;
-
+  XULTreeItemAccessibleBase::Init();
   Name(mCachedName);
-  return true;
 }
 
 void
 XULTreeItemAccessible::Shutdown()
 {
   mColumn = nullptr;
   XULTreeItemAccessibleBase::Shutdown();
 }
--- a/accessible/src/xul/XULTreeAccessible.h
+++ b/accessible/src/xul/XULTreeAccessible.h
@@ -230,17 +230,17 @@ public:
                         nsITreeView* aTreeView, PRInt32 aRow);
 
   // nsISupports and cycle collection
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XULTreeItemAccessible,
                                            XULTreeItemAccessibleBase)
 
   // nsAccessNode
-  virtual bool Init();
+  virtual void Init();
   virtual void Shutdown();
 
   // Accessible
   virtual ENameValueFlag Name(nsString& aName);
   virtual a11y::role NativeRole();
 
   // XULTreeItemAccessibleBase
   virtual void RowInvalidated(PRInt32 aStartColIdx, PRInt32 aEndColIdx);
--- a/accessible/src/xul/XULTreeGridAccessible.cpp
+++ b/accessible/src/xul/XULTreeGridAccessible.cpp
@@ -774,30 +774,29 @@ XULTreeGridCellAccessible::IsSelected(bo
   NS_ENSURE_SUCCESS(rv, rv);
 
   return selection->IsSelected(mRow, aIsSelected);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULTreeGridCellAccessible: nsAccessNode implementation
 
-bool
+void
 XULTreeGridCellAccessible::Init()
 {
-  if (!LeafAccessible::Init() || !mTreeView)
-    return false;
+  LeafAccessible::Init();
+
+  NS_ASSERTION(mTreeView, "mTreeView is null");
 
   PRInt16 type;
   mColumn->GetType(&type);
   if (type == nsITreeColumn::TYPE_CHECKBOX)
     mTreeView->GetCellValue(mRow, mColumn, mCachedTextEquiv);
   else
     mTreeView->GetCellText(mRow, mColumn, mCachedTextEquiv);
-
-  return true;
 }
 
 bool
 XULTreeGridCellAccessible::IsPrimaryForNode() const
 {
   return false;
 }
 
--- a/accessible/src/xul/XULTreeGridAccessible.h
+++ b/accessible/src/xul/XULTreeGridAccessible.h
@@ -145,17 +145,17 @@ public:
 
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 aIndex);
 
   // nsIAccessibleTableCell
   NS_DECL_OR_FORWARD_NSIACCESSIBLETABLECELL_WITH_XPCACCESSIBLETABLECELL
 
   // nsAccessNode
-  virtual bool Init();
+  virtual void Init();
   virtual bool IsPrimaryForNode() const;
 
   // Accessible
   virtual void Shutdown();
   virtual ENameValueFlag Name(nsString& aName);
   virtual Accessible* FocusedChild();
   virtual nsresult GetAttributesInternal(nsIPersistentProperties* aAttributes);
   virtual PRInt32 IndexInParent() const;
--- a/accessible/tests/mochitest/name/markuprules.xml
+++ b/accessible/tests/mochitest/name/markuprules.xml
@@ -197,16 +197,17 @@
                      aria-labelledby="l1 l2"
                      label="test4"
                      title="test5"
                      a11yname="option1">option1</html:option>
         <html:option>option2</html:option>
       </html:select>
     </markup>
 
+<!-- Temporarily disabled for causing bug 733848
     <markup ref="html:img" ruleset="htmlimage">
       <html:span id="l1" a11yname="test2">test2</html:span>
       <html:span id="l2" a11yname="test3">test3</html:span>
       <html:img id="img"
                 aria-label="Logo of Mozilla"
                 aria-labelledby="l1 l2"
                 alt="Mozilla logo"
                 title="This is a logo"
@@ -218,16 +219,17 @@
       <html:span id="l2" a11yname="test3">test3</html:span>
       <html:img id="img"
                  aria-label="Logo of Mozilla"
                  aria-labelledby="l1 l2"
                  title="This is a logo"
                  alt=""
                  src="../moz.png"/>
     </markup>
+-->
 
     <markup ref="html:table/html:tr/html:td" ruleset="htmlelm"
             id="markup4test">
       <html:span id="l1" a11yname="test2">test2</html:span>
       <html:span id="l2" a11yname="test3">test3</html:span>
       <html:label for="tc" a11yname="test4">test4</html:label>
       <html:table>
         <html:tr>
--- a/b2g/app/nsBrowserApp.cpp
+++ b/b2g/app/nsBrowserApp.cpp
@@ -169,29 +169,22 @@ int main(int argc, char* argv[])
 
   strcpy(++lastSlash, XPCOM_DLL);
 
   int gotCounters;
 #if defined(XP_UNIX)
   struct rusage initialRUsage;
   gotCounters = !getrusage(RUSAGE_SELF, &initialRUsage);
 #elif defined(XP_WIN)
-  // GetProcessIoCounters().ReadOperationCount seems to have little to
-  // do with actual read operations. It reports 0 or 1 at this stage
-  // in the program. Luckily 1 coincides with when prefetch is
-  // enabled. If Windows prefetch didn't happen we can do our own
-  // faster dll preloading.
   IO_COUNTERS ioCounters;
   gotCounters = GetProcessIoCounters(GetCurrentProcess(), &ioCounters);
-  if (gotCounters && !ioCounters.ReadOperationCount)
 #endif
-  {
-      XPCOMGlueEnablePreload();
-  }
 
+  // We do this because of data in bug 771745
+  XPCOMGlueEnablePreload();
 
   rv = XPCOMGlueStartup(exePath);
   if (NS_FAILED(rv)) {
     Output("Couldn't load XPCOM.\n");
     return 255;
   }
   // Reset exePath so that it is the directory name and not the xpcom dll name
   *lastSlash = 0;
--- a/browser/app/nsBrowserApp.cpp
+++ b/browser/app/nsBrowserApp.cpp
@@ -169,48 +169,16 @@ static int do_main(int argc, char* argv[
     int result = XRE_main(argc, argv, appData, 0);
     XRE_FreeAppData(appData);
     return result;
   }
 
   return XRE_main(argc, argv, &sAppData, 0);
 }
 
-#ifdef XP_WIN
-/**
- * Determines if the registry is disabled via the service or not.
- * 
- * @return true if prefetch is disabled
- *         false if prefetch is not disabled or an error occurred.
-*/
-bool IsPrefetchDisabledViaService()
-{
-  // We don't need to return false when we don't have MOZ_MAINTENANCE_SERVICE
-  // defined.  The reason is because another product installed that has it
-  // defined may have cleared our prefetch for us.  There is no known way
-  // to figure out which prefetch files are associated with which apps
-  // because of the prefetch hash.  So we disable all of them that start
-  // with FIREFOX.
-  HKEY baseKey;
-  LONG retCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 
-                               L"SOFTWARE\\Mozilla\\MaintenanceService", 0,
-                               KEY_READ | KEY_WOW64_64KEY, &baseKey);
-  if (retCode != ERROR_SUCCESS) {
-    return false;
-  }
-  DWORD disabledValue = 0;
-  DWORD disabledValueSize = sizeof(DWORD);
-  RegQueryValueExW(baseKey, L"FFPrefetchDisabled", 0, NULL,
-                   reinterpret_cast<LPBYTE>(&disabledValue),
-                   &disabledValueSize);
-  RegCloseKey(baseKey);
-  return disabledValue == 1;
-}
-#endif
-
 /* Local implementation of PR_Now, since the executable can't depend on NSPR */
 static PRTime _PR_Now()
 {
 #ifdef XP_WIN
   MOZ_STATIC_ASSERT(sizeof(PRTime) == sizeof(FILETIME), "PRTime must have the same size as FILETIME");
   FILETIME ft;
   GetSystemTimeAsFileTime(&ft);
   PRTime now;
@@ -253,19 +221,18 @@ int main(int argc, char* argv[])
 #if defined(XP_UNIX)
   struct rusage initialRUsage;
   gotCounters = !getrusage(RUSAGE_SELF, &initialRUsage);
 #elif defined(XP_WIN)
   IO_COUNTERS ioCounters;
   gotCounters = GetProcessIoCounters(GetCurrentProcess(), &ioCounters);
 #endif
 
-#if !defined(XP_WIN)
+  // We do this because of data in bug 771745
   XPCOMGlueEnablePreload();
-#endif
 
   rv = XPCOMGlueStartup(exePath);
   if (NS_FAILED(rv)) {
     Output("Couldn't load XPCOM.\n");
     return 255;
   }
   // Reset exePath so that it is the directory name and not the xpcom dll name
   *lastSlash = 0;
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -710,46 +710,32 @@ pref("gecko.handlerService.schemes.ircs.
 pref("gecko.handlerService.schemes.ircs.2.uriTemplate", "chrome://browser-region/locale/region.properties");
 pref("gecko.handlerService.schemes.ircs.3.name", "chrome://browser-region/locale/region.properties");
 pref("gecko.handlerService.schemes.ircs.3.uriTemplate", "chrome://browser-region/locale/region.properties");
 
 // By default, we don't want protocol/content handlers to be registered from a different host, see bug 402287
 pref("gecko.handlerService.allowRegisterFromDifferentHost", false);
 
 #ifdef MOZ_SAFE_BROWSING
-// Safe browsing does nothing unless this pref is set
 pref("browser.safebrowsing.enabled", true);
-
-// Prevent loading of pages identified as malware
 pref("browser.safebrowsing.malware.enabled", true);
-
-// Debug logging to error console
 pref("browser.safebrowsing.debug", false);
 
-// Non-enhanced mode (local url lists) URL list to check for updates
-pref("browser.safebrowsing.provider.0.updateURL", "http://safebrowsing.clients.google.com/safebrowsing/downloads?client={moz:client}&appver={moz:version}&pver=2.2");
-
-pref("browser.safebrowsing.dataProvider", 0);
-
-// Does the provider name need to be localizable?
-pref("browser.safebrowsing.provider.0.name", "Google");
-pref("browser.safebrowsing.provider.0.keyURL", "https://sb-ssl.google.com/safebrowsing/newkey?client={moz:client}&appver={moz:version}&pver=2.2");
-pref("browser.safebrowsing.provider.0.reportURL", "http://safebrowsing.clients.google.com/safebrowsing/report?");
-pref("browser.safebrowsing.provider.0.gethashURL", "http://safebrowsing.clients.google.com/safebrowsing/gethash?client={moz:client}&appver={moz:version}&pver=2.2");
+pref("browser.safebrowsing.updateURL", "http://safebrowsing.clients.google.com/safebrowsing/downloads?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2");
+pref("browser.safebrowsing.keyURL", "https://sb-ssl.google.com/safebrowsing/newkey?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2");
+pref("browser.safebrowsing.gethashURL", "http://safebrowsing.clients.google.com/safebrowsing/gethash?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2");
+pref("browser.safebrowsing.reportURL", "http://safebrowsing.clients.google.com/safebrowsing/report?");
+pref("browser.safebrowsing.reportGenericURL", "http://%LOCALE%.phish-generic.mozilla.com/?hl=%LOCALE%");
+pref("browser.safebrowsing.reportErrorURL", "http://%LOCALE%.phish-error.mozilla.com/?hl=%LOCALE%");
+pref("browser.safebrowsing.reportPhishURL", "http://%LOCALE%.phish-report.mozilla.com/?hl=%LOCALE%");
+pref("browser.safebrowsing.reportMalwareURL", "http://%LOCALE%.malware-report.mozilla.com/?hl=%LOCALE%");
+pref("browser.safebrowsing.reportMalwareErrorURL", "http://%LOCALE%.malware-error.mozilla.com/?hl=%LOCALE%");
 
-// HTML report pages
-pref("browser.safebrowsing.provider.0.reportGenericURL", "http://{moz:locale}.phish-generic.mozilla.com/?hl={moz:locale}");
-pref("browser.safebrowsing.provider.0.reportErrorURL", "http://{moz:locale}.phish-error.mozilla.com/?hl={moz:locale}");
-pref("browser.safebrowsing.provider.0.reportPhishURL", "http://{moz:locale}.phish-report.mozilla.com/?hl={moz:locale}");
-pref("browser.safebrowsing.provider.0.reportMalwareURL", "http://{moz:locale}.malware-report.mozilla.com/?hl={moz:locale}");
-pref("browser.safebrowsing.provider.0.reportMalwareErrorURL", "http://{moz:locale}.malware-error.mozilla.com/?hl={moz:locale}");
-
-// FAQ URLs
 pref("browser.safebrowsing.warning.infoURL", "http://www.mozilla.com/%LOCALE%/firefox/phishing-protection/");
-pref("browser.geolocation.warning.infoURL", "http://www.mozilla.com/%LOCALE%/firefox/geolocation/");
+pref("browser.safebrowsing.malware.reportURL", "http://safebrowsing.clients.google.com/safebrowsing/diagnostic?client=%NAME%&hl=%LOCALE%&site=");
 
 // Name of the about: page contributed by safebrowsing to handle display of error
 // pages on phishing/malware hits.  (bug 399233)
 pref("urlclassifier.alternate_error_page", "blocked");
 
 // The number of random entries to send with a gethash request.
 pref("urlclassifier.gethashnoise", 4);
 
@@ -761,21 +747,19 @@ pref("urlclassifier.gethashtables", "goo
 // the database.
 pref("urlclassifier.confirm-age", 2700);
 
 // Maximum size of the sqlite3 cache during an update, in bytes
 pref("urlclassifier.updatecachemax", 41943040);
 
 // Maximum size of the sqlite3 cache for lookups, in bytes
 pref("urlclassifier.lookupcachemax", 1048576);
+#endif
 
-// URL for checking the reason for a malware warning.
-pref("browser.safebrowsing.malware.reportURL", "http://safebrowsing.clients.google.com/safebrowsing/diagnostic?client=%NAME%&hl=%LOCALE%&site=");
-
-#endif
+pref("browser.geolocation.warning.infoURL", "http://www.mozilla.com/%LOCALE%/firefox/geolocation/");
 
 pref("browser.EULA.version", 3);
 pref("browser.rights.version", 3);
 pref("browser.rights.3.shown", false);
 
 #ifdef DEBUG
 // Don't show the about:rights notification in debug builds.
 pref("browser.rights.override", true);
--- a/browser/base/content/browser-addons.js
+++ b/browser/base/content/browser-addons.js
@@ -72,24 +72,27 @@ const gXPInstallObserver = {
 
       PopupNotifications.show(browser, notificationID, messageString, anchorID,
                               action, null, options);
       break;
     case "addon-install-blocked":
       messageString = gNavigatorBundle.getFormattedString("xpinstallPromptWarning",
                         [brandShortName, installInfo.originatingURI.host]);
 
+      let secHistogram = Components.classes["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry).getHistogramById("SECURITY_UI");
       action = {
         label: gNavigatorBundle.getString("xpinstallPromptAllowButton"),
         accessKey: gNavigatorBundle.getString("xpinstallPromptAllowButton.accesskey"),
         callback: function() {
+          secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_ADDON_ASKING_PREVENTED_CLICK_THROUGH);
           installInfo.install();
         }
       };
 
+      secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_ADDON_ASKING_PREVENTED);
       PopupNotifications.show(browser, notificationID, messageString, anchorID,
                               action, null, options);
       break;
     case "addon-install-started":
       function needsDownload(aInstall) {
         return aInstall.state != AddonManager.STATE_DOWNLOADED;
       }
       // If all installs have already been downloaded then there is no need to
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -155,20 +155,20 @@ let SocialShareButton = {
   init: function SSB_init() {
     this.updateButtonHiddenState();
     this.updateProfileInfo();
   },
 
   updateProfileInfo: function SSB_updateProfileInfo() {
     let profileRow = document.getElementById("editSharePopupHeader");
     let profile = Social.provider.profile;
-    if (profile && profile.portrait && profile.displayName) {
+    if (profile && profile.displayName) {
       profileRow.hidden = false;
       let portrait = document.getElementById("socialUserPortrait");
-      portrait.style.listStyleImage = profile.portrait;
+      portrait.setAttribute("src", profile.portrait || "chrome://browser/skin/social/social.png");
       let displayName = document.getElementById("socialUserDisplayName");
       displayName.setAttribute("label", profile.displayName);
     } else {
       profileRow.hidden = true;
     }
   },
 
   get shareButton() {
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -2514,19 +2514,23 @@ let BrowserOnClick = {
     }
     else if (/^about:home$/i.test(ownerDoc.documentURI)) {
       this.onAboutHome(originalTarget, ownerDoc);
     }
   },
 
   onAboutCertError: function BrowserOnClick_onAboutCertError(aTargetElm, aOwnerDoc) {
     let elmId = aTargetElm.getAttribute("id");
+    let secHistogram = Cc["@mozilla.org/base/telemetry;1"].
+                        getService(Ci.nsITelemetry).
+                        getHistogramById("SECURITY_UI");
 
     switch (elmId) {
       case "exceptionDialogButton":
+        secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_CLICK_ADD_EXCEPTION);
         let params = { exceptionAdded : false, handlePrivateBrowsing : true };
 
         try {
           switch (Services.prefs.getIntPref("browser.ssl_override_behavior")) {
             case 2 : // Pre-fetch & pre-populate
               params.prefetchCert = true;
             case 1 : // Pre-populate
               params.location = aOwnerDoc.location.href;
@@ -2540,39 +2544,59 @@ let BrowserOnClick = {
 
         // If the user added the exception cert, attempt to reload the page
         if (params.exceptionAdded) {
           aOwnerDoc.location.reload();
         }
         break;
 
       case "getMeOutOfHereButton":
+        secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_GET_ME_OUT_OF_HERE);
         getMeOutOfHere();
         break;
+
+      case "technicalContent":
+        secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_TECHNICAL_DETAILS);
+        break;
+
+      case "expertContent":
+        secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_UNDERSTAND_RISKS);
+        break;
+
     }
   },
 
   onAboutBlocked: function BrowserOnClick_onAboutBlocked(aTargetElm, aOwnerDoc) {
     let elmId = aTargetElm.getAttribute("id");
+    let secHistogram = Cc["@mozilla.org/base/telemetry;1"].
+                        getService(Ci.nsITelemetry).
+                        getHistogramById("SECURITY_UI");
 
     // The event came from a button on a malware/phishing block page
     // First check whether it's malware or phishing, so that we can
     // use the right strings/links
     let isMalware = /e=malwareBlocked/.test(aOwnerDoc.documentURI);
+    let bucketName = isMalware ? "WARNING_MALWARE_PAGE_":"WARNING_PHISHING_PAGE_";
+    let nsISecTel = Ci.nsISecurityUITelemetry;
 
     switch (elmId) {
       case "getMeOutButton":
+        secHistogram.add(nsISecTel[bucketName + "GET_ME_OUT_OF_HERE"]);
         getMeOutOfHere();
         break;
 
       case "reportButton":
         // This is the "Why is this site blocked" button.  For malware,
         // we can fetch a site-specific report, for phishing, we redirect
         // to the generic page describing phishing protection.
 
+        // We log even if malware/phishing info URL couldn't be found: 
+        // the measurement is for how many users clicked the WHY BLOCKED button
+        secHistogram.add(nsISecTel[bucketName + "WHY_BLOCKED"]);
+
         if (isMalware) {
           // Get the stop badware "why is this blocked" report url,
           // append the current url, and go there.
           try {
             let reportURL = formatURL("browser.safebrowsing.malware.reportURL", true);
             reportURL += aOwnerDoc.location.href;
             content.location = reportURL;
           } catch (e) {
@@ -2584,16 +2608,17 @@ let BrowserOnClick = {
             content.location = formatURL("browser.safebrowsing.warning.infoURL", true);
           } catch (e) {
             Components.utils.reportError("Couldn't get phishing info URL: " + e);
           }
         }
         break;
 
       case "ignoreWarningButton":
+        secHistogram.add(nsISecTel[bucketName + "IGNORE_WARNING"]);
         this.ignoreWarningButton(isMalware);
         break;
     }
   },
 
   ignoreWarningButton: function BrowserOnClick_ignoreWarningButton(aIsMalware) {
     // Allow users to override and continue through to the site,
     // but add a notify bar as a reminder, so that they don't lose
--- a/browser/base/content/test/browser_social_toolbar.js
+++ b/browser/base/content/test/browser_social_toolbar.js
@@ -18,25 +18,25 @@ function test() {
       SocialService.removeProvider(Social.provider.origin, finish);
     });
   });
 }
 
 var tests = {
   testProfileSet: function(next) {
     let profile = {
-      portrait: "chrome://branding/content/icon48.png",
+      portrait: "https://example.com/portrait.jpg",
       userName: "trickster",
       displayName: "Kuma Lisa",
       profileURL: "http://en.wikipedia.org/wiki/Kuma_Lisa"
     }
     Social.provider.updateUserProfile(profile);
     // check dom values
     let portrait = document.getElementById("social-statusarea-user-portrait").getAttribute("src");
-    is(portrait, profile.portrait, "portrait is set");
+    is(profile.portrait, portrait, "portrait is set");
     let userButton = document.getElementById("social-statusarea-username");
     ok(!userButton.hidden, "username is visible");
     is(userButton.label, profile.userName, "username is set");
     next();
   },
   testAmbientNotifications: function(next) {
     let ambience = {
       name: "testIcon",
--- a/browser/components/safebrowsing/Makefile.in
+++ b/browser/components/safebrowsing/Makefile.in
@@ -1,24 +1,27 @@
-#
 # 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/.
 
 
 DEPTH     = ../../..
 topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH     = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 TEST_DIRS += content/test
 
+# Normally the "client ID" sent in updates is appinfo.name, but for
+# official Firefox releases from Mozilla we use a special identifier.
 ifdef MOZILLA_OFFICIAL
-DEFINES += -DOFFICIAL_BUILD=1
+ifdef MOZ_PHOENIX
+DEFINES += -DUSE_HISTORIC_SAFEBROWSING_ID=1
+endif
 endif
 
 EXTRA_PP_JS_MODULES = \
   SafeBrowsing.jsm \
   $(NULL)
 
 include $(topsrcdir)/config/rules.mk
--- a/browser/components/safebrowsing/SafeBrowsing.jsm
+++ b/browser/components/safebrowsing/SafeBrowsing.jsm
@@ -29,50 +29,39 @@ var SafeBrowsing = {
     if (this.initialized) {
       log("Already initialized");
       return;
     }
 
     Services.prefs.addObserver("browser.safebrowsing", this.readPrefs, false);
     this.readPrefs();
 
-    this.initProviderURLs();
-
-    // Initialize the list manager
+    // Register our two types of tables, and add custom Mozilla entries
     let listManager = Cc["@mozilla.org/url-classifier/listmanager;1"].
                       getService(Ci.nsIUrlListManager);
-
-    listManager.setUpdateUrl(this.updateURL);
-    // setKeyUrl has the side effect of fetching a key from the server.
-    // This shouldn't happen if anti-phishing/anti-malware is disabled.
-    if (this.phishingEnabled || this.malwareEnabled)
-      listManager.setKeyUrl(this.keyURL);
-    listManager.setGethashUrl(this.gethashURL);
-
-    // Register our two types of tables
     listManager.registerTable(phishingList, false);
     listManager.registerTable(malwareList, false);
     this.addMozEntries();
 
     this.controlUpdateChecking();
     this.initialized = true;
 
     log("init() finished");
   },
 
 
   initialized:     false,
   phishingEnabled: false,
   malwareEnabled:  false,
 
-  provName:              null,
   updateURL:             null,
   keyURL:                null,
+  gethashURL:            null,
+
   reportURL:             null,
-  gethashURL:            null,
   reportGenericURL:      null,
   reportErrorURL:        null,
   reportPhishURL:        null,
   reportMalwareURL:      null,
   reportMalwareErrorURL: null,
 
 
   getReportURL: function(kind) {
@@ -81,86 +70,62 @@ var SafeBrowsing = {
 
 
   readPrefs: function() {
     log("reading prefs");
 
     debug = Services.prefs.getBoolPref("browser.safebrowsing.debug");
     this.phishingEnabled = Services.prefs.getBoolPref("browser.safebrowsing.enabled");
     this.malwareEnabled  = Services.prefs.getBoolPref("browser.safebrowsing.malware.enabled");
+    this.updateProviderURLs();
 
     // XXX The listManager backend gets confused if this is called before the
     // lists are registered. So only call it here when a pref changes, and not
     // when doing initialization. I expect to refactor this later, so pardon the hack.
     if (this.initialized)
       this.controlUpdateChecking();
   },
 
 
-  initProviderURLs: function() {
-    log("initializing provider URLs");
+  updateProviderURLs: function() {
+#ifdef USE_HISTORIC_SAFEBROWSING_ID
+    let clientID = "navclient-auto-ffox";
+#else
+    let clientID = Services.appinfo.name;
+#endif
 
-    // XXX remove this as obsolete?
-    let provID = Services.prefs.getIntPref("browser.safebrowsing.dataProvider");
-    if (provID != 0) {
-      Cu.reportError("unknown safebrowsing provider ID " + provID);
-      return;
-    }
-
-    let basePref = "browser.safebrowsing.provider.0.";
-    this.provName = Services.prefs.getCharPref(basePref + "name");
-
-    // Urls used to get data from a provider
-    this.updateURL  = this.getUrlPref(basePref + "updateURL");
-    this.keyURL     = this.getUrlPref(basePref + "keyURL");
-    this.reportURL  = this.getUrlPref(basePref + "reportURL");
-    this.gethashURL = this.getUrlPref(basePref + "gethashURL");
+    log("initializing safe browsing URLs");
+    let basePref = "browser.safebrowsing.";
 
     // Urls to HTML report pages
-    this.reportGenericURL      = this.getUrlPref(basePref + "reportGenericURL");
-    this.reportErrorURL        = this.getUrlPref(basePref + "reportErrorURL");
-    this.reportPhishURL        = this.getUrlPref(basePref + "reportPhishURL");
-    this.reportMalwareURL      = this.getUrlPref(basePref + "reportMalwareURL")
-    this.reportMalwareErrorURL = this.getUrlPref(basePref + "reportMalwareErrorURL")
-  },
-
+    this.reportURL             = Services.urlFormatter.formatURLPref(basePref + "reportURL");
+    this.reportGenericURL      = Services.urlFormatter.formatURLPref(basePref + "reportGenericURL");
+    this.reportErrorURL        = Services.urlFormatter.formatURLPref(basePref + "reportErrorURL");
+    this.reportPhishURL        = Services.urlFormatter.formatURLPref(basePref + "reportPhishURL");
+    this.reportMalwareURL      = Services.urlFormatter.formatURLPref(basePref + "reportMalwareURL");
+    this.reportMalwareErrorURL = Services.urlFormatter.formatURLPref(basePref + "reportMalwareErrorURL");
 
-  getUrlPref: function(prefName) {
-    let MOZ_OFFICIAL_BUILD = false;
-#ifdef OFFICIAL_BUILD
-    MOZ_OFFICIAL_BUILD = true;
-#endif
-
-    let url = Services.prefs.getCharPref(prefName);
-
-    let clientName = MOZ_OFFICIAL_BUILD ? "navclient-auto-ffox" : Services.appinfo.name;
-    let clientVersion = Services.appinfo.version;
+    // Urls used to update DB
+    this.updateURL  = Services.urlFormatter.formatURLPref(basePref + "updateURL");
+    this.keyURL     = Services.urlFormatter.formatURLPref(basePref + "keyURL");
+    this.gethashURL = Services.urlFormatter.formatURLPref(basePref + "gethashURL");
 
-    // Parameter substitution
-    // XXX: we should instead use nsIURLFormatter here.
-    url = url.replace(/\{moz:locale\}/g,  this.getLocale());
-    url = url.replace(/\{moz:client\}/g,  clientName);
-    url = url.replace(/\{moz:buildid\}/g, Services.appinfo.appBuildID);
-    url = url.replace(/\{moz:version\}/g, clientVersion);
+    this.updateURL  = this.updateURL.replace("SAFEBROWSING_ID", clientID);
+    this.keyURL     = this.keyURL.replace("SAFEBROWSING_ID", clientID);
+    this.gethashURL = this.gethashURL.replace("SAFEBROWSING_ID", clientID);
 
-    log(prefName, "is", url);
-    return url;
-  },
+    let listManager = Cc["@mozilla.org/url-classifier/listmanager;1"].
+                      getService(Ci.nsIUrlListManager);
 
-
-  getLocale: function() {
-    const localePref = "general.useragent.locale";
-
-    let locale = Services.prefs.getCharPref(localePref);
-    try {
-      // Dumb. This API only works if pref is localized or has a user value.
-      locale = Services.prefs.getComplexValue(localePref, Ci.nsIPrefLocalizedString).data;
-    } catch (e) { }
-
-    return locale;
+    listManager.setUpdateUrl(this.updateURL);
+    // XXX Bug 779317 - setKeyUrl has the side effect of fetching a key from the server.
+    // This shouldn't happen if anti-phishing/anti-malware is disabled.
+    if (this.phishingEnabled || this.malwareEnabled)
+      listManager.setKeyUrl(this.keyURL);
+    listManager.setGethashUrl(this.gethashURL);
   },
 
 
   controlUpdateChecking: function() {
     log("phishingEnabled:", this.phishingEnabled, "malwareEnabled:", this.malwareEnabled);
 
     let listManager = Cc["@mozilla.org/url-classifier/listmanager;1"].
                       getService(Ci.nsIUrlListManager);
@@ -174,17 +139,17 @@ var SafeBrowsing = {
       listManager.enableUpdate(malwareList);
     else
       listManager.disableUpdate(malwareList);
   },
 
 
   addMozEntries: function() {
     // Add test entries to the DB.
-    // XXX this should really just be done by DB itself for all moz apps.
+    // XXX bug 779008 - this could be done by DB itself?
     const phishURL   = "mozilla.org/firefox/its-a-trap.html";
     const malwareURL = "mozilla.org/firefox/its-an-attack.html";
 
     let update = "n:1000\ni:test-malware-simple\nad:1\n" +
                  "a:1:32:" + malwareURL.length + "\n" +
                  malwareURL;
     update += "n:1000\ni:test-phish-simple\nad:1\n" +
               "a:1:32:" + phishURL.length + "\n" +
--- a/browser/components/shell/src/nsWindowsShellService.cpp
+++ b/browser/components/shell/src/nsWindowsShellService.cpp
@@ -47,21 +47,16 @@
 #define REG_SUCCEEDED(val) \
   (val == ERROR_SUCCESS)
 
 #define REG_FAILED(val) \
   (val != ERROR_SUCCESS)
 
 #define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1"
 
-// We clear the prefetch files one time after the browser is started after
-// 3 minutes.  After this is done once we set a pref so this will never happen
-// again except in updater code.
-#define CLEAR_PREFETCH_TIMEOUT_MS 180000
-
 NS_IMPL_ISUPPORTS2(nsWindowsShellService, nsIWindowsShellService, nsIShellService)
 
 static nsresult
 OpenKeyForReading(HKEY aKeyRoot, const nsAString& aKeyName, HKEY* aKey)
 {
   const nsString &flatName = PromiseFlatString(aKeyName);
 
   DWORD res = ::RegOpenKeyExW(aKeyRoot, flatName.get(), 0, KEY_READ, aKey);
@@ -189,27 +184,16 @@ static SETTING gDDESettings[] = {
   { MAKE_KEY_NAME1("Software\\Classes\\FirefoxURL", SOD) },
 
   // Protocol Handlers
   { MAKE_KEY_NAME1("Software\\Classes\\FTP", SOD) },
   { MAKE_KEY_NAME1("Software\\Classes\\HTTP", SOD) },
   { MAKE_KEY_NAME1("Software\\Classes\\HTTPS", SOD) }
 };
 
-#if defined(MOZ_MAINTENANCE_SERVICE)
-
-#define ONLY_SERVICE_LAUNCHING
-#include "updatehelper.h"
-#include "updatehelper.cpp"
-
-static const char *kPrefetchClearedPref =
-  "app.update.service.lastVersionPrefetchCleared";
-static nsCOMPtr<nsIThread> sThread;
-#endif
-
 nsresult
 GetHelperPath(nsAutoString& aPath)
 {
   nsresult rv;
   nsCOMPtr<nsIProperties> directoryService = 
     do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -996,133 +980,22 @@ nsWindowsShellService::SetDesktopBackgro
   NS_ENSURE_SUCCESS(rv, rv);
 
   return regKey->Close();
 }
 
 nsWindowsShellService::nsWindowsShellService() : 
   mCheckedThisSession(false) 
 {
-#if defined(MOZ_MAINTENANCE_SERVICE)
-
-  // Check to make sure the service is installed
-  PRUint32 installed = 0;
-  nsCOMPtr<nsIWindowsRegKey> regKey = 
-    do_CreateInstance("@mozilla.org/windows-registry-key;1");
-  if (!regKey || 
-      NS_FAILED(regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
-                             NS_LITERAL_STRING(
-                               "SOFTWARE\\Mozilla\\MaintenanceService"),
-                             nsIWindowsRegKey::ACCESS_READ |
-                             nsIWindowsRegKey::WOW64_64)) ||
-      NS_FAILED(regKey->ReadIntValue(NS_LITERAL_STRING("Installed"), 
-                &installed)) ||
-      !installed) {
-    return;
-  }
-
-  // check to see if we have attempted to do the one time operation of clearing
-  // the prefetch.  We do it once per version upgrade.
-  nsCString lastClearedVer;
-  nsCOMPtr<nsIPrefBranch> prefBranch;
-  nsCOMPtr<nsIPrefService> prefs =
-    do_GetService(NS_PREFSERVICE_CONTRACTID);
-  if (!prefs || 
-      NS_FAILED(prefs->GetBranch(nullptr, getter_AddRefs(prefBranch))) ||
-      (NS_SUCCEEDED(prefBranch->GetCharPref(kPrefetchClearedPref, 
-                                            getter_Copies(lastClearedVer))))) {
-    // If the versions are the same, then bail out early.  We only want to clear
-    // once per version.
-    if (!strcmp(MOZ_APP_VERSION, lastClearedVer.get())) {
-      return;
-    }
-  }
-
-  // In a minute after startup is definitely complete, launch the
-  // service command.
-  mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
-  if (mTimer) {
-    mTimer->InitWithFuncCallback(
-      nsWindowsShellService::LaunchPrefetchClearCommand, 
-      nullptr, CLEAR_PREFETCH_TIMEOUT_MS, nsITimer::TYPE_ONE_SHOT);
-  }
-#endif
 }
 
 nsWindowsShellService::~nsWindowsShellService()
 {
-#if defined(MOZ_MAINTENANCE_SERVICE)
- if (mTimer) {
-    mTimer->Cancel();
-    mTimer = nullptr;
-  }
-  if (sThread) {
-    sThread->Shutdown();
-    sThread = nullptr;
-  }
-#endif
 }
 
-#if defined(MOZ_MAINTENANCE_SERVICE)
-
-class ClearPrefetchEvent : public nsRunnable {
-public:
-  ClearPrefetchEvent()
-  {
-  }
-
-  NS_IMETHOD Run() 
-  {
-    // Start the service command
-    LPCWSTR updaterServiceArgv[2];
-    updaterServiceArgv[0] = L"MozillaMaintenance";
-    updaterServiceArgv[1] = L"clear-prefetch";
-    // If this command fails, it is not critical as prefetch will be cleared
-    // on the next software update.
-    StartServiceCommand(NS_ARRAY_LENGTH(updaterServiceArgv), 
-                        updaterServiceArgv);
-    return NS_OK;
-  }
-};
-#endif
-
-/**
- * For faster startup we attempt to clear the prefetch if the maintenance
- * service is installed.  Please see the definition of ClearPrefetch()
- * in toolkit/components/maintenanceservice/prefetch.cpp for more info.
- * For now the only application that gets prefetch cleaned is Firefox
- * since we have not done performance checking for other applications.
- * This is done on every update but also there is a one time operation done
- * from within the program for first time installs.
- */ 
-#if defined(MOZ_MAINTENANCE_SERVICE)
-void
-nsWindowsShellService::LaunchPrefetchClearCommand(nsITimer *aTimer, void*)
-{
-  // Make sure we don't call this again from the application, it will be
-  // called on each application update instead.
-  nsCOMPtr<nsIPrefBranch> prefBranch;
-  nsCOMPtr<nsIPrefService> prefs =
-    do_GetService(NS_PREFSERVICE_CONTRACTID);
-  if (prefs) {
-    if (NS_SUCCEEDED(prefs->GetBranch(nullptr, getter_AddRefs(prefBranch)))) {
-      prefBranch->SetCharPref(kPrefetchClearedPref, MOZ_APP_VERSION);
-    }
-  }
-
-  // Starting the sevice can take a bit of time and we don't want to block the 
-  // main thread, so start an event on another thread to handle the operation
-  NS_NewThread(getter_AddRefs(sThread));
-  if (sThread) {
-    nsCOMPtr<nsIRunnable> prefetchEvent = new ClearPrefetchEvent();
-    sThread->Dispatch(prefetchEvent, NS_DISPATCH_NORMAL);
-  }
-}
-#endif
-
 NS_IMETHODIMP
 nsWindowsShellService::OpenApplicationWithURI(nsIFile* aApplication,
                                               const nsACString& aURI)
 {
   nsresult rv;
   nsCOMPtr<nsIProcess> process = 
     do_CreateInstance("@mozilla.org/process/util;1", &rv);
   if (NS_FAILED(rv))
--- a/browser/components/shell/src/nsWindowsShellService.h
+++ b/browser/components/shell/src/nsWindowsShellService.h
@@ -24,16 +24,12 @@ public:
   NS_DECL_NSISHELLSERVICE
   NS_DECL_NSIWINDOWSSHELLSERVICE
 
 protected:
   static nsresult IsDefaultBrowser(bool* aIsDefaultBrowser);
   static bool IsDefaultBrowserVista(bool* aIsDefaultBrowser);
 
 private:
-#if defined(MOZ_MAINTENANCE_SERVICE)
-  static void LaunchPrefetchClearCommand(nsITimer *aTimer, void*);
-  nsCOMPtr<nsITimer> mTimer;
-#endif
   bool      mCheckedThisSession;
 };
 
 #endif // nswindowsshellservice_h____
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -412,19 +412,19 @@ user_pref("geo.wifi.uri", "http://%(serv
 user_pref("geo.wifi.testing", true);
 user_pref("geo.ignore.location_filter", true);
 
 user_pref("camino.warn_when_closing", false); // Camino-only, harmless to others
 
 // Make url-classifier updates so rare that they won't affect tests
 user_pref("urlclassifier.updateinterval", 172800);
 // Point the url-classifier to the local testing server for fast failures
-user_pref("browser.safebrowsing.provider.0.gethashURL", "http://%(server)s/safebrowsing-dummy/gethash");
-user_pref("browser.safebrowsing.provider.0.keyURL", "http://%(server)s/safebrowsing-dummy/newkey");
-user_pref("browser.safebrowsing.provider.0.updateURL", "http://%(server)s/safebrowsing-dummy/update");
+user_pref("browser.safebrowsing.gethashURL", "http://%(server)s/safebrowsing-dummy/gethash");
+user_pref("browser.safebrowsing.keyURL", "http://%(server)s/safebrowsing-dummy/newkey");
+user_pref("browser.safebrowsing.updateURL", "http://%(server)s/safebrowsing-dummy/update");
 // Point update checks to the local testing server for fast failures
 user_pref("extensions.update.url", "http://%(server)s/extensions-dummy/updateURL");
 user_pref("extensions.update.background.url", "http://%(server)s/extensions-dummy/updateBackgroundURL");
 user_pref("extensions.blocklist.url", "http://%(server)s/extensions-dummy/blocklistURL");
 user_pref("extensions.hotfix.url", "http://%(server)s/extensions-dummy/hotfixURL");
 // Make sure opening about:addons won't hit the network
 user_pref("extensions.webservice.discoverURL", "http://%(server)s/extensions-dummy/discoveryURL");
 // Make sure AddonRepository won't hit the network
--- a/build/mobile/b2gautomation.py
+++ b/build/mobile/b2gautomation.py
@@ -206,18 +206,21 @@ class B2GRemoteAutomation(Automation):
                                           'tcp:%s' % self.marionette.port,
                                           'tcp:%s' % self.marionette.port])
 
         # start a marionette session
         session = self.marionette.start_session()
         if 'b2g' not in session:
             raise Exception("bad session value %s returned by start_session" % session)
 
-        # start the tests by navigating to the mochitest url
-        self.marionette.execute_script("window.location.href='%s';" % self.testURL)
+        # Start the tests by navigating to the mochitest url, by setting it
+        # as the 'src' attribute to the homescreen mozbrowser element
+        # provided by B2G's shell.js.
+        self.marionette.set_context("chrome")
+        self.marionette.execute_script("document.getElementById('homescreen').src='%s';" % self.testURL)
 
         return instance
 
     # be careful here as this inner class doesn't have access to outer class members
     class B2GInstance(object):
         """Represents a B2G instance running on a device, and exposes
            some process-like methods/properties that are expected by the
            automation.
--- a/build/mobile/devicemanager.py
+++ b/build/mobile/devicemanager.py
@@ -432,16 +432,18 @@ class DeviceManager:
   @abstractmethod
   def getInfo(self, directive=None):
     """
     Returns information about the device:
     Directive indicates the information you want to get, your choices are:
     os - name of the os
     id - unique id of the device
     uptime - uptime of the device
+    uptimemillis - uptime of the device in milliseconds (NOT supported on all
+                   platforms)
     systime - system time of the device
     screen - screen resolution
     memory - memory stats
     process - list of running processes (same as ps)
     disk - total, free, available bytes on disk
     power - power status (charge, battery temp)
     all - all of them - or call it with no parameters to get all the information
     returns:
--- a/build/mobile/devicemanagerADB.py
+++ b/build/mobile/devicemanagerADB.py
@@ -658,16 +658,18 @@ class DeviceManagerADB(DeviceManager):
   # uptime - uptime of the device
   # systime - system time of the device
   # screen - screen resolution
   # memory - memory stats
   # process - list of running processes (same as ps)
   # disk - total, free, available bytes on disk
   # power - power status (charge, battery temp)
   # all - all of them - or call it with no parameters to get all the information
+  ### Note that uptimemillis is NOT supported, as there is no way to get this
+  ### data from the shell.
   # returns:
   #   success: dict of info strings by directive name
   #   failure: {}
   def getInfo(self, directive="all"):
     ret = {}
     if (directive == "id" or directive == "all"):
       ret["id"] = self.runCmd(["get-serialno"]).stdout.read()
     if (directive == "os" or directive == "all"):
--- a/build/mobile/devicemanagerSUT.py
+++ b/build/mobile/devicemanagerSUT.py
@@ -912,34 +912,35 @@ class DeviceManagerSUT(DeviceManager):
     if (self.debug > 3): print "INFO: rebt- got status back: " + str(status)
     return status
 
   # Returns information about the device:
   # Directive indicates the information you want to get, your choices are:
   # os - name of the os
   # id - unique id of the device
   # uptime - uptime of the device
+  # uptimemillis - uptime of the device in milliseconds
   # systime - system time of the device
   # screen - screen resolution
   # rotation - rotation of the device (in degrees)
   # memory - memory stats
   # process - list of running processes (same as ps)
   # disk - total, free, available bytes on disk
   # power - power status (charge, battery temp)
   # all - all of them - or call it with no parameters to get all the information
   # returns:
   #   success: dict of info strings by directive name
   #   failure: {}
   def getInfo(self, directive=None):
     data = None
     result = {}
     collapseSpaces = re.compile('  +')
 
-    directives = ['os','id','uptime','systime','screen','rotation','memory','process',
-                  'disk','power']
+    directives = ['os','id','uptime','uptimemillis','systime','screen',
+                  'rotation','memory','process','disk','power']
     if (directive in directives):
       directives = [directive]
 
     for d in directives:
       data = self.runCmds([{ 'cmd': 'info ' + d }])
       if (data is None):
         continue
       data = collapseSpaces.sub(' ', data)
--- a/build/mobile/sutagent/android/watcher/WatcherService.java
+++ b/build/mobile/sutagent/android/watcher/WatcherService.java
@@ -45,17 +45,17 @@ import android.widget.Toast;
 
 public class WatcherService extends Service
 {
     String sErrorPrefix = "##Installer Error## ";
     String currentDir = "/";
     String sPingTarget = "";
     long lDelay = 60000;
     long lPeriod = 300000;
-    int nMaxStrikes = 3; // maximum number of tries before we consider network unreachable
+    int nMaxStrikes = 0; // maximum number of tries before we consider network unreachable (0 means don't check)
     boolean bStartSUTAgent = true;
 
     Process    pProc;
     Context myContext = null;
     Timer myTimer = null;
     private PowerManager.WakeLock pwl = null;
     public static final int NOTIFICATION_ID = 1964;
     boolean bInstalling = false;
@@ -106,17 +106,17 @@ public class WatcherService extends Serv
         String sHold = "";
 
         Log.i("Watcher", String.format("Loading settings from %s", sIniFile));
         this.sPingTarget = GetIniData("watcher", "PingTarget", sIniFile, "www.mozilla.org");
         sHold = GetIniData("watcher", "delay", sIniFile, "60000");
         this.lDelay = Long.parseLong(sHold.trim());
         sHold = GetIniData("watcher", "period", sIniFile,"300000");
         this.lPeriod = Long.parseLong(sHold.trim());
-        sHold = GetIniData("watcher", "strikes", sIniFile,"3");
+        sHold = GetIniData("watcher", "strikes", sIniFile,"0");
         this.nMaxStrikes = Integer.parseInt(sHold.trim());
         Log.i("Watcher", String.format("Pinging %s after a delay of %s sec, period of %s sec, max number of failed attempts is %s (if max # of failed attempts is 0, then no checking)",
                                        this.sPingTarget, this.lDelay / 1000.0, this.lPeriod / 1000.0, nMaxStrikes));
 
         sHold = GetIniData("watcher", "StartSUTAgent", sIniFile, "true");
         this.bStartSUTAgent = Boolean.parseBoolean(sHold.trim());
 
         sHold = GetIniData("watcher", "stayon", sIniFile,"0");
--- a/content/base/public/nsDOMFile.h
+++ b/content/base/public/nsDOMFile.h
@@ -50,16 +50,34 @@ public:
   virtual const nsTArray<nsCOMPtr<nsIDOMBlob> >*
   GetSubBlobs() const { return nullptr; }
 
   NS_DECL_NSIDOMBLOB
   NS_DECL_NSIDOMFILE
   NS_DECL_NSIXHRSENDABLE
   NS_DECL_NSIMUTABLE
 
+  void
+  SetLazyData(const nsAString& aName, const nsAString& aContentType,
+              PRUint64 aLength)
+  {
+    NS_ASSERTION(aLength, "must have length");
+
+    mName = aName;
+    mContentType = aContentType;
+    mLength = aLength;
+
+    mIsFile = !aName.IsVoid();
+  }
+
+  bool IsSizeUnknown() const
+  {
+    return mLength == UINT64_MAX;
+  }
+
 protected:
   nsDOMFileBase(const nsAString& aName, const nsAString& aContentType,
                 PRUint64 aLength)
     : mIsFile(true), mImmutable(false), mContentType(aContentType),
       mName(aName), mStart(0), mLength(aLength)
   {
     // Ensure non-null mContentType by default
     mContentType.SetIsVoid(false);
@@ -81,21 +99,16 @@ protected:
     NS_ASSERTION(aLength != UINT64_MAX,
                  "Must know length when creating slice");
     // Ensure non-null mContentType by default
     mContentType.SetIsVoid(false);
   }
 
   virtual ~nsDOMFileBase() {}
 
-  bool IsSizeUnknown() const
-  {
-    return mLength == UINT64_MAX;
-  }
-
   virtual bool IsStoredFile() const
   {
     return false;
   }
 
   virtual bool IsWholeFile() const
   {
     NS_NOTREACHED("Should only be called on dom blobs backed by files!");
--- a/content/base/public/nsIFrameMessageManager.idl
+++ b/content/base/public/nsIFrameMessageManager.idl
@@ -12,25 +12,29 @@ interface nsIContent;
 [scriptable, function, uuid(938fcb95-3d63-46be-aa72-94d08fd3b418)]
 interface nsIFrameMessageListener : nsISupports
 {
   /**
    * This is for JS only.
    * receiveMessage is called with one parameter, which has the following
    * properties:
    *   {
+   *     target:  %the target of the message. Either an element owning
+   *               the message manager, or message manager itself if no
+   *               element owns it%
    *     name:    %message name%,
    *     sync:    %true or false%.
-   *     json:    %json object or null%,
+   *     json:    %structured clone of the sent message data%,
+   *     json:    %same as .data, deprecated%,
    *     objects: %array of handles or null, always null if sync is false%
    *   }
    * @note objects property isn't implemented yet.
    *
    * if the message is synchronous, possible return value is sent back
-   * as JSON.
+   * as JSON (will be changed to use structured clones).
    *
    * When the listener is called, 'this' value is the target of the message.
    */
   void receiveMessage();
 };
 
 [scriptable, builtinclass, uuid(9be42627-a5db-456f-8de2-9097da45a8c3)]
 interface nsIFrameMessageManager : nsISupports
--- a/content/base/src/Makefile.in
+++ b/content/base/src/Makefile.in
@@ -123,16 +123,17 @@ CPPSRCS		= \
 		nsWebSocket.cpp \
 		nsXHTMLContentSerializer.cpp \
 		nsXMLContentSerializer.cpp \
 		nsXMLHttpRequest.cpp \
 		nsXMLNameSpaceMap.cpp \
 		FragmentOrElement.cpp \
 		Link.cpp \
 		nsBlobProtocolHandler.cpp \
+		nsBlobURI.cpp \
 		nsFrameMessageManager.cpp \
 		nsInProcessTabChildGlobal.cpp \
 		ThirdPartyUtil.cpp \
 		nsEventSource.cpp \
 		FileIOObject.cpp \
 		nsDOMMutationObserver.cpp \
 		$(NULL)
 
--- a/content/base/src/nsBlobProtocolHandler.cpp
+++ b/content/base/src/nsBlobProtocolHandler.cpp
@@ -1,26 +1,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsBlobProtocolHandler.h"
-#include "nsSimpleURI.h"
+#include "nsBlobURI.h"
 #include "nsDOMError.h"
-#include "nsCOMPtr.h"
 #include "nsClassHashtable.h"
 #include "nsNetUtil.h"
-#include "nsIURIWithPrincipal.h"
 #include "nsIPrincipal.h"
 #include "nsIDOMFile.h"
-#include "nsISerializable.h"
-#include "nsIClassInfo.h"
-#include "nsIObjectInputStream.h"
-#include "nsIObjectOutputStream.h"
-#include "nsIProgrammingLanguage.h"
 
 // -----------------------------------------------------------------------
 // Hash table
 struct FileDataInfo
 {
   nsCOMPtr<nsIDOMBlob> mFile;
   nsCOMPtr<nsIPrincipal> mPrincipal;
 };
@@ -85,243 +78,16 @@ GetFileDataInfo(const nsACString& aUri)
   }
   
   FileDataInfo* res;
   gFileDataTable->Get(aUri, &res);
   return res;
 }
 
 // -----------------------------------------------------------------------
-// Uri
-
-#define NS_BLOBURI_CID \
-{ 0xf5475c51, 0x59a7, 0x4757, \
-  { 0xb3, 0xd9, 0xe2, 0x11, 0xa9, 0x41, 0x08, 0x72 } }
-
-static NS_DEFINE_CID(kBLOBURICID, NS_BLOBURI_CID);
-
-class nsBlobURI : public nsSimpleURI,
-                      public nsIURIWithPrincipal
-{
-public:
-  nsBlobURI(nsIPrincipal* aPrincipal) :
-      nsSimpleURI(), mPrincipal(aPrincipal)
-  {}
-  virtual ~nsBlobURI() {}
-
-  // For use only from deserialization
-  nsBlobURI() : nsSimpleURI() {}
-
-  NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_NSIURIWITHPRINCIPAL
-  NS_DECL_NSISERIALIZABLE
-  NS_DECL_NSICLASSINFO
-
-  // Override CloneInternal() and EqualsInternal()
-  virtual nsresult CloneInternal(RefHandlingEnum aRefHandlingMode,
-                                 nsIURI** aClone);
-  virtual nsresult EqualsInternal(nsIURI* aOther,
-                                  RefHandlingEnum aRefHandlingMode,
-                                  bool* aResult);
-
-  // Override StartClone to hand back a nsBlobURI
-  virtual nsSimpleURI* StartClone(RefHandlingEnum /* unused */)
-  { return new nsBlobURI(); }
-
-  nsCOMPtr<nsIPrincipal> mPrincipal;
-};
-
-static NS_DEFINE_CID(kThisSimpleURIImplementationCID,
-                     NS_THIS_SIMPLEURI_IMPLEMENTATION_CID);
-
-NS_IMPL_ADDREF_INHERITED(nsBlobURI, nsSimpleURI)
-NS_IMPL_RELEASE_INHERITED(nsBlobURI, nsSimpleURI)
-
-NS_INTERFACE_MAP_BEGIN(nsBlobURI)
-  NS_INTERFACE_MAP_ENTRY(nsIURIWithPrincipal)
-  if (aIID.Equals(kBLOBURICID))
-    foundInterface = static_cast<nsIURI*>(this);
-  else if (aIID.Equals(kThisSimpleURIImplementationCID)) {
-    // Need to return explicitly here, because if we just set foundInterface
-    // to null the NS_INTERFACE_MAP_END_INHERITING will end up calling into
-    // nsSimplURI::QueryInterface and finding something for this CID.
-    *aInstancePtr = nullptr;
-    return NS_NOINTERFACE;
-  }
-  else
-NS_INTERFACE_MAP_END_INHERITING(nsSimpleURI)
-
-// nsIURIWithPrincipal methods:
-
-NS_IMETHODIMP
-nsBlobURI::GetPrincipal(nsIPrincipal** aPrincipal)
-{
-  NS_IF_ADDREF(*aPrincipal = mPrincipal);
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsBlobURI::GetPrincipalUri(nsIURI** aUri)
-{
-  if (mPrincipal) {
-    mPrincipal->GetURI(aUri);
-  }
-  else {
-    *aUri = nullptr;
-  }
-
-  return NS_OK;
-}
-
-// nsISerializable methods:
-
-NS_IMETHODIMP
-nsBlobURI::Read(nsIObjectInputStream* aStream)
-{
-  nsresult rv = nsSimpleURI::Read(aStream);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_ReadOptionalObject(aStream, true, getter_AddRefs(mPrincipal));
-}
-
-NS_IMETHODIMP
-nsBlobURI::Write(nsIObjectOutputStream* aStream)
-{
-  nsresult rv = nsSimpleURI::Write(aStream);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_WriteOptionalCompoundObject(aStream, mPrincipal,
-                                        NS_GET_IID(nsIPrincipal),
-                                        true);
-}
-
-// nsIURI methods:
-nsresult
-nsBlobURI::CloneInternal(nsSimpleURI::RefHandlingEnum aRefHandlingMode,
-                             nsIURI** aClone)
-{
-  nsCOMPtr<nsIURI> simpleClone;
-  nsresult rv =
-    nsSimpleURI::CloneInternal(aRefHandlingMode, getter_AddRefs(simpleClone));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-#ifdef DEBUG
-  nsRefPtr<nsBlobURI> uriCheck;
-  rv = simpleClone->QueryInterface(kBLOBURICID, getter_AddRefs(uriCheck));
-  NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv) && uriCheck,
-		    "Unexpected!");
-#endif
-
-  nsBlobURI* blobURI = static_cast<nsBlobURI*>(simpleClone.get());
-
-  blobURI->mPrincipal = mPrincipal;
-
-  simpleClone.forget(aClone);
-  return NS_OK;
-}
-
-/* virtual */ nsresult
-nsBlobURI::EqualsInternal(nsIURI* aOther,
-                              nsSimpleURI::RefHandlingEnum aRefHandlingMode,
-                              bool* aResult)
-{
-  if (!aOther) {
-    *aResult = false;
-    return NS_OK;
-  }
-  
-  nsRefPtr<nsBlobURI> otherBlobUri;
-  aOther->QueryInterface(kBLOBURICID, getter_AddRefs(otherBlobUri));
-  if (!otherBlobUri) {
-    *aResult = false;
-    return NS_OK;
-  }
-
-  // Compare the member data that our base class knows about.
-  if (!nsSimpleURI::EqualsInternal(otherBlobUri, aRefHandlingMode)) {
-    *aResult = false;
-    return NS_OK;
-   }
-
-  // Compare the piece of additional member data that we add to base class.
-  if (mPrincipal && otherBlobUri->mPrincipal) {
-    // Both of us have mPrincipals. Compare them.
-    return mPrincipal->Equals(otherBlobUri->mPrincipal, aResult);
-  }
-  // else, at least one of us lacks a principal; only equal if *both* lack it.
-  *aResult = (!mPrincipal && !otherBlobUri->mPrincipal);
-  return NS_OK;
-}
-
-// nsIClassInfo methods:
-NS_IMETHODIMP 
-nsBlobURI::GetInterfaces(PRUint32 *count, nsIID * **array)
-{
-  *count = 0;
-  *array = nullptr;
-  return NS_OK;
-}
-
-NS_IMETHODIMP 
-nsBlobURI::GetHelperForLanguage(PRUint32 language, nsISupports **_retval)
-{
-  *_retval = nullptr;
-  return NS_OK;
-}
-
-NS_IMETHODIMP 
-nsBlobURI::GetContractID(char * *aContractID)
-{
-  // Make sure to modify any subclasses as needed if this ever
-  // changes.
-  *aContractID = nullptr;
-  return NS_OK;
-}
-
-NS_IMETHODIMP 
-nsBlobURI::GetClassDescription(char * *aClassDescription)
-{
-  *aClassDescription = nullptr;
-  return NS_OK;
-}
-
-NS_IMETHODIMP 
-nsBlobURI::GetClassID(nsCID * *aClassID)
-{
-  // Make sure to modify any subclasses as needed if this ever
-  // changes to not call the virtual GetClassIDNoAlloc.
-  *aClassID = (nsCID*) nsMemory::Alloc(sizeof(nsCID));
-  NS_ENSURE_TRUE(*aClassID, NS_ERROR_OUT_OF_MEMORY);
-
-  return GetClassIDNoAlloc(*aClassID);
-}
-
-NS_IMETHODIMP 
-nsBlobURI::GetImplementationLanguage(PRUint32 *aImplementationLanguage)
-{
-  *aImplementationLanguage = nsIProgrammingLanguage::CPLUSPLUS;
-  return NS_OK;
-}
-
-NS_IMETHODIMP 
-nsBlobURI::GetFlags(PRUint32 *aFlags)
-{
-  *aFlags = nsIClassInfo::MAIN_THREAD_ONLY;
-  return NS_OK;
-}
-
-NS_IMETHODIMP 
-nsBlobURI::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
-{
-  *aClassIDNoAlloc = kBLOBURICID;
-  return NS_OK;
-}
-
-// -----------------------------------------------------------------------
 // Protocol handler
 
 NS_IMPL_ISUPPORTS1(nsBlobProtocolHandler, nsIProtocolHandler)
 
 NS_IMETHODIMP
 nsBlobProtocolHandler::GetScheme(nsACString &result)
 {
   result.AssignLiteral(BLOBURI_SCHEME);
new file mode 100644
--- /dev/null
+++ b/content/base/src/nsBlobURI.cpp
@@ -0,0 +1,199 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsBlobURI.h"
+
+#include "nsAutoPtr.h"
+#include "nsIObjectInputStream.h"
+#include "nsIObjectOutputStream.h"
+#include "nsIProgrammingLanguage.h"
+
+static NS_DEFINE_CID(kBLOBURICID, NS_BLOBURI_CID);
+
+static NS_DEFINE_CID(kThisSimpleURIImplementationCID,
+                     NS_THIS_SIMPLEURI_IMPLEMENTATION_CID);
+
+NS_IMPL_ADDREF_INHERITED(nsBlobURI, nsSimpleURI)
+NS_IMPL_RELEASE_INHERITED(nsBlobURI, nsSimpleURI)
+
+NS_INTERFACE_MAP_BEGIN(nsBlobURI)
+  NS_INTERFACE_MAP_ENTRY(nsIURIWithPrincipal)
+  if (aIID.Equals(kBLOBURICID))
+    foundInterface = static_cast<nsIURI*>(this);
+  else if (aIID.Equals(kThisSimpleURIImplementationCID)) {
+    // Need to return explicitly here, because if we just set foundInterface
+    // to null the NS_INTERFACE_MAP_END_INHERITING will end up calling into
+    // nsSimplURI::QueryInterface and finding something for this CID.
+    *aInstancePtr = nullptr;
+    return NS_NOINTERFACE;
+  }
+  else
+NS_INTERFACE_MAP_END_INHERITING(nsSimpleURI)
+
+// nsIURIWithPrincipal methods:
+
+NS_IMETHODIMP
+nsBlobURI::GetPrincipal(nsIPrincipal** aPrincipal)
+{
+  NS_IF_ADDREF(*aPrincipal = mPrincipal);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBlobURI::GetPrincipalUri(nsIURI** aUri)
+{
+  if (mPrincipal) {
+    mPrincipal->GetURI(aUri);
+  }
+  else {
+    *aUri = nullptr;
+  }
+
+  return NS_OK;
+}
+
+// nsISerializable methods:
+
+NS_IMETHODIMP
+nsBlobURI::Read(nsIObjectInputStream* aStream)
+{
+  nsresult rv = nsSimpleURI::Read(aStream);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_ReadOptionalObject(aStream, true, getter_AddRefs(mPrincipal));
+}
+
+NS_IMETHODIMP
+nsBlobURI::Write(nsIObjectOutputStream* aStream)
+{
+  nsresult rv = nsSimpleURI::Write(aStream);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_WriteOptionalCompoundObject(aStream, mPrincipal,
+                                        NS_GET_IID(nsIPrincipal),
+                                        true);
+}
+
+// nsIURI methods:
+nsresult
+nsBlobURI::CloneInternal(nsSimpleURI::RefHandlingEnum aRefHandlingMode,
+                             nsIURI** aClone)
+{
+  nsCOMPtr<nsIURI> simpleClone;
+  nsresult rv =
+    nsSimpleURI::CloneInternal(aRefHandlingMode, getter_AddRefs(simpleClone));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+#ifdef DEBUG
+  nsRefPtr<nsBlobURI> uriCheck;
+  rv = simpleClone->QueryInterface(kBLOBURICID, getter_AddRefs(uriCheck));
+  NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv) && uriCheck,
+		    "Unexpected!");
+#endif
+
+  nsBlobURI* blobURI = static_cast<nsBlobURI*>(simpleClone.get());
+
+  blobURI->mPrincipal = mPrincipal;
+
+  simpleClone.forget(aClone);
+  return NS_OK;
+}
+
+/* virtual */ nsresult
+nsBlobURI::EqualsInternal(nsIURI* aOther,
+                              nsSimpleURI::RefHandlingEnum aRefHandlingMode,
+                              bool* aResult)
+{
+  if (!aOther) {
+    *aResult = false;
+    return NS_OK;
+  }
+  
+  nsRefPtr<nsBlobURI> otherBlobUri;
+  aOther->QueryInterface(kBLOBURICID, getter_AddRefs(otherBlobUri));
+  if (!otherBlobUri) {
+    *aResult = false;
+    return NS_OK;
+  }
+
+  // Compare the member data that our base class knows about.
+  if (!nsSimpleURI::EqualsInternal(otherBlobUri, aRefHandlingMode)) {
+    *aResult = false;
+    return NS_OK;
+   }
+
+  // Compare the piece of additional member data that we add to base class.
+  if (mPrincipal && otherBlobUri->mPrincipal) {
+    // Both of us have mPrincipals. Compare them.
+    return mPrincipal->Equals(otherBlobUri->mPrincipal, aResult);
+  }
+  // else, at least one of us lacks a principal; only equal if *both* lack it.
+  *aResult = (!mPrincipal && !otherBlobUri->mPrincipal);
+  return NS_OK;
+}
+
+// nsIClassInfo methods:
+NS_IMETHODIMP 
+nsBlobURI::GetInterfaces(PRUint32 *count, nsIID * **array)
+{
+  *count = 0;
+  *array = nullptr;
+  return NS_OK;
+}
+
+NS_IMETHODIMP 
+nsBlobURI::GetHelperForLanguage(PRUint32 language, nsISupports **_retval)
+{
+  *_retval = nullptr;
+  return NS_OK;
+}
+
+NS_IMETHODIMP 
+nsBlobURI::GetContractID(char * *aContractID)
+{
+  // Make sure to modify any subclasses as needed if this ever
+  // changes.
+  *aContractID = nullptr;
+  return NS_OK;
+}
+
+NS_IMETHODIMP 
+nsBlobURI::GetClassDescription(char * *aClassDescription)
+{
+  *aClassDescription = nullptr;
+  return NS_OK;
+}
+
+NS_IMETHODIMP 
+nsBlobURI::GetClassID(nsCID * *aClassID)
+{
+  // Make sure to modify any subclasses as needed if this ever
+  // changes to not call the virtual GetClassIDNoAlloc.
+  *aClassID = (nsCID*) nsMemory::Alloc(sizeof(nsCID));
+  NS_ENSURE_TRUE(*aClassID, NS_ERROR_OUT_OF_MEMORY);
+
+  return GetClassIDNoAlloc(*aClassID);
+}
+
+NS_IMETHODIMP 
+nsBlobURI::GetImplementationLanguage(PRUint32 *aImplementationLanguage)
+{
+  *aImplementationLanguage = nsIProgrammingLanguage::CPLUSPLUS;
+  return NS_OK;
+}
+
+NS_IMETHODIMP 
+nsBlobURI::GetFlags(PRUint32 *aFlags)
+{
+  *aFlags = nsIClassInfo::MAIN_THREAD_ONLY;
+  return NS_OK;
+}
+
+NS_IMETHODIMP 
+nsBlobURI::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
+{
+  *aClassIDNoAlloc = kBLOBURICID;
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/content/base/src/nsBlobURI.h
@@ -0,0 +1,50 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsBlobURI_h
+#define nsBlobURI_h
+
+#include "nsCOMPtr.h"
+#include "nsIClassInfo.h"
+#include "nsIPrincipal.h"
+#include "nsISerializable.h"
+#include "nsIURIWithPrincipal.h"
+#include "nsSimpleURI.h"
+
+class nsBlobURI : public nsSimpleURI,
+                  public nsIURIWithPrincipal
+{
+public:
+  nsBlobURI(nsIPrincipal* aPrincipal) :
+      nsSimpleURI(), mPrincipal(aPrincipal)
+  {}
+  virtual ~nsBlobURI() {}
+
+  // For use only from deserialization
+  nsBlobURI() : nsSimpleURI() {}
+
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIURIWITHPRINCIPAL
+  NS_DECL_NSISERIALIZABLE
+  NS_DECL_NSICLASSINFO
+
+  // Override CloneInternal() and EqualsInternal()
+  virtual nsresult CloneInternal(RefHandlingEnum aRefHandlingMode,
+                                 nsIURI** aClone);
+  virtual nsresult EqualsInternal(nsIURI* aOther,
+                                  RefHandlingEnum aRefHandlingMode,
+                                  bool* aResult);
+
+  // Override StartClone to hand back a nsBlobURI
+  virtual nsSimpleURI* StartClone(RefHandlingEnum /* unused */)
+  { return new nsBlobURI(); }
+
+  nsCOMPtr<nsIPrincipal> mPrincipal;
+};
+
+#define NS_BLOBURI_CID \
+{ 0xf5475c51, 0x59a7, 0x4757, \
+  { 0xb3, 0xd9, 0xe2, 0x11, 0xa9, 0x41, 0x08, 0x72 } }
+
+#endif /* nsBlobURI_h */
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -8434,18 +8434,17 @@ nsDocument::CreateTouch(nsIDOMWindow* aV
                                       aForce));
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocument::CreateTouchList(nsIVariant* aPoints,
                             nsIDOMTouchList** aRetVal)
 {
-  nsRefPtr<nsDOMTouchList> retval =
-    new nsDOMTouchList(static_cast<nsIDocument*>(this));
+  nsRefPtr<nsDOMTouchList> retval = new nsDOMTouchList();
   if (aPoints) {
     PRUint16 type;
     aPoints->GetDataType(&type);
     if (type == nsIDataType::VTYPE_INTERFACE ||
         type == nsIDataType::VTYPE_INTERFACE_IS) {
       nsCOMPtr<nsISupports> data;
       aPoints->GetAsISupports(getter_AddRefs(data));
       nsCOMPtr<nsIDOMTouch> point = do_QueryInterface(data);
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -17,16 +17,17 @@
 #include "nsIDOMHTMLFrameElement.h"
 #include "nsIDOMMozBrowserFrame.h"
 #include "nsIDOMWindow.h"
 #include "nsIPresShell.h"
 #include "nsIContent.h"
 #include "nsIContentViewer.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
+#include "nsIDOMFile.h"
 #include "nsPIDOMWindow.h"
 #include "nsIWebNavigation.h"
 #include "nsIWebProgress.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIDocShellTreeNode.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIDocShellLoadInfo.h"
@@ -78,16 +79,17 @@
 #include "mozilla/GuardObjects.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/unused.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/layout/RenderFrameParent.h"
 #include "nsIAppsService.h"
 
 #include "jsapi.h"
+#include "mozilla/dom/StructuredCloneUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::layers;
 using namespace mozilla::layout;
 typedef FrameMetrics::ViewID ViewID;
 
 class nsAsyncDocShellDestroyer : public nsRunnable
@@ -2151,48 +2153,88 @@ bool LoadScript(void* aCallbackData, con
   }
   return true;
 }
 
 class nsAsyncMessageToChild : public nsRunnable
 {
 public:
   nsAsyncMessageToChild(nsFrameLoader* aFrameLoader,
-                        const nsAString& aMessage, const nsAString& aJSON)
-    : mFrameLoader(aFrameLoader), mMessage(aMessage), mJSON(aJSON) {}
+                              const nsAString& aMessage,
+                              const StructuredCloneData& aData)
+    : mFrameLoader(aFrameLoader), mMessage(aMessage)
+  {
+    if (aData.mDataLength && !mData.copy(aData.mData, aData.mDataLength)) {
+      NS_RUNTIMEABORT("OOM");
+    }
+    mClosure = aData.mClosure;
+  }
 
   NS_IMETHOD Run()
   {
     nsInProcessTabChildGlobal* tabChild =
       static_cast<nsInProcessTabChildGlobal*>(mFrameLoader->mChildMessageManager.get());
     if (tabChild && tabChild->GetInnerManager()) {
       nsFrameScriptCx cx(static_cast<nsIDOMEventTarget*>(tabChild), tabChild);
+
+      StructuredCloneData data;
+      data.mData = mData.data();
+      data.mDataLength = mData.nbytes();
+      data.mClosure = mClosure;
+
       nsRefPtr<nsFrameMessageManager> mm = tabChild->GetInnerManager();
       mm->ReceiveMessage(static_cast<nsIDOMEventTarget*>(tabChild), mMessage,
-                         false, mJSON, nullptr, nullptr);
+                         false, &data, nullptr, nullptr, nullptr);
     }
     return NS_OK;
   }
   nsRefPtr<nsFrameLoader> mFrameLoader;
   nsString mMessage;
-  nsString mJSON;
+  JSAutoStructuredCloneBuffer mData;
+  StructuredCloneClosure mClosure;
 };
 
 bool SendAsyncMessageToChild(void* aCallbackData,
                              const nsAString& aMessage,
-                             const nsAString& aJSON)
+                                   const StructuredCloneData& aData)
 {
-  mozilla::dom::PBrowserParent* tabParent =
+  PBrowserParent* tabParent =
     static_cast<nsFrameLoader*>(aCallbackData)->GetRemoteBrowser();
   if (tabParent) {
-    return tabParent->SendAsyncMessage(nsString(aMessage), nsString(aJSON));
+    ClonedMessageData data;
+
+    SerializedStructuredCloneBuffer& buffer = data.data();
+    buffer.data = aData.mData;
+    buffer.dataLength = aData.mDataLength;
+
+    const nsTArray<nsCOMPtr<nsIDOMBlob> >& blobs = aData.mClosure.mBlobs;
+    if (!blobs.IsEmpty()) {
+      InfallibleTArray<PBlobParent*>& blobParents = data.blobsParent();
+
+      PRUint32 length = blobs.Length();
+      blobParents.SetCapacity(length);
+
+      ContentParent* cp = static_cast<ContentParent*>(tabParent->Manager());
+
+      for (PRUint32 i = 0; i < length; ++i) {
+        BlobParent* blobParent = cp->GetOrCreateActorForBlob(blobs[i]);
+        if (!blobParent) {
+          return false;
+        }
+
+        blobParents.AppendElement(blobParent);
+      }
+    }
+
+    return tabParent->SendAsyncMessage(nsString(aMessage), data);
   }
+
   nsRefPtr<nsIRunnable> ev =
     new nsAsyncMessageToChild(static_cast<nsFrameLoader*>(aCallbackData),
-                              aMessage, aJSON);
+                                    aMessage, aData);
   NS_DispatchToCurrentThread(ev);
   return true;
 }
 
 NS_IMETHODIMP
 nsFrameLoader::GetMessageManager(nsIChromeFrameMessageManager** aManager)
 {
   EnsureMessageManager();
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -1,38 +1,44 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "base/basictypes.h"
+
+#include "nsFrameMessageManager.h"
+
 #include "ContentChild.h"
 #include "ContentParent.h"
-#include "nsFrameMessageManager.h"
 #include "nsContentUtils.h"
 #include "nsIXPConnect.h"
 #include "jsapi.h"
 #include "nsJSUtils.h"
 #include "nsJSPrincipals.h"
 #include "nsNetUtil.h"
 #include "nsScriptLoader.h"
 #include "nsIJSContextStack.h"
 #include "nsIXULRuntime.h"
 #include "nsIScriptError.h"
 #include "nsIConsoleService.h"
 #include "nsIProtocolHandler.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIJSRuntimeService.h"
+#include "nsIDOMFile.h"
 #include "xpcpublic.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/dom/StructuredCloneUtils.h"
 
 #ifdef ANDROID
 #include <android/log.h>
 #endif
 
 using namespace mozilla;
+using namespace mozilla::dom;
 
 static bool
 IsChromeProcess()
 {
   nsCOMPtr<nsIXULRuntime> rt = do_GetService("@mozilla.org/xre/runtime;1");
   if (!rt)
     return true;
 
@@ -165,46 +171,68 @@ static JSBool
 JSONCreator(const jschar* aBuf, uint32_t aLen, void* aData)
 {
   nsAString* result = static_cast<nsAString*>(aData);
   result->Append(static_cast<const PRUnichar*>(aBuf),
                  static_cast<PRUint32>(aLen));
   return true;
 }
 
-void
-nsFrameMessageManager::GetParamsForMessage(const jsval& aObject,
-                                           JSContext* aCx,
-                                           nsAString& aJSON)
+static bool
+GetParamsForMessage(JSContext* aCx,
+                    const jsval& aObject,
+                    JSAutoStructuredCloneBuffer& aBuffer,
+                    StructuredCloneClosure& aClosure)
 {
-  aJSON.Truncate();
+  if (WriteStructuredClone(aCx, aObject, aBuffer, aClosure)) {
+    return true;
+  }
+  JS_ClearPendingException(aCx);
+
+  // Not clonable, try JSON
+  //XXX This is ugly but currently structured cloning doesn't handle
+  //    properly cases when interface is implemented in JS and used
+  //    as a dictionary.
+  nsAutoString json;
   JSAutoRequest ar(aCx);
   jsval v = aObject;
-  JS_Stringify(aCx, &v, nullptr, JSVAL_NULL, JSONCreator, &aJSON);
+  NS_ENSURE_TRUE(JS_Stringify(aCx, &v, nullptr, JSVAL_NULL, JSONCreator, &json), false);
+  NS_ENSURE_TRUE(!json.IsEmpty(), false);
+
+  jsval val = JSVAL_NULL;
+  NS_ENSURE_TRUE(JS_ParseJSON(aCx, static_cast<const jschar*>(PromiseFlatString(json).get()),
+                              json.Length(), &val), false);
+
+  return WriteStructuredClone(aCx, val, aBuffer, aClosure);
 }
 
 NS_IMETHODIMP
 nsFrameMessageManager::SendSyncMessage(const nsAString& aMessageName,
                                        const jsval& aObject,
                                        JSContext* aCx,
                                        PRUint8 aArgc,
                                        jsval* aRetval)
 {
   NS_ASSERTION(!IsGlobal(), "Should not call SendSyncMessage in chrome");
   NS_ASSERTION(!IsWindowLevel(), "Should not call SendSyncMessage in chrome");
   NS_ASSERTION(!mParentManager, "Should not have parent manager in content!");
   *aRetval = JSVAL_VOID;
   if (mSyncCallback) {
     NS_ENSURE_TRUE(mCallbackData, NS_ERROR_NOT_INITIALIZED);
-    nsString json;
-    if (aArgc >= 2) {
-      GetParamsForMessage(aObject, aCx, json);
+    StructuredCloneData data;
+    JSAutoStructuredCloneBuffer buffer;
+    if (aArgc >= 2 &&
+        !GetParamsForMessage(aCx, aObject, buffer, data.mClosure)) {
+      return NS_ERROR_DOM_DATA_CLONE_ERR;
     }
+    data.mData = buffer.data();
+    data.mDataLength = buffer.nbytes();
+
     InfallibleTArray<nsString> retval;
-    if (mSyncCallback(mCallbackData, aMessageName, json, &retval)) {
+    if (mSyncCallback(mCallbackData, aMessageName, data, &retval)) {
       JSAutoRequest ar(aCx);
       PRUint32 len = retval.Length();
       JSObject* dataArray = JS_NewArrayObject(aCx, len, NULL);
       NS_ENSURE_TRUE(dataArray, NS_ERROR_OUT_OF_MEMORY);
 
       for (PRUint32 i = 0; i < len; ++i) {
         if (retval[i].IsEmpty()) {
           continue;
@@ -221,41 +249,48 @@ nsFrameMessageManager::SendSyncMessage(c
       *aRetval = OBJECT_TO_JSVAL(dataArray);
     }
   }
   return NS_OK;
 }
 
 nsresult
 nsFrameMessageManager::SendAsyncMessageInternal(const nsAString& aMessage,
-                                                const nsAString& aJSON)
+                                                const StructuredCloneData& aData)
 {
   if (mAsyncCallback) {
     NS_ENSURE_TRUE(mCallbackData, NS_ERROR_NOT_INITIALIZED);
-    mAsyncCallback(mCallbackData, aMessage, aJSON);
+    mAsyncCallback(mCallbackData, aMessage, aData);
   }
   PRInt32 len = mChildManagers.Count();
   for (PRInt32 i = 0; i < len; ++i) {
     static_cast<nsFrameMessageManager*>(mChildManagers[i])->
-      SendAsyncMessageInternal(aMessage, aJSON);
+      SendAsyncMessageInternal(aMessage, aData);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFrameMessageManager::SendAsyncMessage(const nsAString& aMessageName,
                                         const jsval& aObject,
                                         JSContext* aCx,
                                         PRUint8 aArgc)
 {
-  nsString json;
-  if (aArgc >= 2) {
-    GetParamsForMessage(aObject, aCx, json);
+  StructuredCloneData data;
+  JSAutoStructuredCloneBuffer buffer;
+
+  if (aArgc >= 2 &&
+      !GetParamsForMessage(aCx, aObject, buffer, data.mClosure)) {
+    return NS_ERROR_DOM_DATA_CLONE_ERR;
   }
-  return SendAsyncMessageInternal(aMessageName, json);
+
+  data.mData = buffer.data();
+  data.mDataLength = buffer.nbytes();
+
+  return SendAsyncMessageInternal(aMessageName, data);
 }
 
 NS_IMETHODIMP
 nsFrameMessageManager::Dump(const nsAString& aStr)
 {
 #ifdef ANDROID
   __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", NS_ConvertUTF16toUTF8(aStr).get());
 #endif
@@ -337,17 +372,18 @@ public:
 
   bool mWasHandlingMessage;
   nsRefPtr<nsFrameMessageManager> mMM;
 };
 
 nsresult
 nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
                                       const nsAString& aMessage,
-                                      bool aSync, const nsAString& aJSON,
+                                      bool aSync,
+                                      const StructuredCloneData* aCloneData,
                                       JSObject* aObjectsArray,
                                       InfallibleTArray<nsString>* aJSONRetVal,
                                       JSContext* aContext)
 {
   JSContext* ctx = mContext ? mContext : aContext;
   if (!ctx) {
     ctx = nsContentUtils::ThreadJSContextStack()->GetSafeJSContext();
   }
@@ -397,33 +433,33 @@ nsFrameMessageManager::ReceiveMessage(ns
         }
 
         JS::AutoValueRooter objectsv(ctx);
         objectsv.set(OBJECT_TO_JSVAL(aObjectsArray));
         if (!JS_WrapValue(ctx, objectsv.jsval_addr()))
             return NS_ERROR_UNEXPECTED;
 
         jsval json = JSVAL_NULL;
-        if (!aJSON.IsEmpty()) {
-          if (!JS_ParseJSON(ctx, static_cast<const jschar*>(PromiseFlatString(aJSON).get()),
-                            aJSON.Length(), &json)) {
-            json = JSVAL_NULL;
-          }
+        if (aCloneData && aCloneData->mDataLength &&
+            !ReadStructuredClone(ctx, *aCloneData, &json)) {
+          JS_ClearPendingException(ctx);
+          return NS_OK;
         }
         JSString* jsMessage =
           JS_NewUCStringCopyN(ctx,
                               static_cast<const jschar*>(PromiseFlatString(aMessage).get()),
                               aMessage.Length());
         NS_ENSURE_TRUE(jsMessage, NS_ERROR_OUT_OF_MEMORY);
         JS_DefineProperty(ctx, param, "target", targetv, NULL, NULL, JSPROP_ENUMERATE);
         JS_DefineProperty(ctx, param, "name",
                           STRING_TO_JSVAL(jsMessage), NULL, NULL, JSPROP_ENUMERATE);
         JS_DefineProperty(ctx, param, "sync",
                           BOOLEAN_TO_JSVAL(aSync), NULL, NULL, JSPROP_ENUMERATE);
-        JS_DefineProperty(ctx, param, "json", json, NULL, NULL, JSPROP_ENUMERATE);
+        JS_DefineProperty(ctx, param, "json", json, NULL, NULL, JSPROP_ENUMERATE); // deprecated
+        JS_DefineProperty(ctx, param, "data", json, NULL, NULL, JSPROP_ENUMERATE);
         JS_DefineProperty(ctx, param, "objects", objectsv.jsval_value(), NULL, NULL, JSPROP_ENUMERATE);
 
         jsval thisValue = JSVAL_VOID;
 
         JS::Value funval;
         if (JS_ObjectIsCallable(ctx, object)) {
           // If the listener is a JS function:
           funval.setObject(*object);
@@ -474,17 +510,18 @@ nsFrameMessageManager::ReceiveMessage(ns
             }
           }
         }
       }
     }
   }
   nsRefPtr<nsFrameMessageManager> kungfuDeathGrip = mParentManager;
   return mParentManager ? mParentManager->ReceiveMessage(aTarget, aMessage,
-                                                         aSync, aJSON, aObjectsArray,
+                                                         aSync, aCloneData,
+                                                         aObjectsArray,
                                                          aJSONRetVal, mContext) : NS_OK;
 }
 
 void
 nsFrameMessageManager::AddChildManager(nsFrameMessageManager* aManager,
                                        bool aLoadScripts)
 {
   mChildManagers.AppendObject(aManager);
@@ -883,135 +920,214 @@ NS_IMPL_ISUPPORTS1(nsScriptCacheCleaner,
 
 nsFrameMessageManager* nsFrameMessageManager::sChildProcessManager = nullptr;
 nsFrameMessageManager* nsFrameMessageManager::sParentProcessManager = nullptr;
 nsFrameMessageManager* nsFrameMessageManager::sSameProcessParentManager = nullptr;
 nsTArray<nsCOMPtr<nsIRunnable> >* nsFrameMessageManager::sPendingSameProcessAsyncMessages = nullptr;
 
 bool SendAsyncMessageToChildProcess(void* aCallbackData,
                                     const nsAString& aMessage,
-                                    const nsAString& aJSON)
+                                    const StructuredCloneData& aData)
 {
   mozilla::dom::ContentParent* cp =
     static_cast<mozilla::dom::ContentParent*>(aCallbackData);
   NS_WARN_IF_FALSE(cp, "No child process!");
   if (cp) {
-    return cp->SendAsyncMessage(nsString(aMessage), nsString(aJSON));
+    ClonedMessageData data;
+    SerializedStructuredCloneBuffer& buffer = data.data();
+    buffer.data = aData.mData;
+    buffer.dataLength = aData.mDataLength;
+    const nsTArray<nsCOMPtr<nsIDOMBlob> >& blobs = aData.mClosure.mBlobs;
+    if (!blobs.IsEmpty()) {
+      InfallibleTArray<PBlobParent*>& blobParents = data.blobsParent();
+      PRUint32 length = blobs.Length();
+      blobParents.SetCapacity(length);
+      for (PRUint32 i = 0; i < length; ++i) {
+        BlobParent* blobParent = cp->GetOrCreateActorForBlob(blobs[i]);
+        if (!blobParent) {
+          return false;
+  }
+        blobParents.AppendElement(blobParent);
+      }
+    }
+
+    return cp->SendAsyncMessage(nsString(aMessage), data);
   }
   return true;
 }
 
 class nsAsyncMessageToSameProcessChild : public nsRunnable
 {
 public:
-  nsAsyncMessageToSameProcessChild(const nsAString& aMessage, const nsAString& aJSON)
-    : mMessage(aMessage), mJSON(aJSON) {}
+  nsAsyncMessageToSameProcessChild(const nsAString& aMessage,
+                                   const StructuredCloneData& aData)
+    : mMessage(aMessage)
+  {
+    if (aData.mDataLength && !mData.copy(aData.mData, aData.mDataLength)) {
+      NS_RUNTIMEABORT("OOM");
+    }
+    mClosure = aData.mClosure;
+  }
 
   NS_IMETHOD Run()
   {
     if (nsFrameMessageManager::sChildProcessManager) {
+      StructuredCloneData data;
+      data.mData = mData.data();
+      data.mDataLength = mData.nbytes();
+      data.mClosure = mClosure;
+
       nsRefPtr<nsFrameMessageManager> ppm = nsFrameMessageManager::sChildProcessManager;
       ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()), mMessage,
-                          false, mJSON, nullptr, nullptr);
+                          false, &data, nullptr, nullptr, nullptr);
     }
     return NS_OK;
   }
   nsString mMessage;
-  nsString mJSON;
+  JSAutoStructuredCloneBuffer mData;
+  StructuredCloneClosure mClosure;
 };
 
 bool SendAsyncMessageToSameProcessChild(void* aCallbackData,
                                         const nsAString& aMessage,
-                                        const nsAString& aJSON)
+                                        const StructuredCloneData& aData)
 {
   nsRefPtr<nsIRunnable> ev =
-    new nsAsyncMessageToSameProcessChild(aMessage, aJSON);
+    new nsAsyncMessageToSameProcessChild(aMessage, aData);
   NS_DispatchToCurrentThread(ev);
   return true;
 }
 
 bool SendSyncMessageToParentProcess(void* aCallbackData,
                                     const nsAString& aMessage,
-                                    const nsAString& aJSON,
+                                    const StructuredCloneData& aData,
                                     InfallibleTArray<nsString>* aJSONRetVal)
 {
   mozilla::dom::ContentChild* cc =
     mozilla::dom::ContentChild::GetSingleton();
   if (cc) {
+    ClonedMessageData data;
+    SerializedStructuredCloneBuffer& buffer = data.data();
+    buffer.data = aData.mData;
+    buffer.dataLength = aData.mDataLength;
+    const nsTArray<nsCOMPtr<nsIDOMBlob> >& blobs = aData.mClosure.mBlobs;
+    if (!blobs.IsEmpty()) {
+      InfallibleTArray<PBlobChild*>& blobChildList = data.blobsChild();
+      PRUint32 length = blobs.Length();
+      blobChildList.SetCapacity(length);
+      for (PRUint32 i = 0; i < length; ++i) {
+        BlobChild* blobChild = cc->GetOrCreateActorForBlob(blobs[i]);
+        if (!blobChild) {
+          return false;
+        }
+        blobChildList.AppendElement(blobChild);
+      }
+    }
     return
-      cc->SendSyncMessage(nsString(aMessage), nsString(aJSON), aJSONRetVal);
+      cc->SendSyncMessage(nsString(aMessage), data, aJSONRetVal);
   }
   return true;
 }
 
 bool SendSyncMessageToSameProcessParent(void* aCallbackData,
                                         const nsAString& aMessage,
-                                        const nsAString& aJSON,
+                                        const StructuredCloneData& aData,
                                         InfallibleTArray<nsString>* aJSONRetVal)
 {
   nsTArray<nsCOMPtr<nsIRunnable> > asyncMessages;
   if (nsFrameMessageManager::sPendingSameProcessAsyncMessages) {
     asyncMessages.SwapElements(*nsFrameMessageManager::sPendingSameProcessAsyncMessages);
     PRUint32 len = asyncMessages.Length();
     for (PRUint32 i = 0; i < len; ++i) {
       nsCOMPtr<nsIRunnable> async = asyncMessages[i];
       async->Run();
     }
   }
   if (nsFrameMessageManager::sSameProcessParentManager) {
     nsRefPtr<nsFrameMessageManager> ppm = nsFrameMessageManager::sSameProcessParentManager;
     ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()), aMessage,
-                        true, aJSON, nullptr, aJSONRetVal);
+                        true, &aData, nullptr, aJSONRetVal);
   }
   return true;
 }
 
 bool SendAsyncMessageToParentProcess(void* aCallbackData,
                                      const nsAString& aMessage,
-                                     const nsAString& aJSON)
+                                          const StructuredCloneData& aData)
 {
   mozilla::dom::ContentChild* cc =
     mozilla::dom::ContentChild::GetSingleton();
   if (cc) {
-    return cc->SendAsyncMessage(nsString(aMessage), nsString(aJSON));
+    ClonedMessageData data;
+    SerializedStructuredCloneBuffer& buffer = data.data();
+    buffer.data = aData.mData;
+    buffer.dataLength = aData.mDataLength;
+    const nsTArray<nsCOMPtr<nsIDOMBlob> >& blobs = aData.mClosure.mBlobs;
+    if (!blobs.IsEmpty()) {
+      InfallibleTArray<PBlobChild*>& blobChildList = data.blobsChild();
+      PRUint32 length = blobs.Length();
+      blobChildList.SetCapacity(length);
+      for (PRUint32 i = 0; i < length; ++i) {
+        BlobChild* blobChild = cc->GetOrCreateActorForBlob(blobs[i]);
+        if (!blobChild) {
+          return false;
+        }
+        blobChildList.AppendElement(blobChild);
+      }
+    }
+    return cc->SendAsyncMessage(nsString(aMessage), data);
   }
   return true;
 }
 
 class nsAsyncMessageToSameProcessParent : public nsRunnable
 {
 public:
-  nsAsyncMessageToSameProcessParent(const nsAString& aMessage, const nsAString& aJSON)
-    : mMessage(aMessage), mJSON(aJSON) {}
+  nsAsyncMessageToSameProcessParent(const nsAString& aMessage,
+                                         const StructuredCloneData& aData)
+    : mMessage(aMessage)
+  {
+    if (aData.mDataLength && !mData.copy(aData.mData, aData.mDataLength)) {
+      NS_RUNTIMEABORT("OOM");
+    }
+    mClosure = aData.mClosure;
+  }
 
   NS_IMETHOD Run()
   {
     if (nsFrameMessageManager::sPendingSameProcessAsyncMessages) {
       nsFrameMessageManager::sPendingSameProcessAsyncMessages->RemoveElement(this);
     }
     if (nsFrameMessageManager::sSameProcessParentManager) {
-      nsRefPtr<nsFrameMessageManager> ppm = nsFrameMessageManager::sSameProcessParentManager;
-      ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()), mMessage, false,
-                          mJSON, nullptr, nullptr);
-    }
-    return NS_OK;
+      StructuredCloneData data;
+      data.mData = mData.data();
+      data.mDataLength = mData.nbytes();
+      data.mClosure = mClosure;
+
+      nsRefPtr<nsFrameMessageManager> ppm =
+        nsFrameMessageManager::sSameProcessParentManager;
+      ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()),
+                          mMessage, false, &data, nullptr, nullptr, nullptr);
+     }
+     return NS_OK;
   }
   nsString mMessage;
-  nsString mJSON;
+  JSAutoStructuredCloneBuffer mData;
+  StructuredCloneClosure mClosure;
 };
 
 bool SendAsyncMessageToSameProcessParent(void* aCallbackData,
-                                         const nsAString& aMessage,
-                                         const nsAString& aJSON)
+                                              const nsAString& aMessage,
+                                              const StructuredCloneData& aData)
 {
   if (!nsFrameMessageManager::sPendingSameProcessAsyncMessages) {
     nsFrameMessageManager::sPendingSameProcessAsyncMessages = new nsTArray<nsCOMPtr<nsIRunnable> >;
   }
   nsCOMPtr<nsIRunnable> ev =
-    new nsAsyncMessageToSameProcessParent(aMessage, aJSON);
+    new nsAsyncMessageToSameProcessParent(aMessage, aData);
   nsFrameMessageManager::sPendingSameProcessAsyncMessages->AppendElement(ev);
   NS_DispatchToCurrentThread(ev);
   return true;
 }
 
 // This creates the global parent process message manager.
 nsresult
 NS_NewParentProcessMessageManager(nsIFrameMessageManager** aResult)
@@ -1084,9 +1200,9 @@ NS_NewChildProcessMessageManager(nsISync
 bool
 nsFrameMessageManager::MarkForCC()
 {
   PRUint32 len = mListeners.Length();
   for (PRUint32 i = 0; i < len; ++i) {
     xpc_TryUnmarkWrappedGrayObject(mListeners[i].mListener);
   }
   return true;
-}
+}
\ No newline at end of file
--- a/content/base/src/nsFrameMessageManager.h
+++ b/content/base/src/nsFrameMessageManager.h
@@ -20,55 +20,58 @@
 #include "mozilla/Services.h"
 #include "nsIObserverService.h"
 #include "nsThreadUtils.h"
 #include "mozilla/Attributes.h"
 
 namespace mozilla {
 namespace dom {
 class ContentParent;
+struct StructuredCloneData;
 }
 }
 
 class nsAXPCNativeCallContext;
 struct JSContext;
 struct JSObject;
 
 struct nsMessageListenerInfo
 {
   nsCOMPtr<nsIFrameMessageListener> mListener;
   nsCOMPtr<nsIAtom> mMessage;
 };
 
 typedef bool (*nsLoadScriptCallback)(void* aCallbackData, const nsAString& aURL);
 typedef bool (*nsSyncMessageCallback)(void* aCallbackData,
                                       const nsAString& aMessage,
-                                      const nsAString& aJSON,
+                                      const mozilla::dom::StructuredCloneData& aData,
                                       InfallibleTArray<nsString>* aJSONRetVal);
 typedef bool (*nsAsyncMessageCallback)(void* aCallbackData,
                                        const nsAString& aMessage,
-                                       const nsAString& aJSON);
+                                             const mozilla::dom::StructuredCloneData& aData);
 
 class nsFrameMessageManager MOZ_FINAL : public nsIContentFrameMessageManager,
                                         public nsIChromeFrameMessageManager
 {
+  typedef mozilla::dom::StructuredCloneData StructuredCloneData;
 public:
   nsFrameMessageManager(bool aChrome,
                         nsSyncMessageCallback aSyncCallback,
                         nsAsyncMessageCallback aAsyncCallback,
                         nsLoadScriptCallback aLoadScriptCallback,
                         void* aCallbackData,
                         nsFrameMessageManager* aParentManager,
                         JSContext* aContext,
                         bool aGlobal = false,
                         bool aProcessManager = false)
   : mChrome(aChrome), mGlobal(aGlobal), mIsProcessManager(aProcessManager),
     mHandlingMessage(false), mDisconnected(false), mParentManager(aParentManager),
     mSyncCallback(aSyncCallback), mAsyncCallback(aAsyncCallback),
-    mLoadScriptCallback(aLoadScriptCallback), mCallbackData(aCallbackData),
+    mLoadScriptCallback(aLoadScriptCallback),
+    mCallbackData(aCallbackData),
     mContext(aContext)
   {
     NS_ASSERTION(mContext || (aChrome && !aParentManager) || aProcessManager,
                  "Should have mContext in non-global/non-process manager!");
     NS_ASSERTION(aChrome || !aParentManager, "Should not set parent manager!");
     // This is a bit hackish. When parent manager is global, we want
     // to attach the window message manager to it immediately.
     // Is it just the frame message manager which waits until the
@@ -107,35 +110,33 @@ public:
   NS_DECL_NSICONTENTFRAMEMESSAGEMANAGER
   NS_DECL_NSICHROMEFRAMEMESSAGEMANAGER
   NS_DECL_NSITREEITEMFRAMEMESSAGEMANAGER
 
   static nsFrameMessageManager*
   NewProcessMessageManager(mozilla::dom::ContentParent* aProcess);
 
   nsresult ReceiveMessage(nsISupports* aTarget, const nsAString& aMessage,
-                          bool aSync, const nsAString& aJSON,
+                          bool aSync, const StructuredCloneData* aCloneData,
                           JSObject* aObjectsArray,
                           InfallibleTArray<nsString>* aJSONRetVal,
                           JSContext* aContext = nullptr);
+
   void AddChildManager(nsFrameMessageManager* aManager,
                        bool aLoadScripts = true);
   void RemoveChildManager(nsFrameMessageManager* aManager)
   {
     mChildManagers.RemoveObject(aManager);
   }
 
   void Disconnect(bool aRemoveFromParent = true);
   void SetCallbackData(void* aData, bool aLoadScripts = true);
   void* GetCallbackData() { return mCallbackData; }
-  void GetParamsForMessage(const jsval& aObject,
-                           JSContext* aCx,
-                           nsAString& aJSON);
   nsresult SendAsyncMessageInternal(const nsAString& aMessage,
-                                    const nsAString& aJSON);
+                                          const StructuredCloneData& aData);
   JSContext* GetJSContext() { return mContext; }
   void SetJSContext(JSContext* aCx) { mContext = aCx; }
   void RemoveFromParent();
   nsFrameMessageManager* GetParentManager() { return mParentManager; }
   void SetParentManager(nsFrameMessageManager* aParent)
   {
     NS_ASSERTION(!mParentManager, "We have parent manager already!");
     NS_ASSERTION(mChrome, "Should not set parent manager!");
@@ -252,9 +253,9 @@ class nsScriptCacheCleaner MOZ_FINAL : p
                         const char *aTopic,
                         const PRUnichar *aData)
   {
     nsFrameScriptExecutor::Shutdown();
     return NS_OK;
   }
 };
 
-#endif
+#endif
\ No newline at end of file
--- a/content/base/src/nsInProcessTabChildGlobal.cpp
+++ b/content/base/src/nsInProcessTabChildGlobal.cpp
@@ -15,69 +15,86 @@
 #include "nsComponentManagerUtils.h"
 #include "nsNetUtil.h"
 #include "nsScriptLoader.h"
 #include "nsIJSContextStack.h"
 #include "nsFrameLoader.h"
 #include "xpcpublic.h"
 #include "nsIMozBrowserFrame.h"
 #include "nsDOMClassInfoID.h"
+#include "mozilla/dom/StructuredCloneUtils.h"
+
+using mozilla::dom::StructuredCloneData;
+using mozilla::dom::StructuredCloneClosure;
 
 bool SendSyncMessageToParent(void* aCallbackData,
                              const nsAString& aMessage,
-                             const nsAString& aJSON,
+                             const StructuredCloneData& aData,
                              InfallibleTArray<nsString>* aJSONRetVal)
 {
   nsInProcessTabChildGlobal* tabChild =
     static_cast<nsInProcessTabChildGlobal*>(aCallbackData);
   nsCOMPtr<nsIContent> owner = tabChild->mOwner;
   nsTArray<nsCOMPtr<nsIRunnable> > asyncMessages;
   asyncMessages.SwapElements(tabChild->mASyncMessages);
   PRUint32 len = asyncMessages.Length();
   for (PRUint32 i = 0; i < len; ++i) {
     nsCOMPtr<nsIRunnable> async = asyncMessages[i];
     async->Run();
   }
   if (tabChild->mChromeMessageManager) {
     nsRefPtr<nsFrameMessageManager> mm = tabChild->mChromeMessageManager;
-    mm->ReceiveMessage(owner, aMessage, true, aJSON, nullptr, aJSONRetVal);
+    mm->ReceiveMessage(owner, aMessage, true, &aData, nullptr, aJSONRetVal);
   }
   return true;
 }
 
 class nsAsyncMessageToParent : public nsRunnable
 {
 public:
   nsAsyncMessageToParent(nsInProcessTabChildGlobal* aTabChild,
-                         const nsAString& aMessage, const nsAString& aJSON)
-    : mTabChild(aTabChild), mMessage(aMessage), mJSON(aJSON) {}
+                         const nsAString& aMessage,
+                         const StructuredCloneData& aData)
+    : mTabChild(aTabChild), mMessage(aMessage)
+  {
+    if (aData.mDataLength && !mData.copy(aData.mData, aData.mDataLength)) {
+      NS_RUNTIMEABORT("OOM");
+    }
+    mClosure = aData.mClosure;
+  }
 
   NS_IMETHOD Run()
   {
     mTabChild->mASyncMessages.RemoveElement(this);
     if (mTabChild->mChromeMessageManager) {
+      StructuredCloneData data;
+      data.mData = mData.data();
+      data.mDataLength = mData.nbytes();
+      data.mClosure = mClosure;
+
       nsRefPtr<nsFrameMessageManager> mm = mTabChild->mChromeMessageManager;
-      mm->ReceiveMessage(mTabChild->mOwner, mMessage, false,
-                         mJSON, nullptr, nullptr);
+      mm->ReceiveMessage(mTabChild->mOwner, mMessage, false, &data,
+                         nullptr, nullptr, nullptr);
     }
     return NS_OK;
   }
   nsRefPtr<nsInProcessTabChildGlobal> mTabChild;
   nsString mMessage;
-  nsString mJSON;
+  JSAutoStructuredCloneBuffer mData;
+  StructuredCloneClosure mClosure;
 };
 
 bool SendAsyncMessageToParent(void* aCallbackData,
                               const nsAString& aMessage,
-                              const nsAString& aJSON)
+                              const StructuredCloneData& aData)
 {
   nsInProcessTabChildGlobal* tabChild =
     static_cast<nsInProcessTabChildGlobal*>(aCallbackData);
   nsCOMPtr<nsIRunnable> ev =
-    new nsAsyncMessageToParent(tabChild, aMessage, aJSON);
+    new nsAsyncMessageToParent(tabChild, aMessage, aData);
   tabChild->mASyncMessages.AppendElement(ev);
   NS_DispatchToCurrentThread(ev);
   return true;
 }
 
 nsInProcessTabChildGlobal::nsInProcessTabChildGlobal(nsIDocShell* aShell,
                                                      nsIContent* aOwner,
                                                      nsFrameMessageManager* aChrome)
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -547,16 +547,17 @@ MOCHITEST_FILES_B = \
 		test_bug749367.html \
 		test_bug753278.html \
 		test_bug761120.html \
 		test_XHR_onuploadprogress.html \
 		test_XHR_anon.html \
 		file_XHR_anon.sjs \
 		test_XHR_system.html \
 		test_XHR_parameters.html \
+		test_ipc_messagemanager_blob.html \
 		$(NULL)
 
 MOCHITEST_CHROME_FILES =	\
 		test_bug357450.js \
 		$(NULL)
 
 MOCHITEST_FILES_PARTS = $(foreach s,A B,MOCHITEST_FILES_$(s))
 
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_ipc_messagemanager_blob.html
@@ -0,0 +1,111 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for OOP Blobs in MessageManager</title>
+  <script type="application/javascript"
+          src="/tests/SimpleTest/SimpleTest.js">
+  </script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+  <script type="application/javascript;version=1.7">
+    "use strict";
+
+    SimpleTest.waitForExplicitFinish();
+
+    const childFrameURL =
+      "data:text/html,<!DOCTYPE HTML><html><body></body></html>";
+
+    function childFrameScript() {
+      "use strict";
+
+      addMessageListener("test:ipcClonedMessage", function(message) {
+        if (!(message.json instanceof Components.interfaces.nsIDOMBlob)) {
+          sendAsyncMessage(message.name, message.json);
+          return;
+        }
+
+        let reader =
+          Components.classes["@mozilla.org/files/filereader;1"]
+                    .createInstance(Components.interfaces.nsIDOMFileReader);
+        reader.addEventListener("load", function() {
+          let response = reader.result == "this is a great success!" ?
+                         message.json :
+                         "error";
+          sendAsyncMessage(message.name, response);
+        });
+        reader.readAsText(message.json);
+      });
+    }
+
+    function runTests() {
+      ok("Browser prefs set.");
+
+      let iframe = document.createElement("iframe");
+      iframe.mozbrowser = true;
+      iframe.id = "iframe";
+      iframe.src = childFrameURL;
+
+      iframe.addEventListener("mozbrowserloadend", function() {
+        ok(true, "Got iframe load event.");
+
+        const messages = [
+          "hi!",
+          "",
+          2,
+          -.04,
+          3432987324987239872948732982,
+          true,
+          false,
+          null,
+          0,
+          new Blob(["this ", "is ", "a ", "great ", "success!"],
+                   {"type" : "text\/plain"}),
+        ];
+        let receivedMessageIndex = 0;
+
+        let mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
+        mm.addMessageListener("test:ipcClonedMessage", function(message) {
+          is(message.json, messages[receivedMessageIndex++],
+             "Got correct round-tripped response");
+          if (receivedMessageIndex == messages.length) {
+            SimpleTest.finish();
+          }
+        });
+        mm.loadFrameScript("data:,(" + childFrameScript.toString() + ")();",
+                           false);
+
+        for each (let message in messages) {
+          mm.sendAsyncMessage("test:ipcClonedMessage", message);
+        }
+      });
+
+      document.body.appendChild(iframe);
+    }
+
+    addEventListener("load", function() {
+      info("Got load event.");
+
+      let whitelist;
+      try {
+        whitelist =
+          SpecialPowers.getCharPref("dom.mozBrowserFramesWhitelist") + ", ";
+      } catch (e) {
+        whitelist = "";
+      }
+
+      whitelist += window.location.protocol + "//" + window.location.host;
+
+      SpecialPowers.pushPrefEnv({
+        "set": [
+          ["dom.ipc.browser_frames.oop_by_default", true],
+          ["dom.mozBrowserFramesEnabled", true],
+          ["dom.mozBrowserFramesWhitelist", whitelist],
+          ["browser.pageThumbs.enabled", false]
+        ]
+      }, runTests);
+    });
+  </script>
+</body>
+</html>
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -69,22 +69,23 @@ NS_NewCanvasRenderingContextWebGL(nsIDOM
     if (!ctx)
         return NS_ERROR_OUT_OF_MEMORY;
 
     NS_ADDREF(*aResult = ctx);
     return NS_OK;
 }
 
 WebGLContextOptions::WebGLContextOptions()
-    : depth(true), stencil(false),
+    : alpha(true), depth(true), stencil(false),
       premultipliedAlpha(true), antialias(true),
       preserveDrawingBuffer(false)
 {
     // Set default alpha state based on preference.
-    alpha = Preferences::GetBool("webgl.default-no-alpha", false) ? 0 : 1;
+    if (Preferences::GetBool("webgl.default-no-alpha", false))
+        alpha = false;
 }
 
 WebGLContext::WebGLContext()
     : gl(nullptr)
 {
     SetIsDOMBinding();
     mExtensions.SetLength(WebGLExtensionID_number_of_extensions);
 
@@ -145,32 +146,34 @@ WebGLContext::WebGLContext()
     mGLMaxTextureUnits = 0;
     mGLMaxTextureSize = 0;
     mGLMaxCubeMapTextureSize = 0;
     mGLMaxTextureImageUnits = 0;
     mGLMaxVertexTextureImageUnits = 0;
     mGLMaxVaryingVectors = 0;
     mGLMaxFragmentUniformVectors = 0;
     mGLMaxVertexUniformVectors = 0;
-    
+
     // See OpenGL ES 2.0.25 spec, 6.2 State Tables, table 6.13
     mPixelStorePackAlignment = 4;
     mPixelStoreUnpackAlignment = 4;
 
     WebGLMemoryMultiReporterWrapper::AddWebGLContext(this);
 
     mAllowRestore = true;
     mContextLossTimerRunning = false;
     mDrawSinceContextLossTimerSet = false;
     mContextRestorer = do_CreateInstance("@mozilla.org/timer;1");
     mContextStatus = ContextStable;
     mContextLostErrorSet = false;
 
     mAlreadyGeneratedWarnings = 0;
     mAlreadyWarnedAboutFakeVertexAttrib0 = false;
+
+    mLastUseIndex = 0;
 }
 
 WebGLContext::~WebGLContext()
 {
     DestroyResourcesAndContext();
     WebGLMemoryMultiReporterWrapper::RemoveWebGLContext(this);
     TerminateContextLossTimer();
     mContextRestorer = nullptr;
@@ -329,17 +332,17 @@ WebGLContext::SetContextOptions(nsIPrope
     mOptions = newOpts;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 WebGLContext::SetDimensions(PRInt32 width, PRInt32 height)
 {
     /*** early success return cases ***/
-  
+
     if (mCanvasElement) {
         mCanvasElement->InvalidateCanvas();
     }
 
     if (gl && mWidth == width && mHeight == height)
         return NS_OK;
 
     // Zero-sized surfaces can cause problems.
@@ -360,21 +363,27 @@ WebGLContext::SetDimensions(PRInt32 widt
         mHeight = gl->OffscreenActualSize().height;
         mResetLayer = true;
 
         gl->ClearSafely();
 
         return NS_OK;
     }
 
-    /*** end of early success return cases ***/
+    /*** End of early success return cases.
+     *** At this point we know that we're not just resizing an existing context,
+     *** we are initializing a new context.
+     ***/
 
-    // At this point we know that the old context is not going to survive, even though we still don't
-    // know if creating the new context will succeed.
-    DestroyResourcesAndContext();
+    // if we exceeded either the global or the per-principal limit for WebGL contexts,
+    // lose the oldest-used context now to free resources. Note that we can't do that
+    // in the WebGLContext constructor as we don't have a canvas element yet there.
+    // Here is the right place to do so, as we are about to create the OpenGL context
+    // and that is what can fail if we already have too many.
+    LoseOldestWebGLContextIfLimitExceeded();
 
     // Get some prefs for some preferred/overriden things
     NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE);
 
     bool forceOSMesa =
         Preferences::GetBool("webgl.force_osmesa", false);
 #ifdef XP_WIN
     bool preferEGL =
@@ -623,16 +632,101 @@ WebGLContext::Render(gfxContext *ctx, gf
 
     ctx->NewPath();
     ctx->PixelSnappedRectangleAndSetPattern(gfxRect(0, 0, mWidth, mHeight), pat);
     ctx->Fill();
 
     return NS_OK;
 }
 
+void WebGLContext::LoseOldestWebGLContextIfLimitExceeded()
+{
+#ifdef MOZ_GFX_OPTIMIZE_MOBILE
+    // some mobile devices can't have more than 8 GL contexts overall
+    const size_t kMaxWebGLContextsPerPrincipal = 2;
+    const size_t kMaxWebGLContexts             = 4;
+#else
+    const size_t kMaxWebGLContextsPerPrincipal = 8;
+    const size_t kMaxWebGLContexts             = 16;
+#endif
+    MOZ_ASSERT(kMaxWebGLContextsPerPrincipal < kMaxWebGLContexts);
+
+    // it's important to update the index on a new context before losing old contexts,
+    // otherwise new unused contexts would all have index 0 and we couldn't distinguish older ones
+    // when choosing which one to lose first.
+    UpdateLastUseIndex();
+
+    WebGLMemoryMultiReporterWrapper::ContextsArrayType &contexts
+      = WebGLMemoryMultiReporterWrapper::Contexts();
+
+    // quick exit path, should cover a majority of cases
+    if (contexts.Length() <= kMaxWebGLContextsPerPrincipal) {
+        return;
+    }
+
+    // note that here by "context" we mean "non-lost context". See the check for
+    // IsContextLost() below. Indeed, the point of this function is to maybe lose
+    // some currently non-lost context.
+
+    uint64_t oldestIndex = UINT64_MAX;
+    uint64_t oldestIndexThisPrincipal = UINT64_MAX;
+    const WebGLContext *oldestContext = nsnull;
+    const WebGLContext *oldestContextThisPrincipal = nsnull;
+    size_t numContexts = 0;
+    size_t numContextsThisPrincipal = 0;
+
+    for(size_t i = 0; i < contexts.Length(); ++i) {
+
+        // don't want to lose ourselves.
+        if (contexts[i] == this)
+            continue;
+
+        if (contexts[i]->IsContextLost())
+            continue;
+
+        if (!contexts[i]->GetCanvas()) {
+            // Zombie context: the canvas is already destroyed, but something else
+            // (typically the compositor) is still holding on to the context.
+            // Killing zombies is a no-brainer.
+            const_cast<WebGLContext*>(contexts[i])->LoseContext();
+            continue;
+        }
+
+        numContexts++;
+        if (contexts[i]->mLastUseIndex < oldestIndex) {
+            oldestIndex = contexts[i]->mLastUseIndex;
+            oldestContext = contexts[i];
+        }
+
+        nsIPrincipal *ourPrincipal = GetCanvas()->NodePrincipal();
+        nsIPrincipal *theirPrincipal = contexts[i]->GetCanvas()->NodePrincipal();
+        bool samePrincipal;
+        nsresult rv = ourPrincipal->Equals(theirPrincipal, &samePrincipal);
+        if (NS_SUCCEEDED(rv) && samePrincipal) {
+            numContextsThisPrincipal++;
+            if (contexts[i]->mLastUseIndex < oldestIndexThisPrincipal) {
+                oldestIndexThisPrincipal = contexts[i]->mLastUseIndex;
+                oldestContextThisPrincipal = contexts[i];
+            }
+        }
+    }
+
+    if (numContextsThisPrincipal > kMaxWebGLContextsPerPrincipal) {
+        GenerateWarning("Exceeded %d live WebGL contexts for this principal, losing the "
+                        "least recently used one.", kMaxWebGLContextsPerPrincipal);
+        MOZ_ASSERT(oldestContextThisPrincipal); // if we reach this point, this can't be null
+        const_cast<WebGLContext*>(oldestContextThisPrincipal)->LoseContext();
+    } else if (numContexts > kMaxWebGLContexts) {
+        GenerateWarning("Exceeded %d live WebGL contexts, losing the least recently used one.",
+                        kMaxWebGLContexts);
+        MOZ_ASSERT(oldestContext); // if we reach this point, this can't be null
+        const_cast<WebGLContext*>(oldestContext)->LoseContext();
+    }
+}
+
 NS_IMETHODIMP
 WebGLContext::GetInputStream(const char* aMimeType,
                              const PRUnichar* aEncoderOptions,
                              nsIInputStream **aStream)
 {
     NS_ASSERTION(gl, "GetInputStream on invalid context?");
     if (!gl)
         return NS_ERROR_FAILURE;
@@ -685,16 +779,31 @@ WebGLContext::GetInputStream(const char*
 }
 
 NS_IMETHODIMP
 WebGLContext::GetThebesSurface(gfxASurface **surface)
 {
     return NS_ERROR_NOT_AVAILABLE;
 }
 
+void WebGLContext::UpdateLastUseIndex()
+{
+    static CheckedInt<uint64_t> sIndex = 0;
+
+    sIndex++;
+
+    // should never happen with 64-bit; trying to handle this would be riskier than
+    // not handling it as the handler code would never get exercised.
+    if (!sIndex.isValid()) {
+        NS_RUNTIMEABORT("Can't believe it's been 2^64 transactions already!");
+    }
+
+    mLastUseIndex = sIndex.value();
+}
+
 static PRUint8 gWebGLLayerUserData;
 
 namespace mozilla {
 
 class WebGLContextUserData : public LayerUserData {
 public:
     WebGLContextUserData(nsHTMLCanvasElement *aContent)
     : mContent(aContent) {}
@@ -705,16 +814,18 @@ public:
   static void DidTransactionCallback(void* aData)
   {
     WebGLContextUserData *userdata = static_cast<WebGLContextUserData*>(aData);
     nsHTMLCanvasElement *canvas = userdata->mContent;
     WebGLContext *context = static_cast<WebGLContext*>(canvas->GetContextAtIndex(0));
 
     context->mBackbufferClearingStatus = BackbufferClearingStatus::NotClearedSinceLastPresented;
     canvas->MarkContextClean();
+
+    context->UpdateLastUseIndex();
   }
 
 private:
   nsRefPtr<nsHTMLCanvasElement> mContent;
 };
 
 } // end namespace mozilla
 
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -1356,16 +1356,21 @@ protected:
 
     int mAlreadyGeneratedWarnings;
     bool mAlreadyWarnedAboutFakeVertexAttrib0;
 
     bool ShouldGenerateWarnings() const {
         return mAlreadyGeneratedWarnings < 32;
     }
 
+    uint64_t mLastUseIndex;
+
+    void LoseOldestWebGLContextIfLimitExceeded();
+    void UpdateLastUseIndex();
+
 #ifdef XP_MACOSX
     // see bug 713305. This RAII helper guarantees that we're on the discrete GPU, during its lifetime
     // Debouncing note: we don't want to switch GPUs too frequently, so try to not create and destroy
     // these objects at high frequency. Having WebGLContext's hold one such object seems fine,
     // because WebGLContext objects only go away during GC, which shouldn't happen too frequently.
     // If in the future GC becomes much more frequent, we may have to revisit then (maybe use a timer).
     ForceDiscreteGPUHelperCGL mForceDiscreteGPUHelper;
 #endif
@@ -3169,23 +3174,25 @@ class WebGLMemoryMultiReporterWrapper
     ~WebGLMemoryMultiReporterWrapper();
     static WebGLMemoryMultiReporterWrapper* sUniqueInstance;
 
     // here we store plain pointers, not RefPtrs: we don't want the 
     // WebGLMemoryMultiReporterWrapper unique instance to keep alive all		
     // WebGLContexts ever created.
     typedef nsTArray<const WebGLContext*> ContextsArrayType;
     ContextsArrayType mContexts;
-    
+
     nsCOMPtr<nsIMemoryMultiReporter> mReporter;
 
     static WebGLMemoryMultiReporterWrapper* UniqueInstance();
 
     static ContextsArrayType & Contexts() { return UniqueInstance()->mContexts; }
 
+    friend class WebGLContext;
+
   public:
 
     static void AddWebGLContext(const WebGLContext* c) {
         Contexts().AppendElement(c);
     }
 
     static void RemoveWebGLContext(const WebGLContext* c) {
         ContextsArrayType & contexts = Contexts();
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -4499,16 +4499,17 @@ WebGLContext::name##_base(WebGLUniformLo
                                      " (since this uniform is not an array uniform)," \
                                      " got an array of length %d",              \
                                  #name,                                         \
                                  expectedElemSize,                           \
                                  arrayLength);                                  \
     }                                                                           \
                                                                                 \
     uint32_t numElementsToUpload = NS_MIN(info.arraySize, arrayLength/expectedElemSize);     \
+    MakeContextCurrent();                                                       \
     gl->f##name(location, numElementsToUpload, data);    \
 }
 
 #define SIMPLE_MATRIX_METHOD_UNIFORM(name, dim)                                 \
 NS_IMETHODIMP                                                                   \
 WebGLContext::name(nsIWebGLUniformLocation* aLocation, bool aTranspose,         \
                    const JS::Value& aValue, JSContext* aCx)                     \
 {                                                                               \
--- a/content/events/src/nsDOMTouchEvent.cpp
+++ b/content/events/src/nsDOMTouchEvent.cpp
@@ -123,56 +123,52 @@ nsDOMTouch::Equals(nsIDOMTouch* aTouch)
   aTouch->GetRadiusY(&radiusY);
   return mRefPoint != aTouch->mRefPoint ||
          (mForce != force) ||
          (mRotationAngle != orientation) ||
          (mRadius.x != radiusX) || (mRadius.y != radiusY);
 }
 
 // TouchList
+nsDOMTouchList::nsDOMTouchList(nsTArray<nsCOMPtr<nsIDOMTouch> > &aTouches)
+{
+  mPoints.AppendElements(aTouches);
+}
 
 DOMCI_DATA(TouchList, nsDOMTouchList)
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMTouchList)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMTouchList)
-  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_INTERFACE_MAP_ENTRY(nsIDOMTouchList)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(TouchList)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMTouchList)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSTARRAY_OF_NSCOMPTR(mPoints)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mParent)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDOMTouchList)
-  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
-NS_IMPL_CYCLE_COLLECTION_TRACE_END
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMTouchList)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mPoints)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mParent)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMTouchList)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMTouchList)
 
 NS_IMETHODIMP
 nsDOMTouchList::GetLength(PRUint32* aLength)
 {
   *aLength = mPoints.Length();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMTouchList::Item(PRUint32 aIndex, nsIDOMTouch** aRetVal)
 {
-  NS_IF_ADDREF(*aRetVal = nsDOMTouchList::GetItemAt(aIndex));
+  NS_IF_ADDREF(*aRetVal = mPoints.SafeElementAt(aIndex, nsnull));
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMTouchList::IdentifiedTouch(PRInt32 aIdentifier, nsIDOMTouch** aRetVal)
 {
   *aRetVal = nullptr;
   for (PRUint32 i = 0; i < mPoints.Length(); ++i) {
@@ -182,22 +178,16 @@ nsDOMTouchList::IdentifiedTouch(PRInt32 
         aIdentifier == identifier) {
       point.swap(*aRetVal);
       break;
     }
   }
   return NS_OK;
 }
 
-nsIDOMTouch*
-nsDOMTouchList::GetItemAt(PRUint32 aIndex)
-{
-  return mPoints.SafeElementAt(aIndex, nullptr);
-}
-
 // TouchEvent
 
 nsDOMTouchEvent::nsDOMTouchEvent(nsPresContext* aPresContext,
                                  nsTouchEvent* aEvent)
   : nsDOMUIEvent(aPresContext, aEvent ? aEvent :
                                         new nsTouchEvent(false, 0, nullptr))
 {
   if (aEvent) {
@@ -292,21 +282,19 @@ nsDOMTouchEvent::GetTouches(nsIDOMTouchL
     // for touchend events, remove any changed touches from the touches array
     nsTArray<nsCOMPtr<nsIDOMTouch> > unchangedTouches;
     nsTArray<nsCOMPtr<nsIDOMTouch> > touches = touchEvent->touches;
     for (PRUint32 i = 0; i < touches.Length(); ++i) {
       if (!touches[i]->mChanged) {
         unchangedTouches.AppendElement(touches[i]);
       }
     }
-    t = new nsDOMTouchList(static_cast<nsIDOMTouchEvent*>(this),
-                           unchangedTouches);
+    t = new nsDOMTouchList(unchangedTouches);
   } else {
-    t = new nsDOMTouchList(static_cast<nsIDOMTouchEvent*>(this),
-                           touchEvent->touches);
+    t = new nsDOMTouchList(touchEvent->touches);
   }
   mTouches = t;
   return CallQueryInterface(mTouches, aTouches);
 }
 
 NS_IMETHODIMP
 nsDOMTouchEvent::GetTargetTouches(nsIDOMTouchList** aTargetTouches)
 {
@@ -326,18 +314,17 @@ nsDOMTouchEvent::GetTargetTouches(nsIDOM
     if ((mEvent->message != NS_TOUCH_END &&
          mEvent->message != NS_TOUCH_CANCEL) || !touches[i]->mChanged) {
       nsIDOMEventTarget* targetPtr = touches[i]->GetTarget();
       if (targetPtr == mEvent->target) {
         targetTouches.AppendElement(touches[i]);
       }
     }
   }
-  mTargetTouches = new nsDOMTouchList(static_cast<nsIDOMTouchEvent*>(this),
-                                      targetTouches);
+  mTargetTouches = new nsDOMTouchList(targetTouches);
   return CallQueryInterface(mTargetTouches, aTargetTouches);
 }
 
 NS_IMETHODIMP
 nsDOMTouchEvent::GetChangedTouches(nsIDOMTouchList** aChangedTouches)
 {
   NS_ENSURE_ARG_POINTER(aChangedTouches);
   NS_ENSURE_STATE(mEvent);
@@ -349,18 +336,17 @@ nsDOMTouchEvent::GetChangedTouches(nsIDO
   nsTArray<nsCOMPtr<nsIDOMTouch> > changedTouches;
   nsTouchEvent* touchEvent = static_cast<nsTouchEvent*>(mEvent);
   nsTArray<nsCOMPtr<nsIDOMTouch> > touches = touchEvent->touches;
   for (PRUint32 i = 0; i < touches.Length(); ++i) {
     if (touches[i]->mChanged) {
       changedTouches.AppendElement(touches[i]);
     }
   }
-  mChangedTouches = new nsDOMTouchList(static_cast<nsIDOMTouchEvent*>(this),
-                                       changedTouches);
+  mChangedTouches = new nsDOMTouchList(changedTouches);
   return CallQueryInterface(mChangedTouches, aChangedTouches);
 }
 
 NS_IMETHODIMP
 nsDOMTouchEvent::GetAltKey(bool* aAltKey)
 {
   *aAltKey = static_cast<nsInputEvent*>(mEvent)->IsAlt();
   return NS_OK;
--- a/content/events/src/nsDOMTouchEvent.h
+++ b/content/events/src/nsDOMTouchEvent.h
@@ -4,18 +4,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #ifndef nsDOMTouchEvent_h_
 #define nsDOMTouchEvent_h_
 
 #include "nsDOMUIEvent.h"
 #include "nsIDOMTouchEvent.h"
 #include "nsString.h"
 #include "nsTArray.h"
-#include "nsIDocument.h"
-#include "dombindings.h"
 #include "mozilla/Attributes.h"
 
 class nsDOMTouch MOZ_FINAL : public nsIDOMTouch
 {
 public:
   nsDOMTouch(nsIDOMEventTarget* aTarget,
              PRInt32 aIdentifier,
              PRInt32 aPageX,
@@ -94,56 +92,38 @@ public:
   nsIntPoint mScreenPoint;
   nsIntPoint mRadius;
   float mRotationAngle;
   float mForce;
 protected:
   bool mPointsInitialized;
 };
 
-class nsDOMTouchList MOZ_FINAL : public nsIDOMTouchList,
-                                 public nsWrapperCache
+class nsDOMTouchList : public nsIDOMTouchList
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMTouchList)
+  NS_DECL_CYCLE_COLLECTION_CLASS(nsDOMTouchList)
   NS_DECL_NSIDOMTOUCHLIST
 
-  nsDOMTouchList(nsISupports *aParent) : mParent(aParent)
-  {
-    SetIsDOMBinding();
-  }
-  nsDOMTouchList(nsISupports *aParent,
-                 nsTArray<nsCOMPtr<nsIDOMTouch> > &aTouches)
-   : mPoints(aTouches),
-     mParent(aParent)
-  {
-    SetIsDOMBinding();
-  }
-
-  virtual JSObject* WrapObject(JSContext *cx, JSObject *scope,
-                               bool *triedToWrap)
-  {
-    return mozilla::dom::binding::TouchList::create(cx, scope, this,
-                                                    triedToWrap);
-  }
-
-  nsISupports *GetParentObject()
-  {
-    return mParent;
-  }
+  nsDOMTouchList() { }
+  nsDOMTouchList(nsTArray<nsCOMPtr<nsIDOMTouch> > &aTouches);
 
   void Append(nsIDOMTouch* aPoint)
   {
     mPoints.AppendElement(aPoint);
   }
 
+  nsIDOMTouch* GetItemAt(PRUint32 aIndex)
+  {
+    return mPoints.SafeElementAt(aIndex, nsnull);
+  }
+
 protected:
   nsTArray<nsCOMPtr<nsIDOMTouch> > mPoints;
-  nsCOMPtr<nsISupports> mParent;
 };
 
 class nsDOMTouchEvent : public nsDOMUIEvent,
                         public nsIDOMTouchEvent
 {
 public:
   nsDOMTouchEvent(nsPresContext* aPresContext, nsTouchEvent* aEvent);
   virtual ~nsDOMTouchEvent();
--- a/content/html/content/public/nsHTMLMediaElement.h
+++ b/content/html/content/public/nsHTMLMediaElement.h
@@ -284,31 +284,31 @@ public:
   static char const *const gOggCodecs[3];
   static bool IsOpusEnabled();
   static char const *const gOggCodecsWithOpus[4];
 #endif
 
 #ifdef MOZ_WAVE
   static bool IsWaveEnabled();
   static bool IsWaveType(const nsACString& aType);
-  static const char gWaveTypes[4][16];
+  static const char gWaveTypes[4][15];
   static char const *const gWaveCodecs[2];
 #endif
 
 #ifdef MOZ_WEBM
   static bool IsWebMEnabled();
   static bool IsWebMType(const nsACString& aType);
-  static const char gWebMTypes[2][17];
+  static const char gWebMTypes[2][11];
   static char const *const gWebMCodecs[4];
 #endif
 
 #ifdef MOZ_GSTREAMER
   static bool IsH264Enabled();
   static bool IsH264Type(const nsACString& aType);
-  static const char gH264Types[3][17];
+  static const char gH264Types[3][16];
   static char const *const gH264Codecs[7];
 #endif
 
 #ifdef MOZ_MEDIA_PLUGINS
   static bool IsMediaPluginsEnabled();
   static bool IsMediaPluginsType(const nsACString& aType);
 #endif
 
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -483,26 +483,25 @@ nsGenericHTMLElement::GetOffsetRect(nsRe
 
   Element* docElement = GetCurrentDoc()->GetRootElement();
   nsIContent* content = frame->GetContent();
 
   if (content && (content->IsHTML(nsGkAtoms::body) || content == docElement)) {
     parent = frame;
   }
   else {
-    const bool isPositioned = frame->GetStyleDisplay()->IsPositioned();
-    const bool isAbsolutelyPositioned =
-      frame->GetStyleDisplay()->IsAbsolutelyPositioned();
+    const bool isPositioned = frame->IsPositioned();
+    const bool isAbsolutelyPositioned = frame->IsAbsolutelyPositioned();
     origin += frame->GetPositionIgnoringScrolling();
 
     for ( ; parent ; parent = parent->GetParent()) {
       content = parent->GetContent();
 
       // Stop at the first ancestor that is positioned.
-      if (parent->GetStyleDisplay()->IsPositioned()) {
+      if (parent->IsPositioned()) {
         *aOffsetParent = content;
         NS_IF_ADDREF(*aOffsetParent);
         break;
       }
 
       // Add the parent's origin to our own to get to the
       // right coordinate system.
       const bool isOffsetParent = !isPositioned && IsOffsetParent(parent);
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -1954,22 +1954,22 @@ void nsHTMLMediaElement::UnbindFromTree(
                                         bool aNullParent)
 {
   if (!mPaused && mNetworkState != nsIDOMHTMLMediaElement::NETWORK_EMPTY)
     Pause();
   nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
 }
 
 #ifdef MOZ_RAW
-static const char gRawTypes[][16] = {
+static const char gRawTypes[2][16] = {
   "video/x-raw",
   "video/x-raw-yuv"
 };
 
-static const char* gRawCodecs[] = {
+static const char* gRawCodecs[1] = {
   nullptr
 };
 
 bool
 nsHTMLMediaElement::IsRawEnabled()
 {
   return Preferences::GetBool("media.raw.enabled");
 }
@@ -2043,17 +2043,17 @@ nsHTMLMediaElement::IsOggType(const nsAC
   return false;
 }
 #endif
 
 #ifdef MOZ_WAVE
 // See http://www.rfc-editor.org/rfc/rfc2361.txt for the definitions
 // of WAVE media types and codec types. However, the audio/vnd.wave
 // MIME type described there is not used.
-const char nsHTMLMediaElement::gWaveTypes[4][16] = {
+const char nsHTMLMediaElement::gWaveTypes[4][15] = {
   "audio/x-wav",
   "audio/wav",
   "audio/wave",
   "audio/x-pn-wav"
 };
 
 char const *const nsHTMLMediaElement::gWaveCodecs[2] = {
   "1", // Microsoft PCM Format
@@ -2079,17 +2079,17 @@ nsHTMLMediaElement::IsWaveType(const nsA
     }
   }
 
   return false;
 }
 #endif
 
 #ifdef MOZ_WEBM
-const char nsHTMLMediaElement::gWebMTypes[2][17] = {
+const char nsHTMLMediaElement::gWebMTypes[2][11] = {
   "video/webm",
   "audio/webm"
 };
 
 char const *const nsHTMLMediaElement::gWebMCodecs[4] = {
   "vp8",
   "vp8.0",
   "vorbis",
@@ -2115,17 +2115,17 @@ nsHTMLMediaElement::IsWebMType(const nsA
     }
   }
 
   return false;
 }
 #endif
 
 #ifdef MOZ_GSTREAMER
-const char nsHTMLMediaElement::gH264Types[3][17] = {
+const char nsHTMLMediaElement::gH264Types[3][16] = {
   "video/mp4",
   "video/3gpp",
   "video/quicktime",
 };
 
 char const *const nsHTMLMediaElement::gH264Codecs[7] = {
   "avc1.42E01E",
   "avc1.42001E",
--- a/content/media/test/Makefile.in
+++ b/content/media/test/Makefile.in
@@ -100,47 +100,53 @@ MOCHITEST_FILES = \
 		test_playback_errors.html \
 		test_seekable1.html \
 		test_preload_actions.html \
 		test_preload_attribute.html \
 		test_progress.html \
 		test_reactivate.html \
 		test_readyState.html \
 		test_replay_metadata.html \
-		test_seek.html \
 		test_seek2.html \
-		test_seek_out_of_range.html \
 		test_source.html \
 		test_source_write.html \
 	        test_source_null.html \
 		test_standalone.html \
 		test_streams_element_capture.html \
 		test_streams_element_capture_reset.html \
-		test_timeupdate_small_files.html \
 		test_too_many_elements.html \
 		test_volume.html \
 		test_video_to_canvas.html \
 		use_large_cache.js \
 		test_audiowrite.html \
 		test_mozHasAudio.html \
 		test_source_media.html \
-		$(NULL)
-
-# Bug 759221
-MOCHITEST_FILES += \
 		test_autoplay_contentEditable.html \
 		test_buffered.html \
 		test_bug448534.html \
 		test_bug463162.xhtml \
 		test_decoder_disable.html \
 		test_media_selection.html \
 		test_playback.html \
 		test_seekLies.html \
 		$(NULL)
 
+# Tests disabled on Linux for frequent intermittent failures
+ifneq (Linux,$(OS_ARCH))
+MOCHITEST_FILES += \
+		test_seek.html \
+		test_seek_out_of_range.html \
+		test_timeupdate_small_files.html \
+		$(NULL)
+else
+$(warning test_seek.html is disabled on Linux for timeouts. Bug 620598)
+$(warning test_seek_out_of_range.html is disabled on Linux for timeouts. Bug 661076)
+$(warning test_timeupdate_small_files.html is disabled on Linux for timeouts. Bug 687972)
+endif
+
 # Don't run in suite
 ifndef MOZ_SUITE
 MOCHITEST_FILES += test_play_twice.html
 else
 $(warning test_play_twice.html is disabled pending investigation. Bug 598252)
 endif
 
 # These tests are disabled until we figure out random failures.
--- a/content/media/test/manifest.js
+++ b/content/media/test/manifest.js
@@ -2,29 +2,29 @@
 // be ignored. To make sure tests respect that, we include a file of type
 // "bogus/duh" in each list.
 
 // These are small test files, good for just seeing if something loads. We
 // really only need one test file per backend here.
 var gSmallTests = [
   { name:"small-shot.ogg", type:"audio/ogg", duration:0.276 },
   { name:"r11025_s16_c1.wav", type:"audio/x-wav", duration:1.0 },
-  { name:"320x240.ogv", type:"video/ogg", width:320, height:240, duration:0.233 },
+  { name:"320x240.ogv", type:"video/ogg", width:320, height:240, duration:0.266 },
   { name:"seek.webm", type:"video/webm", width:320, height:240, duration:3.966 },
   { name:"detodos.opus", type:"audio/ogg; codecs=opus", duration:2.9135 },
   { name:"bogus.duh", type:"bogus/duh" }
 ];
 
 // Used by test_progress to ensure we get the correct progress information
 // during resource download.
 var gProgressTests = [
   { name:"r11025_u8_c1.wav", type:"audio/x-wav", duration:1.0, size:11069 },
   { name:"big.wav", type:"audio/x-wav", duration:9.278981, size:102444 },
   { name:"seek.ogv", type:"video/ogg", duration:3.966, size:285310 },
-  { name:"320x240.ogv", type:"video/ogg", width:320, height:240, duration:0.233, size:28942 },
+  { name:"320x240.ogv", type:"video/ogg", width:320, height:240, duration:0.266, size:28942 },
   { name:"seek.webm", type:"video/webm", duration:3.966, size:215529 },
   { name:"bogus.duh", type:"bogus/duh" }
 ];
 
 // Used by test_played.html
 var gPlayedTests = [
   { name:"big.wav", type:"audio/x-wav", duration:9.0 },
   { name:"sound.ogg", type:"audio/ogg", duration:4.0 },
@@ -35,17 +35,17 @@ var gPlayedTests = [
 // Used by test_mozLoadFrom.  Need one test file per decoder backend, plus
 // anything for testing clone-specific bugs.
 var cloneKey = Math.floor(Math.random()*100000000);
 var gCloneTests = gSmallTests.concat([
   // Actual duration is ~200ms, we have Content-Duration lie about it.
   { name:"bug520908.ogv", type:"video/ogg", duration:9000 },
   // short-video is more like 1s, so if you load this twice you'll get an unexpected duration
   { name:"dynamic_resource.sjs?key=" + cloneKey + "&res1=320x240.ogv&res2=short-video.ogv",
-    type:"video/ogg", duration:0.233 },
+    type:"video/ogg", duration:0.266 },
 ]);
 
 // Used by test_play_twice.  Need one test file per decoder backend, plus
 // anything for testing bugs that occur when replying a played file.
 var gReplayTests = gSmallTests.concat([
   { name:"bug533822.ogg", type:"audio/ogg" },
 ]);
 
@@ -54,17 +54,17 @@ var gReplayTests = gSmallTests.concat([
 var gPausedAfterEndedTests = gSmallTests.concat([
   { name:"r11025_u8_c1.wav", type:"audio/x-wav", duration:1.0 },
   { name:"small-shot.ogg", type:"video/ogg", duration:0.276 }
 ]);
 
 // Test the mozHasAudio property
 var gMozHasAudioTests = [
   { name:"big.wav", type:"audio/x-wav", duration:9.278981, size:102444, hasAudio:undefined },
-  { name:"320x240.ogv", type:"video/ogg", width:320, height:240, duration:0.233, size:28942, hasAudio:false },
+  { name:"320x240.ogv", type:"video/ogg", width:320, height:240, duration:0.266, size:28942, hasAudio:false },
   { name:"short-video.ogv", type:"video/ogg", duration:1.081, hasAudio:true },
   { name:"seek.webm", type:"video/webm", duration:3.966, size:215529, hasAudio:false },
   { name:"bogus.duh", type:"bogus/duh" }
 ];
 
 // These are files that we want to make sure we can play through.  We can
 // also check metadata.  Put files of the same type together in this list so if
 // something crashes we have some idea of which backend is responsible.
@@ -123,17 +123,17 @@ var gPlayTests = [
   { name:"audio-overhang.ogg", type:"audio/ogg", duration:2.3 },
   { name:"video-overhang.ogg", type:"audio/ogg", duration:3.966 },
 
   // bug461281.ogg with the middle second chopped out.
   { name:"audio-gaps.ogg", type:"audio/ogg", duration:2.208 },
 
   // Test playback/metadata work after a redirect
   { name:"redirect.sjs?domain=mochi.test:8888&file=320x240.ogv",
-    type:"video/ogg", duration:0.233 },
+    type:"video/ogg", duration:0.266 },
 
   // Test playback of a webm file
   { name:"seek.webm", type:"video/webm", duration:3.966 },
 
   // Test playback of a WebM file with non-zero start time.
   { name:"split.webm", type:"video/webm", duration:1.967 },
   
   // Test playback of a raw file
@@ -253,17 +253,17 @@ var gErrorTests = [
   { name:"bogus.duh", type:"bogus/duh" }
 ];
 
 // These are files that have nontrivial duration and are useful for seeking within.
 var gSeekTests = [
   { name:"r11025_s16_c1.wav", type:"audio/x-wav", duration:1.0 },
   { name:"audio.wav", type:"audio/x-wav", duration:0.031247 },
   { name:"seek.ogv", type:"video/ogg", duration:3.966 },
-  { name:"320x240.ogv", type:"video/ogg", duration:0.233 },
+  { name:"320x240.ogv", type:"video/ogg", duration:0.266 },
   { name:"seek.webm", type:"video/webm", duration:3.966 },
   { name:"bug516323.indexed.ogv", type:"video/ogg", duration:4.208 },
   { name:"split.webm", type:"video/webm", duration:1.967 },
   { name:"detodos.opus", type:"audio/ogg; codecs=opus", duration:2.9135 },
   { name:"bogus.duh", type:"bogus/duh", duration:123 }
 ];
 
 // These are files suitable for using with a "new Audio" constructor.
--- a/content/media/test/test_info_leak.html
+++ b/content/media/test/test_info_leak.html
@@ -30,17 +30,17 @@ var manager = new MediaTestManager;
 var gEventTypes = [ 'loadstart', 'progress', 'suspend', 'abort', 'error', 'emptied', 'stalled', 'play', 'pause', 'loadedmetadata', 'loadeddata', 'waiting', 'playing', 'canplay', 'canplaythrough', 'seeking', 'seeked', 'timeupdate', 'ended', 'ratechange', 'durationchange', 'volumechange' ];
 
 var gExpectedEvents = ['loadstart', 'error'];
 
 function createTestArray() {
   var tests = [];
   var tmpVid = document.createElement("video");
 
-  for (var testNum=0; testNum<gSeekTests.length; testNum++) {
+  for (var testNum=0; testNum<gInfoLeakTests.length; testNum++) {
     var test = gInfoLeakTests[testNum];
     if (!tmpVid.canPlayType(test.type)) {
       continue;
     }
 
     var t = new Object;
     t.name = test.src;
     t.type = test.type;
--- a/content/media/test/test_timeupdate_small_files.html
+++ b/content/media/test/test_timeupdate_small_files.html
@@ -49,17 +49,21 @@ function ended(e) {
     500);
 }
 
 var eventsToLog = ["play", "canplay", "canplaythrough", "loadstart", "loadedmetadata",
   "loadeddata", "playing", "progress", "timeupdate", "ended", "suspend", "error", "stalled", "emptied", "abort",
   "waiting", "pause"];
 function logEvent(event) {
   if (event.target.gotEnded > (event.type == "ended" ? 1 : 0)) {
-    ok(false, event.target.currentSrc + " got unexpected " + event.type + " after ended");
+    if (event.target.currentSrc.slice(-9) == "seek.webm" && event.type == "stalled") {
+      todo(false, event.target.currentSrc + " got unexpected stalled after ended (bug 760770)");
+    } else {
+      ok(false, event.target.currentSrc + " got unexpected " + event.type + " after ended");
+    }
   } else {
     info(event.target.currentSrc + " got " + event.type);
   }
 }
 
 function startTest(test, token) {
   var type = /^video/.test(test.type) ? "video" : "audio";
   var v = document.createElement(type);
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -187,16 +187,19 @@
 
 #include "nsXULAppAPI.h"
 
 #include "nsDOMNavigationTiming.h"
 #include "nsITimedChannel.h"
 #include "mozilla/StartupTimeline.h"
 #include "nsIFrameMessageManager.h"
 
+#include "mozilla/Telemetry.h"
+#include "nsISecurityUITelemetry.h"
+
 static NS_DEFINE_CID(kDOMScriptObjectFactoryCID,
                      NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
 
 #if defined(DEBUG_bryner) || defined(DEBUG_chb)
 //#define DEBUG_DOCSHELL_FOCUS
 #define DEBUG_PAGE_CACHE
 #endif
@@ -4035,30 +4038,42 @@ nsDocShell::DisplayLoadError(nsresult aE
                 nsCOMPtr<nsIStrictTransportSecurityService> stss =
                           do_GetService(NS_STSSERVICE_CONTRACTID, &rv);
                 NS_ENSURE_SUCCESS(rv, rv);
 
                 bool isStsHost = false;
                 rv = stss->IsStsURI(aURI, &isStsHost);
                 NS_ENSURE_SUCCESS(rv, rv);
 
-                if (isStsHost)
+                PRUint32 bucketId;
+                if (isStsHost) {
                   cssClass.AssignLiteral("badStsCert");
+                  //measuring STS separately allows us to measure click through
+                  //rates easily
+                  bucketId = nsISecurityUITelemetry::WARNING_BAD_CERT_STS;
+                } else {
+                  bucketId = nsISecurityUITelemetry::WARNING_BAD_CERT;
+                }
+
 
                 if (Preferences::GetBool(
                         "browser.xul.error_pages.expert_bad_cert", false)) {
                     cssClass.AssignLiteral("expertBadCert");
                 }
 
                 // See if an alternate cert error page is registered
                 nsAdoptingCString alternateErrorPage =
                     Preferences::GetCString(
                         "security.alternate_certificate_error_page");
                 if (alternateErrorPage)
                     errorPage.Assign(alternateErrorPage);
+
+                if (errorPage.EqualsIgnoreCase("certerror")) 
+                    mozilla::Telemetry::Accumulate(mozilla::Telemetry::SECURITY_UI, bucketId);
+
             } else {
                 error.AssignLiteral("nssFailure2");
             }
         }
     } else if (NS_ERROR_PHISHING_URI == aError || NS_ERROR_MALWARE_URI == aError) {
         nsCAutoString host;
         aURI->GetHost(host);
         CopyUTF8toUTF16(host, formatStrs[0]);
@@ -4066,20 +4081,29 @@ nsDocShell::DisplayLoadError(nsresult aE
 
         // Malware and phishing detectors may want to use an alternate error
         // page, but if the pref's not set, we'll fall back on the standard page
         nsAdoptingCString alternateErrorPage =
             Preferences::GetCString("urlclassifier.alternate_error_page");
         if (alternateErrorPage)
             errorPage.Assign(alternateErrorPage);
 
-        if (NS_ERROR_PHISHING_URI == aError)
+        PRUint32 bucketId;
+        if (NS_ERROR_PHISHING_URI == aError) {
             error.AssignLiteral("phishingBlocked");
-        else
+            bucketId = nsISecurityUITelemetry::WARNING_PHISHING_PAGE;
+        } else {
             error.AssignLiteral("malwareBlocked");
+            bucketId = nsISecurityUITelemetry::WARNING_MALWARE_PAGE;
+        }
+
+        if (errorPage.EqualsIgnoreCase("blocked"))
+            mozilla::Telemetry::Accumulate(mozilla::Telemetry::SECURITY_UI,
+                                           bucketId);
+
         cssClass.AssignLiteral("blacklist");
     }
     else {
         // Errors requiring simple formatting
         switch (aError) {
         case NS_ERROR_MALFORMED_URI:
             // URI is malformed
             error.AssignLiteral("malformedURI");
@@ -11376,17 +11400,17 @@ nsDocShell::GetIsContent(bool *aIsConten
 NS_IMETHODIMP
 nsDocShell::GetExtendedOrigin(nsIURI *aUri, nsACString &aResult)
 {
     bool isInBrowserElement;
     GetIsInBrowserElement(&isInBrowserElement);
 
     nsCOMPtr<nsIScriptSecurityManager> ssmgr =
       do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
-    NS_ENSURE_TRUE(ssmgr, false);
+    NS_ENSURE_TRUE(ssmgr, NS_ERROR_FAILURE);
 
     return ssmgr->GetExtendedOrigin(aUri, mAppId, isInBrowserElement, aResult);
 }
 
 
 bool
 nsDocShell::IsOKToLoadURI(nsIURI* aURI)
 {
--- a/dom/Makefile.in
+++ b/dom/Makefile.in
@@ -11,19 +11,19 @@ VPATH		= @srcdir@
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= dom
 
 DIRS = \
   interfaces/base \
   interfaces/canvas \
   interfaces/core \
-  interfaces/devicestorage \
   interfaces/html \
   interfaces/events \
+  interfaces/devicestorage \
   interfaces/contacts \
   interfaces/settings \
   interfaces/stylesheets \
   interfaces/sidebar \
   interfaces/css \
   interfaces/traversal \
   interfaces/range \
   interfaces/xbl \
--- a/dom/alarm/AlarmDB.jsm
+++ b/dom/alarm/AlarmDB.jsm
@@ -74,52 +74,81 @@ AlarmDB.prototype = {
       aSuccessCb, 
       aErrorCb
     );
   },
 
   /**
    * @param aId
    *        The ID of record to be removed.
+   * @param aManifestURL
+   *        The manifest URL of the app that alarm belongs to.
+   *        If null, directly remove the ID record; otherwise,
+   *        need to check if the alarm belongs to this app.
    * @param aSuccessCb
    *        Callback function to invoke with result.
    * @param aErrorCb [optional]
    *        Callback function to invoke when there was an error.
    */
-  remove: function remove(aId, aSuccessCb, aErrorCb) {
+  remove: function remove(aId, aManifestURL, aSuccessCb, aErrorCb) {
     debug("remove()");
 
     this.newTxn(
       "readwrite", 
       function txnCb(aTxn, aStore) {
         debug("Going to remove " + aId);
-        aStore.delete(aId);
+
+        // Look up the existing record and compare the manifestURL
+        // to see if the alarm to be removed belongs to this app.
+        aStore.get(aId).onsuccess = function doRemove(aEvent) {
+          let alarm = aEvent.target.result;
+
+          if (!alarm) {
+            debug("Alarm doesn't exist. No need to remove it.");
+            return;
+          }
+
+          if (aManifestURL && aManifestURL != alarm.manifestURL) {
+            debug("Cannot remove the alarm added by other apps.");
+            return;
+          }
+
+          aStore.delete(aId);
+        };
       }, 
       aSuccessCb, 
       aErrorCb
     );
   },
 
   /**
+   * @param aManifestURL
+   *        The manifest URL of the app that alarms belong to.
+   *        If null, directly return all alarms; otherwise,
+   *        only return the alarms that belong to this app.
    * @param aSuccessCb
    *        Callback function to invoke with result array.
    * @param aErrorCb [optional]
    *        Callback function to invoke when there was an error.
    */
-  getAll: function getAll(aSuccessCb, aErrorCb) {
+  getAll: function getAll(aManifestURL, aSuccessCb, aErrorCb) {
     debug("getAll()");
 
     this.newTxn(
       "readonly", 
       function txnCb(aTxn, aStore) {
         if (!aTxn.result)
-          aTxn.result = {};
+          aTxn.result = [];
 
         aStore.mozGetAll().onsuccess = function setTxnResult(aEvent) {
-          aTxn.result = aEvent.target.result;
+          aEvent.target.result.forEach(function addAlarm(aAlarm) {
+            if (!aManifestURL || aManifestURL == aAlarm.manifestURL)
+              aTxn.result.push(aAlarm);
+          });
+
           debug("Request successful. Record count: " + aTxn.result.length);
         };
       }, 
       aSuccessCb, 
       aErrorCb
     );
   }
 };
--- a/dom/alarm/AlarmService.jsm
+++ b/dom/alarm/AlarmService.jsm
@@ -75,16 +75,17 @@ let AlarmService = {
   receiveMessage: function receiveMessage(aMessage) {
     debug("receiveMessage(): " + aMessage.name);
 
     let mm = aMessage.target.QueryInterface(Ci.nsIFrameMessageManager);
     let json = aMessage.json;
     switch (aMessage.name) {
       case "AlarmsManager:GetAll":
         this._db.getAll(
+          json.manifestURL,
           function getAllSuccessCb(aAlarms) {
             debug("Callback after getting alarms from database: " + JSON.stringify(aAlarms));
             this._sendAsyncMessage(mm, "GetAll", true, json.requestId, aAlarms);
           }.bind(this),
           function getAllErrorCb(aErrorMsg) {
             this._sendAsyncMessage(mm, "GetAll", false, json.requestId, aErrorMsg);
           }.bind(this)
         );
@@ -145,30 +146,34 @@ let AlarmService = {
             this._sendAsyncMessage(mm, "Add", false, json.requestId, aErrorMsg);
           }.bind(this)
         );
         break;
 
       case "AlarmsManager:Remove":
         this._removeAlarmFromDb(
           json.id,
+          json.manifestURL,
           function removeSuccessCb() {
             debug("Callback after removing alarm from database.");
 
             // if there is no alarm being set
             if (!this._currentAlarm) {
               this._debugCurrentAlarm();
               return;
             }
 
             // check if the alarm to be removed is in the queue
+            // by ID and whether it belongs to the requesting app
             let alarmQueue = this._alarmQueue;
-            if (this._currentAlarm.id != json.id) {
+            if (this._currentAlarm.id != json.id || 
+                this._currentAlarm.manifestURL != json.manifestURL) {
               for (let i = 0; i < alarmQueue.length; i++) {
-                if (alarmQueue[i].id == json.id) {
+                if (alarmQueue[i].id == json.id && 
+                    alarmQueue[i].manifestURL == json.manifestURL) {
                   alarmQueue.splice(i, 1);
                   break;
                 }
               }
               this._debugCurrentAlarm();
               return;
             }
 
@@ -219,29 +224,30 @@ let AlarmService = {
       default:
         throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
         break;
     }
 
     aMessageManager.sendAsyncMessage("AlarmsManager:" + aMessageName + ":Return:" + (aSuccess ? "OK" : "KO"), json);
   },
 
-  _removeAlarmFromDb: function _removeAlarmFromDb(aId, aRemoveSuccessCb) {
+  _removeAlarmFromDb: function _removeAlarmFromDb(aId, aManifestURL, aRemoveSuccessCb) {
     debug("_removeAlarmFromDb()");
 
     // If the aRemoveSuccessCb is undefined or null, set a 
     // dummy callback for it which is needed for _db.remove()
     if (!aRemoveSuccessCb) {
       aRemoveSuccessCb = function removeSuccessCb() {
         debug("Remove alarm from DB successfully.");
       };
     }
 
     this._db.remove(
       aId,
+      aManifestURL,
       aRemoveSuccessCb,
       function removeErrorCb(aErrorMsg) {
         throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
       }
     );
   },
 
   _fireSystemMessage: function _fireSystemMessage(aAlarm) {
@@ -250,31 +256,31 @@ let AlarmService = {
     messenger.sendMessage("alarm", aAlarm, manifestURI);
   },
 
   _onAlarmFired: function _onAlarmFired() {
     debug("_onAlarmFired()");
 
     if (this._currentAlarm) {
       this._fireSystemMessage(this._currentAlarm);
-      this._removeAlarmFromDb(this._currentAlarm.id);
+      this._removeAlarmFromDb(this._currentAlarm.id, null);
       this._currentAlarm = null;
     }
 
     // Reset the next alarm from the queue.
     let alarmQueue = this._alarmQueue;
     while (alarmQueue.length > 0) {
       let nextAlarm = alarmQueue.shift();
       let nextAlarmTime = this._getAlarmTime(nextAlarm);
 
       // If the next alarm has been expired, directly 
       // fire system message for it instead of setting it.
       if (nextAlarmTime <= Date.now()) {
         this._fireSystemMessage(nextAlarm);
-        this._removeAlarmFromDb(nextAlarm.id);
+        this._removeAlarmFromDb(nextAlarm.id, null);
       } else {
         this._currentAlarm = nextAlarm;
         break;
       }
     }
     this._debugCurrentAlarm();
   },
 
@@ -284,16 +290,17 @@ let AlarmService = {
     this._currentTimezoneOffset = aTimezoneOffset;
     this._restoreAlarmsFromDb();
   },
 
   _restoreAlarmsFromDb: function _restoreAlarmsFromDb() {
     debug("_restoreAlarmsFromDb()");
 
     this._db.getAll(
+      null,
       function getAllSuccessCb(aAlarms) {
         debug("Callback after getting alarms from database: " + JSON.stringify(aAlarms));
 
         // clear any alarms set or queued in the cache
         let alarmQueue = this._alarmQueue;
         alarmQueue.length = 0;
         this._currentAlarm = null;
         
--- a/dom/alarm/AlarmsManager.js
+++ b/dom/alarm/AlarmsManager.js
@@ -73,27 +73,27 @@ AlarmsManager.prototype = {
     return request;
   },
 
   remove: function remove(aId) {
     debug("remove()");
 
     return this._cpmm.sendSyncMessage(
       "AlarmsManager:Remove", 
-      { id: aId }
+      { id: aId, manifestURL: this._manifestURL }
     );
   },
 
   getAll: function getAll() {
     debug("getAll()");
 
     let request = this.createRequest();
     this._cpmm.sendAsyncMessage(
       "AlarmsManager:GetAll", 
-      { requestId: this.getRequestId(request) }
+      { requestId: this.getRequestId(request), manifestURL: this._manifestURL }
     );
     return request;
   },
 
   receiveMessage: function receiveMessage(aMessage) {
     debug("receiveMessage(): " + aMessage.name);
 
     let json = aMessage.json;
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -8,17 +8,16 @@
 #include "base/basictypes.h"
 
 #include "Navigator.h"
 #include "nsIXULAppInfo.h"
 #include "nsPluginArray.h"
 #include "nsMimeTypeArray.h"
 #include "nsDesktopNotification.h"
 #include "nsGeolocation.h"
-#include "nsDeviceStorage.h"
 #include "nsIHttpProtocolHandler.h"
 #include "nsICachingChannel.h"
 #include "nsIDocShell.h"
 #include "nsIWebContentHandlerRegistrar.h"
 #include "nsICookiePermission.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIJSContextStack.h"
 #include "nsCharSeparatedTokenizer.h"
@@ -187,16 +186,22 @@ Navigator::Invalidate()
   mCameraManager = nullptr;
 
 #ifdef MOZ_SYS_MSG
   if (mMessagesManager) {
     mMessagesManager = nullptr;
   }
 #endif
 
+  PRUint32 len = mDeviceStorageStores.Length();
+  for (PRUint32 i = 0; i < len; ++i) {
+    mDeviceStorageStores[i]->Shutdown();
+  }
+  mDeviceStorageStores.Clear();
+
 }
 
 nsPIDOMWindow *
 Navigator::GetWindow()
 {
   nsCOMPtr<nsPIDOMWindow> win(do_QueryReferent(mWindow));
 
   return win;
@@ -868,29 +873,37 @@ Navigator::MozIsLocallyAvailable(const n
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
   return httpChannel->GetRequestSucceeded(aIsAvailable);
 }
 
 //*****************************************************************************
 //    Navigator::nsIDOMNavigatorDeviceStorage
 //*****************************************************************************
 
-NS_IMETHODIMP Navigator::GetDeviceStorage(const nsAString &aType, nsIVariant** _retval)
+NS_IMETHODIMP Navigator::GetDeviceStorage(const nsAString &aType, nsIDOMDeviceStorage** _retval)
 {
   if (!Preferences::GetBool("device.storage.enabled", false)) {
     return NS_OK;
   }
 
   nsCOMPtr<nsPIDOMWindow> win(do_QueryReferent(mWindow));
 
   if (!win || !win->GetOuterWindow() || !win->GetDocShell()) {
     return NS_ERROR_FAILURE;
   }
 
-  nsDOMDeviceStorage::CreateDeviceStoragesFor(win, aType, _retval);
+  nsRefPtr<nsDOMDeviceStorage> storage;
+  nsDOMDeviceStorage::CreateDeviceStoragesFor(win, aType, getter_AddRefs(storage));
+
+  if (!storage) {
+    return NS_OK;
+  }
+
+  NS_ADDREF(*_retval = storage.get());
+  mDeviceStorageStores.AppendElement(storage);                                                                                                                                                                                              
   return NS_OK;
 }
 
 //*****************************************************************************
 //    Navigator::nsIDOMNavigatorGeolocation
 //*****************************************************************************
 
 NS_IMETHODIMP Navigator::GetGeolocation(nsIDOMGeoGeolocation** _retval)
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -12,16 +12,17 @@
 #include "nsIDOMNavigatorDeviceStorage.h"
 #include "nsIDOMNavigatorDesktopNotification.h"
 #include "nsIDOMClientInformation.h"
 #include "nsINavigatorBattery.h"
 #include "nsIDOMNavigatorSms.h"
 #include "nsIDOMNavigatorNetwork.h"
 #include "nsAutoPtr.h"
 #include "nsWeakReference.h"
+#include "DeviceStorage.h"
 
 class nsPluginArray;
 class nsMimeTypeArray;
 class nsGeolocation;
 class nsDesktopNotificationCenter;
 class nsPIDOMWindow;
 class nsIDOMMozConnection;
 
@@ -157,16 +158,17 @@ private:
 #endif
   nsRefPtr<network::Connection> mConnection;
   nsRefPtr<network::MobileConnection> mMobileConnection;
 #ifdef MOZ_B2G_BT
   nsCOMPtr<nsIDOMBluetoothManager> mBluetooth;
 #endif
   nsRefPtr<nsDOMCameraManager> mCameraManager;
   nsCOMPtr<nsIDOMNavigatorSystemMessages> mMessagesManager;
+  nsTArray<nsRefPtr<nsDOMDeviceStorage> > mDeviceStorageStores;
   nsWeakPtr mWindow;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 nsresult NS_GetNavigatorUserAgent(nsAString& aUserAgent);
 nsresult NS_GetNavigatorPlatform(nsAString& aPlatform);
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -135,16 +135,17 @@
 #include "nsIDOMHTMLOptionElement.h"
 #include "nsGenericElement.h"
 
 // Event related includes
 #include "nsEventListenerManager.h"
 #include "nsIDOMEventTarget.h"
 
 // CSS related includes
+#include "nsCSSRules.h"
 #include "nsIDOMStyleSheet.h"
 #include "nsIDOMStyleSheetList.h"
 #include "nsIDOMCSSStyleDeclaration.h"
 #include "nsIDOMCSSRule.h"
 #include "nsICSSRuleList.h"
 #include "nsIDOMRect.h"
 #include "nsIDOMRGBColor.h"
 #include "nsIDOMNSRGBAColor.h"
@@ -284,16 +285,17 @@
 #endif
 #include "nsIDOMProgressEvent.h"
 #include "nsIDOMCSS2Properties.h"
 #include "nsIDOMCSSCharsetRule.h"
 #include "nsIDOMCSSImportRule.h"
 #include "nsIDOMCSSMediaRule.h"
 #include "nsIDOMCSSFontFaceRule.h"
 #include "nsIDOMCSSMozDocumentRule.h"
+#include "nsIDOMCSSSupportsRule.h"
 #include "nsIDOMMozCSSKeyframeRule.h"
 #include "nsIDOMMozCSSKeyframesRule.h"
 #include "nsIDOMCSSPrimitiveValue.h"
 #include "nsIDOMCSSStyleRule.h"
 #include "nsIDOMCSSStyleSheet.h"
 #include "nsDOMCSSValueList.h"
 #define MOZ_GENERATED_EVENTS_INCLUDES
 #include "GeneratedEvents.h"
@@ -1083,16 +1085,19 @@ static nsDOMClassInfoData sClassInfoData
 
   NS_DEFINE_CLASSINFO_DATA(TreeColumns, nsTreeColumnsSH,
                            ARRAY_SCRIPTABLE_FLAGS)
 #endif
 
   NS_DEFINE_CLASSINFO_DATA(CSSMozDocumentRule, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
+  NS_DEFINE_CLASSINFO_DATA(CSSSupportsRule, nsDOMGenericSH,
+                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
+
   NS_DEFINE_CLASSINFO_DATA(BeforeUnloadEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   // SVG document
   NS_DEFINE_CLASSINFO_DATA(SVGDocument, nsDocumentSH,
                            DOCUMENT_SCRIPTABLE_FLAGS)
 
   // SVG element classes
@@ -1426,18 +1431,18 @@ static nsDOMClassInfoData sClassInfoData
                            WINDOW_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(DataContainerEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(MessageEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
-  NS_DEFINE_CLASSINFO_DATA(DeviceStorage, nsDOMGenericSH,
-                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
+  NS_DEFINE_CLASSINFO_DATA(DeviceStorage, nsEventTargetSH,
+                           EVENTTARGET_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(DeviceStorageCursor, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(GeoGeolocation, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   
   NS_DEFINE_CLASSINFO_DATA(GeoPosition, nsDOMGenericSH,
@@ -1619,18 +1624,18 @@ static nsDOMClassInfoData sClassInfoData
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(IDBVersionChangeEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(IDBOpenDBRequest, IDBEventTargetSH,
                            IDBEVENTTARGET_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(Touch, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
-  NS_DEFINE_CLASSINFO_DATA(TouchList, nsDOMGenericSH,
-                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
+  NS_DEFINE_CLASSINFO_DATA(TouchList, nsDOMTouchListSH,
+                           ARRAY_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(TouchEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(MozCSSKeyframeRule, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(MozCSSKeyframesRule, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
@@ -3166,16 +3171,20 @@ nsDOMClassInfo::Init()
     DOM_CLASSINFO_MAP_ENTRY(nsITreeColumns)
   DOM_CLASSINFO_MAP_END
 #endif
 
   DOM_CLASSINFO_MAP_BEGIN(CSSMozDocumentRule, nsIDOMCSSMozDocumentRule)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSMozDocumentRule)
   DOM_CLASSINFO_MAP_END
 
+  DOM_CLASSINFO_MAP_BEGIN(CSSSupportsRule, nsIDOMCSSSupportsRule)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSSupportsRule)
+  DOM_CLASSINFO_MAP_END
+
   DOM_CLASSINFO_MAP_BEGIN(BeforeUnloadEvent, nsIDOMBeforeUnloadEvent)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMBeforeUnloadEvent)
     DOM_CLASSINFO_EVENT_MAP_ENTRIES
   DOM_CLASSINFO_MAP_END
 
 #define DOM_CLASSINFO_SVG_ELEMENT_MAP_ENTRIES                           \
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)                          \
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMSVGElement)                           \
@@ -4022,16 +4031,17 @@ nsDOMClassInfo::Init()
 
   DOM_CLASSINFO_MAP_BEGIN(MessageEvent, nsIDOMMessageEvent)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMMessageEvent)
     DOM_CLASSINFO_EVENT_MAP_ENTRIES
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(DeviceStorage, nsIDOMDeviceStorage)
      DOM_CLASSINFO_MAP_ENTRY(nsIDOMDeviceStorage)
+     DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(DeviceStorageCursor, nsIDOMDeviceStorageCursor)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMDeviceStorageCursor)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMDOMRequest)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
   DOM_CLASSINFO_MAP_END
 
@@ -4863,16 +4873,24 @@ nsDOMClassInfo::NewResolve(nsIXPConnectW
 {
   if (id == sConstructor_id && !(flags & JSRESOLVE_ASSIGNING)) {
     return ResolveConstructor(cx, obj, objp);
   }
 
   return NS_OK;
 }
 
+nsISupports*
+nsDOMTouchListSH::GetItemAt(nsISupports *aNative, PRUint32 aIndex,
+                            nsWrapperCache **aCache, nsresult *aResult)
+{
+  nsDOMTouchList* list = static_cast<nsDOMTouchList*>(aNative);
+  return list->GetItemAt(aIndex);
+}
+
 NS_IMETHODIMP
 nsDOMClassInfo::Convert(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                         JSObject *obj, PRUint32 type, jsval *vp,
                         bool *_retval)
 {
   NS_WARNING("nsDOMClassInfo::Convert Don't call me!");
 
   return NS_ERROR_UNEXPECTED;
@@ -5810,26 +5828,22 @@ DefineInterfaceConstants(JSContext *cx, 
       case nsXPTType::T_I16:
       case nsXPTType::T_U16:
       {
         v = INT_TO_JSVAL(c->GetValue()->val.u16);
         break;
       }
       case nsXPTType::T_I32:
       {
-        if (!JS_NewNumberValue(cx, c->GetValue()->val.i32, &v)) {
-          return NS_ERROR_UNEXPECTED;
-        }
+        v = JS_NumberValue(c->GetValue()->val.i32);
         break;
       }
       case nsXPTType::T_U32:
       {
-        if (!JS_NewNumberValue(cx, c->GetValue()->val.u32, &v)) {
-          return NS_ERROR_UNEXPECTED;
-        }
+        v = JS_NumberValue(c->GetValue()->val.u32);
         break;
       }
       default:
       {
 #ifdef DEBUG
         NS_ERROR("Non-numeric constant found in interface.");
 #endif
         continue;
@@ -6692,16 +6706,23 @@ ConstructorEnabled(const nsGlobalNameStr
 
   // For now don't expose server events unless user has explicitly enabled them
   if (aStruct->mDOMClassInfoID == eDOMClassInfo_EventSource_id) {
     if (!nsEventSource::PrefEnabled()) {
       return false;
     }
   }
 
+  // Don't expose CSSSupportsRule unless @supports processing is enabled.
+  if (aStruct->mDOMClassInfoID == eDOMClassInfo_CSSSupportsRule_id) {
+    if (!CSSSupportsRule::PrefEnabled()) {
+      return false;
+    }
+  }
+
   return true;
 }
 
 // static
 nsresult
 nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
                           JSObject *obj, jsid id, bool *did_resolve)
 {
--- a/dom/base/nsDOMClassInfo.h
+++ b/dom/base/nsDOMClassInfo.h
@@ -1211,16 +1211,37 @@ protected:
 
 public:
   static nsIClassInfo *doCreate(nsDOMClassInfoData* aData)
   {
     return new nsCSSRuleListSH(aData);
   }
 };
 
+class nsDOMTouchListSH : public nsArraySH
+{
+  protected:
+  nsDOMTouchListSH(nsDOMClassInfoData* aData) : nsArraySH(aData)
+  {
+  }
+
+  virtual ~nsDOMTouchListSH()
+  {
+  }
+
+  virtual nsISupports* GetItemAt(nsISupports *aNative, PRUint32 aIndex,
+                                 nsWrapperCache **aCache, nsresult *aResult);
+
+  public:
+  static nsIClassInfo* doCreate(nsDOMClassInfoData* aData)
+  {
+    return new nsDOMTouchListSH(aData);
+  }
+};
+
 #ifdef MOZ_XUL
 // TreeColumns helper
 
 class nsTreeColumnsSH : public nsNamedArraySH
 {
 protected:
   nsTreeColumnsSH(nsDOMClassInfoData* aData) : nsNamedArraySH(aData)
   {
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -188,16 +188,17 @@ DOMCI_CLASS(XULTreeBuilder)
 DOMCI_CLASS(DOMStringList)
 
 #ifdef MOZ_XUL
 DOMCI_CLASS(TreeColumn)
 DOMCI_CLASS(TreeColumns)
 #endif
 
 DOMCI_CLASS(CSSMozDocumentRule)
+DOMCI_CLASS(CSSSupportsRule)
 
 DOMCI_CLASS(BeforeUnloadEvent)
 
 // The SVG document
 DOMCI_CLASS(SVGDocument)
 
 // SVG element classes
 DOMCI_CLASS(SVGAElement)
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -8350,17 +8350,18 @@ nsGlobalWindow::GetIndexedDB(nsIIDBFacto
       if (isThirdParty) {
         NS_WARNING("IndexedDB is not permitted in a third-party window.");
         *_retval = nullptr;
         return NS_OK;
       }
     }
 
     // This may be null if being created from a file.
-    rv = indexedDB::IDBFactory::Create(this, getter_AddRefs(mIndexedDB));
+    rv = indexedDB::IDBFactory::Create(this, nullptr,
+                                       getter_AddRefs(mIndexedDB));
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   nsCOMPtr<nsIIDBFactory> request(mIndexedDB);
   request.forget(_retval);
   return NS_OK;
 }
 
--- a/dom/base/nsHistory.cpp
+++ b/dom/base/nsHistory.cpp
@@ -234,18 +234,18 @@ nsHistory::Go(PRInt32 aDelta)
   NS_ENSURE_TRUE(session_history, NS_ERROR_FAILURE);
 
   // QI SHistory to nsIWebNavigation
   nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(session_history));
   NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
 
   PRInt32 curIndex=-1;
   PRInt32 len = 0;
-  nsresult rv = session_history->GetIndex(&curIndex);
-  rv = session_history->GetCount(&len);
+  session_history->GetIndex(&curIndex);
+  session_history->GetCount(&len);
 
   PRInt32 index = curIndex + aDelta;
   if (index > -1  &&  index < len)
     webnav->GotoIndex(index);
 
   // We always want to return a NS_OK, since returning errors 
   // from GotoIndex() can lead to exceptions and a possible leak
   // of history length
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -2407,31 +2407,29 @@ nsJSContext::AddSupportsPrimitiveTojsval
     case nsISupportsPrimitive::TYPE_FLOAT : {
       nsCOMPtr<nsISupportsFloat> p(do_QueryInterface(argPrimitive));
       NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
 
       float data;
 
       p->GetData(&data);
 
-      JSBool ok = ::JS_NewNumberValue(cx, data, aArgv);
-      NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
+      *aArgv = ::JS_NumberValue(data);
 
       break;
     }
     case nsISupportsPrimitive::TYPE_DOUBLE : {
       nsCOMPtr<nsISupportsDouble> p(do_QueryInterface(argPrimitive));
       NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
 
       double data;
 
       p->GetData(&data);
 
-      JSBool ok = ::JS_NewNumberValue(cx, data, aArgv);
-      NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
+      *aArgv = ::JS_NumberValue(data);
 
       break;
     }
     case nsISupportsPrimitive::TYPE_INTERFACE_POINTER : {
       nsCOMPtr<nsISupportsInterfacePointer> p(do_QueryInterface(argPrimitive));
       NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
 
       nsCOMPtr<nsISupports> data;
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -2051,19 +2051,19 @@ for (uint32_t i = 0; i < length; ++i) {
                 return conversionCode
 
             if isinstance(defaultValue, IDLNullValue):
                 assert(type.nullable())
                 return handleDefault(conversionCode,
                                      "%s.SetNull()" % varName)
             return handleDefault(
                 conversionCode,
-                ("static const PRUnichar data[] = { %s, 0 };\n"
+                ("static const PRUnichar data[] = { %s };\n"
                  "%s.SetData(data, ArrayLength(data) - 1)" %
-                 (", ".join("'" + char + "'" for char in defaultValue.value),
+                 (", ".join(["'" + char + "'" for char in defaultValue.value] + ["0"]),
                   varName)))
 
         if isMember:
             # We have to make a copy, because our jsval may well not
             # live as long as our string needs to.
             declType = CGGeneric("nsString")
             return (
                 "{\n"
@@ -2581,17 +2581,17 @@ if (!%(resultStr)s) {
     if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16,
                IDLType.Tags.uint16, IDLType.Tags.int32]:
         return setValue("INT_TO_JSVAL(int32_t(%s))" % result)
 
     elif tag in [IDLType.Tags.int64, IDLType.Tags.uint64, IDLType.Tags.float,
                  IDLType.Tags.double]:
         # XXXbz will cast to double do the "even significand" thing that webidl
         # calls for for 64-bit ints?  Do we care?
-        return wrapAndSetPtr("JS_NewNumberValue(cx, double(%s), ${jsvalPtr})" % result)
+        return setValue("JS_NumberValue(double(%s))" % result)
 
     elif tag == IDLType.Tags.uint32:
         return setValue("UINT_TO_JSVAL(%s)" % result)
 
     elif tag == IDLType.Tags.bool:
         return setValue("BOOLEAN_TO_JSVAL(%s)" % result)
 
     else:
--- a/dom/bindings/test/TestCodeGen.webidl
+++ b/dom/bindings/test/TestCodeGen.webidl
@@ -352,16 +352,17 @@ DiamondBranch1B implements DiamondImplem
 
 dictionary Dict : ParentDict {
   TestEnum someEnum;
   long x;
   long a;
   long b = 8;
   long z = 9;
   DOMString str;
+  DOMString empty = "";
   TestEnum otherEnum = "b";
   DOMString otherStr = "def";
   DOMString? yetAnotherStr = null;
 };
 
 dictionary ParentDict : GrandparentDict {
   long c = 5;
   TestInterface someInterface;
--- a/dom/bluetooth/BluetoothAdapter.cpp
+++ b/dom/bluetooth/BluetoothAdapter.cpp
@@ -149,19 +149,19 @@ BluetoothAdapter::SetPropertyByValue(con
     }
   } else if (name.EqualsLiteral("Devices")) {
     mDeviceAddresses = value.get_ArrayOfnsString();
     nsresult rv;
     nsIScriptContext* sc = GetContextForEventHandlers(&rv);
     if (sc) {
       rv =
         StringArrayToJSArray(sc->GetNativeContext(),
-                             sc->GetNativeGlobal(), mUuids, &mJsDeviceAddresses);
+                             sc->GetNativeGlobal(), mDeviceAddresses, &mJsDeviceAddresses);
       if (NS_FAILED(rv)) {
-        NS_WARNING("Cannot set JS Devices Addresses object!");
+        NS_WARNING("Cannot set JS Device Addresses object!");
         return;
       }
       Root();
     } else {
       NS_WARNING("Could not get context!");
     }
   } else {
 #ifdef DEBUG
@@ -330,17 +330,17 @@ BluetoothAdapter::GetDiscoverableTimeout
 
 NS_IMETHODIMP
 BluetoothAdapter::GetDevices(JSContext* aCx, jsval* aDevices)
 {
   if (mJsDeviceAddresses) {
     aDevices->setObject(*mJsDeviceAddresses);
   }
   else {
-    NS_WARNING("UUIDs not yet set!\n");
+    NS_WARNING("Devices not yet set!\n");
     return NS_ERROR_FAILURE;
   }    
   return NS_OK;
 }
 
 nsresult
 BluetoothAdapter::GetUuids(JSContext* aCx, jsval* aValue)
 {
--- a/dom/browser-element/BrowserElementParent.js
+++ b/dom/browser-element/BrowserElementParent.js
@@ -129,16 +129,17 @@ BrowserElementParentFactory.prototype = 
 };
 
 function BrowserElementParent(frameLoader, hasRemoteFrame) {
   debug("Creating new BrowserElementParent object for " + frameLoader);
   this._domRequestCounter = 0;
   this._pendingDOMRequests = {};
   this._hasRemoteFrame = hasRemoteFrame;
 
+  this._frameLoader = frameLoader;
   this._frameElement = frameLoader.QueryInterface(Ci.nsIFrameLoader).ownerElement;
   if (!this._frameElement) {
     debug("No frame element?");
     return;
   }
 
   this._mm = frameLoader.messageManager;
 
@@ -173,16 +174,17 @@ function BrowserElementParent(frameLoade
   addMessageListener('got-can-go-back', this._gotDOMRequestResult);
   addMessageListener('got-can-go-forward', this._gotDOMRequestResult);
   addMessageListener('fullscreen-origin-change', this._remoteFullscreenOriginChange);
   addMessageListener('rollback-fullscreen', this._remoteFrameFullscreenReverted);
   addMessageListener('exit-fullscreen', this._exitFullscreen);
 
   let os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
   os.addObserver(this, 'ask-children-to-exit-fullscreen', /* ownsWeak = */ true);
+  os.addObserver(this, 'oop-frameloader-crashed', /* ownsWeak = */ true);
 
   function defineMethod(name, fn) {
     XPCNativeWrapper.unwrap(self._frameElement)[name] = function() {
       if (self._isAlive()) {
         return fn.apply(self, arguments);
       }
     };
   }
@@ -469,18 +471,36 @@ BrowserElementParent.prototype = {
     let origin = data.json;
     this._windowUtils.remoteFrameFullscreenChanged(this._frameElement, origin);
   },
 
   _remoteFrameFullscreenReverted: function(data) {
     this._windowUtils.remoteFrameFullscreenReverted();
   },
 
+  _fireFatalError: function() {
+    let evt = this._createEvent('error', {type: 'fatal'},
+                                /* cancelable = */ false);
+    this._frameElement.dispatchEvent(evt);
+  },
+
   observe: function(subject, topic, data) {
-    if (topic == 'ask-children-to-exit-fullscreen' &&
-        this._isAlive() &&
-        this._frameElement.ownerDocument == subject &&
-        this._hasRemoteFrame)
-      this._sendAsyncMsg('exit-fullscreen');
+    switch(topic) {
+    case 'oop-frameloader-crashed':
+      if (this._isAlive() && subject == this._frameLoader) {
+        this._fireFatalError();
+      }
+      break;
+    case 'ask-children-to-exit-fullscreen':
+      if (this._isAlive() &&
+          this._frameElement.ownerDocument == subject &&
+          this._hasRemoteFrame) {
+        this._sendAsyncMsg('exit-fullscreen');
+      }
+      break;
+    default:
+      debug('Unknown topic: ' + topic);
+      break;
+    };
   },
 };
 
 var NSGetFactory = XPCOMUtils.generateNSGetFactory([BrowserElementParentFactory]);
--- a/dom/camera/GonkCameraCapabilities.cpp
+++ b/dom/camera/GonkCameraCapabilities.cpp
@@ -45,22 +45,20 @@ static nsresult
 ParseZoomRatioItemAndAdd(JSContext* aCx, JSObject* aArray, PRUint32 aIndex, const char* aStart, char** aEnd)
 {
   if (!*aEnd) {
     // make 'aEnd' follow the same semantics as strchr().
     aEnd = nullptr;
   }
 
   double d = strtod(aStart, aEnd);
-  jsval v;
+  d /= 100;
 
-  d /= 100;
-  if (!JS_NewNumberValue(aCx, d, &v)) {
-    return NS_ERROR_FAILURE;
-  }
+  jsval v = JS_NumberValue(d);
+
   if (!JS_SetElement(aCx, aArray, aIndex, &v)) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 static nsresult
new file mode 100644
--- /dev/null
+++ b/dom/devicestorage/DeviceStorage.h
@@ -0,0 +1,73 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef DeviceStorage_h
+#define DeviceStorage_h
+
+#include "nsIDOMDeviceStorage.h"
+#include "nsIFile.h"
+#include "nsIObserver.h"
+#include "nsDOMEventTargetHelper.h"
+
+class nsDOMDeviceStorage MOZ_FINAL
+  : public nsIDOMDeviceStorage
+  , public nsIFileUpdateListener
+  , public nsDOMEventTargetHelper
+  , public nsIObserver
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMDEVICESTORAGE
+
+  NS_DECL_NSIFILEUPDATELISTENER
+  NS_DECL_NSIOBSERVER
+  NS_DECL_NSIDOMEVENTTARGET
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsDOMDeviceStorage, nsDOMEventTargetHelper)
+  NS_DECL_EVENT_HANDLER(change)
+
+  nsDOMDeviceStorage();
+
+  nsresult Init(nsPIDOMWindow* aWindow, const nsAString &aType);
+
+  void SetRootFileForType(const nsAString& aType);
+
+  static void CreateDeviceStoragesFor(nsPIDOMWindow* aWin,
+                                      const nsAString &aType,
+                                      nsDOMDeviceStorage** aStore);
+  void Shutdown();
+
+private:
+  ~nsDOMDeviceStorage();
+
+  nsresult GetInternal(const JS::Value & aName,
+                       JSContext* aCx,
+                       nsIDOMDOMRequest** aRetval,
+                       bool aEditable);
+
+  nsresult EnumerateInternal(const JS::Value & aName,
+                             const JS::Value & aOptions,
+                             JSContext* aCx,
+                             PRUint8 aArgc, 
+                             bool aEditable, 
+                             nsIDOMDeviceStorageCursor** aRetval);
+
+  PRInt32 mStorageType;
+  nsCOMPtr<nsIFile> mFile;
+
+  nsCOMPtr<nsIURI> mURI;
+
+  friend class WatchFileEvent;
+  friend class DeviceStorageRequest;
+
+  bool  mIsWatchingFile;
+
+  // nsIDOMDeviceStorage.type
+  enum {
+      DEVICE_STORAGE_TYPE_DEFAULT = 0,
+      DEVICE_STORAGE_TYPE_SHARED,
+      DEVICE_STORAGE_TYPE_EXTERNAL,
+  };
+};
+
+#endif
--- a/dom/devicestorage/Makefile.in
+++ b/dom/devicestorage/Makefile.in
@@ -26,17 +26,17 @@ EXPORTS_mozilla/dom/devicestorage = \
 
 CPPSRCS		= \
 		nsDeviceStorage.cpp \
 		DeviceStorageRequestParent.cpp \
 		DeviceStorageRequestChild.cpp \
 		$(NULL)
 
 EXPORTS         = \
-		nsDeviceStorage.h \
+		DeviceStorage.h \
 		$(NULL)
 
 LOCAL_INCLUDES = \
 		-I$(topsrcdir)/dom/base \
 		-I$(topsrcdir)/dom/ipc \
 		-I$(topsrcdir)/content/base/src \
 		-I$(topsrcdir)/content/events/src \
 		$(NULL)
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -1,13 +1,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/PBrowserChild.h"
+#include "mozilla/dom/devicestorage/PDeviceStorageRequestChild.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/dom/PContentPermissionRequestChild.h"
+
 #include "nsDeviceStorage.h"
+
+#include "nsDOMEvent.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIFile.h"
 #include "nsIDirectoryEnumerator.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsIDOMFile.h"
 #include "nsDOMBlobBuilder.h"
 #include "nsNetUtil.h"
@@ -16,16 +24,21 @@
 #include "mozilla/Preferences.h"
 #include "nsJSUtils.h"
 #include "DictionaryHelpers.h"
 #include "mozilla/Attributes.h"
 #include "nsContentUtils.h"
 #include "nsXULAppAPI.h"
 #include "TabChild.h"
 #include "DeviceStorageRequestChild.h"
+#include "nsIDOMDeviceStorageChangeEvent.h"
+#include "nsCRT.h"
+#include "mozilla/Services.h"
+#include "nsIObserverService.h"
+#include "GeneratedEvents.h"
 
 // Microsoft's API Name hackery sucks
 #undef CreateEvent
 
 #ifdef MOZ_WIDGET_GONK
 #include "nsIVolumeService.h"
 #endif
 
@@ -85,18 +98,18 @@ DeviceStorageFile::IsSafePath()
    }
   // split on /.  if any token is "", ., or .., return false.
   NS_ConvertUTF16toUTF8 cname(mPath);
   char* buffer = cname.BeginWriting();
   const char* token;
 
   while ((token = nsCRT::strtok(buffer, "/", &buffer))) {
     if (PL_strcmp(token, "") == 0 ||
-	PL_strcmp(token, ".") == 0 ||
-	PL_strcmp(token, "..") == 0 ) {
+        PL_strcmp(token, ".") == 0 ||
+        PL_strcmp(token, "..") == 0 ) {
       return false;
     }
   }
   return true;
 }
 
 void
 DeviceStorageFile::NormalizeFilePath() {
@@ -148,18 +161,18 @@ DeviceStorageFile::Write(nsIDOMBlob* aBl
   NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), mFile);
 
   if (!outputStream) {
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIOutputStream> bufferedOutputStream;
   NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream),
-			     outputStream,
-			     4096*4);
+                             outputStream,
+                             4096*4);
 
   if (!bufferedOutputStream) {
     return NS_ERROR_FAILURE;
   }
 
   PRUint32 wrote;
   bufferedOutputStream->WriteFrom(stream, bufSize, &wrote);
   bufferedOutputStream->Close();
@@ -192,28 +205,31 @@ DeviceStorageFile::Write(InfallibleTArra
   if (aBits.Length() != wrote) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 void
 DeviceStorageFile::CollectFiles(nsTArray<nsRefPtr<DeviceStorageFile> > &aFiles,
-				PRUint64 aSince)
+                                PRUint64 aSince)
 {
   nsString rootPath;
-  mFile->GetPath(rootPath);
+  nsresult rv = mFile->GetPath(rootPath);
+  if (NS_FAILED(rv)) {
+    return;
+  }
 
   return collectFilesInternal(aFiles, aSince, rootPath);
 }
 
 void
 DeviceStorageFile::collectFilesInternal(nsTArray<nsRefPtr<DeviceStorageFile> > &aFiles,
-					PRUint64 aSince,
-					nsAString& aRootPath)
+                                        PRUint64 aSince,
+                                        nsAString& aRootPath)
 {
   nsCOMPtr<nsISimpleEnumerator> e;
   mFile->GetDirectoryEntries(getter_AddRefs(e));
 
   if (!e) {
     return;
   }
 
@@ -231,17 +247,20 @@ DeviceStorageFile::collectFilesInternal(
 
     bool isDir;
     f->IsDirectory(&isDir);
 
     bool isFile;
     f->IsFile(&isFile);
 
     nsString fullpath;
-    f->GetPath(fullpath);
+    nsresult rv = f->GetPath(fullpath);
+    if (NS_FAILED(rv)) {
+      continue;
+    }
 
     if (!StringBeginsWith(fullpath, aRootPath)) {
       NS_ERROR("collectFiles returned a path that does not belong!");
       continue;
     }
 
     nsAString::size_type len = aRootPath.Length() + 1; // +1 for the trailing /
     nsDependentSubstring newPath = Substring(fullpath, len);
@@ -256,117 +275,94 @@ DeviceStorageFile::collectFilesInternal(
       aFiles.AppendElement(dsf);
     }
   }
 }
 
 NS_IMPL_THREADSAFE_ISUPPORTS0(DeviceStorageFile)
 
 
-// TODO - eventually, we will want to factor this method
-// out into different system specific subclasses (or
-// something)
-PRInt32
-nsDOMDeviceStorage::SetRootFileForType(const nsAString& aType, const PRInt32 aIndex)
+void
+nsDOMDeviceStorage::SetRootFileForType(const nsAString& aType)
 {
-  PRInt32 typeResult = DEVICE_STORAGE_TYPE_DEFAULT;
-
   nsCOMPtr<nsIFile> f;
   nsCOMPtr<nsIProperties> dirService = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
   NS_ASSERTION(dirService, "Must have directory service");
 
 #ifdef MOZ_WIDGET_GONK
   mFile = nullptr;
 
   nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
   if (!vs) {
-    return typeResult;
+    return;
   }
 
   nsCOMPtr<nsIVolume> v;
   vs->GetVolumeByPath(NS_LITERAL_STRING("/sdcard"), getter_AddRefs(v));
   
   if (!v) {
-    return typeResult;
+    return;
   }
 
   PRInt32 state;
   v->GetState(&state);
 
   if (state != nsIVolume::STATE_MOUNTED) {
-    return typeResult;
+    return;
   }
 #endif
 
   // Picture directory
   if (aType.Equals(NS_LITERAL_STRING("pictures"))) {
 #ifdef MOZ_WIDGET_GONK
-    if (aIndex == 0) {
-      NS_NewLocalFile(NS_LITERAL_STRING("/sdcard/DCIM"), false, getter_AddRefs(f));
-    }
+    NS_NewLocalFile(NS_LITERAL_STRING("/sdcard/DCIM"), false, getter_AddRefs(f));
 #elif defined (MOZ_WIDGET_COCOA)
-    if (aIndex == 0) {
-      dirService->Get(NS_OSX_PICTURE_DOCUMENTS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
-    }
+    dirService->Get(NS_OSX_PICTURE_DOCUMENTS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
 #elif defined (XP_UNIX)
-    if (aIndex == 0) {
-      dirService->Get(NS_UNIX_XDG_PICTURES_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
-    }
+    dirService->Get(NS_UNIX_XDG_PICTURES_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
 #endif
   }
 
   // Video directory
   if (aType.Equals(NS_LITERAL_STRING("videos"))) {
 #ifdef MOZ_WIDGET_GONK
-    if (aIndex == 0) {
-      NS_NewLocalFile(NS_LITERAL_STRING("/sdcard/Movies"), false, getter_AddRefs(f));
-    }
+    NS_NewLocalFile(NS_LITERAL_STRING("/sdcard/Movies"), false, getter_AddRefs(f));
 #elif defined (MOZ_WIDGET_COCOA)
-    if (aIndex == 0) {
-      dirService->Get(NS_OSX_MOVIE_DOCUMENTS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
-    }
+    dirService->Get(NS_OSX_MOVIE_DOCUMENTS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
 #elif defined (XP_UNIX)
-    if (aIndex == 0) {
-      dirService->Get(NS_UNIX_XDG_VIDEOS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
-    }
+    dirService->Get(NS_UNIX_XDG_VIDEOS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
 #endif
   }
 
   // Music directory
   if (aType.Equals(NS_LITERAL_STRING("music"))) {
 #ifdef MOZ_WIDGET_GONK
-    if (aIndex == 0) {
-      NS_NewLocalFile(NS_LITERAL_STRING("/sdcard/Music"), false, getter_AddRefs(f));
-    }
+    NS_NewLocalFile(NS_LITERAL_STRING("/sdcard/Music"), false, getter_AddRefs(f));
 #elif defined (MOZ_WIDGET_COCOA)
-    if (aIndex == 0) {
-      dirService->Get(NS_OSX_MUSIC_DOCUMENTS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
-    }
+    dirService->Get(NS_OSX_MUSIC_DOCUMENTS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
 #elif defined (XP_UNIX)
-    if (aIndex == 0) {
-      dirService->Get(NS_UNIX_XDG_MUSIC_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
-    }
+    dirService->Get(NS_UNIX_XDG_MUSIC_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")) && aIndex == 0) {
+    if (aType.Equals(NS_LITERAL_STRING("testing"))) {
       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();
       }
     }
   } 
 
   mFile = f;
-  return typeResult;
 }
 
 static jsval nsIFileToJsval(nsPIDOMWindow* aWindow, DeviceStorageFile* aFile)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aWindow, "Null Window");
 
   if (aFile->mEditable) {
@@ -483,21 +479,21 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(DeviceStorageCursorRequest)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(DeviceStorageCursorRequest)
 NS_IMPL_CYCLE_COLLECTION_CLASS(DeviceStorageCursorRequest)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DeviceStorageCursorRequest)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCursor)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCursor)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DeviceStorageCursorRequest)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mCursor, nsIDOMDeviceStorageCursor)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mCursor, nsIDOMDeviceStorageCursor)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 
 class PostErrorEvent : public nsRunnable
 {
 public:
   PostErrorEvent(nsRefPtr<DOMRequest>& aRequest, const char* aMessage, DeviceStorageFile* aFile)
   {
@@ -702,17 +698,22 @@ nsDOMDeviceStorageCursor::Allow()
                                                  mFile);
     NS_DispatchToMainThread(r);
     return NS_OK;
   }
 
   if (XRE_GetProcessType() != GeckoProcessType_Default) {
 
     nsString fullpath;
-    mFile->mFile->GetPath(fullpath);
+    nsresult rv = mFile->mFile->GetPath(fullpath);
+
+    if (NS_FAILED(rv)) {
+      // just do nothing
+      return NS_OK;
+    }
 
     PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(this, mFile);
     DeviceStorageEnumerationParams params(fullpath, mSince);
     ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
     return NS_OK;
   }
 
   nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
@@ -822,24 +823,24 @@ public:
     NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
     nsresult rv = mFile->Write(mBlob);
 
     if (NS_FAILED(rv)) {
       mFile->mFile->Remove(false);
 
       nsCOMPtr<PostErrorEvent> event = new PostErrorEvent(mRequest,
-							  POST_ERROR_EVENT_UNKNOWN,
-							  mFile);
+                                                          POST_ERROR_EVENT_UNKNOWN,
+                                                          mFile);
       NS_DispatchToMainThread(event);
       return NS_OK;
     }
 
     nsCOMPtr<PostResultEvent> event = new PostResultEvent(mRequest,
-							  mFile->mPath);
+                                                          mFile->mPath);
     NS_DispatchToMainThread(event);
 
     return NS_OK;
   }
 
 private:
   nsCOMPtr<nsIDOMBlob> mBlob;
   nsRefPtr<DeviceStorageFile> mFile;
@@ -922,51 +923,69 @@ private:
 
 class DeviceStorageRequest MOZ_FINAL
   : public nsIContentPermissionRequest
   , public nsIRunnable
   , public PCOMContentPermissionRequestChild
 {
 public:
 
-    enum {
+    enum DeviceStorageRequestType {
         DEVICE_STORAGE_REQUEST_READ,
         DEVICE_STORAGE_REQUEST_WRITE,
-        DEVICE_STORAGE_REQUEST_DELETE
+        DEVICE_STORAGE_REQUEST_DELETE,
+        DEVICE_STORAGE_REQUEST_WATCH
     };
-    DeviceStorageRequest(const PRInt32 aRequestType,
+
+    DeviceStorageRequest(const DeviceStorageRequestType aRequestType,
+                         nsPIDOMWindow *aWindow,
+                         nsIURI *aURI,
+                         DeviceStorageFile *aFile,
+                         DOMRequest* aRequest,
+                         nsDOMDeviceStorage *aDeviceStorage,
+                         nsIDOMEventListener *aListener)
+      : mRequestType(aRequestType)
+      , mWindow(aWindow)
+      , mURI(aURI)
+      , mFile(aFile)
+      , mRequest(aRequest)
+      , mDeviceStorage(aDeviceStorage)
+      , mListener(aListener) {}  
+
+    DeviceStorageRequest(const DeviceStorageRequestType aRequestType,
                          nsPIDOMWindow *aWindow,
                          nsIURI *aURI,
                          DeviceStorageFile *aFile,
                          DOMRequest* aRequest,
                          nsIDOMBlob *aBlob = nullptr)
-        : mRequestType(aRequestType)
-        , mWindow(aWindow)
-        , mURI(aURI)
-        , mFile(aFile)
-        , mRequest(aRequest)
-        , mBlob(aBlob) {}
+      : mRequestType(aRequestType)
+      , mWindow(aWindow)
+      , mURI(aURI)
+      , mFile(aFile)
+      , mRequest(aRequest)
+      , mBlob(aBlob) {}
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(DeviceStorageRequest, nsIContentPermissionRequest)
 
   NS_IMETHOD Run() {
 
     if (mozilla::Preferences::GetBool("device.storage.prompt.testing", false)) {
       Allow();
       return NS_OK;
     }
 
     if (XRE_GetProcessType() == GeckoProcessType_Content) {
 
       // because owner implements nsITabChild, we can assume that it is
       // the one and only TabChild.
       TabChild* child = GetTabChildFrom(mWindow->GetDocShell());
-      if (!child)
+      if (!child) {
         return NS_OK;
+      }
 
       // Retain a reference so the object isn't deleted without IPDL's knowledge.
       // Corresponding release occurs in DeallocPContentPermissionRequest.
       AddRef();
 
       nsCString type = NS_LITERAL_CSTRING("device-storage");
       child->SendPContentPermissionRequestConstructor(this, type, IPC::URI(mURI));
 
@@ -1018,71 +1037,98 @@ public:
   {
     nsCOMPtr<nsIRunnable> r;
 
     if (!mRequest) {
       return NS_ERROR_FAILURE;
     }
 
     nsString fullpath;
-    mFile->mFile->GetPath(fullpath);
+    nsresult rv = mFile->mFile->GetPath(fullpath);
+
+    if (NS_FAILED(rv)) {
+      // just do nothing
+      return NS_OK;
+    }
 
     switch(mRequestType) {
       case DEVICE_STORAGE_REQUEST_WRITE:
       {
         if (!mBlob) {
           return NS_ERROR_FAILURE;
         }
 
-	if (XRE_GetProcessType() != GeckoProcessType_Default) {
-	  PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile);
+        if (XRE_GetProcessType() != GeckoProcessType_Default) {
+          PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile);
 
-	  nsCOMPtr<nsIInputStream> stream;
-	  mBlob->GetInternalStream(getter_AddRefs(stream));
+          nsCOMPtr<nsIInputStream> stream;
+          mBlob->GetInternalStream(getter_AddRefs(stream));
 
-	  InfallibleTArray<PRUint8> bits;
-	  PRUint32 bufSize, numRead;
+          InfallibleTArray<PRUint8> bits;
+          PRUint32 bufSize, numRead;
 
-	  stream->Available(&bufSize);
-	  bits.SetCapacity(bufSize);
+          stream->Available(&bufSize);
+          bits.SetCapacity(bufSize);
 
-	  void* buffer = (void*) bits.Elements();
+          void* buffer = (void*) bits.Elements();
 
-	  stream->Read((char*)buffer, bufSize, &numRead);
+          stream->Read((char*)buffer, bufSize, &numRead);
 
-	  DeviceStorageAddParams params(fullpath, bits);
-	  ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
-	  return NS_OK;
-	}
-	r = new WriteFileEvent(mBlob, mFile, mRequest);
+          DeviceStorageAddParams params(fullpath, bits);
+          ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
+          return NS_OK;
+        }
+        r = new WriteFileEvent(mBlob, mFile, mRequest);
         break;
       }
+
       case DEVICE_STORAGE_REQUEST_READ:
       {
-	if (XRE_GetProcessType() != GeckoProcessType_Default) {
-	  PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile);
-	  DeviceStorageGetParams params(fullpath);
-	  ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
-	  return NS_OK;
-	}
+        if (XRE_GetProcessType() != GeckoProcessType_Default) {
+          PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile);
+          DeviceStorageGetParams params(fullpath);
+          ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
+          return NS_OK;
+        }
 
         r = new ReadFileEvent(mFile, mRequest);
         break;
       }
+
       case DEVICE_STORAGE_REQUEST_DELETE:
       {
-	if (XRE_GetProcessType() != GeckoProcessType_Default) {
-	  PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile);
-	  DeviceStorageDeleteParams params(fullpath);
-	  ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
-	  return NS_OK;
-	}
+        if (XRE_GetProcessType() != GeckoProcessType_Default) {
+          PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile);
+          DeviceStorageDeleteParams params(fullpath);
+          ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
+          return NS_OK;
+        }
         r = new DeleteFileEvent(mFile, mRequest);
         break;
       }
+
+      case DEVICE_STORAGE_REQUEST_WATCH:
+      {
+         if (XRE_GetProcessType() != GeckoProcessType_Default) {
+           nsString fullpath;
+           mFile->mFile->GetPath(fullpath);
+           nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+           obs->AddObserver(mDeviceStorage, "file-watcher-update", false);
+           ContentChild::GetSingleton()->SendAddFileWatch(fullpath);
+         } else {
+           if (!mDeviceStorage->mIsWatchingFile) {
+
+             //TODO
+
+             mFile->mFile->Watch(mDeviceStorage);
+             mDeviceStorage->mIsWatchingFile = true;
+           }
+         }
+        return NS_OK;
+      }
     }
 
     if (r) {
       nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
       NS_ASSERTION(target, "Must have stream transport service");
       target->Dispatch(r, NS_DISPATCH_NORMAL);
     }
     return NS_OK;
@@ -1107,70 +1153,83 @@ public:
 private:
   PRInt32 mRequestType;
   nsCOMPtr<nsPIDOMWindow> mWindow;
   nsCOMPtr<nsIURI> mURI;
   nsRefPtr<DeviceStorageFile> mFile;
 
   nsRefPtr<DOMRequest> mRequest;
   nsCOMPtr<nsIDOMBlob> mBlob;
+  nsRefPtr<nsDOMDeviceStorage> mDeviceStorage;
+  nsCOMPtr<nsIDOMEventListener> mListener;
 };
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeviceStorageRequest)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
   NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
   NS_INTERFACE_MAP_ENTRY(nsIRunnable)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(DeviceStorageRequest)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(DeviceStorageRequest)
 NS_IMPL_CYCLE_COLLECTION_CLASS(DeviceStorageRequest)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DeviceStorageRequest)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mRequest)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mWindow)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mBlob)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDeviceStorage)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mListener)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DeviceStorageRequest)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mRequest, nsIDOMDOMRequest)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mWindow, nsPIDOMWindow)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mBlob, nsIDOMBlob)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mDeviceStorage, nsIDOMDeviceStorage)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mListener, nsIDOMEventListener)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
+NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMDeviceStorage)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMDeviceStorage, nsDOMEventTargetHelper)
+  NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(change)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMDeviceStorage, nsDOMEventTargetHelper)
+  NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(change)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
 DOMCI_DATA(DeviceStorage, nsDOMDeviceStorage)
 
-NS_INTERFACE_MAP_BEGIN(nsDOMDeviceStorage)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMDeviceStorage)
   NS_INTERFACE_MAP_ENTRY(nsIDOMDeviceStorage)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMDeviceStorage)
+  NS_INTERFACE_MAP_ENTRY(nsIFileUpdateListener)
+  NS_INTERFACE_MAP_ENTRY(nsIObserver)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(DeviceStorage)
-NS_INTERFACE_MAP_END
+NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
-NS_IMPL_THREADSAFE_ADDREF(nsDOMDeviceStorage)
-NS_IMPL_THREADSAFE_RELEASE(nsDOMDeviceStorage)
+NS_IMPL_ADDREF_INHERITED(nsDOMDeviceStorage, nsDOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(nsDOMDeviceStorage, nsDOMEventTargetHelper)
 
 nsDOMDeviceStorage::nsDOMDeviceStorage()
- : mStorageType(DEVICE_STORAGE_TYPE_DEFAULT)
-{
-}
+  : mIsWatchingFile(false)
+{ }
 
 nsresult
-nsDOMDeviceStorage::Init(nsPIDOMWindow* aWindow, const nsAString &aType, const PRInt32 aIndex)
+nsDOMDeviceStorage::Init(nsPIDOMWindow* aWindow, const nsAString &aType)
 {
   NS_ASSERTION(aWindow, "Must have a content dom");
 
-  mStorageType = SetRootFileForType(aType, aIndex);
+  SetRootFileForType(aType);
   if (!mFile) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  mOwner = do_GetWeakReference(aWindow);
-  if (!mOwner) {
-    return NS_ERROR_FAILURE;
-  }
+  BindToOwner(aWindow);
 
   // Grab the uri of the document
   nsCOMPtr<nsIDOMDocument> domdoc;
   aWindow->GetDocument(getter_AddRefs(domdoc));
   nsCOMPtr<nsIDocument> doc = do_QueryInterface(domdoc);
   if (!doc) {
     return NS_ERROR_FAILURE;
   }
@@ -1178,60 +1237,44 @@ nsDOMDeviceStorage::Init(nsPIDOMWindow* 
   return NS_OK;
 }
 
 nsDOMDeviceStorage::~nsDOMDeviceStorage()
 {
 }
 
 void
+nsDOMDeviceStorage::Shutdown()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  if (mIsWatchingFile) {
+    if (XRE_GetProcessType() != GeckoProcessType_Default) {
+      nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+      obs->RemoveObserver(this, "file-watcher-update");
+
+      nsString fullpath;
+      mFile->GetPath(fullpath);
+      ContentChild::GetSingleton()->SendRemoveFileWatch(fullpath);
+    }
+    else {
+      mFile->Unwatch(this);
+    }
+  }
+}
+
+void
 nsDOMDeviceStorage::CreateDeviceStoragesFor(nsPIDOMWindow* aWin,
                                             const nsAString &aType,
-                                            nsIVariant** _retval)
+                                            nsDOMDeviceStorage** aStore)
 {
-  nsTArray<nsRefPtr<nsIDOMDeviceStorage> > stores;
-
-  PRInt32 index = 0;
-  while (1) {
-    nsresult rv;
-    nsRefPtr<nsDOMDeviceStorage> storage = new nsDOMDeviceStorage();
-    rv = storage->Init(aWin, aType, index++);
-    if (NS_FAILED(rv))
-      break;
-    stores.AppendElement(storage);
-  }
-
-  nsCOMPtr<nsIWritableVariant> result = do_CreateInstance("@mozilla.org/variant;1");
-  if (!result) {
-    return;
+  nsRefPtr<nsDOMDeviceStorage> storage = new nsDOMDeviceStorage();
+  if (NS_SUCCEEDED(storage->Init(aWin, aType))) {
+    NS_ADDREF(*aStore = storage);
   }
-
-  result->SetAsArray(nsIDataType::VTYPE_INTERFACE,
-                     &NS_GET_IID(nsIDOMDeviceStorage),
-                     stores.Length(),
-                     const_cast<void*>(static_cast<const void*>(stores.Elements())));
-  NS_ADDREF(*_retval = result);
-}
-
-NS_IMETHODIMP
-nsDOMDeviceStorage::GetType(nsAString & aType)
-{
-  switch(mStorageType) {
-    case DEVICE_STORAGE_TYPE_EXTERNAL:
-      aType.AssignLiteral("external");
-      break;
-    case DEVICE_STORAGE_TYPE_SHARED:
-      aType.AssignLiteral("shared");
-      break;
-    case DEVICE_STORAGE_TYPE_DEFAULT:
-    default:
-      aType.AssignLiteral("default");
-      break;
-  }
-  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorage::Add(nsIDOMBlob *aBlob, nsIDOMDOMRequest * *_retval)
 {
   // possible race here w/ unique filename
   char buffer[128];
   NS_MakeRandomString(buffer, 128);
@@ -1246,17 +1289,17 @@ NS_IMETHODIMP
 nsDOMDeviceStorage::AddNamed(nsIDOMBlob *aBlob,
                              const nsAString & aPath,
                              nsIDOMDOMRequest * *_retval)
 {
   // if the blob is null here, bail
   if (aBlob == nullptr)
     return NS_OK;
 
-  nsCOMPtr<nsPIDOMWindow> win = do_QueryReferent(mOwner);
+  nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     return NS_ERROR_UNEXPECTED;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
   NS_ADDREF(*_retval = request);
 
   nsCOMPtr<nsIRunnable> r;
@@ -1291,17 +1334,17 @@ nsDOMDeviceStorage::GetEditable(const JS
 }
 
 nsresult
 nsDOMDeviceStorage::GetInternal(const JS::Value & aPath,
                                 JSContext* aCx,
                                 nsIDOMDOMRequest * *_retval,
                                 bool aEditable)
 {
-  nsCOMPtr<nsPIDOMWindow> win = do_QueryReferent(mOwner);
+  nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     return NS_ERROR_UNEXPECTED;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
   NS_ADDREF(*_retval = request);
 
   nsCOMPtr<nsIRunnable> r;
@@ -1330,17 +1373,17 @@ nsDOMDeviceStorage::GetInternal(const JS
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorage::Delete(const JS::Value & aPath, JSContext* aCx, nsIDOMDOMRequest * *_retval)
 {
   nsCOMPtr<nsIRunnable> r;
 
-  nsCOMPtr<nsPIDOMWindow> win = do_QueryReferent(mOwner);
+  nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     return NS_ERROR_UNEXPECTED;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
   NS_ADDREF(*_retval = request);
 
   JSString* jsstr = JS_ValueToString(aCx, aPath);
@@ -1406,17 +1449,17 @@ ExtractDateFromOptions(JSContext* aCx, c
 nsresult
 nsDOMDeviceStorage::EnumerateInternal(const JS::Value & aName,
                                      const JS::Value & aOptions,
                                      JSContext* aCx,
                                      PRUint8 aArgc,
                                      bool aEditable,
                                      nsIDOMDeviceStorageCursor** aRetval)
 {
-  nsCOMPtr<nsPIDOMWindow> win = do_QueryReferent(mOwner);
+  nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win)
     return NS_ERROR_UNEXPECTED;
 
   PRTime since = 0;
   nsString path;
   path.SetIsVoid(true);
 
   if (aArgc > 0) {
@@ -1473,8 +1516,213 @@ nsDOMDeviceStorage::EnumerateInternal(co
 
   nsCOMPtr<nsIContentPermissionPrompt> prompt = do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
   if (prompt) {
     prompt->Prompt(r);
   }
 
   return NS_OK;
 }
+
+NS_IMETHODIMP
+nsDOMDeviceStorage::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *aData)
+{
+  // data strings will have the format of
+  //  reason:path
+  nsDependentString data(aData);
+
+  nsAString::const_iterator start, end;
+  nsAString::const_iterator colon;
+
+  data.BeginReading(start);
+  data.EndReading(end);
+  colon = end;
+
+  nsString reason;
+  nsString filepath;
+  if (!FindInReadable(NS_LITERAL_STRING(":"), start, colon)) {
+    return NS_OK;
+  }
+   
+  filepath = Substring(colon, end);
+  data.BeginReading(start);
+  reason = Substring(start, --colon);
+
+  nsCOMPtr<nsIFile> f;
+  NS_NewLocalFile(filepath, false, getter_AddRefs(f));
+ 
+  nsCString creason;
+  creason.AssignWithConversion(reason);
+  CopyUTF16toUTF8(reason, creason);
+
+  Update(creason.get(), f);
+ 
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDeviceStorage::Update(const char* aReason, nsIFile* aFile)
+{
+  nsString rootpath;
+  nsresult rv = mFile->GetPath(rootpath);
+  if (NS_FAILED(rv)) {
+    return NS_OK;
+  }
+  
+  nsString fullpath;
+  rv = aFile->GetPath(fullpath);
+  if (NS_FAILED(rv)) {
+    return NS_OK;
+  }
+
+  NS_ASSERTION(fullpath.Length() >= rootpath.Length(), "Root path longer than full path!");
+  
+  if (!StringBeginsWith(fullpath, rootpath)) {
+    NS_WARNING("Observing a path outside of our root!");
+    return NS_OK;
+  }
+
+  nsAString::size_type len = rootpath.Length() + 1; // +1 for the trailing /
+  nsDependentSubstring newPath (fullpath, len, fullpath.Length() - len);
+
+  nsCOMPtr<nsIDOMEvent> event;
+  NS_NewDOMDeviceStorageChangeEvent(getter_AddRefs(event), nullptr, nullptr);
+
+  nsCOMPtr<nsIDOMDeviceStorageChangeEvent> ce = do_QueryInterface(event);
+
+  nsString reason;
+  reason.AssignWithConversion(aReason);
+  rv = ce->InitDeviceStorageChangeEvent(NS_LITERAL_STRING("change"), true, false, newPath, reason);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool ignore;
+  DispatchEvent(ce, &ignore);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDeviceStorage::AddEventListener(const nsAString & aType,
+                                     nsIDOMEventListener *aListener,
+                                     bool aUseCapture,
+                                     bool aWantsUntrusted,
+                                     PRUint8 aArgc)
+{
+  nsCOMPtr<nsPIDOMWindow> win = GetOwner();
+  if (!win) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  nsRefPtr<DOMRequest> request = new DOMRequest(win);
+  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mFile);
+  nsCOMPtr<nsIRunnable> r = new DeviceStorageRequest(DeviceStorageRequest::DEVICE_STORAGE_REQUEST_WATCH,
+                                                     win, mURI, dsf, request, this, aListener);
+  NS_DispatchToMainThread(r);
+  return nsDOMEventTargetHelper::AddEventListener(aType, aListener, aUseCapture, aWantsUntrusted, aArgc);
+}
+
+NS_IMETHODIMP
+nsDOMDeviceStorage::AddSystemEventListener(const nsAString & aType,
+                                           nsIDOMEventListener *aListener,
+                                           bool aUseCapture,
+                                           bool aWantsUntrusted,
+                                           PRUint8 aArgc)
+{
+  return nsDOMDeviceStorage::AddEventListener(aType,aListener,aUseCapture,aWantsUntrusted, aArgc);
+}
+
+NS_IMETHODIMP
+nsDOMDeviceStorage::RemoveEventListener(const nsAString & aType,
+                                        nsIDOMEventListener *aListener,
+                                        bool aUseCapture)
+{
+  nsDOMEventTargetHelper::RemoveEventListener(aType, aListener, false);
+
+  if (mIsWatchingFile && !HasListenersFor(NS_LITERAL_STRING("change"))) {
+    if (XRE_GetProcessType() != GeckoProcessType_Default) {
+      nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+      obs->RemoveObserver(this, "file-watcher-update");
+
+      nsString fullpath;
+      mFile->GetPath(fullpath);
+      ContentChild::GetSingleton()->SendRemoveFileWatch(fullpath);
+    } else {
+      mFile->Unwatch(this);
+    }
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDeviceStorage::RemoveSystemEventListener(const nsAString & aType,
+                                              nsIDOMEventListener *aListener,
+                                              bool aUseCapture)
+{
+  return nsDOMDeviceStorage::RemoveEventListener(aType, aListener, aUseCapture);
+}
+
+NS_IMETHODIMP
+nsDOMDeviceStorage::DispatchEvent(nsIDOMEvent *aEvt,
+                                  bool *aRetval)
+{
+  return nsDOMEventTargetHelper::DispatchEvent(aEvt, aRetval);
+}
+
+nsIDOMEventTarget *
+nsDOMDeviceStorage::GetTargetForDOMEvent()
+{
+  return nsDOMEventTargetHelper::GetTargetForDOMEvent();
+}
+
+nsIDOMEventTarget *
+nsDOMDeviceStorage::GetTargetForEventTargetChain()
+{
+  return nsDOMEventTargetHelper::GetTargetForEventTargetChain();
+}
+
+nsresult
+nsDOMDeviceStorage::PreHandleEvent(nsEventChainPreVisitor & aVisitor)
+{
+  return nsDOMEventTargetHelper::PreHandleEvent(aVisitor);
+}
+
+nsresult
+nsDOMDeviceStorage::WillHandleEvent(nsEventChainPostVisitor & aVisitor)
+{
+  return nsDOMEventTargetHelper::WillHandleEvent(aVisitor);
+}
+
+nsresult
+nsDOMDeviceStorage::PostHandleEvent(nsEventChainPostVisitor & aVisitor)
+{
+  return nsDOMEventTargetHelper::PostHandleEvent(aVisitor);
+}
+
+nsresult
+nsDOMDeviceStorage::DispatchDOMEvent(nsEvent *aEvent,
+                                     nsIDOMEvent *aDOMEvent,
+                                     nsPresContext *aPresContext,
+                                     nsEventStatus *aEventStatus)
+{
+  return nsDOMEventTargetHelper::DispatchDOMEvent(aEvent,
+                                                  aDOMEvent,
+                                                  aPresContext,
+                                                  aEventStatus);
+}
+
+nsEventListenerManager *
+nsDOMDeviceStorage::GetListenerManager(bool aMayCreate)
+{
+  return nsDOMEventTargetHelper::GetListenerManager(aMayCreate);
+}
+
+nsIScriptContext *
+nsDOMDeviceStorage::GetContextForEventHandlers(nsresult *aRv)
+{
+  return nsDOMEventTargetHelper::GetContextForEventHandlers(aRv);
+}
+
+JSContext *
+nsDOMDeviceStorage::GetJSContextForEventHandlers()
+{
+  return nsDOMEventTargetHelper::GetJSContextForEventHandlers();
+}
+
+NS_IMPL_EVENT_HANDLER(nsDOMDeviceStorage, change)
--- a/dom/devicestorage/nsDeviceStorage.h
+++ b/dom/devicestorage/nsDeviceStorage.h
@@ -1,38 +1,35 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsDeviceStorage_h
 #define nsDeviceStorage_h
 
 class nsPIDOMWindow;
-
-#include "mozilla/dom/ContentChild.h"
-#include "mozilla/dom/PBrowserChild.h"
-#include "mozilla/dom/devicestorage/PDeviceStorageRequestChild.h"
-
+#include "PCOMContentPermissionRequestChild.h"
 
 #include "DOMRequest.h"
-#include "PCOMContentPermissionRequestChild.h"
-#include "mozilla/Attributes.h"
-#include "mozilla/dom/PContentPermissionRequestChild.h"
 #include "nsAutoPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsDOMClassInfoID.h"
 #include "nsIClassInfo.h"
 #include "nsIContentPermissionPrompt.h"
-#include "nsIDOMDeviceStorage.h"
 #include "nsIDOMDeviceStorageCursor.h"
 #include "nsIDOMWindow.h"
 #include "nsIURI.h"
 #include "nsInterfaceHashtable.h"
 #include "nsString.h"
 #include "nsWeakPtr.h"
+#include "nsIDOMEventListener.h"
+#include "nsIDOMEventTarget.h"
+#include "nsIObserver.h"
+#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_UNKNOWN                     "Unknown"
 #define POST_ERROR_EVENT_NON_STRING_TYPE_UNSUPPORTED "Non-string type unsupported"
@@ -61,51 +58,16 @@ public:
   void CollectFiles(nsTArray<nsRefPtr<DeviceStorageFile> > &aFiles, PRUint64 aSince = 0);
   void collectFilesInternal(nsTArray<nsRefPtr<DeviceStorageFile> > &aFiles, PRUint64 aSince, nsAString& aRootPath);
 
 private:
   void NormalizeFilePath();
   void AppendRelativePath();
 };
 
-class nsDOMDeviceStorage MOZ_FINAL : public nsIDOMDeviceStorage
-{
-public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIDOMDEVICESTORAGE
-
-  nsDOMDeviceStorage();
-
-  nsresult Init(nsPIDOMWindow* aWindow, const nsAString &aType, const PRInt32 aIndex);
-
-  PRInt32 SetRootFileForType(const nsAString& aType, const PRInt32 aIndex);
-
-  static void CreateDeviceStoragesFor(nsPIDOMWindow* aWin, const nsAString &aType, nsIVariant** _retval);
-
-private:
-  ~nsDOMDeviceStorage();
-
-  nsresult GetInternal(const JS::Value & aName, JSContext* aCx, nsIDOMDOMRequest * *_retval, bool aEditable);
-
-  nsresult EnumerateInternal(const JS::Value & aName, const JS::Value & aOptions, JSContext* aCx, PRUint8 aArgc, bool aEditable, nsIDOMDeviceStorageCursor** aRetval);
-
-  PRInt32 mStorageType;
-  nsCOMPtr<nsIFile> mFile;
-
-  nsWeakPtr mOwner;
-  nsCOMPtr<nsIURI> mURI;
-
-  // nsIDOMDeviceStorage.type
-  enum {
-      DEVICE_STORAGE_TYPE_DEFAULT = 0,
-      DEVICE_STORAGE_TYPE_SHARED,
-      DEVICE_STORAGE_TYPE_EXTERNAL,
-  };
-};
-
 class ContinueCursorEvent MOZ_FINAL: public nsRunnable
 {
 public:
   ContinueCursorEvent(nsRefPtr<DOMRequest>& aRequest);
   ContinueCursorEvent(DOMRequest* aRequest);
   ~ContinueCursorEvent();
   NS_IMETHOD Run();
 private:
--- a/dom/devicestorage/test/devicestorage_common.js
+++ b/dom/devicestorage/test/devicestorage_common.js
@@ -54,21 +54,21 @@ function getRandomBuffer() {
   return buffer;
 }
 
 function createRandomBlob() {
   return blob = new Blob([getRandomBuffer()], {type: 'binary/random'});
 }
 
 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);
-    }
-    return result;
+  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);
+  }
+  return result;
 }
 
 function reportErrorAndQuit(e) {
   ok(false, "handleError was called : " + e.target.error.name);
   devicestorage_cleanup();
 }
--- a/dom/devicestorage/test/test_basic.html
+++ b/dom/devicestorage/test/test_basic.html
@@ -49,17 +49,17 @@ function getAfterDeleteError(e) {
 }
 
 function deleteSuccess(e) {
 
   ok(e.target.result == gFileName, "File name should match");
   dump(e.target.result + "\n")
 
   var storage = navigator.getDeviceStorage("testing");
-  request = storage[0].get(e.target.result);
+  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();
@@ -72,17 +72,17 @@ function getSuccess(e) {
   ok(e.target.result.name == gFileName, "File name should match");
 
   var name = e.target.result.name;
 
   gFileReader.readAsArrayBuffer(gDataBlob);
   gFileReader.onload = function(e) {
     readerCallback(e);
 
-    request = storage[0].delete(name)
+    request = storage.delete(name)
     request.onsuccess = deleteSuccess;
     request.onerror = deleteError;
   }
 }
 
 function readerCallback(e) {
 
   ab = e.target.result;
@@ -97,34 +97,34 @@ function getError(e) {
   devicestorage_cleanup();
 }
 
 function addSuccess(e) {
 
   ok(e.target.result == gFileName, "File name should match");
 
   var storage = navigator.getDeviceStorage("testing");
-  request = storage[0].get(gFileName);
+  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");
 ok(storage, "Should have gotten a storage");
 
-request = storage[0].addNamed(gDataBlob, "devicestorage/hi");
+request = storage.addNamed(gDataBlob, "devicestorage/hi");
 ok(request, "Should have a non-null request");
 
 request.onsuccess = addSuccess;
 request.onerror = addError;
 
 </script>
 </pre>
 </body>
--- a/dom/devicestorage/test/test_dotdot.html
+++ b/dom/devicestorage/test/test_dotdot.html
@@ -20,17 +20,17 @@ 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")[0];
+  return navigator.getDeviceStorage("testing");
 }
 
 var tests = [
   function () { return testingStorage().addNamed(createRandomBlob(), gFileName); },
   function () { return testingStorage().delete(gFileName); },
   function () { return testingStorage().get(gFileName); },
   function () { var r = testingStorage().enumerate("../"); return r; }
 ];
--- a/dom/devicestorage/test/test_enumerate.html
+++ b/dom/devicestorage/test/test_enumerate.html
@@ -36,32 +36,32 @@ function enumerateSuccess(e) {
   var filename = e.target.result.name;
 
   var index = files.indexOf(filename);
   files.remove(index);
 
   ok(index > -1, "filename should be in the enumeration : " + e.target.result.name);
 
   // clean up
-  var cleanup = storage[0].delete(prefix + "/" + filename);
+  var cleanup = storage.delete(prefix + "/" + filename);
   cleanup.onsuccess = function(e) {}  // todo - can i remove this?
 
   e.target.continue();
 }
 
 function handleError(e) {
   ok(false, "handleError was called : " + e.target.error.name);
   devicestorage_cleanup();
 }
 
 function addSuccess(e) {
   addedSoFar = addedSoFar + 1;
   if (addedSoFar == files.length) {
 
-    var cursor = storage[0].enumerate(prefix);
+    var cursor = storage.enumerate(prefix);
     cursor.onsuccess = enumerateSuccess;
     cursor.onerror = handleError;
   }
 }
 
 function addError(e) {
   ok(false, "addError was called : " + e.target.error.name);
   devicestorage_cleanup();
@@ -72,17 +72,17 @@ ok(navigator.getDeviceStorage, "Should h
 var prefix = "devicestorage/" + randomFilename(12)
 
 var files = [ "a", "b", "c", "d/a", "d/b", "d/c", "d/d", "The/quick/brown/fox/jumps/over/the/lazy/dog"]
 var addedSoFar = 0;
 
 
 for (var i=0; i<files.length; i++) {
 
- request = storage[0].addNamed(createRandomBlob(), prefix + '/' + files[i]);
+ request = storage.addNamed(createRandomBlob(), 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")[0].enumerate();
+var cursor = navigator.getDeviceStorage("testing").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
@@ -40,32 +40,32 @@ function enumerateSuccess(e) {
   
   var filename = e.target.result.name;
   var index = files.indexOf(filename);
   files.remove(index);
 
   ok(index > -1, "filename should be in the enumeration : " + e.target.result.name);
 
   // clean up
-  var cleanup = storage[0].delete(prefix + "/" + filename);
+  var cleanup = storage.delete(prefix + "/" + filename);
   cleanup.onsuccess = function(e) {}  // todo - can i remove this?
 
   e.target.continue();
 }
 
 function handleError(e) {
   ok(false, "handleError was called : " + e.target.error.name);
   devicestorage_cleanup();
 }
 
 function addSuccess(e) {
   addedSoFar = addedSoFar + 1;
   if (addedSoFar == files.length) {
 
-    var cursor = storage[0].enumerate();
+    var cursor = storage.enumerate();
     cursor.onsuccess = enumerateSuccess;
     cursor.onerror = handleError;
   }
 }
 
 function addError(e) {
   ok(false, "addError was called : " + e.target.error.name);
   devicestorage_cleanup();
@@ -76,17 +76,17 @@ ok(navigator.getDeviceStorage, "Should h
 var prefix = "devicestorage/" + randomFilename(12)
 
 var files = [ "a", "b", "c" ]
 var addedSoFar = 0;
 
 
 for (var i=0; i<files.length; i++) {
 
- request = storage[0].addNamed(createRandomBlob(), prefix + '/' + files[i]);
+ request = storage.addNamed(createRandomBlob(), 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
@@ -25,53 +25,53 @@ https://bugzilla.mozilla.org/show_bug.cg
 
 devicestorage_setup()
 
 storage = navigator.getDeviceStorage("testing");
 
 
 throws = false;
 try {
-var cursor = storage[0].enumerate();
+var cursor = storage.enumerate();
 } catch(e) {throws = true}
 ok(!throws, "enumerate no parameter");
 
 throws = false;
 try {
-var cursor = storage[0].enumerate("string");
+var cursor = storage.enumerate("string");
 } catch(e) {throws = true}
 ok(!throws, "enumerate one string parameter");
 
 throws = false;
 try {
-var cursor = storage[0].enumerate("string", "string2");
+var cursor = storage.enumerate("string", "string2");
 } catch(e) {throws = true}
 ok(throws, "enumerate two string parameter");
 
 throws = false;
 try {
-var cursor = storage[0].enumerate("string", {"since": 1});
+var cursor = storage.enumerate("string", {"since": 1});
 } catch(e) {throws = true}
 ok(!throws, "enumerate a string and object parameter");
 
 throws = false;
 try {
-var cursor = storage[0].enumerate({"path": "a"});
+var cursor = storage.enumerate({"path": "a"});
 } catch(e) {throws = true}
 ok(!throws, "enumerate object parameter with path");
 
 throws = false;
 try {
-var cursor = storage[0].enumerate({}, "string");
+var cursor = storage.enumerate({}, "string");
 } catch(e) {throws = true}
 ok(throws, "enumerate object then a string");
 
 throws = false;
 try {
-var cursor = storage[0].enumerate({"path": "a", "since": 0});
+var cursor = storage.enumerate({"path": "a", "since": 0});
 } catch(e) {throws = true}
 ok(!throws, "enumerate object parameter with path");
 
 
 
 
 devicestorage_cleanup()
 </script>
--- a/dom/devicestorage/test/test_lastModificationFilter.html
+++ b/dom/devicestorage/test/test_lastModificationFilter.html
@@ -40,17 +40,17 @@ function verifyAndDelete(prefix, files, 
   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 cleanup = storage[0].delete(prefix + "/" + filename);
+  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;
 
@@ -83,17 +83,17 @@ var newFiles = ["d", "e", "f"];
 addFiles(prefix, oldFiles, 157795200, addNewFiles);
 
 function enumerateNew() {
 
   var storage = navigator.getDeviceStorage("testing");
   ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
 
 // 836031600 is a long time ago
-  var cursor = storage[0].enumerate(prefix, {"since": new Date(836031600)});
+  var cursor = storage.enumerate(prefix, {"since": new Date(836031600)});
   cursor.onsuccess = function(e) {
     verifyAndDelete(prefix, newFiles, e);
     if (e.target.result) {
       e.target.continue();
     }
   }
 
   cursor.onerror = function (e) {
--- a/dom/devicestorage/test/test_overwrite.html
+++ b/dom/devicestorage/test/test_overwrite.html
@@ -41,46 +41,46 @@ 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");
-  request = storage[0].delete(filename)
+  request = storage.delete(filename)
   request.onsuccess = deleteSuccess;
   request.onerror = deleteError;
 }
 
 function addSuccess(e) {
   ok(true, "addSuccess was called");
 
   var storage = navigator.getDeviceStorage("testing");
   ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
 
-  request = storage[0].addNamed(createRandomBlob(), filename);
+  request = storage.addNamed(createRandomBlob(), 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");
-  request = storage[0].delete(filename)
+  request = storage.delete(filename)
   request.onsuccess = runtest;
 }
 
 function runtest() {
   var storage = navigator.getDeviceStorage("testing");
   ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
 
-  request = storage[0].addNamed(createRandomBlob(), filename);
+  request = storage.addNamed(createRandomBlob(), 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
@@ -36,17 +36,17 @@ try {
 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");
 
-var cursor = storage[0].enumerate();
+var cursor = storage.enumerate();
 ok(cursor, "Should have a non-null cursor");
 
 devicestorage_cleanup();
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/file/LockedFile.cpp
+++ b/dom/file/LockedFile.cpp
@@ -511,20 +511,19 @@ NS_IMETHODIMP
 LockedFile::GetLocation(JSContext* aCx,
                         jsval* aLocation)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (mLocation == LL_MAXUINT) {
     *aLocation = JSVAL_NULL;
   }
-  else if (!JS_NewNumberValue(aCx, double(mLocation), aLocation)) {
-    return NS_ERROR_FAILURE;
+  else {
+    *aLocation = JS_NumberValue(double(mLocation));
   }
-
   return NS_OK;
 }
 
 NS_IMETHODIMP
 LockedFile::SetLocation(JSContext* aCx,
                         const jsval& aLocation)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
--- a/dom/file/MetadataHelper.cpp
+++ b/dom/file/MetadataHelper.cpp
@@ -27,27 +27,17 @@ MetadataHelper::DoAsyncRun(nsISupports* 
 nsresult
 MetadataHelper::GetSuccessResult(JSContext* aCx,
                                  jsval* aVal)
 {
   JSObject* obj = JS_NewObject(aCx, nullptr, nullptr, nullptr);
   NS_ENSURE_TRUE(obj, NS_ERROR_OUT_OF_MEMORY);
 
   if (mParams->SizeRequested()) {
-    jsval val;
-
-    if (mParams->Size() <= JSVAL_INT_MAX) {
-      val = INT_TO_JSVAL(mParams->Size());
-    }
-    else {
-      double size = mParams->Size();
-      if (!JS_NewNumberValue(aCx, size, &val)) {
-        return NS_ERROR_FAILURE;
-      }
-    }
+    jsval val = JS_NumberValue(mParams->Size());
 
     if (!JS_DefineProperty(aCx, obj, "size", val, nullptr, nullptr,
                            JSPROP_ENUMERATE)) {
       return NS_ERROR_FAILURE;
     }
   }
 
   if (mParams->LastModifiedRequested()) {
--- a/dom/file/test/Makefile.in
+++ b/dom/file/test/Makefile.in
@@ -7,27 +7,29 @@ topsrcdir        = @top_srcdir@
 srcdir           = @srcdir@
 VPATH            = @srcdir@
 relativesrcdir   = dom/file/test
 
 include $(DEPTH)/config/autoconf.mk
 
 
 MOCHITEST_FILES = \
+  dummy_worker.js \
   helpers.js \
   test_append_read_data.html \
   test_getFileId.html \
   test_location.html \
   test_lockedfile_lifetimes.html \
   test_lockedfile_lifetimes_nested.html \
   test_lockedfile_ordering.html \
   test_overlapping_lockedfiles.html \
   test_progress_events.html \
   test_readonly_lockedfiles.html \
   test_request_readyState.html \
   test_stream_tracking.html \
   test_success_events_after_abort.html \
   test_truncate.html \
   test_write_read_data.html \
+  test_workers.html \
   test_archivereader.html \
   $(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/file/test/dummy_worker.js
@@ -0,0 +1,8 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+onmessage = function(event) {
+  throw("Shouldn't be called!");
+}
new file mode 100644
--- /dev/null
+++ b/dom/file/test/test_workers.html
@@ -0,0 +1,61 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>File Handle Test</title>
+
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+  <script type="text/javascript;version=1.7">
+  function testSteps()
+  {
+    var testBuffer = getRandomBuffer(100000);
+
+    for each (let fileStorage in fileStorages) {
+      let request = getFileHandle(fileStorage.key, "test.txt");
+      request.onerror = errorHandler;
+      request.onsuccess = grabEventAndContinueHandler;
+      let event = yield;
+
+      let fileHandle = event.target.result;
+      fileHandle.onerror = errorHandler;
+
+      let lockedFile = fileHandle.open("readwrite");
+
+      request = lockedFile.write(testBuffer);
+      request.onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      request = fileHandle.getFile();
+      request.onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      let file = event.target.result;
+
+      var worker = new Worker("dummy_worker.js");
+      try {
+        worker.postMessage(file);
+        ok(false, "Should have thrown!");
+      }
+      catch (e) {
+        ok(e instanceof DOMException, "Got exception.");
+        is(e.name, "DataCloneError", "Good error.");
+        is(e.code, DOMException.DATA_CLONE_ERR, "Good error code.")
+      }
+      worker.terminate();
+    }
+
+    finishTest();
+    yield;
+  }
+  </script>
+  <script type="text/javascript;version=1.7" src="helpers.js"></script>
+
+</head>
+
+<body onload="runTest();"></body>
+
+</html>
--- a/dom/indexedDB/IDBCursor.cpp
+++ b/dom/indexedDB/IDBCursor.cpp
@@ -744,17 +744,17 @@ IDBCursor::Advance(PRInt64 aCount)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (aCount < 1 || aCount > PR_UINT32_MAX) {
     return NS_ERROR_TYPE_ERR;
   }
 
   Key key;
-  return ContinueInternal(key, aCount);
+  return ContinueInternal(key, PRInt32(aCount));
 }
 
 void
 CursorHelper::ReleaseMainThreadObjects()
 {
   mCursor = nullptr;
   AsyncConnectionHelper::ReleaseMainThreadObjects();
 }
@@ -871,30 +871,48 @@ ContinueHelper::MaybeSendResponseToChild
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
   if (!actor) {
     return Success_NotSent;
   }
 
-  if (!mCloneReadInfo.mFileInfos.IsEmpty()) {
-    NS_WARNING("No support for transferring blobs across processes yet!");
-    return Error;
+  InfallibleTArray<PBlobParent*> blobsParent;
+
+  if (NS_SUCCEEDED(aResultCode)) {
+    IDBDatabase* database = mTransaction->Database();
+    NS_ASSERTION(database, "This should never be null!");
+
+    ContentParent* contentParent = database->GetContentParent();
+    NS_ASSERTION(contentParent, "This should never be null!");
+
+    FileManager* fileManager = database->Manager();
+    NS_ASSERTION(fileManager, "This should never be null!");
+
+    const nsTArray<StructuredCloneFile>& files = mCloneReadInfo.mFiles;
+
+    aResultCode =
+      IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files,
+                                           blobsParent);
+    if (NS_FAILED(aResultCode)) {
+      NS_WARNING("ConvertBlobsToActors failed!");
+    }
   }
 
   ResponseValue response;
   if (NS_FAILED(aResultCode)) {
     response = aResultCode;
   }
   else {
     ContinueResponse continueResponse;
     continueResponse.key() = mKey;
     continueResponse.objectKey() = mObjectKey;
     continueResponse.cloneInfo() = mCloneReadInfo;
+    continueResponse.blobsParent().SwapElements(blobsParent);
     response = continueResponse;
   }
 
   if (!actor->Send__delete__(actor, response)) {
     return Error;
   }
 
   UpdateCursorState();
@@ -920,16 +938,18 @@ ContinueHelper::UnpackResponseFromParent
                (cloneInfo.dataLength && cloneInfo.data),
                "Inconsistent clone info!");
 
   if (!mCloneReadInfo.SetFromSerialized(cloneInfo)) {
     NS_WARNING("Failed to copy clone buffer!");
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
+  IDBObjectStore::ConvertActorsToBlobs(response.blobsChild(),
+                                       mCloneReadInfo.mFiles);
   return NS_OK;
 }
 
 nsresult
 ContinueObjectStoreHelper::BindArgumentsToStatement(
                                                mozIStorageStatement* aStatement)
 {
   // Bind object store id.
--- a/dom/indexedDB/IDBDatabase.cpp
+++ b/dom/indexedDB/IDBDatabase.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "base/basictypes.h"
 
 #include "IDBDatabase.h"
 
 #include "mozilla/Mutex.h"
 #include "mozilla/storage.h"
+#include "mozilla/dom/ContentParent.h"
 #include "nsDOMClassInfo.h"
 #include "nsDOMLists.h"
 #include "nsJSUtils.h"
 #include "nsProxyRelease.h"
 #include "nsThreadUtils.h"
 
 #include "AsyncConnectionHelper.h"
 #include "CheckQuotaHelper.h"
@@ -28,16 +29,17 @@
 #include "IndexedDatabaseManager.h"
 #include "TransactionThreadPool.h"
 #include "DictionaryHelpers.h"
 #include "nsContentUtils.h"
 
 #include "ipc/IndexedDBChild.h"
 
 USING_INDEXEDDB_NAMESPACE
+using mozilla::dom::ContentParent;
 
 namespace {
 
 class NoRequestDatabaseHelper : public AsyncConnectionHelper
 {
 public:
   NoRequestDatabaseHelper(IDBTransaction* aTransaction)
   : AsyncConnectionHelper(aTransaction, nullptr)
@@ -165,17 +167,18 @@ private:
 
 } // anonymous namespace
 
 // static
 already_AddRefed<IDBDatabase>
 IDBDatabase::Create(IDBWrapperCache* aOwnerCache,
                     already_AddRefed<DatabaseInfo> aDatabaseInfo,
                     const nsACString& aASCIIOrigin,
-                    FileManager* aFileManager)
+                    FileManager* aFileManager,
+                    mozilla::dom::ContentParent* aContentParent)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(!aASCIIOrigin.IsEmpty(), "Empty origin!");
 
   nsRefPtr<DatabaseInfo> databaseInfo(aDatabaseInfo);
   NS_ASSERTION(databaseInfo, "Null pointer!");
 
   nsRefPtr<IDBDatabase> db(new IDBDatabase());
@@ -186,32 +189,34 @@ IDBDatabase::Create(IDBWrapperCache* aOw
   }
 
   db->mDatabaseId = databaseInfo->id;
   db->mName = databaseInfo->name;
   db->mFilePath = databaseInfo->filePath;
   databaseInfo.swap(db->mDatabaseInfo);
   db->mASCIIOrigin = aASCIIOrigin;
   db->mFileManager = aFileManager;
+  db->mContentParent = aContentParent;
 
   IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
   NS_ASSERTION(mgr, "This should never be null!");
 
   if (!mgr->RegisterDatabase(db)) {
     // Either out of memory or shutting down.
     return nullptr;
   }
 
   return db.forget();
 }
 
 IDBDatabase::IDBDatabase()
 : mDatabaseId(0),
   mActorChild(nullptr),
   mActorParent(nullptr),
+  mContentParent(nullptr),
   mInvalidated(0),
   mRegistered(false),
   mClosed(false),
   mRunningVersionChange(false)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 }
 
--- a/dom/indexedDB/IDBDatabase.h
+++ b/dom/indexedDB/IDBDatabase.h
@@ -14,16 +14,22 @@
 #include "nsIIDBDatabase.h"
 #include "nsDOMEventTargetHelper.h"
 #include "mozilla/dom/indexedDB/IDBWrapperCache.h"
 #include "mozilla/dom/indexedDB/FileManager.h"
 
 class nsIScriptContext;
 class nsPIDOMWindow;
 
+namespace mozilla {
+namespace dom {
+class ContentParent;
+}
+}
+
 BEGIN_INDEXEDDB_NAMESPACE
 
 class AsyncConnectionHelper;
 struct DatabaseInfo;
 class IDBIndex;
 class IDBObjectStore;
 class IDBTransaction;
 class IndexedDatabaseManager;
@@ -45,17 +51,18 @@ public:
   NS_DECL_NSIFILESTORAGE
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBDatabase, IDBWrapperCache)
 
   static already_AddRefed<IDBDatabase>
   Create(IDBWrapperCache* aOwnerCache,
          already_AddRefed<DatabaseInfo> aDatabaseInfo,
          const nsACString& aASCIIOrigin,
-         FileManager* aFileManager);
+         FileManager* aFileManager,
+         mozilla::dom::ContentParent* aContentParent);
 
   // nsIDOMEventTarget
   virtual nsresult PostHandleEvent(nsEventChainPostVisitor& aVisitor);
 
   nsIAtom* Id() const
   {
     return mDatabaseId;
   }
@@ -130,16 +137,22 @@ public:
   }
 
   IndexedDBDatabaseChild*
   GetActorChild() const
   {
     return mActorChild;
   }
 
+  mozilla::dom::ContentParent*
+  GetContentParent() const
+  {
+    return mContentParent;
+  }
+
   nsresult
   CreateObjectStoreInternal(IDBTransaction* aTransaction,
                             const ObjectStoreInfoGuts& aInfo,
                             IDBObjectStore** _retval);
 
 private:
   IDBDatabase();
   ~IDBDatabase();
@@ -160,16 +173,18 @@ private:
   // Only touched on the main thread.
   NS_DECL_EVENT_HANDLER(abort)
   NS_DECL_EVENT_HANDLER(error)
   NS_DECL_EVENT_HANDLER(versionchange)
 
   IndexedDBDatabaseChild* mActorChild;
   IndexedDBDatabaseParent* mActorParent;
 
+  mozilla::dom::ContentParent* mContentParent;
+
   PRInt32 mInvalidated;
   bool mRegistered;
   bool mClosed;
   bool mRunningVersionChange;
 };
 
 END_INDEXEDDB_NAMESPACE
 
--- a/dom/indexedDB/IDBEvents.cpp
+++ b/dom/indexedDB/IDBEvents.cpp
@@ -114,14 +114,14 @@ NS_IMETHODIMP
 IDBVersionChangeEvent::GetNewVersion(JSContext* aCx,
                                      JS::Value* aNewVersion)
 {
   NS_ENSURE_ARG_POINTER(aNewVersion);
 
   if (!mNewVersion) {
     *aNewVersion = JSVAL_NULL;
   }
-  else if (!JS_NewNumberValue(aCx, double(mNewVersion), aNewVersion)) {
-    return NS_ERROR_FAILURE;
+  else {
+    *aNewVersion = JS_NumberValue(double(mNewVersion));
   }
 
   return NS_OK;
 }
--- a/dom/indexedDB/IDBFactory.cpp
+++ b/dom/indexedDB/IDBFactory.cpp
@@ -2,25 +2,25 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "base/basictypes.h"
 
 #include "IDBFactory.h"
-
 #include "nsIFile.h"
 #include "nsIJSContextStack.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptContext.h"
 #include "nsIXPConnect.h"
 #include "nsIXPCScriptable.h"
 
 #include "jsdbgapi.h"
+#include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/PBrowserChild.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/storage.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsContentUtils.h"
@@ -41,16 +41,17 @@
 #include "IndexedDatabaseManager.h"
 #include "Key.h"
 
 #include "ipc/IndexedDBChild.h"
 
 USING_INDEXEDDB_NAMESPACE
 
 using mozilla::dom::ContentChild;
+using mozilla::dom::ContentParent;
 using mozilla::dom::TabChild;
 
 namespace {
 
 struct ObjectStoreInfoMap
 {
   ObjectStoreInfoMap()
   : id(LL_MININT), info(nullptr) { }
@@ -58,17 +59,17 @@ struct ObjectStoreInfoMap
   PRInt64 id;
   ObjectStoreInfo* info;
 };
 
 } // anonymous namespace
 
 IDBFactory::IDBFactory()
 : mOwningObject(nullptr), mActorChild(nullptr), mActorParent(nullptr),
-  mRootedOwningObject(false)
+  mContentParent(nullptr), mRootedOwningObject(false)
 {
 }
 
 IDBFactory::~IDBFactory()
 {
   NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!");
   if (mActorChild) {
     NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
@@ -79,16 +80,17 @@ IDBFactory::~IDBFactory()
     NS_DROP_JS_OBJECTS(this, IDBFactory);
   }
 }
 
 // static
 nsresult
 IDBFactory::Create(nsPIDOMWindow* aWindow,
                    const nsACString& aASCIIOrigin,
+                   ContentParent* aContentParent,
                    IDBFactory** aFactory)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aASCIIOrigin.IsEmpty() || nsContentUtils::IsCallerChrome(),
                "Non-chrome may not supply their own origin!");
 
   NS_ENSURE_TRUE(aWindow, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
@@ -113,16 +115,17 @@ IDBFactory::Create(nsPIDOMWindow* aWindo
       *aFactory = nullptr;
       return NS_OK;
     }
   }
 
   nsRefPtr<IDBFactory> factory = new IDBFactory();
   factory->mASCIIOrigin = origin;
   factory->mWindow = aWindow;
+  factory->mContentParent = aContentParent;
 
   if (!IndexedDatabaseManager::IsMainProcess()) {
     TabChild* tabChild = GetTabChildFrom(aWindow);
     NS_ENSURE_TRUE(tabChild, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
     IndexedDBChild* actor = new IndexedDBChild(origin);
 
     bool allowed;
@@ -140,16 +143,17 @@ IDBFactory::Create(nsPIDOMWindow* aWindo
   factory.forget(aFactory);
   return NS_OK;
 }
 
 // static
 nsresult
 IDBFactory::Create(JSContext* aCx,
                    JSObject* aOwningObject,
+                   ContentParent* aContentParent,
                    IDBFactory** aFactory)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aCx, "Null context!");
   NS_ASSERTION(aOwningObject, "Null object!");
   NS_ASSERTION(JS_GetGlobalForObject(aCx, aOwningObject) == aOwningObject,
                "Not a global object!");
   NS_ASSERTION(nsContentUtils::IsCallerChrome(), "Only for chrome!");
@@ -157,16 +161,17 @@ IDBFactory::Create(JSContext* aCx,
   nsCString origin;
   nsresult rv =
     IndexedDatabaseManager::GetASCIIOriginFromWindow(nullptr, origin);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsRefPtr<IDBFactory> factory = new IDBFactory();
   factory->mASCIIOrigin = origin;
   factory->mOwningObject = aOwningObject;
+  factory->mContentParent = aContentParent;
 
   if (!IndexedDatabaseManager::IsMainProcess()) {
     ContentChild* contentChild = ContentChild::GetSingleton();
     NS_ENSURE_TRUE(contentChild, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
     IndexedDBChild* actor = new IndexedDBChild(origin);
 
     contentChild->SendPIndexedDBConstructor(actor);
@@ -175,21 +180,23 @@ IDBFactory::Create(JSContext* aCx,
   }
 
   factory.forget(aFactory);
   return NS_OK;
 }
 
 // static
 nsresult
-IDBFactory::Create(IDBFactory** aFactory)
+IDBFactory::Create(ContentParent* aContentParent,
+                   IDBFactory** aFactory)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
   NS_ASSERTION(nsContentUtils::IsCallerChrome(), "Only for chrome!");
+  NS_ASSERTION(aContentParent, "Null ContentParent!");
 
 #ifdef DEBUG
   {
     nsIThreadJSContextStack* cxStack = nsContentUtils::ThreadJSContextStack();
     NS_ASSERTION(cxStack, "Couldn't get ThreadJSContextStack!");
 
     JSContext* lastCx;
     if (NS_SUCCEEDED(cxStack->Peek(&lastCx))) {
@@ -238,17 +245,17 @@ IDBFactory::Create(IDBFactory** aFactory
 
   JSAutoEnterCompartment ac;
   if (!ac.enter(cx, global)) {
     NS_WARNING("Failed to enter compartment!");
     return NS_ERROR_FAILURE;
   }
 
   nsRefPtr<IDBFactory> factory;
-  rv = Create(cx, global, getter_AddRefs(factory));
+  rv = Create(cx, global, aContentParent, getter_AddRefs(factory));
   NS_ENSURE_SUCCESS(rv, rv);
 
   NS_HOLD_JS_OBJECTS(factory, IDBFactory);
   factory->mRootedOwningObject = true;
 
   factory.forget(aFactory);
   return NS_OK;
 }
@@ -524,20 +531,20 @@ IDBFactory::OpenCommon(const nsAString& 
   }
 
   nsRefPtr<IDBOpenDBRequest> request =
     IDBOpenDBRequest::Create(window, scriptOwner, aCallingCx);
   NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsresult rv;
 
-  if (IndexedDatabaseManager::IsMainProcess()) {                       
+  if (IndexedDatabaseManager::IsMainProcess()) {
     nsRefPtr<OpenDatabaseHelper> openHelper =
       new OpenDatabaseHelper(request, aName, mASCIIOrigin, aVersion, aDeleting,
-                             privilege);
+                             mContentParent, privilege);
 
     rv = openHelper->Init();
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
     nsRefPtr<CheckPermissionsHelper> permissionHelper =
       new CheckPermissionsHelper(openHelper, window, aDeleting);
 
     IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
@@ -623,9 +630,9 @@ IDBFactory::Cmp(const jsval& aFirst,
   }
 
   if (first.IsUnset() || second.IsUnset()) {
     return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
   }
 
   *_retval = Key::CompareKeys(first, second);
   return NS_OK;
-}
+}
\ No newline at end of file
--- a/dom/indexedDB/IDBFactory.h
+++ b/dom/indexedDB/IDBFactory.h
@@ -12,61 +12,73 @@
 #include "mozIStorageConnection.h"
 #include "nsIIDBFactory.h"
 
 #include "nsCycleCollectionParticipant.h"
 
 class nsIAtom;
 class nsPIDOMWindow;
 
+namespace mozilla {
+namespace dom {
+class ContentParent;
+}
+}
+
 BEGIN_INDEXEDDB_NAMESPACE
 
 struct DatabaseInfo;
 class IDBDatabase;
 class IDBOpenDBRequest;
 class IndexedDBChild;
 class IndexedDBParent;
 
 struct ObjectStoreInfo;
 
 class IDBFactory MOZ_FINAL : public nsIIDBFactory
 {
+  typedef mozilla::dom::ContentParent ContentParent;
   typedef nsTArray<nsRefPtr<ObjectStoreInfo> > ObjectStoreInfoArray;
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBFactory)
   NS_DECL_NSIIDBFACTORY
 
   // Called when using IndexedDB from a window in a different process.
   static nsresult Create(nsPIDOMWindow* aWindow,
                          const nsACString& aASCIIOrigin,
+                         ContentParent* aContentParent,
                          IDBFactory** aFactory);
 
   // Called when using IndexedDB from a window in the current process.
   static nsresult Create(nsPIDOMWindow* aWindow,
+                         ContentParent* aContentParent,
                          nsIIDBFactory** aFactory)
   {
     nsRefPtr<IDBFactory> factory;
-    nsresult rv = Create(aWindow, EmptyCString(), getter_AddRefs(factory));
+    nsresult rv =
+      Create(aWindow, EmptyCString(), aContentParent, getter_AddRefs(factory));
     NS_ENSURE_SUCCESS(rv, rv);
 
     factory.forget(aFactory);
     return NS_OK;
   }
 
   // Called when using IndexedDB from a JS component or a JSM in the current
   // process.
   static nsresult Create(JSContext* aCx,
                          JSObject* aOwningObject,
+                         ContentParent* aContentParent,
                          IDBFactory** aFactory);
 
   // Called when using IndexedDB from a JS component or a JSM in a different
   // process.
-  static nsresult Create(IDBFactory** aFactory);
+  static nsresult Create(ContentParent* aContentParent,
+                         IDBFactory** aFactory);
 
   static already_AddRefed<mozIStorageConnection>
   GetConnection(const nsAString& aDatabaseFilePath);
 
   static nsresult
   LoadDatabaseInformation(mozIStorageConnection* aConnection,
                           nsIAtom* aDatabaseId,
                           PRUint64* aVersion,
@@ -113,14 +125,16 @@ private:
   // If this factory lives on a window then mWindow must be non-null. Otherwise
   // mOwningObject must be non-null.
   nsCOMPtr<nsPIDOMWindow> mWindow;
   JSObject* mOwningObject;
 
   IndexedDBChild* mActorChild;
   IndexedDBParent* mActorParent;
 
+  mozilla::dom::ContentParent* mContentParent;
+
   bool mRootedOwningObject;
 };
 
 END_INDEXEDDB_NAMESPACE
 
 #endif // mozilla_dom_indexeddb_idbfactory_h__
--- a/dom/indexedDB/IDBIndex.cpp
+++ b/dom/indexedDB/IDBIndex.cpp
@@ -6,16 +6,19 @@
 
 #include "base/basictypes.h"
 
 #include "IDBIndex.h"
 
 #include "nsIIDBKeyRange.h"
 #include "nsIJSContextStack.h"
 
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/ipc/Blob.h"
 #include "nsContentUtils.h"
 #include "nsDOMClassInfoID.h"
 #include "nsEventDispatcher.h"
 #include "nsThreadUtils.h"
 #include "mozilla/storage.h"
 #include "xpcpublic.h"
 
 #include "AsyncConnectionHelper.h"
@@ -27,16 +30,17 @@
 #include "DatabaseInfo.h"
 
 #include "ipc/IndexedDBChild.h"
 #include "ipc/IndexedDBParent.h"
 
 #include "IndexedDatabaseInlines.h"
 
 USING_INDEXEDDB_NAMESPACE
+using namespace mozilla::dom;
 using namespace mozilla::dom::indexedDB::ipc;
 
 namespace {
 
 class IndexHelper : public AsyncConnectionHelper
 {
 public:
   IndexHelper(IDBTransaction* aTransaction,
@@ -629,16 +633,17 @@ IDBIndex::OpenCursorFromChildProcess(IDB
 
 nsresult
 IDBIndex::OpenCursorFromChildProcess(
                             IDBRequest* aRequest,
                             size_t aDirection,
                             const Key& aKey,
                             const Key& aObjectKey,
                             const SerializedStructuredCloneReadInfo& aCloneInfo,
+                            nsTArray<StructuredCloneFile>& aBlobs,
                             IDBCursor** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION((!aCloneInfo.dataLength && !aCloneInfo.data) ||
                (aCloneInfo.dataLength && aCloneInfo.data),
                "Inconsistent clone info!");
 
   IDBCursor::Direction direction =
@@ -646,16 +651,18 @@ IDBIndex::OpenCursorFromChildProcess(
 
   StructuredCloneReadInfo cloneInfo;
 
   if (!cloneInfo.SetFromSerialized(aCloneInfo)) {
     NS_WARNING("Failed to copy clone buffer!");
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
+  cloneInfo.mFiles.SwapElements(aBlobs);
+
   nsRefPtr<IDBCursor> cursor =
     IDBCursor::Create(aRequest, mObjectStore->Transaction(), this, direction,
                       Key(), EmptyCString(), EmptyCString(), aKey, aObjectKey,
                       cloneInfo);
   NS_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   NS_ASSERTION(!cloneInfo.mCloneBuffer.data(), "Should have swapped!");
 
@@ -1231,57 +1238,76 @@ GetHelper::MaybeSendResponseToChildProce
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
   if (!actor) {
     return Success_NotSent;
   }
 
-  if (!mCloneReadInfo.mFileInfos.IsEmpty()) {
-    NS_WARNING("No support for transferring blobs across processes yet!");
-    return Error;
+  InfallibleTArray<PBlobParent*> blobsParent;
+
+  if (NS_SUCCEEDED(aResultCode)) {
+    IDBDatabase* database = mIndex->ObjectStore()->Transaction()->Database();
+    NS_ASSERTION(database, "This should never be null!");
+
+    ContentParent* contentParent = database->GetContentParent();
+    NS_ASSERTION(contentParent, "This should never be null!");
+
+    FileManager* fileManager = database->Manager();
+    NS_ASSERTION(fileManager, "This should never be null!");
+
+    const nsTArray<StructuredCloneFile>& files = mCloneReadInfo.mFiles;
+
+    aResultCode =
+      IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files,
+                                           blobsParent);
+    if (NS_FAILED(aResultCode)) {
+      NS_WARNING("ConvertBlobActors failed!");
+    }
   }
 
   ResponseValue response;
   if (NS_FAILED(aResultCode)) {
     response = aResultCode;
   }
   else {
-    SerializedStructuredCloneReadInfo readInfo;
-    readInfo = mCloneReadInfo;
-    GetResponse getResponse = readInfo;
+    GetResponse getResponse;
+    getResponse.cloneInfo() = mCloneReadInfo;
+    getResponse.blobsParent().SwapElements(blobsParent);
     response = getResponse;
   }
 
   if (!actor->Send__delete__(actor, response)) {
     return Error;
   }
 
   return Success_Sent;
 }
 
 nsresult
 GetHelper::UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
 {
   NS_ASSERTION(aResponseValue.type() == ResponseValue::TGetResponse,
                "Bad response type!");
 
-  const SerializedStructuredCloneReadInfo& cloneInfo =
-    aResponseValue.get_GetResponse().cloneInfo();
+  const GetResponse& getResponse = aResponseValue.get_GetResponse();
+  const SerializedStructuredCloneReadInfo& cloneInfo = getResponse.cloneInfo();
 
   NS_ASSERTION((!cloneInfo.dataLength && !cloneInfo.data) ||
                (cloneInfo.dataLength && cloneInfo.data),
                "Inconsistent clone info!");
 
   if (!mCloneReadInfo.SetFromSerialized(cloneInfo)) {
     NS_WARNING("Failed to copy clone buffer!");
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
+  IDBObjectStore::ConvertActorsToBlobs(getResponse.blobsChild(),
+                                       mCloneReadInfo.mFiles);
   return NS_OK;
 }
 
 nsresult
 GetAllKeysHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
 {
   nsCString tableName;
   if (mIndex->IsUnique()) {
@@ -1562,40 +1588,68 @@ GetAllHelper::MaybeSendResponseToChildPr
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
   if (!actor) {
     return Success_NotSent;
   }
 
-  for (PRUint32 index = 0; index < mCloneReadInfos.Length(); index++) {
-    if (!mCloneReadInfos[index].mFileInfos.IsEmpty()) {
-      NS_WARNING("No support for transferring blobs across processes yet!");
-      return Error;
+  GetAllResponse getAllResponse;
+
+  if (NS_SUCCEEDED(aResultCode) && !mCloneReadInfos.IsEmpty()) {
+    IDBDatabase* database = mIndex->ObjectStore()->Transaction()->Database();
+    NS_ASSERTION(database, "This should never be null!");
+
+    ContentParent* contentParent = database->GetContentParent();
+    NS_ASSERTION(contentParent, "This should never be null!");
+
+    FileManager* fileManager = database->Manager();
+    NS_ASSERTION(fileManager, "This should never be null!");
+
+    PRUint32 length = mCloneReadInfos.Length();
+
+    InfallibleTArray<SerializedStructuredCloneReadInfo>& infos =
+      getAllResponse.cloneInfos();
+    infos.SetCapacity(length);
+
+    InfallibleTArray<BlobArray>& blobArrays = getAllResponse.blobs();
+    blobArrays.SetCapacity(length);
+
+    for (PRUint32 index = 0;
+         NS_SUCCEEDED(aResultCode) && index < length;
+         index++) {
+      const StructuredCloneReadInfo& clone = mCloneReadInfos[index];
+
+      // Append the structured clone data.
+      SerializedStructuredCloneReadInfo* info = infos.AppendElement();
+      *info = clone;
+
+      const nsTArray<StructuredCloneFile>& files = clone.mFiles;
+
+      // Now take care of the files.
+      BlobArray* blobArray = blobArrays.AppendElement();
+
+      InfallibleTArray<PBlobParent*>& blobs = blobArray->blobsParent();
+
+      aResultCode =
+        IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files,
+                                             blobs);
+      if (NS_FAILED(aResultCode)) {
+        NS_WARNING("ConvertBlobsToActors failed!");
+        break;
+      }
     }
   }
 
   ResponseValue response;
   if (NS_FAILED(aResultCode)) {
     response = aResultCode;
   }
   else {
-    GetAllResponse getAllResponse;
-
-    InfallibleTArray<SerializedStructuredCloneReadInfo>& infos =
-      getAllResponse.cloneInfos();
-
-    infos.SetCapacity(mCloneReadInfos.Length());
-
-    for (PRUint32 index = 0; index < mCloneReadInfos.Length(); index++) {
-      SerializedStructuredCloneReadInfo* info = infos.AppendElement();
-      *info = mCloneReadInfos[index];
-    }
-
     response = getAllResponse;
   }
 
   if (!actor->Send__delete__(actor, response)) {
     return Error;
   }
 
   return Success_Sent;
@@ -1603,29 +1657,34 @@ GetAllHelper::MaybeSendResponseToChildPr
 
 nsresult
 GetAllHelper::UnpackResponseFromParentProcess(
                                             const ResponseValue& aResponseValue)
 {
   NS_ASSERTION(aResponseValue.type() == ResponseValue::TGetAllResponse,
                "Bad response type!");
 
+  const GetAllResponse& getAllResponse = aResponseValue.get_GetAllResponse();
   const InfallibleTArray<SerializedStructuredCloneReadInfo>& cloneInfos =
-    aResponseValue.get_GetAllResponse().cloneInfos();
+    getAllResponse.cloneInfos();
+  const InfallibleTArray<BlobArray>& blobArrays = getAllResponse.blobs();
 
   mCloneReadInfos.SetCapacity(cloneInfos.Length());
 
   for (PRUint32 index = 0; index < cloneInfos.Length(); index++) {
     const SerializedStructuredCloneReadInfo srcInfo = cloneInfos[index];
+    const InfallibleTArray<PBlobChild*> blobs = blobArrays[index].blobsChild();
 
     StructuredCloneReadInfo* destInfo = mCloneReadInfos.AppendElement();
     if (!destInfo->SetFromSerialized(srcInfo)) {
       NS_WARNING("Failed to copy clone buffer!");
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
+
+    IDBObjectStore::ConvertActorsToBlobs(blobs, destInfo->mFiles);
   }
 
   return NS_OK;
 }
 
 nsresult
 OpenKeyCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
 {
@@ -2194,23 +2253,40 @@ OpenCursorHelper::MaybeSendResponseToChi
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
   if (!actor) {
     return Success_NotSent;
   }
 
-  if (!mCloneReadInfo.mFileInfos.IsEmpty()) {
-    NS_WARNING("No support for transferring blobs across processes yet!");
-    return Error;
+  NS_ASSERTION(!mCursor, "Shouldn't have this yet!");
+
+  InfallibleTArray<PBlobParent*> blobsParent;
+
+  if (NS_SUCCEEDED(aResultCode)) {
+    IDBDatabase* database = mIndex->ObjectStore()->Transaction()->Database();
+    NS_ASSERTION(database, "This should never be null!");
+
+    ContentParent* contentParent = database->GetContentParent();
+    NS_ASSERTION(contentParent, "This should never be null!");
+
+    FileManager* fileManager = database->Manager();
+    NS_ASSERTION(fileManager, "This should never be null!");
+
+    const nsTArray<StructuredCloneFile>& files = mCloneReadInfo.mFiles;
+
+    aResultCode =
+      IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files,
+                                           blobsParent);
+    if (NS_FAILED(aResultCode)) {
+      NS_WARNING("ConvertBlobsToActors failed!");
+    }
   }
 
-  NS_ASSERTION(!mCursor, "Shouldn't have this yet!");
-
   if (NS_SUCCEEDED(aResultCode)) {
     nsresult rv = EnsureCursor();
     if (NS_FAILED(rv)) {
       NS_WARNING("EnsureCursor failed!");
       aResultCode = rv;
     }
   }
 
@@ -2236,16 +2312,17 @@ OpenCursorHelper::MaybeSendResponseToChi
                    "Shouldn't be possible!");
 
       IndexCursorConstructorParams params;
       params.requestParent() = requestActor;
       params.direction() = mDirection;
       params.key() = mKey;
       params.objectKey() = mObjectKey;
       params.optionalCloneInfo() = mSerializedCloneReadInfo;
+      params.blobsParent().SwapElements(blobsParent);
 
       IndexedDBCursorParent* cursorActor = new IndexedDBCursorParent(mCursor);
 
       if (!indexActor->SendPIndexedDBCursorConstructor(cursorActor, params)) {
         return Error;
       }
 
       openCursorResponse = cursorActor;
@@ -2319,21 +2396,17 @@ CountHelper::DoDatabaseWork(mozIStorageC
   mCount = stmt->AsInt64(0);
   return NS_OK;
 }
 
 nsresult
 CountHelper::GetSuccessResult(JSContext* aCx,
                               jsval* aVal)
 {
-  if (!JS_NewNumberValue(aCx, static_cast<double>(mCount), aVal)) {
-    NS_WARNING("Failed to make number value!");
-    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
-  }
-
+  *aVal = JS_NumberValue(static_cast<double>(mCount));
   return NS_OK;
 }
 
 void
 CountHelper::ReleaseMainThreadObjects()
 {
   mKeyRange = nullptr;
   IndexHelper::ReleaseMainThreadObjects();
--- a/dom/indexedDB/IDBIndex.h
+++ b/dom/indexedDB/IDBIndex.h
@@ -140,16 +140,17 @@ public:
                               IDBRequest** _retval);
 
   nsresult OpenCursorFromChildProcess(
                             IDBRequest* aRequest,
                             size_t aDirection,
                             const Key& aKey,
                             const Key& aObjectKey,
                             const SerializedStructuredCloneReadInfo& aCloneInfo,
+                            nsTArray<StructuredCloneFile>& aBlobs,
                             IDBCursor** _retval);
 
 private:
   IDBIndex();
   ~IDBIndex();
 
   nsRefPtr<IDBObjectStore> mObjectStore;
 
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -3,21 +3,25 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "base/basictypes.h"
 
 #include "IDBObjectStore.h"
 
+#include "mozilla/dom/ipc/nsIRemoteBlob.h"
 #include "nsIJSContextStack.h"
 #include "nsIOutputStream.h"
 
 #include "jsfriendapi.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/StructuredCloneTags.h"
+#include "mozilla/dom/ipc/Blob.h"
 #include "mozilla/storage.h"
 #include "nsContentUtils.h"
 #include "nsDOMClassInfo.h"
 #include "nsDOMFile.h"
 #include "nsDOMLists.h"
 #include "nsEventDispatcher.h"
 #include "nsJSUtils.h"
 #include "nsServiceManagerUtils.h"
@@ -40,16 +44,17 @@
 #include "ipc/IndexedDBChild.h"
 #include "ipc/IndexedDBParent.h"
 
 #include "IndexedDatabaseInlines.h"
 
 #define FILE_COPY_BUFFER_SIZE 32768
 
 USING_INDEXEDDB_NAMESPACE
+using namespace mozilla::dom;
 using namespace mozilla::dom::indexedDB::ipc;
 
 namespace {
 
 inline
 bool
 IgnoreNothing(PRUnichar c)
 {
@@ -576,16 +581,57 @@ GetAddInfoCallback(JSContext* aCx, void*
   if (!IDBObjectStore::SerializeValue(aCx, data->mCloneWriteInfo,
                                       data->mValue)) {
     return NS_ERROR_DOM_DATA_CLONE_ERR;
   }
 
   return NS_OK;
 }
 
+inline
+BlobChild*
+ActorFromRemoteBlob(nsIDOMBlob* aBlob)
+{
+  NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(aBlob);
+  if (remoteBlob) {
+    BlobChild* actor =
+      static_cast<BlobChild*>(static_cast<PBlobChild*>(remoteBlob->GetPBlob()));
+    NS_ASSERTION(actor, "Null actor?!");
+    return actor;
+  }
+  return nullptr;
+}
+
+inline
+bool
+ResolveMysteryBlob(nsIDOMBlob* aBlob, const nsString& aName,
+                   const nsString& aContentType, PRUint64 aSize)
+{
+  BlobChild* actor = ActorFromRemoteBlob(aBlob);
+  if (actor) {
+    return actor->SetMysteryBlobInfo(aName, aContentType, aSize);
+  }
+  return true;
+}
+
+inline
+bool
+ResolveMysteryBlob(nsIDOMBlob* aBlob, const nsString& aContentType,
+                   PRUint64 aSize)
+{
+  BlobChild* actor = ActorFromRemoteBlob(aBlob);
+  if (actor) {
+    return actor->SetMysteryBlobInfo(aContentType, aSize);
+  }
+  return true;
+}
+
 } // anonymous namespace
 
 JSClass IDBObjectStore::sDummyPropJSClass = {
   "dummy", 0,
   JS_PropertyStub,  JS_PropertyStub,
   JS_PropertyStub,  JS_StrictPropertyStub,
   JS_EnumerateStub, JS_ResolveStub,
   JS_ConvertStub
@@ -876,17 +922,18 @@ IDBObjectStore::GetStructuredCloneReadIn
     FileManager* fileManager = aDatabase->Manager();
 
     for (PRUint32 i = 0; i < array.Length(); i++) {
       const PRInt64& id = array[i];
 
       nsRefPtr<FileInfo> fileInfo = fileManager->GetFileInfo(id);
       NS_ASSERTION(fileInfo, "Null file info!");
 
-      aInfo.mFileInfos.AppendElement(fileInfo);
+      StructuredCloneFile* file = aInfo.mFiles.AppendElement();
+      file->mFileInfo.swap(fileInfo);
     }
   }
 
   aInfo.mDatabase = aDatabase;
 
   return NS_OK;
 }
 
@@ -949,19 +996,19 @@ IDBObjectStore::SerializeValue(JSContext
 
   return buffer.write(aCx, aValue, &callbacks, &aCloneWriteInfo);
 }
 
 static inline PRUint32
 SwapBytes(PRUint32 u)
 {
 #ifdef IS_BIG_ENDIAN
-  return ((u & 0x000000ffU) << 24) |                                          
-         ((u & 0x0000ff00U) << 8) |                                           
-         ((u & 0x00ff0000U) >> 8) |                                           
+  return ((u & 0x000000ffU) << 24) |
+         ((u & 0x0000ff00U) << 8) |
+         ((u & 0x00ff0000U) >> 8) |
          ((u & 0xff000000U) >> 24);
 #else
   return u;
 #endif
 }
 
 static inline double
 SwapBytes(PRUint64 u)
@@ -1000,34 +1047,38 @@ StructuredCloneReadString(JSStructuredCl
   if (!JS_ReadBytes(aReader, buffer, length)) {
     NS_WARNING("Failed to read type!");
     return false;
   }
 
   return true;
 }
 
+// static
 JSObject*
 IDBObjectStore::StructuredCloneReadCallback(JSContext* aCx,
                                             JSStructuredCloneReader* aReader,
                                             uint32_t aTag,
                                             uint32_t aData,
                                             void* aClosure)
 {
   if (aTag == SCTAG_DOM_FILEHANDLE || aTag == SCTAG_DOM_BLOB ||
       aTag == SCTAG_DOM_FILE) {
     StructuredCloneReadInfo* cloneReadInfo =
       reinterpret_cast<StructuredCloneReadInfo*>(aClosure);
 
-    if (aData >= cloneReadInfo->mFileInfos.Length()) {
+    if (aData >= cloneReadInfo->mFiles.Length()) {
       NS_ERROR("Bad blob index!");
       return nullptr;
     }
 
-    nsRefPtr<FileInfo> fileInfo = cloneReadInfo->mFileInfos[aData];
+    nsresult rv;
+
+    StructuredCloneFile& file = cloneReadInfo->mFiles[aData];
+    nsRefPtr<FileInfo>& fileInfo = file.mFileInfo;
     IDBDatabase* database = cloneReadInfo->mDatabase;
 
     if (aTag == SCTAG_DOM_FILEHANDLE) {
       nsCString type;
       if (!StructuredCloneReadString(aReader, type)) {
         return nullptr;
       }
       NS_ConvertUTF8toUTF16 convType(type);
@@ -1037,85 +1088,108 @@ IDBObjectStore::StructuredCloneReadCallb
         return nullptr;
       }
       NS_ConvertUTF8toUTF16 convName(name);
 
       nsRefPtr<IDBFileHandle> fileHandle = IDBFileHandle::Create(database,
         convName, convType, fileInfo.forget());
 
       jsval wrappedFileHandle;
-      nsresult rv =
+      rv =
         nsContentUtils::WrapNative(aCx, JS_GetGlobalForScopeChain(aCx),
                                    static_cast<nsIDOMFileHandle*>(fileHandle),
                                    &NS_GET_IID(nsIDOMFileHandle),
                                    &wrappedFileHandle);
       if (NS_FAILED(rv)) {
         NS_WARNING("Failed to wrap native!");
         return nullptr;
       }
 
       return JSVAL_TO_OBJECT(wrappedFileHandle);
     }
 
-    FileManager* fileManager = database->Manager();
-
-    nsCOMPtr<nsIFile> directory = fileManager->GetDirectory();
-    if (!directory) {
-      NS_WARNING("Failed to get directory!");
-      return nullptr;
-    }
-
-    nsCOMPtr<nsIFile> nativeFile =
-      fileManager->GetFileForId(directory, fileInfo->Id());
-    if (!nativeFile) {
-      NS_WARNING("Failed to get file!");
-      return nullptr;
-    }
-
     PRUint64 size;
     if (!JS_ReadBytes(aReader, &size, sizeof(PRUint64))) {
       NS_WARNING("Failed to read size!");
       return nullptr;
     }
     size = SwapBytes(size);
 
     nsCString type;
     if (!StructuredCloneReadString(aReader, type)) {
       return nullptr;
     }
     NS_ConvertUTF8toUTF16 convType(type);
 
+    nsCOMPtr<nsIFile> nativeFile;
+    if (!file.mFile) {
+      FileManager* fileManager = database->Manager();
+        NS_ASSERTION(fileManager, "This should never be null!");
+
+      nsCOMPtr<nsIFile> directory = fileManager->GetDirectory();
+      if (!directory) {
+        NS_WARNING("Failed to get directory!");
+        return nullptr;
+      }
+
+      nativeFile = fileManager->GetFileForId(directory, fileInfo->Id());
+      if (!nativeFile) {
+        NS_WARNING("Failed to get file!");
+        return nullptr;
+      }
+    }
+
     if (aTag == SCTAG_DOM_BLOB) {
-      nsCOMPtr<nsIDOMBlob> blob = new nsDOMFileFile(convType, size,
-                                                    nativeFile, fileInfo);
+      nsCOMPtr<nsIDOMBlob> domBlob;
+      if (file.mFile) {
+        if (!ResolveMysteryBlob(file.mFile, convType, size)) {
+          return nullptr;
+        }
+        domBlob = file.mFile;
+      }
+      else {
+        domBlob = new nsDOMFileFile(convType, size, nativeFile, fileInfo);
+      }
 
       jsval wrappedBlob;
-      nsresult rv =
-        nsContentUtils::WrapNative(aCx, JS_GetGlobalForScopeChain(aCx), blob,
+       rv =
+        nsContentUtils::WrapNative(aCx, JS_GetGlobalForScopeChain(aCx), domBlob,
                                    &NS_GET_IID(nsIDOMBlob), &wrappedBlob);
       if (NS_FAILED(rv)) {
         NS_WARNING("Failed to wrap native!");
         return nullptr;
       }
 
       return JSVAL_TO_OBJECT(wrappedBlob);
     }
 
+    NS_ASSERTION(aTag == SCTAG_DOM_FILE, "Huh?!");
+
     nsCString name;
     if (!StructuredCloneReadString(aReader, name)) {
       return nullptr;
     }
     NS_ConvertUTF8toUTF16 convName(name);
 
-    nsCOMPtr<nsIDOMFile> file = new nsDOMFileFile(convName, convType, size,
-                                                  nativeFile, fileInfo);
+    nsCOMPtr<nsIDOMFile> domFile;
+    if (file.mFile) {
+      if (!ResolveMysteryBlob(file.mFile, convName, convType, size)) {
+        return nullptr;
+      }
+      domFile = do_QueryInterface(file.mFile);
+      NS_ASSERTION(domFile, "This should never fail!");
+    }
+    else {
+      domFile = new nsDOMFileFile(convName, convType, size, nativeFile,
+                                  fileInfo);
+    }
 
     jsval wrappedFile;
-    nsresult rv =
-      nsContentUtils::WrapNative(aCx, JS_GetGlobalForScopeChain(aCx), file,
+    rv =
+      nsContentUtils::WrapNative(aCx, JS_GetGlobalForScopeChain(aCx), domFile,
                                  &NS_GET_IID(nsIDOMFile), &wrappedFile);
     if (NS_FAILED(rv)) {
       NS_WARNING("Failed to wrap native!");
       return nullptr;
     }
 
     return JSVAL_TO_OBJECT(wrappedFile);
   }
@@ -1125,16 +1199,17 @@ IDBObjectStore::StructuredCloneReadCallb
 
   if (runtimeCallbacks) {
     return runtimeCallbacks->read(aCx, aReader, aTag, aData, nullptr);
   }
 
   return nullptr;
 }
 
+// static
 JSBool
 IDBObjectStore::StructuredCloneWriteCallback(JSContext* aCx,
                                              JSStructuredCloneWriter* aWriter,
                                              JSObject* aObj,
                                              void* aClosure)
 {
   StructuredCloneWriteInfo* cloneWriteInfo =
     reinterpret_cast<StructuredCloneWriteInfo*>(aClosure);
@@ -1155,39 +1230,38 @@ IDBObjectStore::StructuredCloneWriteCall
   if (wrappedNative) {
     nsISupports* supports = wrappedNative->Native();
 
     IDBTransaction* transaction = cloneWriteInfo->mTransaction;
     FileManager* fileManager = transaction->Database()->Manager();
 
     nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(supports);
     if (blob) {
+      nsCOMPtr<nsIInputStream> inputStream;
+
       // Check if it is a blob created from this db or the blob was already
       // stored in this db
-
       nsRefPtr<FileInfo> fileInfo = transaction->GetFileInfo(blob);
-      nsCOMPtr<nsIInputStream> inputStream;
-
-      if (!fileInfo) {
+      if (!fileInfo && fileManager) {
         fileInfo = blob->GetFileInfo(fileManager);
-      }
-
-      if (!fileInfo) {
-        fileInfo = fileManager->GetNewFileInfo();
+
         if (!fileInfo) {
-          NS_WARNING("Failed to get new file info!");
-          return false;
+          fileInfo = fileManager->GetNewFileInfo();
+          if (!fileInfo) {
+            NS_WARNING("Failed to get new file info!");
+            return false;
+          }
+
+          if (NS_FAILED(blob->GetInternalStream(getter_AddRefs(inputStream)))) {
+            NS_WARNING("Failed to get internal steam!");
+            return false;
+          }
+
+          transaction->AddFileInfo(blob, fileInfo);
         }
-
-        if (NS_FAILED(blob->GetInternalStream(getter_AddRefs(inputStream)))) {
-          NS_WARNING("Failed to get internal steam!");
-          return false;
-        }
-
-        transaction->AddFileInfo(blob, fileInfo);
       }
 
       PRUint64 size;
       if (NS_FAILED(blob->GetSize(&size))) {
         NS_WARNING("Failed to get size!");
         return false;
       }
       size = SwapBytes(size);
@@ -1199,32 +1273,32 @@ IDBObjectStore::StructuredCloneWriteCall
       }
       NS_ConvertUTF16toUTF8 convType(type);
       PRUint32 convTypeLength = SwapBytes(convType.Length());
 
       nsCOMPtr<nsIDOMFile> file = do_QueryInterface(blob);
 
       if (!JS_WriteUint32Pair(aWriter, file ? SCTAG_DOM_FILE : SCTAG_DOM_BLOB,
                               cloneWriteInfo->mFiles.Length()) ||
-          !JS_WriteBytes(aWriter, &size, sizeof(PRUint64)) ||
-          !JS_WriteBytes(aWriter, &convTypeLength, sizeof(PRUint32)) ||
+          !JS_WriteBytes(aWriter, &size, sizeof(size)) ||
+          !JS_WriteBytes(aWriter, &convTypeLength, sizeof(convTypeLength)) ||
           !JS_WriteBytes(aWriter, convType.get(), convType.Length())) {
         return false;
       }
 
       if (file) {
         nsString name;
         if (NS_FAILED(file->GetName(name))) {
           NS_WARNING("Failed to get name!");
           return false;
         }
         NS_ConvertUTF16toUTF8 convName(name);
         PRUint32 convNameLength = SwapBytes(convName.Length());
 
-        if (!JS_WriteBytes(aWriter, &convNameLength, sizeof(PRUint32)) ||
+        if (!JS_WriteBytes(aWriter, &convNameLength, sizeof(convNameLength)) ||
             !JS_WriteBytes(aWriter, convName.get(), convName.Length())) {
           return false;
         }
       }
 
       StructuredCloneFile* cloneFile = cloneWriteInfo->mFiles.AppendElement();
       cloneFile->mFile = blob.forget();
       cloneFile->mFileInfo = fileInfo.forget();
@@ -1280,16 +1354,17 @@ IDBObjectStore::StructuredCloneWriteCall
     js::GetContextStructuredCloneCallbacks(aCx);
   if (runtimeCallbacks) {
     return runtimeCallbacks->write(aCx, aWriter, aObj, nullptr);
   }
 
   return false;
 }
 
+// static
 nsresult
 IDBObjectStore::ConvertFileIdsToArray(const nsAString& aFileIds,
                                       nsTArray<PRInt64>& aResult)
 {
   nsCharSeparatedTokenizerTemplate<IgnoreNothing> tokenizer(aFileIds, ' ');
 
   while (tokenizer.hasMoreTokens()) {
     nsString token(tokenizer.nextToken());
@@ -1302,16 +1377,89 @@ IDBObjectStore::ConvertFileIdsToArray(co
     
     PRInt64* element = aResult.AppendElement();
     *element = id;
   }
 
   return NS_OK;
 }
 
+// static
+void
+IDBObjectStore::ConvertActorsToBlobs(
+                                   const InfallibleTArray<PBlobChild*>& aActors,
+                                   nsTArray<StructuredCloneFile>& aFiles)
+{
+  NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aFiles.IsEmpty(), "Should be empty!");
+
+  if (!aActors.IsEmpty()) {
+    ContentChild* contentChild = ContentChild::GetSingleton();
+    NS_ASSERTION(contentChild, "This should never be null!");
+
+    PRUint32 length = aActors.Length();
+    aFiles.SetCapacity(length);
+
+    for (PRUint32 index = 0; index < length; index++) {
+      BlobChild* actor = static_cast<BlobChild*>(aActors[index]);
+
+      StructuredCloneFile* file = aFiles.AppendElement();
+      file->mFile = actor->GetBlob();
+    }
+  }
+}
+
+// static
+nsresult
+IDBObjectStore::ConvertBlobsToActors(
+                                    ContentParent* aContentParent,
+                                    FileManager* aFileManager,
+                                    const nsTArray<StructuredCloneFile>& aFiles,
+                                    InfallibleTArray<PBlobParent*>& aActors)
+{
+  NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aContentParent, "Null contentParent!");
+  NS_ASSERTION(aFileManager, "Null file manager!");
+
+  if (!aFiles.IsEmpty()) {
+    nsCOMPtr<nsIFile> directory = aFileManager->GetDirectory();
+    if (!directory) {
+      NS_WARNING("Failed to get directory!");
+      return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+    }
+
+    PRUint32 fileCount = aFiles.Length();
+    aActors.SetCapacity(fileCount);
+
+    for (PRUint32 index = 0; index < fileCount; index++) {
+      const StructuredCloneFile& file = aFiles[index];
+      NS_ASSERTION(file.mFileInfo, "This should never be null!");
+
+      nsCOMPtr<nsIFile> nativeFile =
+        aFileManager->GetFileForId(directory, file.mFileInfo->Id());
+      if (!nativeFile) {
+        NS_WARNING("Failed to get file!");
+        return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+      }
+
+      nsCOMPtr<nsIDOMBlob> blob = new nsDOMFileFile(nativeFile);
+
+      BlobParent* actor =
+        aContentParent->GetOrCreateActorForBlob(blob);
+      NS_ASSERTION(actor, "This should never fail without aborting!");
+
+      aActors.AppendElement(actor);
+    }
+  }
+
+  return NS_OK;
+}
+
 IDBObjectStore::IDBObjectStore()
 : mId(LL_MININT),
   mKeyPath(0),
   mCachedKeyPath(JSVAL_VOID),
   mRooted(false),
   mAutoIncrement(false),
   mActorChild(nullptr),
   mActorParent(nullptr)
@@ -1443,16 +1591,17 @@ IDBObjectStore::AddOrPut(const jsval& aV
   return NS_OK;
 }
 
 nsresult
 IDBObjectStore::AddOrPutInternal(
                       const SerializedStructuredCloneWriteInfo& aCloneWriteInfo,
                       const Key& aKey,
                       const InfallibleTArray<IndexUpdateInfo>& aUpdateInfoArray,
+                      const nsTArray<nsCOMPtr<nsIDOMBlob> >& aBlobs,
                       bool aOverwrite,
                       IDBRequest** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   if (!mTransaction->IsOpen()) {
     return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
@@ -1466,16 +1615,58 @@ IDBObjectStore::AddOrPutInternal(
   NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   StructuredCloneWriteInfo cloneWriteInfo;
   if (!cloneWriteInfo.SetFromSerialized(aCloneWriteInfo)) {
     NS_WARNING("Failed to copy structured clone buffer!");
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
+  if (!aBlobs.IsEmpty()) {
+    FileManager* fileManager = Transaction()->Database()->Manager();
+    NS_ASSERTION(fileManager, "Null file manager?!");
+
+    PRUint32 length = aBlobs.Length();
+    cloneWriteInfo.mFiles.SetCapacity(length);
+
+    for (PRUint32 index = 0; index < length; index++) {
+      const nsCOMPtr<nsIDOMBlob>& blob = aBlobs[index];
+
+      nsCOMPtr<nsIInputStream> inputStream;
+
+      nsRefPtr<FileInfo> fileInfo = Transaction()->GetFileInfo(blob);
+      if (!fileInfo) {
+        fileInfo = blob->GetFileInfo(fileManager);
+
+        if (!fileInfo) {
+          fileInfo = fileManager->GetNewFileInfo();
+          if (!fileInfo) {
+            NS_WARNING("Failed to get new file info!");
+            return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+          }
+
+          if (NS_FAILED(blob->GetInternalStream(getter_AddRefs(inputStream)))) {
+            NS_WARNING("Failed to get internal steam!");
+            return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+          }
+
+          // XXXbent This is where we should send a message back to the child to
+          //         update the file id.
+
+          Transaction()->AddFileInfo(blob, fileInfo);
+        }
+      }
+
+      StructuredCloneFile* file = cloneWriteInfo.mFiles.AppendElement();
+      file->mFile = blob;
+      file->mFileInfo.swap(fileInfo);
+      file->mInputStream.swap(inputStream);
+    }
+  }
+
   Key key(aKey);
 
   nsTArray<IndexUpdateInfo> updateInfo(aUpdateInfoArray);
 
   nsRefPtr<AddHelper> helper =
     new AddHelper(mTransaction, request, this, cloneWriteInfo, key, aOverwrite,
                   updateInfo);
 
@@ -1643,16 +1834,17 @@ IDBObjectStore::OpenCursorInternal(IDBKe
 }
 
 nsresult
 IDBObjectStore::OpenCursorFromChildProcess(
                             IDBRequest* aRequest,
                             size_t aDirection,
                             const Key& aKey,
                             const SerializedStructuredCloneReadInfo& aCloneInfo,
+                            nsTArray<StructuredCloneFile>& aBlobs,
                             IDBCursor** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION((!aCloneInfo.dataLength && !aCloneInfo.data) ||
                (aCloneInfo.dataLength && aCloneInfo.data),
                "Inconsistent clone info!");
 
   IDBCursor::Direction direction =
@@ -1660,16 +1852,18 @@ IDBObjectStore::OpenCursorFromChildProce
 
   StructuredCloneReadInfo cloneInfo;
 
   if (!cloneInfo.SetFromSerialized(aCloneInfo)) {
     NS_WARNING("Failed to copy clone buffer!");
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
+  cloneInfo.mFiles.SwapElements(aBlobs);
+
   nsRefPtr<IDBCursor> cursor =
     IDBCursor::Create(aRequest, mTransaction, this, direction, Key(),
                       EmptyCString(), EmptyCString(), aKey, cloneInfo);
   NS_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   NS_ASSERTION(!cloneInfo.mCloneBuffer.data(), "Should have swapped!");
 
   cursor.forget(_retval);
@@ -2541,16 +2735,41 @@ AddHelper::ReleaseMainThreadObjects()
 nsresult
 AddHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams)
 {
   AddPutParams commonParams;
   commonParams.cloneInfo() = mCloneWriteInfo;
   commonParams.key() = mKey;
   commonParams.indexUpdateInfos().AppendElements(mIndexUpdateInfo);
 
+  const nsTArray<StructuredCloneFile>& files = mCloneWriteInfo.mFiles;
+
+  if (!files.IsEmpty()) {
+    PRUint32 fileCount = files.Length();
+
+    InfallibleTArray<PBlobChild*>& blobsChild = commonParams.blobsChild();
+    blobsChild.SetCapacity(fileCount);
+
+    ContentChild* contentChild = ContentChild::GetSingleton();
+    NS_ASSERTION(contentChild, "This should never be null!");
+
+    for (PRUint32 index = 0; index < fileCount; index++) {
+      const StructuredCloneFile& file = files[index];
+
+      NS_ASSERTION(file.mFile, "This should never be null!");
+      NS_ASSERTION(!file.mFileInfo, "This is not yet supported!");
+
+      BlobChild* actor =
+        contentChild->GetOrCreateActorForBlob(file.mFile);
+      NS_ASSERTION(actor, "This should never fail without aborting!");
+
+      blobsChild.AppendElement(actor);
+    }
+  }
+
   if (mOverwrite) {
     PutParams putParams;
     putParams.commonParams() = commonParams;
     aParams = putParams;
   }
   else {
     AddParams addParams;
     addParams.commonParams() = commonParams;
@@ -2685,57 +2904,76 @@ GetHelper::MaybeSendResponseToChildProce
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
   if (!actor) {
     return Success_NotSent;
   }
 
-  if (!mCloneReadInfo.mFileInfos.IsEmpty()) {
-    NS_WARNING("No support for transferring blobs across processes yet!");
-    return Error;
+  InfallibleTArray<PBlobParent*> blobsParent;
+
+  if (NS_SUCCEEDED(aResultCode)) {
+    IDBDatabase* database = mObjectStore->Transaction()->Database();
+    NS_ASSERTION(database, "This should never be null!");
+
+    ContentParent* contentParent = database->GetContentParent();
+    NS_ASSERTION(contentParent, "This should never be null!");
+
+    FileManager* fileManager = database->Manager();
+    NS_ASSERTION(fileManager, "This should never be null!");
+
+    const nsTArray<StructuredCloneFile>& files = mCloneReadInfo.mFiles;
+
+    aResultCode =
+      IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files,
+                                           blobsParent);
+    if (NS_FAILED(aResultCode)) {
+      NS_WARNING("ConvertBlobsToActors failed!");
+  }
   }
 
   ResponseValue response;
   if (NS_FAILED(aResultCode)) {
     response = aResultCode;
   }
   else {
-    SerializedStructuredCloneReadInfo readInfo;
-    readInfo = mCloneReadInfo;
-    GetResponse getResponse = readInfo;
+    GetResponse getResponse;
+    getResponse.cloneInfo() = mCloneReadInfo;
+    getResponse.blobsParent().SwapElements(blobsParent);
     response = getResponse;
   }
 
   if (!actor->Send__delete__(actor, response)) {
     return Error;
   }
 
   return Success_Sent;
 }
 
 nsresult
 GetHelper::UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
 {
   NS_ASSERTION(aResponseValue.type() == ResponseValue::TGetResponse,
                "Bad response type!");
 
-  const SerializedStructuredCloneReadInfo& cloneInfo =
-    aResponseValue.get_GetResponse().cloneInfo();
+  const GetResponse& getResponse = aResponseValue.get_GetResponse();
+  const SerializedStructuredCloneReadInfo& cloneInfo = getResponse.cloneInfo();
 
   NS_ASSERTION((!cloneInfo.dataLength && !cloneInfo.data) ||
                (cloneInfo.dataLength && cloneInfo.data),
                "Inconsistent clone info!");
 
   if (!mCloneReadInfo.SetFromSerialized(cloneInfo)) {
     NS_WARNING("Failed to copy clone buffer!");
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
+  IDBObjectStore::ConvertActorsToBlobs(getResponse.blobsChild(),
+                                       mCloneReadInfo.mFiles);
   return NS_OK;
 }
 
 nsresult
 DeleteHelper::DoDatabaseWork(mozIStorageConnection* /*aConnection */)
 {
   NS_ASSERTION(mKeyRange, "Must have a key range here!");
 
@@ -3094,23 +3332,40 @@ OpenCursorHelper::MaybeSendResponseToChi
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
   if (!actor) {
     return Success_NotSent;
   }
 
-  if (!mCloneReadInfo.mFileInfos.IsEmpty()) {
-    NS_WARNING("No support for transferring blobs across processes yet!");
-    return Error;
+  NS_ASSERTION(!mCursor, "Shouldn't have this yet!");
+
+  InfallibleTArray<PBlobParent*> blobsParent;
+
+  if (NS_SUCCEEDED(aResultCode)) {
+    IDBDatabase* database = mObjectStore->Transaction()->Database();
+    NS_ASSERTION(database, "This should never be null!");
+
+    ContentParent* contentParent = database->GetContentParent();
+    NS_ASSERTION(contentParent, "This should never be null!");
+
+    FileManager* fileManager = database->Manager();
+    NS_ASSERTION(fileManager, "This should never be null!");
+
+    const nsTArray<StructuredCloneFile>& files = mCloneReadInfo.mFiles;
+
+    aResultCode =
+      IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files,
+                                           blobsParent);
+    if (NS_FAILED(aResultCode)) {
+      NS_WARNING("ConvertBlobsToActors failed!");
+    }
   }
 
-  NS_ASSERTION(!mCursor, "Shouldn't have this yet!");
-
   if (NS_SUCCEEDED(aResultCode)) {
     nsresult rv = EnsureCursor();
     if (NS_FAILED(rv)) {
       NS_WARNING("EnsureCursor failed!");
       aResultCode = rv;
     }
   }
 
@@ -3136,16 +3391,17 @@ OpenCursorHelper::MaybeSendResponseToChi
                    mSerializedCloneReadInfo.dataLength,
                    "Shouldn't be possible!");
 
       ObjectStoreCursorConstructorParams params;
       params.requestParent() = requestActor;
       params.direction() = mDirection;
       params.key() = mKey;
       params.cloneInfo() = mSerializedCloneReadInfo;
+      params.blobsParent().SwapElements(blobsParent);
 
       IndexedDBCursorParent* cursorActor = new IndexedDBCursorParent(mCursor);
 
       if (!objectStoreActor->SendPIndexedDBCursorConstructor(cursorActor,
                                                              params)) {
         return Error;
       }
 
@@ -3520,41 +3776,65 @@ GetAllHelper::MaybeSendResponseToChildPr
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
   if (!actor) {
     return Success_NotSent;
   }
 
-  for (PRUint32 index = 0; index < mCloneReadInfos.Length(); index++) {
-    if (!mCloneReadInfos[index].mFileInfos.IsEmpty()) {
-      NS_WARNING("No support for transferring blobs across processes yet!");
-      return Error;
+    GetAllResponse getAllResponse;
+
+  if (NS_SUCCEEDED(aResultCode) && !mCloneReadInfos.IsEmpty()) {
+    IDBDatabase* database = mObjectStore->Transaction()->Database();
+    NS_ASSERTION(database, "This should never be null!");
+
+    ContentParent* contentParent = database->GetContentParent();
+    NS_ASSERTION(contentParent, "This should never be null!");
+
+    FileManager* fileManager = database->Manager();
+    NS_ASSERTION(fileManager, "This should never be null!");
+
+    PRUint32 length = mCloneReadInfos.Length();
+
+    InfallibleTArray<SerializedStructuredCloneReadInfo>& infos =
+      getAllResponse.cloneInfos();
+    infos.SetCapacity(length);
+
+    InfallibleTArray<BlobArray>& blobArrays = getAllResponse.blobs();
+    blobArrays.SetCapacity(length);
+
+    for (PRUint32 index = 0;
+         NS_SUCCEEDED(aResultCode) && index < length;
+         index++) {
+      // Append the structured clone data.
+      const StructuredCloneReadInfo& clone = mCloneReadInfos[index];
+      SerializedStructuredCloneReadInfo* info = infos.AppendElement();
+      *info = clone;
+
+      // Now take care of the files.
+      const nsTArray<StructuredCloneFile>& files = clone.mFiles;
+      BlobArray* blobArray = blobArrays.AppendElement();
+      InfallibleTArray<PBlobParent*>& blobs = blobArray->blobsParent();
+
+      aResultCode =
+        IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files,
+                                             blobs);
+      if (NS_FAILED(aResultCode)) {
+        NS_WARNING("ConvertBlobsToActors failed!");
+        break;
+    }
     }
   }
 
   ResponseValue response;
   if (NS_FAILED(aResultCode)) {
     response = aResultCode;
   }
   else {
-    GetAllResponse getAllResponse;
-
-    InfallibleTArray<SerializedStructuredCloneReadInfo>& infos =
-      getAllResponse.cloneInfos();
-
-    infos.SetCapacity(mCloneReadInfos.Length());
-
-    for (PRUint32 index = 0; index < mCloneReadInfos.Length(); index++) {
-      SerializedStructuredCloneReadInfo* info = infos.AppendElement();
-      *info = mCloneReadInfos[index];
-    }
-
-    getAllResponse = infos;
     response = getAllResponse;
   }
 
   if (!actor->Send__delete__(actor, response)) {
     return Error;
   }
 
   return Success_Sent;
@@ -3562,29 +3842,34 @@ GetAllHelper::MaybeSendResponseToChildPr
 
 nsresult
 GetAllHelper::UnpackResponseFromParentProcess(
                                             const ResponseValue& aResponseValue)
 {
   NS_ASSERTION(aResponseValue.type() == ResponseValue::TGetAllResponse,
                "Bad response type!");
 
+  const GetAllResponse& getAllResponse = aResponseValue.get_GetAllResponse();
   const InfallibleTArray<SerializedStructuredCloneReadInfo>& cloneInfos =
-    aResponseValue.get_GetAllResponse().cloneInfos();
+    getAllResponse.cloneInfos();
+  const InfallibleTArray<BlobArray>& blobArrays = getAllResponse.blobs();
 
   mCloneReadInfos.SetCapacity(cloneInfos.Length());
 
   for (PRUint32 index = 0; index < cloneInfos.Length(); index++) {
     const SerializedStructuredCloneReadInfo srcInfo = cloneInfos[index];
+    const InfallibleTArray<PBlobChild*> blobs = blobArrays[index].blobsChild();
 
     StructuredCloneReadInfo* destInfo = mCloneReadInfos.AppendElement();
     if (!destInfo->SetFromSerialized(srcInfo)) {
       NS_WARNING("Failed to copy clone buffer!");
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
+
+    IDBObjectStore::ConvertActorsToBlobs(blobs, destInfo->mFiles);
   }
 
   return NS_OK;
 }
 
 nsresult
 CountHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
 {
@@ -3648,21 +3933,17 @@ CountHelper::DoDatabaseWork(mozIStorageC
   mCount = stmt->AsInt64(0);
   return NS_OK;
 }
 
 nsresult
 CountHelper::GetSuccessResult(JSContext* aCx,
                               jsval* aVal)
 {
-  if (!JS_NewNumberValue(aCx, static_cast<double>(mCount), aVal)) {
-    NS_WARNING("Failed to make number value!");
-    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
-  }
-
+  *aVal = JS_NumberValue(static_cast<double>(mCount));
   return NS_OK;
 }
 
 void
 CountHelper::ReleaseMainThreadObjects()
 {
   mKeyRange = nullptr;
   ObjectStoreHelper::ReleaseMainThreadObjects();
--- a/dom/indexedDB/IDBObjectStore.h
+++ b/dom/indexedDB/IDBObjectStore.h
@@ -12,34 +12,42 @@
 #include "nsIIDBObjectStore.h"
 #include "nsIIDBTransaction.h"
 
 #include "nsCycleCollectionParticipant.h"
 
 #include "mozilla/dom/indexedDB/IDBTransaction.h"
 #include "mozilla/dom/indexedDB/KeyPath.h"
 
+class nsIDOMBlob;
 class nsIScriptContext;
 class nsPIDOMWindow;
 
+namespace mozilla {
+namespace dom {
+class ContentParent;
+class PBlobChild;
+class PBlobParent;
+}
+}
+
 BEGIN_INDEXEDDB_NAMESPACE
 
 class AsyncConnectionHelper;
+class FileManager;
 class IDBCursor;
 class IDBKeyRange;
 class IDBRequest;
 class IndexedDBObjectStoreChild;
 class IndexedDBObjectStoreParent;
 class Key;
 
 struct IndexInfo;
 struct IndexUpdateInfo;
 struct ObjectStoreInfo;
-struct StructuredCloneReadInfo;
-struct StructuredCloneWriteInfo;
 
 class IDBObjectStore MOZ_FINAL : public nsIIDBObjectStore
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSIIDBOBJECTSTORE
 
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBObjectStore)
@@ -98,16 +106,28 @@ public:
                                JSStructuredCloneWriter* aWriter,
                                JSObject* aObj,
                                void* aClosure);
 
   static nsresult
   ConvertFileIdsToArray(const nsAString& aFileIds,
                         nsTArray<PRInt64>& aResult);
 
+  // Called only in the main process.
+  static nsresult
+  ConvertBlobsToActors(ContentParent* aContentParent,
+                       FileManager* aFileManager,
+                       const nsTArray<StructuredCloneFile>& aFiles,
+                       InfallibleTArray<PBlobParent*>& aActors);
+
+  // Called only in the child process.
+  static void
+  ConvertActorsToBlobs(const InfallibleTArray<PBlobChild*>& aActors,
+                       nsTArray<StructuredCloneFile>& aFiles);
+
   const nsString& Name() const
   {
     return mName;
   }
 
   bool IsAutoIncrement() const
   {
     return mAutoIncrement;
@@ -178,16 +198,17 @@ public:
   nsresult
   IndexInternal(const nsAString& aName,
                 IDBIndex** _retval);
 
   nsresult AddOrPutInternal(
                       const SerializedStructuredCloneWriteInfo& aCloneWriteInfo,
                       const Key& aKey,
                       const InfallibleTArray<IndexUpdateInfo>& aUpdateInfoArray,
+                      const nsTArray<nsCOMPtr<nsIDOMBlob> >& aBlobs,
                       bool aOverwrite,
                       IDBRequest** _retval);
 
   nsresult GetInternal(IDBKeyRange* aKeyRange,
                        JSContext* aCx,
                        IDBRequest** _retval);
 
   nsresult GetAllInternal(IDBKeyRange* aKeyRange,
@@ -211,16 +232,17 @@ public:
                               JSContext* aCx,
                               IDBRequest** _retval);
 
   nsresult OpenCursorFromChildProcess(
                             IDBRequest* aRequest,
                             size_t aDirection,
                             const Key& aKey,
                             const SerializedStructuredCloneReadInfo& aCloneInfo,
+                            nsTArray<StructuredCloneFile>& aBlobs,
                             IDBCursor** _retval);
 
   void
   SetInfo(ObjectStoreInfo* aInfo);
 
   static JSClass sDummyPropJSClass;
 
 protected:
--- a/dom/indexedDB/IndexedDatabase.h
+++ b/dom/indexedDB/IndexedDatabase.h
@@ -44,36 +44,50 @@ enum FactoryPrivilege {
 template <class T>
 void SwapData(T& aData1, T& aData2)
 {
   T temp = aData2;
   aData2 = aData1;
   aData1 = temp;
 }
 
+struct StructuredCloneFile
+{
+  bool operator==(const StructuredCloneFile& aOther) const
+  {
+    return this->mFile == aOther.mFile &&
+           this->mFileInfo == aOther.mFileInfo &&
+           this->mInputStream == aOther.mInputStream;
+  }
+
+  nsCOMPtr<nsIDOMBlob> mFile;
+  nsRefPtr<FileInfo> mFileInfo;
+  nsCOMPtr<nsIInputStream> mInputStream;
+};
+
 struct SerializedStructuredCloneReadInfo;
 
 struct StructuredCloneReadInfo
 {
   // In IndexedDatabaseInlines.h
   inline StructuredCloneReadInfo();
 
   void Swap(StructuredCloneReadInfo& aCloneReadInfo)
   {
     mCloneBuffer.swap(aCloneReadInfo.mCloneBuffer);
-    mFileInfos.SwapElements(aCloneReadInfo.mFileInfos);
+    mFiles.SwapElements(aCloneReadInfo.mFiles);
     SwapData(mDatabase, aCloneReadInfo.mDatabase);
   }
 
   // In IndexedDatabaseInlines.h
   inline bool
   SetFromSerialized(const SerializedStructuredCloneReadInfo& aOther);
 
   JSAutoStructuredCloneBuffer mCloneBuffer;
-  nsTArray<nsRefPtr<FileInfo> > mFileInfos;
+  nsTArray<StructuredCloneFile> mFiles;
   IDBDatabase* mDatabase;
 };
 
 struct SerializedStructuredCloneReadInfo
 {
   SerializedStructuredCloneReadInfo()
   : data(nullptr), dataLength(0)
   { }
@@ -93,30 +107,16 @@ struct SerializedStructuredCloneReadInfo
     return *this;
   }
 
   // Make sure to update ipc/SerializationHelpers.h when changing members here!
   uint64_t* data;
   size_t dataLength;
 };
 
-struct StructuredCloneFile
-{
-  bool operator==(const StructuredCloneFile& aOther) const
-  {
-    return this->mFile == aOther.mFile &&
-           this->mFileInfo == aOther.mFileInfo &&
-           this->mInputStream == aOther.mInputStream;
-  }
-
-  nsCOMPtr<nsIDOMBlob> mFile;
-  nsRefPtr<FileInfo> mFileInfo;
-  nsCOMPtr<nsIInputStream> mInputStream;
-};
-
 struct SerializedStructuredCloneWriteInfo;
 
 struct StructuredCloneWriteInfo
 {
   // In IndexedDatabaseInlines.h
   inline StructuredCloneWriteInfo();
 
   void Swap(StructuredCloneWriteInfo& aCloneWriteInfo)
--- a/dom/indexedDB/IndexedDatabaseInlines.h
+++ b/dom/indexedDB/IndexedDatabaseInlines.h
@@ -45,17 +45,17 @@ bool
 StructuredCloneReadInfo::SetFromSerialized(
                                 const SerializedStructuredCloneReadInfo& aOther)
 {
   if (aOther.dataLength &&
       !mCloneBuffer.copy(aOther.data, aOther.dataLength)) {
     return false;
   }
 
-  mFileInfos.Clear();
+  mFiles.Clear();
   return true;
 }
 
 inline
 void
 AppendConditionClause(const nsACString& aColumnName,
                       const nsACString& aArgName,
                       bool aLessThan,
--- a/dom/indexedDB/IndexedDatabaseManager.cpp
+++ b/dom/indexedDB/IndexedDatabaseManager.cpp
@@ -212,33 +212,36 @@ IndexedDatabaseManager::GetOrCreate()
       instance->mCurrentWindowIndex = BAD_TLS_INDEX;
       return nullptr;
     }
 
     nsresult rv;
 
     if (sIsMainProcess) {
       nsCOMPtr<nsIFile> dbBaseDirectory;
-      rv = NS_GetSpecialDirectory(NS_APP_INDEXEDDB_PARENT_DIR, getter_AddRefs(dbBaseDirectory));
+      rv = NS_GetSpecialDirectory(NS_APP_INDEXEDDB_PARENT_DIR,
+                                  getter_AddRefs(dbBaseDirectory));
       if (NS_FAILED(rv)) {
-          rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(dbBaseDirectory));
+          rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+                                      getter_AddRefs(dbBaseDirectory));
       }
       NS_ENSURE_SUCCESS(rv, nullptr);
 
       rv = dbBaseDirectory->Append(NS_LITERAL_STRING("indexedDB"));
       NS_ENSURE_SUCCESS(rv, nullptr);
 
       rv = dbBaseDirectory->GetPath(instance->mDatabaseBasePath);
       NS_ENSURE_SUCCESS(rv, nullptr);
 
       // Make a lazy thread for any IO we need (like clearing or enumerating the
       // contents of indexedDB database directories).
-      instance->mIOThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS,
-                                               NS_LITERAL_CSTRING("IndexedDB I/O"),
-                                               LazyIdleThread::ManualShutdown);
+      instance->mIOThread =
+        new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS,
+                           NS_LITERAL_CSTRING("IndexedDB I/O"),
+                           LazyIdleThread::ManualShutdown);
 
       // We need one quota callback object to hand to SQLite.
       instance->mQuotaCallbackSingleton = new QuotaCallback();
 
       // Make a timer here to avoid potential failures later. We don't actually
       // initialize the timer until shutdown.
       instance->mShutdownTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
       NS_ENSURE_TRUE(instance->mShutdownTimer, nullptr);
@@ -1905,17 +1908,18 @@ IndexedDatabaseManager::InitWindowless(c
   // exceptions.
   nsCOMPtr<nsIDOMScriptObjectFactory> sof(do_GetService(kDOMSOF_CID));
 
   JSObject* obj = JSVAL_TO_OBJECT(aObj);
 
   JSObject* global = JS_GetGlobalForObject(aCx, obj);
 
   nsRefPtr<IDBFactory> factory;
-  nsresult rv = IDBFactory::Create(aCx, global, getter_AddRefs(factory));
+  nsresult rv =
+    IDBFactory::Create(aCx, global, nullptr, getter_AddRefs(factory));
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   NS_ASSERTION(factory, "This should never fail for chrome!");
 
   jsval indexedDBVal;
   rv = nsContentUtils::WrapNative(aCx, obj, factory, &indexedDBVal);
   NS_ENSURE_SUCCESS(rv, rv);
 
--- a/dom/indexedDB/OpenDatabaseHelper.cpp
+++ b/dom/indexedDB/OpenDatabaseHelper.cpp
@@ -2101,20 +2101,18 @@ OpenDatabaseHelper::EnsureSuccessResult(
 
     NS_ASSERTION(mObjectStores.IsEmpty(), "Should have swapped!");
   }
 
   dbInfo->nextObjectStoreId = mLastObjectStoreId + 1;
   dbInfo->nextIndexId = mLastIndexId + 1;
 
   nsRefPtr<IDBDatabase> database =
-    IDBDatabase::Create(mOpenDBRequest,
-                        dbInfo.forget(),
-                        mASCIIOrigin,
-                        mFileManager);
+    IDBDatabase::Create(mOpenDBRequest, dbInfo.forget(), mASCIIOrigin,
+                        mFileManager, mContentParent);
   if (!database) {
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
   NS_ASSERTION(!mDatabase, "Shouldn't have a database yet!");
   mDatabase.swap(database);
 
   return NS_OK;
--- a/dom/indexedDB/OpenDatabaseHelper.h
+++ b/dom/indexedDB/OpenDatabaseHelper.h
@@ -9,32 +9,40 @@
 #include "DatabaseInfo.h"
 #include "IDBDatabase.h"
 #include "IDBRequest.h"
 
 #include "nsIRunnable.h"
 
 class mozIStorageConnection;
 
+namespace mozilla {
+namespace dom {
+class ContentParent;
+}
+}
+
 BEGIN_INDEXEDDB_NAMESPACE
 
 class OpenDatabaseHelper : public HelperBase
 {
 public:
   OpenDatabaseHelper(IDBOpenDBRequest* aRequest,
                      const nsAString& aName,
                      const nsACString& aASCIIOrigin,
                      PRUint64 aRequestedVersion,
                      bool aForDeletion,
+                     mozilla::dom::ContentParent* aContentParent,
                      FactoryPrivilege aPrivilege)
     : HelperBase(aRequest), mOpenDBRequest(aRequest), mName(aName),
       mASCIIOrigin(aASCIIOrigin), mRequestedVersion(aRequestedVersion),
       mForDeletion(aForDeletion), mPrivilege(aPrivilege), mDatabaseId(nullptr),
-      mCurrentVersion(0), mLastObjectStoreId(0), mLastIndexId(0),
-      mState(eCreated), mResultCode(NS_OK), mLoadDBMetadata(false)
+      mContentParent(aContentParent), mCurrentVersion(0), mLastObjectStoreId(0),
+      mLastIndexId(0), mState(eCreated), mResultCode(NS_OK),
+      mLoadDBMetadata(false)
   {
     NS_ASSERTION(!aForDeletion || !aRequestedVersion,
                  "Can't be for deletion and request a version!");
   }
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIRUNNABLE
 
@@ -98,16 +106,17 @@ protected:
   // In-params.
   nsRefPtr<IDBOpenDBRequest> mOpenDBRequest;
   nsString mName;
   nsCString mASCIIOrigin;
   PRUint64 mRequestedVersion;
   bool mForDeletion;
   FactoryPrivilege mPrivilege;
   nsCOMPtr<nsIAtom> mDatabaseId;
+  mozilla::dom::ContentParent* mContentParent;
 
   // Out-params.
   nsTArray<nsRefPtr<ObjectStoreInfo> > mObjectStores;
   PRUint64 mCurrentVersion;
   nsString mDatabaseFilePath;
   PRInt64 mLastObjectStoreId;
   PRInt64 mLastIndexId;
   nsRefPtr<IDBDatabase> mDatabase;
@@ -128,9 +137,9 @@ protected:
   nsRefPtr<FileManager> mFileManager;
 
   nsRefPtr<DatabaseInfo> mDBInfo;
   bool mLoadDBMetadata;
 };
 
 END_INDEXEDDB_NAMESPACE
 
-#endif // mozilla_dom_indexeddb_opendatabasehelper_h__
+#endif // mozilla_dom_indexeddb_opendatabasehelper_h__
\ No newline at end of file
--- a/dom/indexedDB/ipc/IndexedDBChild.cpp
+++ b/dom/indexedDB/ipc/IndexedDBChild.cpp
@@ -1,31 +1,34 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "base/basictypes.h"
 
 #include "IndexedDBChild.h"
 
-#include "mozilla/Assertions.h"
+#include "nsIAtom.h"
 
-#include "nsIAtom.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/dom/ContentChild.h"
 
 #include "AsyncConnectionHelper.h"
 #include "DatabaseInfo.h"
 #include "IDBEvents.h"
 #include "IDBFactory.h"
 #include "IDBIndex.h"
 #include "IDBObjectStore.h"
 #include "IDBTransaction.h"
 #include "IndexedDatabaseManager.h"
 
 USING_INDEXEDDB_NAMESPACE
 
+using namespace mozilla::dom;
+
 namespace {
 
 class IPCOpenDatabaseHelper : public AsyncConnectionHelper
 {
 public:
   IPCOpenDatabaseHelper(IDBDatabase* aDatabase, IDBOpenDBRequest* aRequest)
   : AsyncConnectionHelper(aDatabase, aRequest)
   {
@@ -284,17 +287,18 @@ IndexedDBDatabaseChild::EnsureDatabase(
         NS_WARNING("Out of memory!");
         return false;
       }
     }
   }
 
   if (!mDatabase) {
     nsRefPtr<IDBDatabase> database =
-      IDBDatabase::Create(aRequest, dbInfo.forget(), aDBInfo.origin, NULL);
+      IDBDatabase::Create(aRequest, dbInfo.forget(), aDBInfo.origin, NULL,
+                          NULL);
     if (!database) {
       NS_WARNING("Failed to create database!");
       return false;
     }
 
     database->SetActor(this);
 
     mDatabase = database;
@@ -641,23 +645,28 @@ IndexedDBObjectStoreChild::RecvPIndexedD
     static_cast<IndexedDBObjectStoreRequestChild*>(aParams.requestChild());
   NS_ASSERTION(requestActor, "Must have an actor here!");
 
   nsRefPtr<IDBRequest> request = requestActor->GetRequest();
   NS_ASSERTION(request, "Must have a request here!");
 
   size_t direction = static_cast<size_t>(aParams.direction());
 
+  nsTArray<StructuredCloneFile> blobs;
+  IDBObjectStore::ConvertActorsToBlobs(aParams.blobsChild(), blobs);
+
   nsRefPtr<IDBCursor> cursor;
   nsresult rv =
     mObjectStore->OpenCursorFromChildProcess(request, direction, aParams.key(),
-                                             aParams.cloneInfo(),
+                                             aParams.cloneInfo(), blobs,
                                              getter_AddRefs(cursor));
   NS_ENSURE_SUCCESS(rv, false);
 
+  MOZ_ASSERT(blobs.IsEmpty(), "Should have swapped blob elements!");
+
   actor->SetCursor(cursor);
   return true;
 }
 
 PIndexedDBRequestChild*
 IndexedDBObjectStoreChild::AllocPIndexedDBRequest(
                                         const ObjectStoreRequestParams& aParams)
 {
@@ -749,26 +758,32 @@ IndexedDBIndexChild::RecvPIndexedDBCurso
 
   nsRefPtr<IDBCursor> cursor;
   nsresult rv;
 
   typedef ipc::OptionalStructuredCloneReadInfo CursorUnionType;
 
   switch (aParams.optionalCloneInfo().type()) {
     case CursorUnionType::TSerializedStructuredCloneReadInfo: {
+      nsTArray<StructuredCloneFile> blobs;
+      IDBObjectStore::ConvertActorsToBlobs(aParams.blobsChild(), blobs);
+
       const SerializedStructuredCloneReadInfo& cloneInfo =
         aParams.optionalCloneInfo().get_SerializedStructuredCloneReadInfo();
 
       rv = mIndex->OpenCursorFromChildProcess(request, direction, aParams.key(),
                                               aParams.objectKey(), cloneInfo,
+                                              blobs,
                                               getter_AddRefs(cursor));
       NS_ENSURE_SUCCESS(rv, false);
     } break;
 
     case CursorUnionType::Tvoid_t:
+      MOZ_ASSERT(aParams.blobsChild().IsEmpty());
+
       rv = mIndex->OpenCursorFromChildProcess(request, direction, aParams.key(),
                                               aParams.objectKey(),
                                               getter_AddRefs(cursor));
       NS_ENSURE_SUCCESS(rv, false);
       break;
 
     default:
       MOZ_NOT_REACHED("Unknown union type!");
--- a/dom/indexedDB/ipc/IndexedDBParent.cpp
+++ b/dom/indexedDB/ipc/IndexedDBParent.cpp
@@ -1,36 +1,41 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "base/basictypes.h"
 
 #include "IndexedDBParent.h"
 
+#include "nsIDOMFile.h"
 #include "nsIDOMEvent.h"
 #include "nsIIDBVersionChangeEvent.h"
 #include "nsIJSContextStack.h"
 #include "nsIXPConnect.h"
 
 #include "mozilla/Assertions.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/ipc/Blob.h"
 #include "nsContentUtils.h"
 
 #include "AsyncConnectionHelper.h"
 #include "DatabaseInfo.h"
 #include "IDBDatabase.h"
 #include "IDBEvents.h"
 #include "IDBFactory.h"
 #include "IDBIndex.h"
 #include "IDBKeyRange.h"
 #include "IDBObjectStore.h"
 #include "IDBTransaction.h"
 
 USING_INDEXEDDB_NAMESPACE
 
+using namespace mozilla::dom;
+
 /*******************************************************************************
  * AutoSetCurrentTransaction
  ******************************************************************************/
 
 AutoSetCurrentTransaction::AutoSetCurrentTransaction(
                                                    IDBTransaction* aTransaction)
 {
   MOZ_ASSERT(aTransaction);
@@ -1178,16 +1183,39 @@ IndexedDBObjectStoreRequestParent::Index
              aRequestType <= ParamsUnionType::T__Last);
 }
 
 IndexedDBObjectStoreRequestParent::~IndexedDBObjectStoreRequestParent()
 {
   MOZ_COUNT_DTOR(IndexedDBObjectStoreRequestParent);
 }
 
+void
+IndexedDBObjectStoreRequestParent::ConvertBlobActors(
+                                  const InfallibleTArray<PBlobParent*>& aActors,
+                                  nsTArray<nsCOMPtr<nsIDOMBlob> >& aBlobs)
+{
+  MOZ_ASSERT(aBlobs.IsEmpty());
+
+  if (!aActors.IsEmpty()) {
+    // Walk the chain to get to ContentParent.
+    ContentParent* contentParent =
+      mObjectStore->Transaction()->Database()->GetContentParent();
+    MOZ_ASSERT(contentParent);
+
+    uint32_t length = aActors.Length();
+    aBlobs.SetCapacity(length);
+
+    for (uint32_t index = 0; index < length; index++) {
+      BlobParent* actor = static_cast<BlobParent*>(aActors[index]);
+      aBlobs.AppendElement(actor->GetBlob());
+    }
+  }
+}
+
 bool
 IndexedDBObjectStoreRequestParent::Get(const GetParams& aParams)
 {
   MOZ_ASSERT(mRequestType == ParamsUnionType::TGetParams);
 
   nsRefPtr<IDBRequest> request;
 
   nsRefPtr<IDBKeyRange> keyRange =
@@ -1249,48 +1277,54 @@ IndexedDBObjectStoreRequestParent::GetAl
 
 bool
 IndexedDBObjectStoreRequestParent::Add(const AddParams& aParams)
 {
   MOZ_ASSERT(mRequestType == ParamsUnionType::TAddParams);
 
   ipc::AddPutParams params = aParams.commonParams();
 
+  nsTArray<nsCOMPtr<nsIDOMBlob> > blobs;
+  ConvertBlobActors(params.blobsParent(), blobs);
+
   nsRefPtr<IDBRequest> request;
 
   {
     AutoSetCurrentTransaction asct(mObjectStore->Transaction());
 
     nsresult rv =
       mObjectStore->AddOrPutInternal(params.cloneInfo(), params.key(),
-                                     params.indexUpdateInfos(), false,
+                                     params.indexUpdateInfos(), blobs, false,
                                      getter_AddRefs(request));
     NS_ENSURE_SUCCESS(rv, false);
   }
 
   request->SetActor(this);
   mRequest.swap(request);
   return true;
 }
 
 bool
 IndexedDBObjectStoreRequestParent::Put(const PutParams& aParams)
 {
   MOZ_ASSERT(mRequestType == ParamsUnionType::TPutParams);
 
   ipc::AddPutParams params = aParams.commonParams();
 
+  nsTArray<nsCOMPtr<nsIDOMBlob> > blobs;
+  ConvertBlobActors(params.blobsParent(), blobs);
+
   nsRefPtr<IDBRequest> request;
 
   {
     AutoSetCurrentTransaction asct(mObjectStore->Transaction());
 
     nsresult rv =
       mObjectStore->AddOrPutInternal(params.cloneInfo(), params.key(),
-                                     params.indexUpdateInfos(), true,
+                                     params.indexUpdateInfos(), blobs, true,
                                      getter_AddRefs(request));
     NS_ENSURE_SUCCESS(rv, false);
   }
 
   request->SetActor(this);
   mRequest.swap(request);
   return true;
 }
--- a/dom/indexedDB/ipc/IndexedDBParent.h
+++ b/dom/indexedDB/ipc/IndexedDBParent.h
@@ -18,20 +18,22 @@
 
 #include "mozilla/Attributes.h"
 
 #include "nsIDOMEventListener.h"
 
 namespace mozilla {
 namespace dom {
 class ContentParent;
+class PBlobParent;
 class TabParent;
 }
 }
 
+class nsIDOMBlob;
 class nsIDOMEvent;
 
 BEGIN_INDEXEDDB_NAMESPACE
 
 class IDBCursor;
 class IDBDatabase;
 class IDBFactory;
 class IDBIndex;
@@ -526,16 +528,21 @@ public:
   bool
   Clear(const ClearParams& aParams);
 
   bool
   Count(const CountParams& aParams);
 
   bool
   OpenCursor(const OpenCursorParams& aParams);
+
+protected:
+  void
+  ConvertBlobActors(const InfallibleTArray<PBlobParent*>& aActors,
+                    nsTArray<nsCOMPtr<nsIDOMBlob> >& aBlobs);
 };
 
 /*******************************************************************************
  * IndexedDBIndexRequestParent
  ******************************************************************************/
 
 class IndexedDBIndexRequestParent : public IndexedDBRequestParentBase
 {
--- a/dom/indexedDB/ipc/Makefile.in
+++ b/dom/indexedDB/ipc/Makefile.in
@@ -43,9 +43,9 @@ include $(topsrcdir)/config/config.mk
 include $(topsrcdir)/ipc/chromium/chromium-config.mk
 include $(topsrcdir)/config/rules.mk
 
 # Copy all the normal xpcshell tests from the regular unit directory.
 copy-xpcshell-tests:
 	$(call install_cmd,$(wildcard $(topsrcdir)/dom/indexedDB/test/unit/test_*.js) \
 		$(testxpcobjdir)/$(relativesrcdir)/$(XPCSHELL_TESTS))
 
-libs-xpcshell-tests: copy-xpcshell-tests
+libs-xpcshell-tests: copy-xpcshell-tests
\ No newline at end of file
--- a/dom/indexedDB/ipc/PIndexedDBIndex.ipdl
+++ b/dom/indexedDB/ipc/PIndexedDBIndex.ipdl
@@ -1,12 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+include protocol PBlob;
 include protocol PIndexedDBCursor;
 include protocol PIndexedDBObjectStore;
 include protocol PIndexedDBRequest;
 
 include "mozilla/dom/indexedDB/SerializationHelpers.h";
 
 using mozilla::dom::indexedDB::Key;
 using mozilla::dom::indexedDB::IDBCursor::Direction;
@@ -97,16 +98,17 @@ union OptionalStructuredCloneReadInfo
 
 struct IndexCursorConstructorParams
 {
   PIndexedDBRequest request;
   Direction direction;
   Key key;
   Key objectKey;
   OptionalStructuredCloneReadInfo optionalCloneInfo;
+  PBlob[] blobs;
 };
 
 } // namespace ipc
 
 protocol PIndexedDBIndex
 {
   manager PIndexedDBObjectStore;
 
--- a/dom/indexedDB/ipc/PIndexedDBObjectStore.ipdl
+++ b/dom/indexedDB/ipc/PIndexedDBObjectStore.ipdl
@@ -1,12 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+include protocol PBlob;
 include protocol PIndexedDBCursor;
 include protocol PIndexedDBIndex;
 include protocol PIndexedDBRequest;
 include protocol PIndexedDBTransaction;
 
 include "mozilla/dom/indexedDB/SerializationHelpers.h";
 
 using mozilla::dom::indexedDB::Key;
@@ -65,16 +66,17 @@ struct OpenCursorParams
 
 } // namespace FIXME_Bug_521898_objectstore
 
 struct AddPutParams
 {
   SerializedStructuredCloneWriteInfo cloneInfo;
   Key key;
   IndexUpdateInfo[] indexUpdateInfos;
+  PBlob[] blobs;
 };
 
 struct AddParams
 {
   AddPutParams commonParams;
 };
 
 struct PutParams
@@ -121,16 +123,17 @@ union IndexConstructorParams
 };
 
 struct ObjectStoreCursorConstructorParams
 {
   PIndexedDBRequest request;
   Direction direction;
   Key key;
   SerializedStructuredCloneReadInfo cloneInfo;
+  PBlob[] blobs;
 };
 
 } // namespace ipc
 
 protocol PIndexedDBObjectStore
 {
   manager PIndexedDBTransaction;
 
--- a/dom/indexedDB/ipc/PIndexedDBRequest.ipdl
+++ b/dom/indexedDB/ipc/PIndexedDBRequest.ipdl
@@ -1,12 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+include protocol PBlob;
 include protocol PIndexedDBCursor;
 include protocol PIndexedDBIndex;
 include protocol PIndexedDBObjectStore;
 
 include "mozilla/dom/indexedDB/SerializationHelpers.h";
 
 using mozilla::dom::indexedDB::Key;
 using mozilla::dom::indexedDB::SerializedStructuredCloneReadInfo;
@@ -17,26 +18,33 @@ namespace mozilla {
 namespace dom {
 namespace indexedDB {
 
 namespace ipc {
 
 struct GetResponse
 {
   SerializedStructuredCloneReadInfo cloneInfo;
+  PBlob[] blobs;
 };
 
 struct GetKeyResponse
 {
   Key key;
 };
 
+struct BlobArray
+{
+  PBlob[] blobs;
+};
+
 struct GetAllResponse
 {
   SerializedStructuredCloneReadInfo[] cloneInfos;
+  BlobArray[] blobs;
 };
 
 struct GetAllKeysResponse
 {
   Key[] keys;
 };
 
 struct AddResponse
@@ -70,16 +78,17 @@ union OpenCursorResponse
   void_t;
 };
 
 struct ContinueResponse
 {
   Key key;
   Key objectKey;
   SerializedStructuredCloneReadInfo cloneInfo;
+  PBlob[] blobs;
 };
 
 union ResponseValue
 {
   nsresult;
   GetResponse;
   GetKeyResponse;
   GetAllResponse;
--- a/dom/indexedDB/test/Makefile.in
+++ b/dom/indexedDB/test/Makefile.in
@@ -24,16 +24,17 @@ MOCHITEST_FILES = \
   helpers.js \
   leaving_page_iframe.html \
   test_add_put.html \
   test_add_twice_failure.html \
   test_advance.html \
   test_autoIncrement_indexes.html \
   test_autoIncrement.html \
   test_bfcache.html \
+  test_blob_simple.html \
   test_clear.html \
   test_complex_keyPaths.html \
   test_count.html \
   test_create_index.html \
   test_create_index_with_integer_keys.html \
   test_create_objectStore.html \
   test_cursors.html \
   test_cursor_mutation.html \
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/test_blob_simple.html
@@ -0,0 +1,191 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>Indexed Database Property Test</title>
+
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+  <script type="text/javascript;version=1.7">
+  function testSteps()
+  {
+    const BLOB_DATA = ["fun ", "times ", "all ", "around!"];
+    const INDEX_KEY = 5;
+
+    let request = indexedDB.open(window.location.pathname, 1);
+    request.onerror = errorHandler;
+    request.onupgradeneeded = grabEventAndContinueHandler;
+    request.onsuccess = unexpectedSuccessHandler;
+    let event = yield;
+
+    let db = event.target.result;
+    db.onerror = errorHandler;
+
+    let objectStore = db.createObjectStore("foo", { autoIncrement: true });
+    let index = objectStore.createIndex("foo", "index");
+
+    request.onsuccess = grabEventAndContinueHandler;
+    event = yield;
+
+    let blob = new Blob(BLOB_DATA, { type: "text/plain" });
+    let data = { blob: blob, index: INDEX_KEY };
+
+    objectStore = db.transaction("foo", "readwrite").objectStore("foo");
+    objectStore.add(data).onsuccess = grabEventAndContinueHandler;
+    event = yield;
+
+    let key = event.target.result;
+
+    objectStore.add(data).onsuccess = grabEventAndContinueHandler;
+    event = yield;
+
+    objectStore = db.transaction("foo").objectStore("foo");
+    objectStore.get(key).onsuccess = grabEventAndContinueHandler;
+    event = yield;
+
+    let fileReader = new FileReader();
+    fileReader.onload = grabEventAndContinueHandler;
+    fileReader.readAsText(event.target.result.blob);
+    event = yield;
+
+    is(event.target.result, BLOB_DATA.join(""), "Correct text");
+
+    objectStore = db.transaction("foo").objectStore("foo");
+    objectStore.mozGetAll().onsuccess = grabEventAndContinueHandler;
+    event = yield;
+
+    is(event.target.result.length, 2, "Got right number of items");
+
+    fileReader = new FileReader();
+    fileReader.onload = grabEventAndContinueHandler;
+    fileReader.readAsText(event.target.result[0].blob);
+    event = yield;
+
+    is(event.target.result, BLOB_DATA.join(""), "Correct text");
+
+    let cursorResults = [];
+
+    objectStore = db.transaction("foo").objectStore("foo");
+    objectStore.openCursor().onsuccess = function(event) {
+      let cursor = event.target.result;
+      if (cursor) {
+        cursorResults.push(cursor.value);
+        cursor.continue();
+      }
+      else {
+        continueToNextStep();
+      }
+    };
+    yield;
+
+    is(cursorResults.length, 2, "Got right number of items");
+
+    fileReader = new FileReader();
+    fileReader.onload = grabEventAndContinueHandler;
+    fileReader.readAsText(cursorResults[0].blob);
+    event = yield;
+
+    is(event.target.result, BLOB_DATA.join(""), "Correct text");
+
+    let index = db.transaction("foo").objectStore("foo").index("foo");
+    index.get(INDEX_KEY).onsuccess = grabEventAndContinueHandler;
+    event = yield;
+
+    fileReader = new FileReader();
+    fileReader.onload = grabEventAndContinueHandler;
+    fileReader.readAsText(event.target.result.blob);
+    event = yield;
+
+    is(event.target.result, BLOB_DATA.join(""), "Correct text");
+
+    index = db.transaction("foo").objectStore("foo").index("foo");
+    index.mozGetAll().onsuccess = grabEventAndContinueHandler;
+    event = yield;
+
+    is(event.target.result.length, 2, "Got right number of items");
+
+    fileReader = new FileReader();
+    fileReader.onload = grabEventAndContinueHandler;
+    fileReader.readAsText(event.target.result[0].blob);
+    event = yield;
+
+    is(event.target.result, BLOB_DATA.join(""), "Correct text");
+
+    cursorResults = [];
+
+    index = db.transaction("foo").objectStore("foo").index("foo");
+    index.openCursor().onsuccess = function(event) {
+      let cursor = event.target.result;
+      if (cursor) {
+        cursorResults.push(cursor.value);
+        cursor.continue();
+      }
+      else {
+        continueToNextStep();
+      }
+    };
+    yield;
+
+    is(cursorResults.length, 2, "Got right number of items");
+
+    fileReader = new FileReader();
+    fileReader.onload = grabEventAndContinueHandler;
+    fileReader.readAsText(cursorResults[0].blob);
+    event = yield;
+
+    is(event.target.result, BLOB_DATA.join(""), "Correct text");
+
+    fileReader = new FileReader();
+    fileReader.onload = grabEventAndContinueHandler;
+    fileReader.readAsText(cursorResults[1].blob);
+    event = yield;
+
+    is(event.target.result, BLOB_DATA.join(""), "Correct text");
+
+    let slice = cursorResults[1].blob.slice(0, BLOB_DATA[0].length);
+
+    fileReader = new FileReader();
+    fileReader.onload = grabEventAndContinueHandler;
+    fileReader.readAsText(slice);
+    event = yield;
+
+    is(event.target.result, BLOB_DATA[0], "Correct text");
+
+    function workerScript() {
+      onmessage = function(event) {
+        var reader = new FileReaderSync();
+        postMessage(reader.readAsText(event.data));
+
+        var slice = event.data.slice(1, 2);
+        postMessage(reader.readAsText(slice));
+
+      }
+    }
+
+    let url =
+      URL.createObjectURL(new Blob(["(", workerScript.toSource(), ")()"]));
+
+    let worker = new Worker(url);
+    worker.postMessage(slice);
+    worker.onmessage = grabEventAndContinueHandler;
+    event = yield;
+
+    is(event.data, BLOB_DATA[0], "Correct text");
+    event = yield;
+
+    is(event.data, BLOB_DATA[0][1], "Correct text");
+
+    finishTest();
+    yield;
+  }
+  </script>
+  <script type="text/javascript;version=1.7" src="helpers.js"></script>
+
+</head>
+
+<body onload="runTest();"></body>
+
+</html>
--- a/dom/interfaces/css/Makefile.in
+++ b/dom/interfaces/css/Makefile.in
@@ -27,16 +27,17 @@ SDK_XPIDLSRCS = 				\
 
 XPIDLSRCS =					\
 	nsIDOMCSS2Properties.idl		\
 	nsIDOMCSSCharsetRule.idl		\
 	nsIDOMCSSFontFaceRule.idl		\
 	nsIDOMCSSImportRule.idl			\
 	nsIDOMCSSMediaRule.idl			\
 	nsIDOMCSSMozDocumentRule.idl		\
+	nsIDOMCSSSupportsRule.idl		\
 	nsIDOMMozCSSKeyframeRule.idl		\
 	nsIDOMMozCSSKeyframesRule.idl		\
 	nsIDOMCSSPageRule.idl			\
 	nsIDOMCSSStyleRule.idl			\
 	nsIDOMCSSUnknownRule.idl		\
 	nsIDOMCounter.idl			\
 	nsIDOMRGBColor.idl			\
 	nsIDOMRect.idl				\
--- a/dom/interfaces/css/nsIDOMCSSRule.idl
+++ b/dom/interfaces/css/nsIDOMCSSRule.idl
@@ -22,16 +22,17 @@ interface nsIDOMCSSRule : nsISupports
   const unsigned short      CHARSET_RULE                   = 2;
   const unsigned short      IMPORT_RULE                    = 3;
   const unsigned short      MEDIA_RULE                     = 4;
   const unsigned short      FONT_FACE_RULE                 = 5;
   const unsigned short      PAGE_RULE                      = 6;
   const unsigned short      MOZ_KEYFRAMES_RULE             = 7;
   const unsigned short      MOZ_KEYFRAME_RULE              = 8;
   const unsigned short      NAMESPACE_RULE                 = 10;
+  const unsigned short      SUPPORTS_RULE                  = 12;
 
   readonly attribute unsigned short      type;
            attribute DOMString           cssText;
                                         // raises(DOMException) on setting
 
   readonly attribute nsIDOMCSSStyleSheet parentStyleSheet;
   readonly attribute nsIDOMCSSRule       parentRule;
 };
new file mode 100644
--- /dev/null
+++ b/dom/interfaces/css/nsIDOMCSSSupportsRule.idl
@@ -0,0 +1,21 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIDOMCSSRule.idl"
+
+/**
+ * Interface for @supports rules in the CSS OM.
+ */
+[scriptable, uuid(5f409a4d-92f9-4a62-8e8a-cc1c02c32918)]
+interface nsIDOMCSSSupportsRule : nsIDOMCSSRule
+{
+  readonly attribute nsIDOMCSSRuleList cssRules;
+
+  unsigned long      insertRule(in DOMString rule,
+                                in unsigned long index)
+                                        raises(DOMException);
+  void               deleteRule(in unsigned long index)
+                                        raises(DOMException);
+};
--- a/dom/interfaces/devicestorage/Makefile.in
+++ b/dom/interfaces/devicestorage/Makefile.in
@@ -15,12 +15,13 @@ XPIDL_MODULE     = dom_devicestorage
 LIBXUL_LIBRARY   = 1
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/dom/dom-config.mk
 
 XPIDLSRCS = \
 	nsIDOMDeviceStorage.idl \
 	nsIDOMDeviceStorageCursor.idl \
-	nsIDOMNavigatorDeviceStorage.idl
+	nsIDOMNavigatorDeviceStorage.idl \
+	nsIDOMDeviceStorageChangeEvent.idl
 
 include $(topsrcdir)/config/rules.mk
 
--- a/dom/interfaces/devicestorage/nsIDOMDeviceStorage.idl
+++ b/dom/interfaces/devicestorage/nsIDOMDeviceStorage.idl
@@ -1,31 +1,29 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "domstubs.idl"
+#include "nsIDOMEventTarget.idl"
 interface nsIDOMBlob;
 interface nsIDOMDOMRequest;
 interface nsIDOMDeviceStorageCursor;
+interface nsIDOMDeviceStorageChangeEvent;
+interface nsIDOMEventListener;
 
 dictionary DeviceStorageEnumerationParameters
 {
   jsval since;
 };
 
-[scriptable, uuid(05C0D0C8-D698-4CCD-899C-7198A33BD7EC)]
-interface nsIDOMDeviceStorage : nsISupports
+[scriptable, uuid(3dbe0137-ca73-44c5-bcde-25c297bf7c65), builtinclass]
+interface nsIDOMDeviceStorage : nsIDOMEventTarget
 {
-    /*
-     * Hint as to what kind of storage this object is.
-     * May be "external", "shared", or "default".
-     */
-    readonly attribute DOMString type;
-
+    attribute nsIDOMEventListener onchange;
     nsIDOMDOMRequest add(in nsIDOMBlob aBlob);
     nsIDOMDOMRequest addNamed(in nsIDOMBlob aBlob, in DOMString aName);
 
     [implicit_jscontext]
     nsIDOMDOMRequest get(in jsval aName);
 
     [implicit_jscontext]
     nsIDOMDOMRequest getEditable(in jsval aName);
new file mode 100644
--- /dev/null
+++ b/dom/interfaces/devicestorage/nsIDOMDeviceStorageChangeEvent.idl
@@ -0,0 +1,26 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "domstubs.idl"
+#include "nsIDOMEvent.idl"
+
+[scriptable, uuid(468304d6-aab6-4e1e-8ab2-569d9e703431), builtinclass]
+interface nsIDOMDeviceStorageChangeEvent : nsIDOMEvent
+{
+
+  [noscript] void initDeviceStorageChangeEvent(in DOMString eventTypeArg,
+                                               in boolean canBubbleArg,
+                                               in boolean cancelableArg,
+                                               in DOMString path,
+                                               in DOMString reason);
+  
+  readonly attribute DOMString path;
+  readonly attribute DOMString reason;
+};
+
+dictionary DeviceStorageChangeEventInit : EventInit
+{
+  DOMString path;
+  DOMString reason;
+};
--- a/dom/interfaces/devicestorage/nsIDOMNavigatorDeviceStorage.idl
+++ b/dom/interfaces/devicestorage/nsIDOMNavigatorDeviceStorage.idl
@@ -1,17 +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/. */
 
 #include "domstubs.idl"
-interface nsIVariant;
+interface nsIDOMDeviceStorage;
 
 /**
  * Property that extends the navigator object.
  */
-[scriptable, uuid(A4B2831D-6065-472F-8A6D-2C9085C74C15)]
+[scriptable, uuid(da1fbf6e-259c-40bc-ba8c-4ae81748dca3)]
 interface nsIDOMNavigatorDeviceStorage : nsISupports
 {
-    // returns an array of nsIDOMDeviceStorage
-    nsIVariant getDeviceStorage(in DOMString type);
+  nsIDOMDeviceStorage getDeviceStorage(in DOMString type);
 };
-
--- a/dom/interfaces/events/nsIDOMTouchEvent.idl
+++ b/dom/interfaces/events/nsIDOMTouchEvent.idl
@@ -36,18 +36,17 @@ interface nsIDOMTouch : nsISupports {
     bool mChanged;
     PRUint32 mMessage;
   %}
 };
 
 [scriptable, uuid(60706eb7-d50d-4379-b01c-e78e6af84213)]
 interface nsIDOMTouchList : nsISupports {
   readonly attribute unsigned long length;
-  [getter,forward(getItemAt)] nsIDOMTouch item(in unsigned long index);
-  [noscript,notxpcom,nostdcall] nsIDOMTouch getItemAt(in unsigned long index);
+  nsIDOMTouch item(in unsigned long index);
   nsIDOMTouch identifiedTouch(in long identifier);
 };
 
 [scriptable, builtinclass, uuid(57809468-7c03-4e8a-b080-4e30f157db21)]
 interface nsIDOMTouchEvent : nsIDOMUIEvent {
   readonly attribute nsIDOMTouchList touches;
   readonly attribute nsIDOMTouchList targetTouches;
   readonly attribute nsIDOMTouchList changedTouches;
new file mode 100644
--- /dev/null
+++ b/dom/ipc/Blob.cpp
@@ -0,0 +1,932 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "base/basictypes.h"
+
+#include "Blob.h"
+
+#include "nsIDOMFile.h"
+#include "nsIInputStream.h"
+#include "nsIRemoteBlob.h"
+#include "nsISeekableStream.h"
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/unused.h"
+#include "mozilla/net/NeckoMessageUtils.h"
+#include "nsDOMFile.h"
+#include "nsThreadUtils.h"
+
+#include "ContentChild.h"
+#include "ContentParent.h"
+
+using namespace mozilla::dom;
+using namespace mozilla::dom::ipc;
+
+namespace {
+
+class RemoteInputStream : public nsIInputStream,
+                          public nsISeekableStream
+{
+  mozilla::Monitor mMonitor;
+  nsCOMPtr<nsIDOMBlob> mSourceBlob;
+  nsCOMPtr<nsIInputStream> mStream;
+  nsCOMPtr<nsISeekableStream> mSeekableStream;
+
+public:
+  NS_DECL_ISUPPORTS
+
+  RemoteInputStream(nsIDOMBlob* aSourceBlob)
+  : mMonitor("RemoteInputStream.mMonitor"), mSourceBlob(aSourceBlob)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(aSourceBlob);
+  }
+
+  void
+  SetStream(nsIInputStream* aStream)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(aStream);
+
+    nsCOMPtr<nsIInputStream> stream = aStream;
+    nsCOMPtr<nsISeekableStream> seekableStream = do_QueryInterface(aStream);
+
+    {
+      mozilla::MonitorAutoLock lock(mMonitor);
+
+      MOZ_ASSERT(!mStream);
+      MOZ_ASSERT(!mSeekableStream);
+
+      mStream.swap(stream);
+      mSeekableStream.swap(seekableStream);
+
+      mMonitor.Notify();
+    }
+  }
+
+  NS_IMETHOD
+  Close() MOZ_OVERRIDE
+  {
+    nsresult rv = BlockAndWaitForStream();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsIDOMBlob> sourceBlob;
+    mSourceBlob.swap(sourceBlob);
+
+    rv = mStream->Close();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  Available(PRUint32* aAvailable) MOZ_OVERRIDE
+  {
+    // See large comment in FileInputStreamWrapper::Available.
+    if (NS_IsMainThread()) {
+      return NS_BASE_STREAM_CLOSED;
+    }
+
+    nsresult rv = BlockAndWaitForStream();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = mStream->Available(aAvailable);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  Read(char* aBuffer, PRUint32 aCount, PRUint32* aResult) MOZ_OVERRIDE
+  {
+    nsresult rv = BlockAndWaitForStream();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = mStream->Read(aBuffer, aCount, aResult);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, PRUint32 aCount,
+               PRUint32* aResult) MOZ_OVERRIDE
+  {
+    nsresult rv = BlockAndWaitForStream();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = mStream->ReadSegments(aWriter, aClosure, aCount, aResult);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  IsNonBlocking(bool* aNonBlocking) MOZ_OVERRIDE
+  {
+    NS_ENSURE_ARG_POINTER(aNonBlocking);
+
+    *aNonBlocking = false;
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  Seek(PRInt32 aWhence, PRInt64 aOffset) MOZ_OVERRIDE
+  {
+    nsresult rv = BlockAndWaitForStream();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (!mSeekableStream) {
+      NS_WARNING("Underlying blob stream is not seekable!");
+      return NS_ERROR_NO_INTERFACE;
+    }
+
+    rv = mSeekableStream->Seek(aWhence, aOffset);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  Tell(PRInt64* aResult)
+  {
+    nsresult rv = BlockAndWaitForStream();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (!mSeekableStream) {
+      NS_WARNING("Underlying blob stream is not seekable!");
+      return NS_ERROR_NO_INTERFACE;
+    }
+
+    rv = mSeekableStream->Tell(aResult);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  SetEOF()
+  {
+    nsresult rv = BlockAndWaitForStream();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (!mSeekableStream) {
+      NS_WARNING("Underlying blob stream is not seekable!");
+      return NS_ERROR_NO_INTERFACE;
+    }
+
+    rv = mSeekableStream->SetEOF();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return NS_OK;
+  }
+
+private:
+  virtual ~RemoteInputStream()
+  { }
+
+  void
+  ReallyBlockAndWaitForStream()
+  {
+    mozilla::MonitorAutoLock lock(mMonitor);
+    while (!mStream) {
+      mMonitor.Wait();
+    }
+  }
+
+  nsresult
+  BlockAndWaitForStream()
+  {
+    if (NS_IsMainThread()) {
+      NS_WARNING("Blocking the main thread is not supported!");
+      return NS_ERROR_FAILURE;
+    }
+
+    ReallyBlockAndWaitForStream();
+    return NS_OK;
+  }
+
+  bool
+  IsSeekableStream()
+  {
+    if (NS_IsMainThread()) {
+      if (!mStream) {
+        NS_WARNING("Don't know if this stream is seekable yet!");
+        return true;
+      }
+    }
+    else {
+      ReallyBlockAndWaitForStream();
+    }
+
+    return !!mSeekableStream;
+  }
+};
+
+template <ActorFlavorEnum ActorFlavor>
+class InputStreamActor : public BlobTraits<ActorFlavor>::StreamType
+{
+  typedef typename BlobTraits<ActorFlavor>::StreamType::InputStream InputStream;
+  nsRefPtr<RemoteInputStream> mRemoteStream;
+
+public:
+  InputStreamActor(RemoteInputStream* aRemoteStream)
+  : mRemoteStream(aRemoteStream)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(aRemoteStream);
+  }
+
+  InputStreamActor()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+  }
+
+private:
+  // This method is only called by the IPDL message machinery.
+  virtual bool
+  Recv__delete__(const InputStream& aStream) MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(mRemoteStream);
+
+    mRemoteStream->SetStream(aStream);
+    return true;
+  }
+};
+
+template <ActorFlavorEnum ActorFlavor>
+inline
+already_AddRefed<nsIDOMBlob>
+GetBlobFromParams(const SlicedBlobConstructorParams& aParams)
+{
+  MOZ_STATIC_ASSERT(ActorFlavor == mozilla::dom::ipc::Parent,
+                    "No other flavor is supported here!");
+
+  BlobParent* actor =
+    const_cast<BlobParent*>(
+      static_cast<const BlobParent*>(aParams.sourceParent()));
+  MOZ_ASSERT(actor);
+
+  return actor->GetBlob();
+}
+
+template <>
+inline
+already_AddRefed<nsIDOMBlob>
+GetBlobFromParams<Child>(const SlicedBlobConstructorParams& aParams)
+{
+  BlobChild* actor =
+    const_cast<BlobChild*>(
+      static_cast<const BlobChild*>(aParams.sourceChild()));
+  MOZ_ASSERT(actor);
+
+  return actor->GetBlob();
+}
+
+inline
+void
+SetBlobOnParams(BlobChild* aActor, SlicedBlobConstructorParams& aParams)
+{
+  aParams.sourceChild() = aActor;
+}
+
+inline
+void
+SetBlobOnParams(BlobParent* aActor, SlicedBlobConstructorParams& aParams)
+{
+  aParams.sourceParent() = aActor;
+}
+
+inline
+nsDOMFileBase*
+ToConcreteBlob(nsIDOMBlob* aBlob)
+{
+  // XXX This is only safe so long as all blob implementations in our tree
+  //     inherit nsDOMFileBase. If that ever changes then this will need to grow
+  //     a real interface or something.
+  return static_cast<nsDOMFileBase*>(aBlob);
+}
+
+} // anonymous namespace
+
+namespace mozilla {
+namespace dom {
+namespace ipc {
+
+template <ActorFlavorEnum ActorFlavor>
+class RemoteBlob : public nsDOMFile,
+                   public nsIRemoteBlob
+{
+public:
+  typedef RemoteBlob<ActorFlavor> SelfType;
+  typedef Blob<ActorFlavor> ActorType;
+  typedef InputStreamActor<ActorFlavor> StreamActorType;
+
+private:
+  ActorType* mActor;
+
+  class StreamHelper : public nsRunnable
+  {
+    typedef Blob<ActorFlavor> ActorType;
+    typedef InputStreamActor<ActorFlavor> StreamActorType;
+
+    mozilla::Monitor mMonitor;
+    ActorType* mActor;
+    nsCOMPtr<nsIDOMBlob> mSourceBlob;
+    nsRefPtr<RemoteInputStream> mInputStream;
+    bool mDone;
+
+  public:
+    StreamHelper(ActorType* aActor, nsIDOMBlob* aSourceBlob)
+    : mMonitor("RemoteBlob::StreamHelper::mMonitor"), mActor(aActor),
+      mSourceBlob(aSourceBlob), mDone(false)
+    {
+      // This may be created on any thread.
+      MOZ_ASSERT(aActor);
+      MOZ_ASSERT(aSourceBlob);
+    }
+
+    nsresult
+    GetStream(nsIInputStream** aInputStream)
+    {
+      // This may be called on any thread.
+      MOZ_ASSERT(aInputStream);
+      MOZ_ASSERT(mActor);
+      MOZ_ASSERT(!mInputStream);
+      MOZ_ASSERT(!mDone);
+
+      if (NS_IsMainThread()) {
+        RunInternal(false);
+      }
+      else {
+        nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+        NS_ENSURE_TRUE(mainThread, NS_ERROR_FAILURE);
+
+        nsresult rv = mainThread->Dispatch(this, NS_DISPATCH_NORMAL);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        {
+          MonitorAutoLock lock(mMonitor);
+          while (!mDone) {
+            lock.Wait();
+          }
+        }
+      }
+
+      MOZ_ASSERT(!mActor);
+      MOZ_ASSERT(mDone);
+
+      if (!mInputStream) {
+        return NS_ERROR_UNEXPECTED;
+      }
+
+      mInputStream.forget(aInputStream);
+      return NS_OK;
+    }
+
+    NS_IMETHOD
+    Run()
+    {
+      MOZ_ASSERT(NS_IsMainThread());
+      RunInternal(true);
+      return NS_OK;
+    }
+
+  private:
+    void
+    RunInternal(bool aNotify)
+    {
+      MOZ_ASSERT(NS_IsMainThread());
+      MOZ_ASSERT(mActor);
+      MOZ_ASSERT(!mInputStream);
+      MOZ_ASSERT(!mDone);
+
+      nsRefPtr<RemoteInputStream> stream = new RemoteInputStream(mSourceBlob);
+
+      StreamActorType* streamActor = new StreamActorType(stream);
+      if (mActor->SendPBlobStreamConstructor(streamActor)) {
+        stream.swap(mInputStream);
+      }
+
+      mActor = nullptr;
+
+      if (aNotify) {
+        MonitorAutoLock lock(mMonitor);
+        mDone = true;
+        lock.Notify();
+      }
+      else {
+        mDone = true;
+      }
+    }
+  };
+
+  class SliceHelper : public nsRunnable
+  {
+    typedef Blob<ActorFlavor> ActorType;
+
+    mozilla::Monitor mMonitor;
+    ActorType* mActor;
+    nsCOMPtr<nsIDOMBlob> mSlice;
+    PRUint64 mStart;
+    PRUint64 mLength;
+    nsString mContentType;
+    bool mDone;
+
+  public:
+    SliceHelper(ActorType* aActor)
+    : mMonitor("RemoteBlob::SliceHelper::mMonitor"), mActor(aActor), mStart(0),
+      mLength(0), mDone(false)
+    {
+      // This may be created on any thread.
+      MOZ_ASSERT(aActor);
+    }
+
+    nsresult
+    GetSlice(PRUint64 aStart, PRUint64 aLength, const nsAString& aContentType,
+             nsIDOMBlob** aSlice)
+    {
+      // This may be called on any thread.
+      MOZ_ASSERT(aSlice);
+      MOZ_ASSERT(mActor);
+      MOZ_ASSERT(!mSlice);
+      MOZ_ASSERT(!mDone);
+
+      mStart = aStart;
+      mLength = aLength;
+      mContentType = aContentType;
+
+      if (NS_IsMainThread()) {
+        RunInternal(false);
+      }
+      else {
+        nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+        NS_ENSURE_TRUE(mainThread, NS_ERROR_FAILURE);
+
+        nsresult rv = mainThread->Dispatch(this, NS_DISPATCH_NORMAL);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        {
+          MonitorAutoLock lock(mMonitor);
+          while (!mDone) {
+            lock.Wait();
+          }
+        }
+      }
+
+      MOZ_ASSERT(!mActor);
+      MOZ_ASSERT(mDone);
+
+      if (!mSlice) {
+        return NS_ERROR_UNEXPECTED;
+      }
+
+      mSlice.forget(aSlice);
+      return NS_OK;
+    }
+
+    NS_IMETHOD
+    Run()
+    {
+      MOZ_ASSERT(NS_IsMainThread());
+      RunInternal(true);
+      return NS_OK;
+    }
+
+  private:
+    void
+    RunInternal(bool aNotify)
+    {
+      MOZ_ASSERT(NS_IsMainThread());
+      MOZ_ASSERT(mActor);
+      MOZ_ASSERT(!mSlice);
+      MOZ_ASSERT(!mDone);
+
+      NormalBlobConstructorParams normalParams;
+      normalParams.contentType() = mContentType;
+      normalParams.length() = mLength;
+
+      ActorType* newActor = ActorType::Create(normalParams);
+      MOZ_ASSERT(newActor);
+
+      SlicedBlobConstructorParams slicedParams;
+      slicedParams.contentType() = mContentType;
+      slicedParams.begin() = mStart;
+      slicedParams.end() = mStart + mLength;
+      SetBlobOnParams(mActor, slicedParams);
+
+      if (mActor->Manager()->SendPBlobConstructor(newActor, slicedParams)) {
+        mSlice = newActor->GetBlob();
+      }
+
+      mActor = nullptr;
+
+      if (aNotify) {
+        MonitorAutoLock lock(mMonitor);
+        mDone = true;
+        lock.Notify();
+      }
+      else {
+        mDone = true;
+      }
+    }
+  };
+
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+
+  RemoteBlob(const nsAString& aName, const nsAString& aContentType,
+             PRUint64 aLength)
+  : nsDOMFile(aName, aContentType, aLength), mActor(nullptr)
+  {
+    mImmutable = true;
+  }
+
+  RemoteBlob(const nsAString& aContentType, PRUint64 aLength)
+  : nsDOMFile(aContentType, aLength), mActor(nullptr)
+  {
+    mImmutable = true;
+  }
+
+  RemoteBlob()
+  : nsDOMFile(EmptyString(), EmptyString(), UINT64_MAX), mActor(nullptr)
+  {
+    mImmutable = true;
+  }
+
+  virtual ~RemoteBlob()
+  {
+    if (mActor) {
+      mActor->NoteDyingRemoteBlob();
+    }
+  }
+
+  void
+  SetActor(ActorType* aActor)
+  {
+    MOZ_ASSERT(!aActor || !mActor);
+    mActor = aActor;
+  }
+
+  virtual already_AddRefed<nsIDOMBlob>
+  CreateSlice(PRUint64 aStart, PRUint64 aLength, const nsAString& aContentType)
+              MOZ_OVERRIDE
+  {
+    if (!mActor) {
+      return nullptr;
+    }
+
+    nsRefPtr<SliceHelper> helper = new SliceHelper(mActor);
+
+    nsCOMPtr<nsIDOMBlob> slice;
+    nsresult rv =
+      helper->GetSlice(aStart, aLength, aContentType, getter_AddRefs(slice));
+    NS_ENSURE_SUCCESS(rv, nullptr);
+
+    return slice.forget();
+  }
+
+  NS_IMETHOD
+  GetInternalStream(nsIInputStream** aStream) MOZ_OVERRIDE
+  {
+    if (!mActor) {
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    nsRefPtr<StreamHelper> helper = new StreamHelper(mActor, this);
+    return helper->GetStream(aStream);
+  }
+
+  virtual void*
+  GetPBlob() MOZ_OVERRIDE
+  {
+    return static_cast<typename ActorType::BaseType*>(mActor);
+  }
+};
+
+} // namespace ipc
+} // namespace dom
+} // namespace mozilla
+
+template <ActorFlavorEnum ActorFlavor>
+Blob<ActorFlavor>::Blob(nsIDOMBlob* aBlob)
+: mBlob(aBlob), mRemoteBlob(nullptr), mOwnsBlob(true), mBlobIsFile(false)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aBlob);
+  aBlob->AddRef();
+
+  nsCOMPtr<nsIDOMFile> file = do_QueryInterface(aBlob);
+  mBlobIsFile = !!file;
+}
+
+template <ActorFlavorEnum ActorFlavor>
+Blob<ActorFlavor>::Blob(const BlobConstructorParams& aParams)
+: mBlob(nullptr), mRemoteBlob(nullptr), mOwnsBlob(false), mBlobIsFile(false)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsRefPtr<RemoteBlobType> remoteBlob;
+
+  switch (aParams.type()) {
+    case BlobConstructorParams::TNormalBlobConstructorParams: {
+      const NormalBlobConstructorParams& params =
+        aParams.get_NormalBlobConstructorParams();
+      remoteBlob = new RemoteBlobType(params.contentType(), params.length());
+      break;
+    }
+
+    case BlobConstructorParams::TFileBlobConstructorParams: {
+      const FileBlobConstructorParams& params =
+        aParams.get_FileBlobConstructorParams();
+      remoteBlob =
+        new RemoteBlobType(params.name(), params.contentType(),
+                           params.length());
+      mBlobIsFile = true;
+      break;
+    }
+
+    case BlobConstructorParams::TMysteryBlobConstructorParams: {
+      remoteBlob = new RemoteBlobType();
+      mBlobIsFile = true;
+      break;
+    }
+
+    default:
+      MOZ_NOT_REACHED("Unknown params!");
+  }
+
+  MOZ_ASSERT(remoteBlob);
+
+  SetRemoteBlob(remoteBlob);
+}
+
+template <ActorFlavorEnum ActorFlavor>
+Blob<ActorFlavor>*
+Blob<ActorFlavor>::Create(const BlobConstructorParams& aParams)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  switch (aParams.type()) {
+    case BlobConstructorParams::TNormalBlobConstructorParams:
+    case BlobConstructorParams::TFileBlobConstructorParams:
+    case BlobConstructorParams::TMysteryBlobConstructorParams:
+      return new Blob<ActorFlavor>(aParams);
+
+    case BlobConstructorParams::TSlicedBlobConstructorParams: {
+      const SlicedBlobConstructorParams& params =
+        aParams.get_SlicedBlobConstructorParams();
+
+      nsCOMPtr<nsIDOMBlob> source = GetBlobFromParams<ActorFlavor>(params);
+      MOZ_ASSERT(source);
+
+      nsCOMPtr<nsIDOMBlob> slice;
+      nsresult rv =
+        source->Slice(params.begin(), params.end(), params.contentType(), 3,
+                      getter_AddRefs(slice));
+      NS_ENSURE_SUCCESS(rv, nullptr);
+
+      return new Blob<ActorFlavor>(slice);
+    }
+
+    default:
+      MOZ_NOT_REACHED("Unknown params!");
+  }
+
+  return nullptr;
+}
+
+template <ActorFlavorEnum ActorFlavor>
+already_AddRefed<nsIDOMBlob>
+Blob<ActorFlavor>::GetBlob()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mBlob);
+
+  nsCOMPtr<nsIDOMBlob> blob;
+
+  // Remote blobs are held alive until the first call to GetBlob. Thereafter we
+  // only hold a weak reference. Normal blobs are held alive until the actor is
+  // destroyed.
+  if (mRemoteBlob && mOwnsBlob) {
+    blob = dont_AddRef(mBlob);
+    mOwnsBlob = false;
+  }
+  else {
+    blob = mBlob;
+  }
+
+  MOZ_ASSERT(blob);
+
+  return blob.forget();
+}
+
+template <ActorFlavorEnum ActorFlavor>
+bool
+Blob<ActorFlavor>::SetMysteryBlobInfo(const nsString& aName,
+                                      const nsString& aContentType,
+                                      PRUint64 aLength)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mBlob);
+  MOZ_ASSERT(mRemoteBlob);
+  MOZ_ASSERT(aLength);
+
+  ToConcreteBlob(mBlob)->SetLazyData(aName, aContentType, aLength);
+
+  FileBlobConstructorParams params(aName, aContentType, aLength);
+  return BaseType::SendResolveMystery(params);
+}
+
+template <ActorFlavorEnum ActorFlavor>
+bool
+Blob<ActorFlavor>::SetMysteryBlobInfo(const nsString& aContentType,
+                                      PRUint64 aLength)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mBlob);
+  MOZ_ASSERT(mRemoteBlob);
+  MOZ_ASSERT(aLength);
+
+  nsString voidString;
+  voidString.SetIsVoid(true);
+
+  ToConcreteBlob(mBlob)->SetLazyData(voidString, aContentType, aLength);
+
+  NormalBlobConstructorParams params(aContentType, aLength);
+  return BaseType::SendResolveMystery(params);
+}
+
+template <ActorFlavorEnum ActorFlavor>
+void
+Blob<ActorFlavor>::SetRemoteBlob(nsRefPtr<RemoteBlobType>& aRemoteBlob)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!mBlob);
+  MOZ_ASSERT(!mRemoteBlob);
+  MOZ_ASSERT(!mOwnsBlob);
+  MOZ_ASSERT(aRemoteBlob);
+
+  if (NS_FAILED(aRemoteBlob->SetMutable(false))) {
+    MOZ_NOT_REACHED("Failed to make remote blob immutable!");
+  }
+
+  aRemoteBlob->SetActor(this);
+  aRemoteBlob.forget(&mRemoteBlob);
+
+  mBlob = mRemoteBlob;
+  mOwnsBlob = true;
+}
+
+template <ActorFlavorEnum ActorFlavor>
+void
+Blob<ActorFlavor>::NoteDyingRemoteBlob()
+{
+  MOZ_ASSERT(mBlob);
+  MOZ_ASSERT(mRemoteBlob);
+  MOZ_ASSERT(!mOwnsBlob);
+
+  // This may be called on any thread due to the fact that RemoteBlob is
+  // designed to be passed between threads. We must start the shutdown process
+  // on the main thread, so we proxy here if necessary.
+  if (!NS_IsMainThread()) {
+    nsCOMPtr<nsIRunnable> runnable =
+      NS_NewNonOwningRunnableMethod(this,
+                                    &Blob<ActorFlavor>::NoteDyingRemoteBlob);
+    if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
+      MOZ_ASSERT(false, "Should never fail!");
+    }
+
+    return;
+  }
+
+  // Must do this before calling Send__delete__ or we'll crash there trying to
+  // access a dangling pointer.
+  mRemoteBlob = nullptr;
+
+  mozilla::unused << BaseType::Send__delete__(this);
+}
+
+template <ActorFlavorEnum ActorFlavor>
+void
+Blob<ActorFlavor>::ActorDestroy(ActorDestroyReason aWhy)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mBlob);
+
+  if (mRemoteBlob) {
+    mRemoteBlob->SetActor(nullptr);
+  }
+
+  if (mOwnsBlob) {
+    mBlob->Release();
+  }
+}
+
+template <ActorFlavorEnum ActorFlavor>
+bool
+Blob<ActorFlavor>::RecvResolveMystery(const ResolveMysteryParams& aParams)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mBlob);
+  MOZ_ASSERT(!mRemoteBlob);
+  MOZ_ASSERT(mOwnsBlob);
+
+  if (!mBlobIsFile) {
+    MOZ_ASSERT(false, "Must always be a file!");
+    return false;
+  }
+
+  nsDOMFileBase* blob = ToConcreteBlob(mBlob);
+
+  switch (aParams.type()) {
+    case ResolveMysteryParams::TNormalBlobConstructorParams: {
+      const NormalBlobConstructorParams& params =
+        aParams.get_NormalBlobConstructorParams();
+      nsString voidString;
+      voidString.SetIsVoid(true);
+      blob->SetLazyData(voidString, params.contentType(), params.length());
+      break;
+    }
+
+    case ResolveMysteryParams::TFileBlobConstructorParams: {
+      const FileBlobConstructorParams& params =
+        aParams.get_FileBlobConstructorParams();
+      blob->SetLazyData(params.name(), params.contentType(), params.length());
+      break;
+    }
+
+    default:
+      MOZ_NOT_REACHED("Unknown params!");
+  }
+
+  return true;
+}
+
+template <ActorFlavorEnum ActorFlavor>
+bool
+Blob<ActorFlavor>::RecvPBlobStreamConstructor(StreamType* aActor)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mBlob);
+  MOZ_ASSERT(!mRemoteBlob);
+
+  nsCOMPtr<nsIInputStream> stream;
+  nsresult rv = mBlob->GetInternalStream(getter_AddRefs(stream));
+  NS_ENSURE_SUCCESS(rv, false);
+
+  return aActor->Send__delete__(aActor, stream.get());
+}
+
+template <ActorFlavorEnum ActorFlavor>
+typename Blob<ActorFlavor>::StreamType*
+Blob<ActorFlavor>::AllocPBlobStream()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  return new InputStreamActor<ActorFlavor>();
+}
+
+template <ActorFlavorEnum ActorFlavor>
+bool
+Blob<ActorFlavor>::DeallocPBlobStream(StreamType* aActor)