Bug 1126694 - Impl of DeviceStorageAreaListener. r=bz, dhylands
authorKershaw Chang <kechang@mozilla.com>
Mon, 04 May 2015 08:11:00 +0200
changeset 273707 aaa4487657420dfdade7210a7599e07bcf52d2be
parent 273706 bf66afc999a26a905f4701d2385c6df877a9728d
child 273708 c5d30567f41915f45a4864472b9ee030241ce1e8
push id863
push userraliiev@mozilla.com
push dateMon, 03 Aug 2015 13:22:43 +0000
treeherdermozilla-release@f6321b14228d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, dhylands
bugs1126694
milestone40.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1126694 - Impl of DeviceStorageAreaListener. r=bz, dhylands
dom/base/Navigator.cpp
dom/base/Navigator.h
dom/devicestorage/DeviceStorage.h
dom/devicestorage/DeviceStorageAreaListener.cpp
dom/devicestorage/DeviceStorageAreaListener.h
dom/devicestorage/moz.build
dom/devicestorage/nsDeviceStorage.cpp
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
dom/system/gonk/nsIVolume.idl
dom/system/gonk/nsIVolumeService.idl
dom/system/gonk/nsVolumeService.cpp
dom/system/gonk/nsVolumeService.h
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -27,16 +27,17 @@
 #include "nsICookiePermission.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsContentUtils.h"
 #include "nsUnicharUtils.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 #include "BatteryManager.h"
+#include "mozilla/dom/DeviceStorageAreaListener.h"
 #include "mozilla/dom/PowerManager.h"
 #include "mozilla/dom/WakeLock.h"
 #include "mozilla/dom/power/PowerManagerService.h"
 #include "mozilla/dom/CellBroadcast.h"
 #include "mozilla/dom/IccManager.h"
 #include "mozilla/dom/InputPortManager.h"
 #include "mozilla/dom/MobileMessageManager.h"
 #include "mozilla/dom/ServiceWorkerContainer.h"
@@ -205,16 +206,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTimeManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServiceWorkerContainer)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedResolveResults)
 #ifdef MOZ_EME
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaKeySystemAccessManager)
 #endif
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDeviceStorageAreaListener)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(Navigator)
 
 void
 Navigator::Invalidate()
 {
@@ -329,16 +331,20 @@ Navigator::Invalidate()
   mServiceWorkerContainer = nullptr;
 
 #ifdef MOZ_EME
   if (mMediaKeySystemAccessManager) {
     mMediaKeySystemAccessManager->Shutdown();
     mMediaKeySystemAccessManager = nullptr;
   }
 #endif
+
+  if (mDeviceStorageAreaListener) {
+    mDeviceStorageAreaListener = nullptr;
+  }
 }
 
 //*****************************************************************************
 //    Navigator::nsIDOMNavigator
 //*****************************************************************************
 
 NS_IMETHODIMP
 Navigator::GetUserAgent(nsAString& aUserAgent)
@@ -909,16 +915,30 @@ Navigator::RegisterProtocolHandler(const
   if (!registrar) {
     return;
   }
 
   aRv = registrar->RegisterProtocolHandler(aProtocol, aURI, aTitle,
                                            mWindow->GetOuterWindow());
 }
 
+DeviceStorageAreaListener*
+Navigator::GetDeviceStorageAreaListener(ErrorResult& aRv)
+{
+  if (!mDeviceStorageAreaListener) {
+    if (!mWindow || !mWindow->GetOuterWindow() || !mWindow->GetDocShell()) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return nullptr;
+    }
+    mDeviceStorageAreaListener = new DeviceStorageAreaListener(mWindow);
+  }
+
+  return mDeviceStorageAreaListener;
+}
+
 nsDOMDeviceStorage*
 Navigator::GetDeviceStorage(const nsAString& aType, ErrorResult& aRv)
 {
   if (!mWindow || !mWindow->GetOuterWindow() || !mWindow->GetDocShell()) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
@@ -944,16 +964,38 @@ Navigator::GetDeviceStorages(const nsASt
     return;
   }
 
   nsDOMDeviceStorage::CreateDeviceStoragesFor(mWindow, aType, aStores);
 
   mDeviceStorageStores.AppendElements(aStores);
 }
 
+nsDOMDeviceStorage*
+Navigator::GetDeviceStorageByNameAndType(const nsAString& aName,
+                                         const nsAString& aType,
+                                         ErrorResult& aRv)
+{
+  if (!mWindow || !mWindow->GetOuterWindow() || !mWindow->GetDocShell()) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  nsRefPtr<nsDOMDeviceStorage> storage;
+  nsDOMDeviceStorage::CreateDeviceStorageByNameAndType(mWindow, aName, aType,
+                                                       getter_AddRefs(storage));
+
+  if (!storage) {
+    return nullptr;
+  }
+
+  mDeviceStorageStores.AppendElement(storage);
+  return storage;
+}
+
 Geolocation*
 Navigator::GetGeolocation(ErrorResult& aRv)
 {
   if (mGeolocation) {
     return mGeolocation;
   }
 
   if (!mWindow || !mWindow->GetOuterWindow() || !mWindow->GetDocShell()) {
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -89,16 +89,17 @@ class MobileConnectionArray;
 
 class PowerManager;
 class CellBroadcast;
 class IccManager;
 class Telephony;
 class Voicemail;
 class TVManager;
 class InputPortManager;
+class DeviceStorageAreaListener;
 
 namespace time {
 class TimeManager;
 } // namespace time
 
 namespace system {
 #ifdef MOZ_AUDIO_CHANNEL_MANAGER
 class AudioChannelManager;
@@ -209,21 +210,25 @@ public:
   bool TaintEnabled()
   {
     return false;
   }
   void AddIdleObserver(MozIdleObserver& aObserver, ErrorResult& aRv);
   void RemoveIdleObserver(MozIdleObserver& aObserver, ErrorResult& aRv);
   already_AddRefed<WakeLock> RequestWakeLock(const nsAString &aTopic,
                                              ErrorResult& aRv);
+  DeviceStorageAreaListener* GetDeviceStorageAreaListener(ErrorResult& aRv);
   nsDOMDeviceStorage* GetDeviceStorage(const nsAString& aType,
                                        ErrorResult& aRv);
   void GetDeviceStorages(const nsAString& aType,
                          nsTArray<nsRefPtr<nsDOMDeviceStorage> >& aStores,
                          ErrorResult& aRv);
+  nsDOMDeviceStorage* GetDeviceStorageByNameAndType(const nsAString& aName,
+                                                    const nsAString& aType,
+                                                    ErrorResult& aRv);
   DesktopNotificationCenter* GetMozNotification(ErrorResult& aRv);
   CellBroadcast* GetMozCellBroadcast(ErrorResult& aRv);
   IccManager* GetMozIccManager(ErrorResult& aRv);
   MobileMessageManager* GetMozMobileMessage();
   Telephony* GetMozTelephony(ErrorResult& aRv);
   Voicemail* GetMozVoicemail(ErrorResult& aRv);
   TVManager* GetTv();
   InputPortManager* GetInputPortManager(ErrorResult& aRv);
@@ -374,16 +379,17 @@ private:
 #endif
   nsRefPtr<nsDOMCameraManager> mCameraManager;
   nsRefPtr<MediaDevices> mMediaDevices;
   nsCOMPtr<nsIDOMNavigatorSystemMessages> mMessagesManager;
   nsTArray<nsRefPtr<nsDOMDeviceStorage> > mDeviceStorageStores;
   nsRefPtr<time::TimeManager> mTimeManager;
   nsRefPtr<ServiceWorkerContainer> mServiceWorkerContainer;
   nsCOMPtr<nsPIDOMWindow> mWindow;
+  nsRefPtr<DeviceStorageAreaListener> mDeviceStorageAreaListener;
 
   // Hashtable for saving cached objects DoResolve created, so we don't create
   // the object twice if asked for it twice, whether due to use of "delete" or
   // due to Xrays.  We could probably use a nsJSThingHashtable here, but then
   // we'd need to figure out exactly how to trace that, and that seems to be
   // rocket science.  :(
   nsInterfaceHashtable<nsStringHashKey, nsISupports> mCachedResolveResults;
 };
--- a/dom/devicestorage/DeviceStorage.h
+++ b/dom/devicestorage/DeviceStorage.h
@@ -287,16 +287,22 @@ public:
                          const nsAString& aType,
                          nsDOMDeviceStorage** aStore);
 
   static void
   CreateDeviceStoragesFor(nsPIDOMWindow* aWin,
                           const nsAString& aType,
                           nsTArray<nsRefPtr<nsDOMDeviceStorage> >& aStores);
 
+  static void
+  CreateDeviceStorageByNameAndType(nsPIDOMWindow* aWin,
+                                   const nsAString& aName,
+                                   const nsAString& aType,
+                                   nsDOMDeviceStorage** aStore);
+
   void Shutdown();
 
   static void GetOrderedVolumeNames(nsTArray<nsString>& aVolumeNames);
 
   static void GetDefaultStorageName(const nsAString& aStorageType,
                                     nsAString &aStorageName);
 
   static bool ParseFullPath(const nsAString& aFullPath,
@@ -329,16 +335,21 @@ private:
   bool mIsShareable;
   bool mIsRemovable;
 
   already_AddRefed<nsDOMDeviceStorage> GetStorage(const nsAString& aFullPath,
                                                   nsAString& aOutStoragePath);
   already_AddRefed<nsDOMDeviceStorage>
     GetStorageByName(const nsAString &aStorageName);
 
+  static already_AddRefed<nsDOMDeviceStorage>
+    GetStorageByNameAndType(nsPIDOMWindow* aWin,
+                            const nsAString& aStorageName,
+                            const nsAString& aType);
+
   nsCOMPtr<nsIPrincipal> mPrincipal;
 
   bool mIsWatchingFile;
   bool mAllowedToWatchFile;
   bool mIsDefaultLocation;
   void DispatchDefaultChangeEvent();
 
   nsresult Notify(const char* aReason, class DeviceStorageFile* aFile);
new file mode 100755
--- /dev/null
+++ b/dom/devicestorage/DeviceStorageAreaListener.cpp
@@ -0,0 +1,149 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/DeviceStorageAreaListener.h"
+#include "mozilla/dom/DeviceStorageAreaListenerBinding.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Services.h"
+#include "nsIObserverService.h"
+#ifdef MOZ_WIDGET_GONK
+#include "nsIVolume.h"
+#include "nsIVolumeService.h"
+#endif
+
+namespace mozilla {
+namespace dom {
+
+class VolumeStateObserver final : public nsIObserver
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+
+  explicit VolumeStateObserver(DeviceStorageAreaListener* aListener)
+    : mDeviceStorageAreaListener(aListener) {}
+  void ForgetListener() { mDeviceStorageAreaListener = nullptr; }
+
+private:
+  ~VolumeStateObserver() {};
+
+  // This reference is non-owning and it's cleared by
+  // DeviceStorageAreaListener's destructor.
+  DeviceStorageAreaListener* MOZ_NON_OWNING_REF mDeviceStorageAreaListener;
+};
+
+NS_IMPL_ISUPPORTS(VolumeStateObserver, nsIObserver)
+
+NS_IMETHODIMP
+VolumeStateObserver::Observe(nsISupports *aSubject,
+                             const char *aTopic,
+                             const char16_t *aData)
+{
+  if (!mDeviceStorageAreaListener) {
+    return NS_OK;
+  }
+
+#ifdef MOZ_WIDGET_GONK
+  if (!strcmp(aTopic, NS_VOLUME_STATE_CHANGED)) {
+    nsCOMPtr<nsIVolume> vol = do_QueryInterface(aSubject);
+    MOZ_ASSERT(vol);
+
+    int32_t state;
+    nsresult rv = vol->GetState(&state);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsString volName;
+    vol->GetName(volName);
+
+    switch (state) {
+      case nsIVolume::STATE_MOUNTED:
+        mDeviceStorageAreaListener->DispatchStorageAreaChangedEvent(
+          volName,
+          DeviceStorageAreaChangedEventOperation::Added);
+        break;
+      default:
+        mDeviceStorageAreaListener->DispatchStorageAreaChangedEvent(
+          volName,
+          DeviceStorageAreaChangedEventOperation::Removed);
+        break;
+    }
+  }
+#endif
+  return NS_OK;
+}
+
+NS_IMPL_ADDREF_INHERITED(DeviceStorageAreaListener, DOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(DeviceStorageAreaListener, DOMEventTargetHelper)
+
+NS_INTERFACE_MAP_BEGIN(DeviceStorageAreaListener)
+NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
+
+DeviceStorageAreaListener::DeviceStorageAreaListener(nsPIDOMWindow* aWindow)
+  : DOMEventTargetHelper(aWindow)
+{
+  MOZ_ASSERT(aWindow);
+
+  mVolumeStateObserver = new VolumeStateObserver(this);
+#ifdef MOZ_WIDGET_GONK
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  if (obs) {
+    obs->AddObserver(mVolumeStateObserver, NS_VOLUME_STATE_CHANGED, false);
+  }
+#endif
+}
+
+DeviceStorageAreaListener::~DeviceStorageAreaListener()
+{
+#ifdef MOZ_WIDGET_GONK
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  if (obs) {
+    obs->RemoveObserver(mVolumeStateObserver, NS_VOLUME_STATE_CHANGED);
+  }
+#endif
+  mVolumeStateObserver->ForgetListener();
+}
+
+JSObject*
+DeviceStorageAreaListener::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+  return DeviceStorageAreaListenerBinding::Wrap(aCx, this, aGivenProto);
+}
+
+void
+DeviceStorageAreaListener::DispatchStorageAreaChangedEvent(
+  const nsString& aStorageName,
+  DeviceStorageAreaChangedEventOperation aOperation)
+{
+  StateMapType::const_iterator iter = mStorageAreaStateMap.find(aStorageName);
+  if (iter == mStorageAreaStateMap.end() &&
+      aOperation != DeviceStorageAreaChangedEventOperation::Added) {
+    // The operation of the first event to dispatch should be "Added".
+    return;
+  }
+  if (iter != mStorageAreaStateMap.end() &&
+      iter->second == aOperation) {
+    // No need to disptach the event if the state is unchanged.
+    return;
+  }
+
+  DeviceStorageAreaChangedEventInit init;
+  init.mOperation = aOperation;
+  init.mStorageName = aStorageName;
+
+  nsRefPtr<DeviceStorageAreaChangedEvent> event =
+    DeviceStorageAreaChangedEvent::Constructor(this,
+                                               NS_LITERAL_STRING("storageareachanged"),
+                                               init);
+  event->SetTrusted(true);
+
+  bool ignore;
+  DOMEventTargetHelper::DispatchEvent(event, &ignore);
+
+  mStorageAreaStateMap[aStorageName] = aOperation;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100755
--- /dev/null
+++ b/dom/devicestorage/DeviceStorageAreaListener.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 mozilla_dom_DeviceStorageAreaListener_h
+#define mozilla_dom_DeviceStorageAreaListener_h
+
+#include <map>
+#include "mozilla/DOMEventTargetHelper.h"
+#include "mozilla/dom/DeviceStorageAreaChangedEvent.h"
+
+namespace mozilla {
+namespace dom {
+
+class VolumeStateObserver;
+
+class DeviceStorageAreaListener final : public DOMEventTargetHelper
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  IMPL_EVENT_HANDLER(storageareachanged)
+
+  explicit DeviceStorageAreaListener(nsPIDOMWindow* aWindow);
+
+  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+private:
+  friend class VolumeStateObserver;
+
+  typedef std::map<nsString, DeviceStorageAreaChangedEventOperation> StateMapType;
+  StateMapType mStorageAreaStateMap;
+
+  nsRefPtr<VolumeStateObserver> mVolumeStateObserver;
+
+  ~DeviceStorageAreaListener();
+
+  void DispatchStorageAreaChangedEvent(
+    const nsString& aStorageName,
+    DeviceStorageAreaChangedEventOperation aOperation);
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_DeviceStorageAreaListener_h
--- a/dom/devicestorage/moz.build
+++ b/dom/devicestorage/moz.build
@@ -5,22 +5,27 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS += [
     'DeviceStorage.h',
     'DeviceStorageFileDescriptor.h',
     'nsDeviceStorage.h',
 ]
 
+EXPORTS.mozilla.dom += [
+    'DeviceStorageAreaListener.h',
+]
+
 EXPORTS.mozilla.dom.devicestorage += [
     'DeviceStorageRequestChild.h',
     'DeviceStorageRequestParent.h',
 ]
 
 UNIFIED_SOURCES += [
+    'DeviceStorageAreaListener.cpp',
     'DeviceStorageRequestChild.cpp',
     'DeviceStorageRequestParent.cpp',
     'nsDeviceStorage.cpp',
 ]
 
 IPDL_SOURCES += [
     'PDeviceStorageRequest.ipdl',
 ]
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -3575,16 +3575,40 @@ nsDOMDeviceStorage::CreateDeviceStorages
     if (NS_FAILED(rv)) {
       break;
     }
     aStores.AppendElement(storage);
   }
 }
 
 // static
+void
+nsDOMDeviceStorage::CreateDeviceStorageByNameAndType(
+  nsPIDOMWindow* aWin,
+  const nsAString& aName,
+  const nsAString& aType,
+  nsDOMDeviceStorage** aStore)
+{
+  if (!DeviceStorageTypeChecker::IsVolumeBased(aType)) {
+    nsRefPtr<nsDOMDeviceStorage> storage = new nsDOMDeviceStorage(aWin);
+    if (NS_FAILED(storage->Init(aWin, aType, EmptyString()))) {
+      *aStore = nullptr;
+      return;
+    }
+    NS_ADDREF(*aStore = storage.get());
+    return;
+  }
+
+  nsRefPtr<nsDOMDeviceStorage> storage = GetStorageByNameAndType(aWin,
+                                                                 aName,
+                                                                 aType);
+  NS_ADDREF(*aStore = storage.get());
+}
+
+// static
 bool
 nsDOMDeviceStorage::ParseFullPath(const nsAString& aFullPath,
                                   nsAString& aOutStorageName,
                                   nsAString& aOutStoragePath)
 {
   aOutStorageName.Truncate();
   aOutStoragePath.Truncate();
 
@@ -3633,24 +3657,38 @@ nsDOMDeviceStorage::GetStorageByName(con
   MOZ_ASSERT(NS_IsMainThread());
 
   nsRefPtr<nsDOMDeviceStorage> ds;
 
   if (mStorageName.Equals(aStorageName)) {
     ds = this;
     return ds.forget();
   }
+
+  return GetStorageByNameAndType(GetOwner(), aStorageName, mStorageType);
+}
+
+// static
+already_AddRefed<nsDOMDeviceStorage>
+nsDOMDeviceStorage::GetStorageByNameAndType(nsPIDOMWindow* aWin,
+                                            const nsAString& aStorageName,
+                                            const nsAString& aType)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsRefPtr<nsDOMDeviceStorage> ds;
+
   VolumeNameArray volNames;
   GetOrderedVolumeNames(volNames);
   VolumeNameArray::size_type numVolumes = volNames.Length();
   VolumeNameArray::index_type i;
   for (i = 0; i < numVolumes; i++) {
     if (volNames[i].Equals(aStorageName)) {
-      ds = new nsDOMDeviceStorage(GetOwner());
-      nsresult rv = ds->Init(GetOwner(), mStorageType, aStorageName);
+      ds = new nsDOMDeviceStorage(aWin);
+      nsresult rv = ds->Init(aWin, aType, aStorageName);
       if (NS_FAILED(rv)) {
         return nullptr;
       }
       return ds.forget();
     }
   }
   return nullptr;
 }
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -2357,16 +2357,31 @@ ContentChild::RecvFileSystemUpdate(const
     unused << aIsUnmounting;
     unused << aIsRemovable;
     unused << aIsHotSwappable;
 #endif
     return true;
 }
 
 bool
+ContentChild::RecvVolumeRemoved(const nsString& aFsName)
+{
+#ifdef MOZ_WIDGET_GONK
+    nsRefPtr<nsVolumeService> vs = nsVolumeService::GetSingleton();
+    if (vs) {
+        vs->RemoveVolumeByName(aFsName);
+    }
+#else
+    // Remove warnings about unused arguments
+    unused << aFsName;
+#endif
+    return true;
+}
+
+bool
 ContentChild::RecvNotifyProcessPriorityChanged(
     const hal::ProcessPriority& aPriority)
 {
     nsCOMPtr<nsIObserverService> os = services::GetObserverService();
     NS_ENSURE_TRUE(os, true);
 
     nsRefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
     props->SetPropertyAsInt32(NS_LITERAL_STRING("priority"),
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -355,16 +355,17 @@ public:
                                       const int32_t& aMountGeneration,
                                       const bool& aIsMediaPresent,
                                       const bool& aIsSharing,
                                       const bool& aIsFormatting,
                                       const bool& aIsFake,
                                       const bool& aIsUnmounting,
                                       const bool& aIsRemovable,
                                       const bool& aIsHotSwappable) override;
+    virtual bool RecvVolumeRemoved(const nsString& aFsName) override;
 
     virtual bool RecvNuwaFork() override;
 
     virtual bool
     RecvNotifyProcessPriorityChanged(const hal::ProcessPriority& aPriority) override;
     virtual bool RecvMinimizeMemoryUsage() override;
 
     virtual bool RecvLoadAndRegisterSheet(const URIParams& aURI,
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -649,16 +649,17 @@ static const char* sObserverTopics[] = {
     "memory-pressure",
     "child-gc-request",
     "child-cc-request",
     "child-mmu-request",
     "last-pb-context-exited",
     "file-watcher-update",
 #ifdef MOZ_WIDGET_GONK
     NS_VOLUME_STATE_CHANGED,
+    NS_VOLUME_REMOVED,
     "phone-state-changed",
 #endif
 #ifdef ACCESSIBILITY
     "a11y-init-or-shutdown",
 #endif
     "app-theme-changed",
 #ifdef MOZ_ENABLE_PROFILER_SPS
     "profiler-started",
@@ -3161,16 +3162,25 @@ ContentParent::Observe(nsISupports* aSub
                                            mountGeneration, isMediaPresent,
                                            isSharing, isFormatting, isFake,
                                            isUnmounting, isRemovable, isHotSwappable);
         }
     } else if (!strcmp(aTopic, "phone-state-changed")) {
         nsString state(aData);
         unused << SendNotifyPhoneStateChange(state);
     }
+    else if(!strcmp(aTopic, NS_VOLUME_REMOVED)) {
+#ifdef MOZ_NUWA_PROCESS
+        if (!(IsNuwaReady() && IsNuwaProcess()))
+#endif
+        {
+            nsString volName(aData);
+            unused << SendVolumeRemoved(volName);
+        }
+    }
 #endif
 #ifdef ACCESSIBILITY
     // Make sure accessibility is running in content process when accessibility
     // gets initiated in chrome process.
     else if (aData && (*aData == '1') &&
              !strcmp(aTopic, "a11y-init-or-shutdown")) {
         unused << SendActivateA11y();
     }
@@ -4557,16 +4567,32 @@ ContentParent::RecvSetFakeVolumeState(co
     return true;
 #else
     NS_WARNING("ContentParent::RecvSetFakeVolumeState shouldn't be called when MOZ_WIDGET_GONK is not defined");
     return false;
 #endif
 }
 
 bool
+ContentParent::RecvRemoveFakeVolume(const nsString& fsName)
+{
+#ifdef MOZ_WIDGET_GONK
+    nsresult rv;
+    nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID, &rv);
+    if (vs) {
+        vs->RemoveFakeVolume(fsName);
+    }
+    return true;
+#else
+    NS_WARNING("ContentParent::RecvRemoveFakeVolume shouldn't be called when MOZ_WIDGET_GONK is not defined");
+    return false;
+#endif
+}
+
+bool
 ContentParent::RecvKeywordToURI(const nsCString& aKeyword,
                                 nsString* aProviderName,
                                 OptionalInputStreamParams* aPostData,
                                 OptionalURIParams* aURI)
 {
     nsCOMPtr<nsIURIFixup> fixup = do_GetService(NS_URIFIXUP_CONTRACTID);
     if (!fixup) {
         return true;
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -782,16 +782,18 @@ private:
 
     virtual bool RecvAddNewProcess(const uint32_t& aPid,
                                    InfallibleTArray<ProtocolFdMapping>&& aFds) override;
 
     virtual bool RecvCreateFakeVolume(const nsString& fsName, const nsString& mountPoint) override;
 
     virtual bool RecvSetFakeVolumeState(const nsString& fsName, const int32_t& fsState) override;
 
+    virtual bool RecvRemoveFakeVolume(const nsString& fsName) override;
+
     virtual bool RecvKeywordToURI(const nsCString& aKeyword,
                                   nsString* aProviderName,
                                   OptionalInputStreamParams* aPostData,
                                   OptionalURIParams* aURI) override;
 
     virtual bool RecvNotifyKeywordSearchLoading(const nsString &aProvider,
                                                 const nsString &aKeyword) override;
 
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -569,16 +569,19 @@ child:
 
     // Note: Any changes to this structure should also be changed in
     // VolumeInfo above.
     FileSystemUpdate(nsString fsName, nsString mountPoint, int32_t fsState,
                      int32_t mountGeneration, bool isMediaPresent,
                      bool isSharing, bool isFormatting, bool isFake,
                      bool isUnmounting, bool isRemovable, bool isHotSwappable);
 
+    // Notify volume is removed.
+    VolumeRemoved(nsString fsName);
+
     // Ask the Nuwa process to create a new child process.
     NuwaFork();
 
     NotifyProcessPriorityChanged(ProcessPriority priority);
     MinimizeMemoryUsage();
 
     /**
      * Used to manage nsIStyleSheetService across processes.
@@ -893,16 +896,17 @@ parent:
     // parent's signal to make it freeze.
     NuwaWaitForFreeze();
 
     sync AddNewProcess(uint32_t pid, ProtocolFdMapping[] aFds);
 
     // called by the child (test code only) to propagate volume changes to the parent
     async CreateFakeVolume(nsString fsName, nsString mountPoint);
     async SetFakeVolumeState(nsString fsName, int32_t fsState);
+    async RemoveFakeVolume(nsString fsName);
 
     sync KeywordToURI(nsCString keyword)
         returns (nsString providerName, OptionalInputStreamParams postData, OptionalURIParams uri);
 
     sync NotifyKeywordSearchLoading(nsString providerName, nsString keyword);
 
     async CopyFavicon(URIParams oldURI, URIParams newURI, bool isPrivate);
 
--- a/dom/system/gonk/nsIVolume.idl
+++ b/dom/system/gonk/nsIVolume.idl
@@ -96,16 +96,17 @@ interface nsIVolume : nsISupports
   // Whether this is a hot-swappable volume
   readonly attribute boolean isHotSwappable;
 
 };
 
 %{C++
 // For use with the ObserverService
 #define NS_VOLUME_STATE_CHANGED  "volume-state-changed"
+#define NS_VOLUME_REMOVED        "volume-removed"
 
 namespace mozilla {
 namespace system {
 
 // Convert a state into a loggable/printable string.
 const char* NS_VolumeStateStr(int32_t aState);
 
 } // system
--- a/dom/system/gonk/nsIVolumeService.idl
+++ b/dom/system/gonk/nsIVolumeService.idl
@@ -3,31 +3,34 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 #include "nsIVolume.idl"
 #include "nsIVolumeMountLock.idl"
 
 interface nsIArray;
 
-[scriptable, uuid(879874c6-5532-437a-bf76-703d0c2e7e77)]
+[scriptable, uuid(cfbf9880-cba5-11e4-8830-0800200c9a66)]
 interface nsIVolumeService : nsISupports
 {
     nsIVolume getVolumeByName(in DOMString volName);
     nsIVolume getVolumeByPath(in DOMString path);
     nsIVolume createOrGetVolumeByPath(in DOMString path);
 
     nsIVolumeMountLock createMountLock(in DOMString volName);
 
     nsIArray getVolumeNames();
 
     void Dump(in DOMString label);
 
     /* for test case only to simulate sdcard insertion/removal */
     void createFakeVolume(in DOMString name, in DOMString path);
     void SetFakeVolumeState(in DOMString name, in long state);
+
+    /* for test case only to test removal of storage area */
+    void removeFakeVolume(in DOMString name);
 };
 
 %{C++
 #define NS_VOLUMESERVICE_CID \
   {0x7c179fb7, 0x67a0, 0x43a3, {0x93, 0x37, 0x29, 0x4e, 0x03, 0x60, 0xb8, 0x58}}
 #define NS_VOLUMESERVICE_CONTRACTID "@mozilla.org/telephony/volume-service;1"
 %}
--- a/dom/system/gonk/nsVolumeService.cpp
+++ b/dom/system/gonk/nsVolumeService.cpp
@@ -434,17 +434,17 @@ nsVolumeService::UpdateVolume(nsIVolume*
   NS_ConvertUTF8toUTF16 stateStr(vol->StateStr());
   obs->NotifyObservers(vol, NS_VOLUME_STATE_CHANGED, stateStr.get());
 }
 
 NS_IMETHODIMP
 nsVolumeService::CreateFakeVolume(const nsAString& name, const nsAString& path)
 {
   if (XRE_GetProcessType() == GeckoProcessType_Default) {
-    nsRefPtr<nsVolume> vol = new nsVolume(name, path, nsIVolume::STATE_INIT,
+    nsRefPtr<nsVolume> vol = new nsVolume(name, path, nsIVolume::STATE_MOUNTED,
                                           -1    /* mountGeneration */,
                                           true  /* isMediaPresent */,
                                           false /* isSharing */,
                                           false /* isFormatting */,
                                           true  /* isFake */,
                                           false /* isUnmounting */,
                                           false /* isRemovable */,
                                           false /* isHotSwappable */);
@@ -464,26 +464,67 @@ nsVolumeService::SetFakeVolumeState(cons
     nsRefPtr<nsVolume> vol;
     {
       MonitorAutoLock autoLock(mArrayMonitor);
       vol = FindVolumeByName(name);
     }
     if (!vol || !vol->IsFake()) {
       return NS_ERROR_NOT_AVAILABLE;
     }
-    vol->SetState(state);
-    vol->LogState();
-    UpdateVolume(vol.get());
+
+    // UpdateVolume expects the volume passed in to NOT be the
+    // same pointer as what CreateOrFindVolumeByName would return,
+    // which is why we allocate a temporary volume here.
+    nsRefPtr<nsVolume> volume = new nsVolume(name);
+    volume->Set(vol);
+    volume->SetState(state);
+    volume->LogState();
+    UpdateVolume(volume.get());
     return NS_OK;
   }
 
   ContentChild::GetSingleton()->SendSetFakeVolumeState(nsString(name), state);
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsVolumeService::RemoveFakeVolume(const nsAString& name)
+{
+  if (XRE_GetProcessType() == GeckoProcessType_Default) {
+    SetFakeVolumeState(name, nsIVolume::STATE_NOMEDIA);
+    RemoveVolumeByName(name);
+    return NS_OK;
+  }
+
+  ContentChild::GetSingleton()->SendRemoveFakeVolume(nsString(name));
+  return NS_OK;
+}
+
+void
+nsVolumeService::RemoveVolumeByName(const nsAString& aName)
+{
+  nsRefPtr<nsVolume> vol;
+  {
+    MonitorAutoLock autoLock(mArrayMonitor);
+    vol = FindVolumeByName(aName);
+  }
+  if (!vol) {
+    return;
+  }
+  mVolumeArray.RemoveElement(vol);
+
+  if (XRE_GetProcessType() == GeckoProcessType_Default) {
+    nsCOMPtr<nsIObserverService> obs = GetObserverService();
+    if (!obs) {
+      return;
+    }
+    obs->NotifyObservers(nullptr, NS_VOLUME_REMOVED, nsString(aName).get());
+  }
+}
+
 /***************************************************************************
 * The UpdateVolumeRunnable creates an nsVolume and updates the main thread
 * data structure while running on the main thread.
 */
 class UpdateVolumeRunnable : public nsRunnable
 {
 public:
   UpdateVolumeRunnable(nsVolumeService* aVolumeService, const Volume* aVolume)
--- a/dom/system/gonk/nsVolumeService.h
+++ b/dom/system/gonk/nsVolumeService.h
@@ -41,22 +41,25 @@ public:
   nsVolumeService();
 
   static already_AddRefed<nsVolumeService> GetSingleton();
   //static nsVolumeService* GetSingleton();
   static void Shutdown();
 
   void DumpNoLock(const char* aLabel);
 
+  // To use this function, you have to create a new volume and pass it in.
   void UpdateVolume(nsIVolume* aVolume, bool aNotifyObservers = true);
   void UpdateVolumeIOThread(const Volume* aVolume);
 
   void RecvVolumesFromParent(const nsTArray<dom::VolumeInfo>& aVolumes);
   void GetVolumesForIPC(nsTArray<dom::VolumeInfo>* aResult);
 
+  void RemoveVolumeByName(const nsAString& aName);
+
 private:
   ~nsVolumeService();
 
   void CheckMountLock(const nsAString& aMountLockName,
                       const nsAString& aMountLockState);
   already_AddRefed<nsVolume> FindVolumeByMountLockName(const nsAString& aMountLockName);
   already_AddRefed<nsVolume> FindVolumeByName(const nsAString& aName);
   already_AddRefed<nsVolume> CreateOrFindVolumeByName(const nsAString& aName, bool aIsFake = false);