Bug 1060196 - Mark the volume as if we've started unmounting. r=dhylands, a=bajaj
authorAlphan Chen <alchen@mozilla.com>
Mon, 22 Sep 2014 11:42:33 +0800
changeset 225057 373b644d6c0ad6daf6dc0fd9311b5784ca979b30
parent 225056 ec4507790224dda54b4d96061abba99e8c377566
child 225058 96e15bbed7ab4385a1a2e24e2339776b26dc6ddc
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdhylands, bajaj
bugs1060196
milestone34.0a2
Bug 1060196 - Mark the volume as if we've started unmounting. r=dhylands, a=bajaj
dom/devicestorage/nsDeviceStorage.cpp
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/PContent.ipdl
dom/system/gonk/AutoMounter.cpp
dom/system/gonk/Volume.cpp
dom/system/gonk/Volume.h
dom/system/gonk/nsIVolume.idl
dom/system/gonk/nsVolume.cpp
dom/system/gonk/nsVolume.h
dom/system/gonk/nsVolumeService.cpp
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -1614,16 +1614,23 @@ DeviceStorageFile::GetStatus(nsAString& 
   }
   bool isFormatting;
   rv = vol->GetIsFormatting(&isFormatting);
   NS_ENSURE_SUCCESS_VOID(rv);
   if (isFormatting) {
     aStatus.AssignLiteral("unavailable");
     return;
   }
+  bool isUnmounting;
+  rv = vol->GetIsUnmounting(&isUnmounting);
+  NS_ENSURE_SUCCESS_VOID(rv);
+  if (isUnmounting) {
+    aStatus.AssignLiteral("unavailable");
+    return;
+  }
   int32_t volState;
   rv = vol->GetState(&volState);
   NS_ENSURE_SUCCESS_VOID(rv);
   if (volState == nsIVolume::STATE_MOUNTED) {
     aStatus.AssignLiteral("available");
   }
 #endif
 }
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -1773,37 +1773,40 @@ ContentChild::RecvFilePathUpdate(const n
 bool
 ContentChild::RecvFileSystemUpdate(const nsString& aFsName,
                                    const nsString& aVolumeName,
                                    const int32_t& aState,
                                    const int32_t& aMountGeneration,
                                    const bool& aIsMediaPresent,
                                    const bool& aIsSharing,
                                    const bool& aIsFormatting,
-                                   const bool& aIsFake)
+                                   const bool& aIsFake,
+                                   const bool& aIsUnmounting)
 {
 #ifdef MOZ_WIDGET_GONK
     nsRefPtr<nsVolume> volume = new nsVolume(aFsName, aVolumeName, aState,
                                              aMountGeneration, aIsMediaPresent,
-                                             aIsSharing, aIsFormatting, aIsFake);
+                                             aIsSharing, aIsFormatting, aIsFake,
+                                             aIsUnmounting);
 
     nsRefPtr<nsVolumeService> vs = nsVolumeService::GetSingleton();
     if (vs) {
         vs->UpdateVolume(volume);
     }
 #else
     // Remove warnings about unused arguments
     unused << aFsName;
     unused << aVolumeName;
     unused << aState;
     unused << aMountGeneration;
     unused << aIsMediaPresent;
     unused << aIsSharing;
     unused << aIsFormatting;
     unused << aIsFake;
+    unused << aIsUnmounting;
 #endif
     return true;
 }
 
 bool
 ContentChild::RecvNotifyProcessPriorityChanged(
     const hal::ProcessPriority& aPriority)
 {
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -294,17 +294,18 @@ public:
                                     const nsCString& aReason) MOZ_OVERRIDE;
     virtual bool RecvFileSystemUpdate(const nsString& aFsName,
                                       const nsString& aVolumeName,
                                       const int32_t& aState,
                                       const int32_t& aMountGeneration,
                                       const bool& aIsMediaPresent,
                                       const bool& aIsSharing,
                                       const bool& aIsFormatting,
-                                      const bool& aIsFake) MOZ_OVERRIDE;
+                                      const bool& aIsFake,
+                                      const bool& aIsUnmounting) MOZ_OVERRIDE;
 
     virtual bool RecvNuwaFork() MOZ_OVERRIDE;
 
     virtual bool
     RecvNotifyProcessPriorityChanged(const hal::ProcessPriority& aPriority) MOZ_OVERRIDE;
     virtual bool RecvMinimizeMemoryUsage() MOZ_OVERRIDE;
 
     virtual bool RecvLoadAndRegisterSheet(const URIParams& aURI,
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -2634,33 +2634,36 @@ ContentParent::Observe(nsISupports* aSub
         nsString volName;
         nsString mountPoint;
         int32_t  state;
         int32_t  mountGeneration;
         bool     isMediaPresent;
         bool     isSharing;
         bool     isFormatting;
         bool     isFake;
+        bool     isUnmounting;
 
         vol->GetName(volName);
         vol->GetMountPoint(mountPoint);
         vol->GetState(&state);
         vol->GetMountGeneration(&mountGeneration);
         vol->GetIsMediaPresent(&isMediaPresent);
         vol->GetIsSharing(&isSharing);
         vol->GetIsFormatting(&isFormatting);
         vol->GetIsFake(&isFake);
+        vol->GetIsUnmounting(&isUnmounting);
 
 #ifdef MOZ_NUWA_PROCESS
         if (!(IsNuwaReady() && IsNuwaProcess()))
 #endif
         {
             unused << SendFileSystemUpdate(volName, mountPoint, state,
                                            mountGeneration, isMediaPresent,
-                                           isSharing, isFormatting, isFake);
+                                           isSharing, isFormatting, isFake,
+                                           isUnmounting);
         }
     } else if (!strcmp(aTopic, "phone-state-changed")) {
         nsString state(aData);
         unused << SendNotifyPhoneStateChange(state);
     }
 #endif
 #ifdef ACCESSIBILITY
     // Make sure accessibility is running in content process when accessibility
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -295,16 +295,17 @@ struct VolumeInfo {
   nsString name;
   nsString mountPoint;
   int32_t volState;
   int32_t mountGeneration;
   bool isMediaPresent;
   bool isSharing;
   bool isFormatting;
   bool isFake;
+  bool isUnmounting;
 };
 
 intr protocol PContent
 {
     parent opens PCompositor;
     parent opens PSharedBufferManager;
     parent opens PImageBridge;
     child opens PBackground;
@@ -437,17 +438,17 @@ child:
 
     FilePathUpdate(nsString storageType, nsString storageName, nsString filepath,
                    nsCString reasons);
 
     // 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 isSharing, bool isFormatting, bool isFake, bool isUnmounting);
 
     // Ask the Nuwa process to create a new child process.
     NuwaFork();
 
     NotifyProcessPriorityChanged(ProcessPriority priority);
     MinimizeMemoryUsage();
 
     /**
--- a/dom/system/gonk/AutoMounter.cpp
+++ b/dom/system/gonk/AutoMounter.cpp
@@ -904,24 +904,26 @@ AutoMounter::UpdateState()
           if (vol->IsMountLocked()) {
             // The volume is currently locked, so leave it in the mounted
             // state.
             LOGW("UpdateState: Mounted volume %s is locked, not sharing or formatting",
                  vol->NameStr());
             break;
           }
 
-          // Mark the volume as if we've started sharing. This will cause
-          // apps which watch device storage notifications to see the volume
-          // go into the shared state, and prompt them to close any open files
-          // that they might have.
+          // Mark the volume as if we've started sharing/formatting/unmmounting.
+          // This will cause apps which watch device storage notifications to see
+          // the volume go into the different state, and prompt them to close any
+          // open files that they might have.
           if (tryToShare && vol->IsSharingEnabled()) {
             vol->SetIsSharing(true);
           } else if (vol->IsFormatRequested()){
             vol->SetIsFormatting(true);
+          } else if (vol->IsUnmountRequested()){
+            vol->SetIsUnmounting(true);
           }
 
           // Check to see if there are any open files on the volume and
           // don't initiate the unmount while there are open files.
           OpenFileFinder::Info fileInfo;
           OpenFileFinder fileFinder(vol->MountPoint());
           if (fileFinder.First(&fileInfo)) {
             LOGW("The following files are open under '%s'",
--- a/dom/system/gonk/Volume.cpp
+++ b/dom/system/gonk/Volume.cpp
@@ -62,16 +62,17 @@ Volume::Volume(const nsCSubstring& aName
     mMountLocked(true),  // Needs to agree with nsVolume::nsVolume
     mSharingEnabled(false),
     mFormatRequested(false),
     mMountRequested(false),
     mUnmountRequested(false),
     mCanBeShared(true),
     mIsSharing(false),
     mIsFormatting(false),
+    mIsUnmounting(false),
     mId(sNextId++)
 {
   DBG("Volume %s: created", NameStr());
 }
 
 void
 Volume::SetIsSharing(bool aIsSharing)
 {
@@ -94,16 +95,28 @@ Volume::SetIsFormatting(bool aIsFormatti
   LOG("Volume %s: IsFormatting set to %d state %s",
       NameStr(), (int)mIsFormatting, StateStr(mState));
   if (mIsFormatting) {
     mEventObserverList.Broadcast(this);
   }
 }
 
 void
+Volume::SetIsUnmounting(bool aIsUnmounting)
+{
+  if (aIsUnmounting == mIsUnmounting) {
+    return;
+  }
+  mIsUnmounting = aIsUnmounting;
+  LOG("Volume %s: IsUnmounting set to %d state %s",
+      NameStr(), (int)mIsUnmounting, StateStr(mState));
+  mEventObserverList.Broadcast(this);
+}
+
+void
 Volume::SetMediaPresent(bool aMediaPresent)
 {
   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
 
   // mMediaPresent is slightly redunant to the state, however
   // when media is removed (while Idle), we get the following:
   //    631 Volume sdcard /mnt/sdcard disk removed (179:0)
@@ -196,36 +209,47 @@ Volume::SetState(Volume::STATE aNewState
 
   switch (aNewState) {
      case nsIVolume::STATE_NOMEDIA:
        // Cover the startup case where we don't get insertion/removal events
        mMediaPresent = false;
        mIsSharing = false;
        mUnmountRequested = false;
        mMountRequested = false;
+       mIsUnmounting = false;
        break;
 
      case nsIVolume::STATE_MOUNTED:
        mMountRequested = false;
        mIsFormatting = false;
        mIsSharing = false;
+       mIsUnmounting = false;
        break;
      case nsIVolume::STATE_FORMATTING:
        mFormatRequested = false;
        mIsFormatting = true;
        mIsSharing = false;
+       mIsUnmounting = false;
        break;
 
      case nsIVolume::STATE_SHARED:
      case nsIVolume::STATE_SHAREDMNT:
        // Covers startup cases. Normally, mIsSharing would be set to true
        // when we issue the command to initiate the sharing process, but
        // it's conceivable that a volume could already be in a shared state
        // when b2g starts.
        mIsSharing = true;
+       mIsUnmounting = false;
+       mIsFormatting = false;
+       break;
+
+     case nsIVolume::STATE_UNMOUNTING:
+       mIsUnmounting = true;
+       mIsFormatting = false;
+       mIsSharing = false;
        break;
 
      case nsIVolume::STATE_IDLE:
        break;
      default:
        break;
   }
   mState = aNewState;
--- a/dom/system/gonk/Volume.h
+++ b/dom/system/gonk/Volume.h
@@ -53,16 +53,17 @@ public:
   bool CanBeFormatted() const         { return CanBeShared(); }
   bool CanBeMounted() const           { return CanBeShared(); }
   bool IsSharingEnabled() const       { return mCanBeShared && mSharingEnabled; }
   bool IsFormatRequested() const      { return CanBeFormatted() && mFormatRequested; }
   bool IsMountRequested() const       { return CanBeMounted() && mMountRequested; }
   bool IsUnmountRequested() const     { return CanBeMounted() && mUnmountRequested; }
   bool IsSharing() const              { return mIsSharing; }
   bool IsFormatting() const           { return mIsFormatting; }
+  bool IsUnmounting() const           { return mIsUnmounting; }
 
   void SetSharingEnabled(bool aSharingEnabled);
   void SetFormatRequested(bool aFormatRequested);
   void SetMountRequested(bool aMountRequested);
   void SetUnmountRequested(bool aUnmountRequested);
 
   typedef mozilla::Observer<Volume *>     EventObserver;
   typedef mozilla::ObserverList<Volume *> EventObserverList;
@@ -83,16 +84,17 @@ private:
   void StartMount(VolumeResponseCallback* aCallback);
   void StartUnmount(VolumeResponseCallback* aCallback);
   void StartFormat(VolumeResponseCallback* aCallback);
   void StartShare(VolumeResponseCallback* aCallback);
   void StartUnshare(VolumeResponseCallback* aCallback);
 
   void SetIsSharing(bool aIsSharing);
   void SetIsFormatting(bool aIsFormatting);
+  void SetIsUnmounting(bool aIsUnmounting);
   void SetState(STATE aNewState);
   void SetMediaPresent(bool aMediaPresent);
   void SetMountPoint(const nsCSubstring& aMountPoint);
   void StartCommand(VolumeCommand* aCommand);
 
   void HandleVoldResponse(int aResponseCode, nsCWhitespaceTokenizer& aTokenizer);
 
   static void UpdateMountLock(const nsACString& aVolumeName,
@@ -107,16 +109,17 @@ private:
   bool              mMountLocked;
   bool              mSharingEnabled;
   bool              mFormatRequested;
   bool              mMountRequested;
   bool              mUnmountRequested;
   bool              mCanBeShared;
   bool              mIsSharing;
   bool              mIsFormatting;
+  bool              mIsUnmounting;
   uint32_t          mId;                // Unique ID (used by MTP)
 
   static EventObserverList mEventObserverList;
 };
 
 } // system
 } // mozilla
 
--- a/dom/system/gonk/nsIVolume.idl
+++ b/dom/system/gonk/nsIVolume.idl
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 #include "nsIVolumeStat.idl"
 
-[scriptable, uuid(13caa69c-8f1f-11e3-8e36-10bf48d707fb)]
+[scriptable, uuid(9D0DC356-395D-11E4-9306-A97C1D5D46B0)]
 interface nsIVolume : nsISupports
 {
   // These MUST match the states from android's system/vold/Volume.h header
   const long STATE_INIT        = -1;
   const long STATE_NOMEDIA     = 0;
   const long STATE_IDLE        = 1;
   const long STATE_PENDING     = 2;
   const long STATE_CHECKING    = 3;
@@ -62,16 +62,18 @@ interface nsIVolume : nsISupports
   // transitioning from mounted to sharing and back again.
   readonly attribute boolean isSharing;
 
   // Determines if the volume is currently formatting. This sets true once 
   // mFormatRequest == true and mState == STATE_MOUNTED, and sets false
   // once the volume has been formatted and mounted again.
   readonly attribute boolean isFormatting;
 
+  readonly attribute boolean isUnmounting;
+
   nsIVolumeStat getStats();
 
   // Formats the volume in IO thread, if the volume is ready to be formatted.
   // Automounter will unmount it, format it and then mount it again.
   void format();
 
   // Mounts the volume in IO thread, if the volume is already unmounted.
   // Automounter will mount it. Otherwise Automounter will skip this.
--- a/dom/system/gonk/nsVolume.cpp
+++ b/dom/system/gonk/nsVolume.cpp
@@ -51,17 +51,18 @@ nsVolume::nsVolume(const Volume* aVolume
   : mName(NS_ConvertUTF8toUTF16(aVolume->Name())),
     mMountPoint(NS_ConvertUTF8toUTF16(aVolume->MountPoint())),
     mState(aVolume->State()),
     mMountGeneration(aVolume->MountGeneration()),
     mMountLocked(aVolume->IsMountLocked()),
     mIsFake(false),
     mIsMediaPresent(aVolume->MediaPresent()),
     mIsSharing(aVolume->IsSharing()),
-    mIsFormatting(aVolume->IsFormatting())
+    mIsFormatting(aVolume->IsFormatting()),
+    mIsUnmounting(aVolume->IsUnmounting())
 {
 }
 
 bool nsVolume::Equals(nsIVolume* aVolume)
 {
   nsString volName;
   aVolume->GetName(volName);
   if (!mName.Equals(volName)) {
@@ -105,43 +106,55 @@ bool nsVolume::Equals(nsIVolume* aVolume
   }
 
   bool isFormatting;
   aVolume->GetIsFormatting(&isFormatting);
   if (mIsFormatting != isFormatting) {
     return false;
   }
 
+  bool isUnmounting;
+  aVolume->GetIsUnmounting(&isUnmounting);
+  if (mIsUnmounting != isUnmounting) {
+    return false;
+  }
+
   return true;
 }
 
-NS_IMETHODIMP nsVolume::GetIsMediaPresent(bool *aIsMediaPresent)
+NS_IMETHODIMP nsVolume::GetIsMediaPresent(bool* aIsMediaPresent)
 {
   *aIsMediaPresent = mIsMediaPresent;
   return NS_OK;
 }
 
-NS_IMETHODIMP nsVolume::GetIsMountLocked(bool *aIsMountLocked)
+NS_IMETHODIMP nsVolume::GetIsMountLocked(bool* aIsMountLocked)
 {
   *aIsMountLocked = mMountLocked;
   return NS_OK;
 }
 
-NS_IMETHODIMP nsVolume::GetIsSharing(bool *aIsSharing)
+NS_IMETHODIMP nsVolume::GetIsSharing(bool* aIsSharing)
 {
   *aIsSharing = mIsSharing;
   return NS_OK;
 }
 
-NS_IMETHODIMP nsVolume::GetIsFormatting(bool *aIsFormatting)
+NS_IMETHODIMP nsVolume::GetIsFormatting(bool* aIsFormatting)
 {
   *aIsFormatting = mIsFormatting;
   return NS_OK;
 }
 
+NS_IMETHODIMP nsVolume::GetIsUnmounting(bool* aIsUnmounting)
+{
+  *aIsUnmounting = mIsUnmounting;
+  return NS_OK;
+}
+
 NS_IMETHODIMP nsVolume::GetName(nsAString& aName)
 {
   aName = mName;
   return NS_OK;
 }
 
 NS_IMETHODIMP nsVolume::GetMountGeneration(int32_t* aMountGeneration)
 {
@@ -257,21 +270,21 @@ void nsVolume::UnmountVolumeIOThread(con
   AutoMounterUnmountVolume(aVolume);
 }
 
 void
 nsVolume::LogState() const
 {
   if (mState == nsIVolume::STATE_MOUNTED) {
     LOG("nsVolume: %s state %s @ '%s' gen %d locked %d fake %d "
-        "media %d sharing %d formatting %d",
+        "media %d sharing %d formatting %d unmounting %d",
         NameStr().get(), StateStr(), MountPointStr().get(),
         MountGeneration(), (int)IsMountLocked(), (int)IsFake(),
         (int)IsMediaPresent(), (int)IsSharing(),
-        (int)IsFormatting());
+        (int)IsFormatting(), (int)IsUnmounting());
     return;
   }
 
   LOG("nsVolume: %s state %s", NameStr().get(), StateStr());
 }
 
 void nsVolume::Set(nsIVolume* aVolume)
 {
@@ -279,16 +292,17 @@ void nsVolume::Set(nsIVolume* aVolume)
 
   aVolume->GetName(mName);
   aVolume->GetMountPoint(mMountPoint);
   aVolume->GetState(&mState);
   aVolume->GetIsFake(&mIsFake);
   aVolume->GetIsMediaPresent(&mIsMediaPresent);
   aVolume->GetIsSharing(&mIsSharing);
   aVolume->GetIsFormatting(&mIsFormatting);
+  aVolume->GetIsUnmounting(&mIsUnmounting);
 
   int32_t volMountGeneration;
   aVolume->GetMountGeneration(&volMountGeneration);
 
   if (mState != nsIVolume::STATE_MOUNTED) {
     // Since we're not in the mounted state, we need to
     // forgot whatever mount generation we may have had.
     mMountGeneration = -1;
--- a/dom/system/gonk/nsVolume.h
+++ b/dom/system/gonk/nsVolume.h
@@ -25,40 +25,43 @@ public:
   // This constructor is used by the UpdateVolumeRunnable constructor
   nsVolume(const Volume* aVolume);
 
   // This constructor is used by ContentChild::RecvFileSystemUpdate which is
   // used to update the volume cache maintained in the child process.
   nsVolume(const nsAString& aName, const nsAString& aMountPoint,
            const int32_t& aState, const int32_t& aMountGeneration,
            const bool& aIsMediaPresent, const bool& aIsSharing,
-           const bool& aIsFormatting, const bool& aIsFake)
+           const bool& aIsFormatting, const bool& aIsFake,
+           const bool& aIsUnmounting)
     : mName(aName),
       mMountPoint(aMountPoint),
       mState(aState),
       mMountGeneration(aMountGeneration),
       mMountLocked(false),
       mIsFake(aIsFake),
       mIsMediaPresent(aIsMediaPresent),
       mIsSharing(aIsSharing),
-      mIsFormatting(aIsFormatting)
+      mIsFormatting(aIsFormatting),
+      mIsUnmounting(aIsUnmounting)
   {
   }
 
   // This constructor is used by nsVolumeService::FindAddVolumeByName, and
   // will be followed shortly by a Set call.
   nsVolume(const nsAString& aName)
     : mName(aName),
       mState(STATE_INIT),
       mMountGeneration(-1),
       mMountLocked(true),  // Needs to agree with Volume::Volume
       mIsFake(false),
       mIsMediaPresent(false),
       mIsSharing(false),
-      mIsFormatting(false)
+      mIsFormatting(false),
+      mIsUnmounting(false)
   {
   }
 
   bool Equals(nsIVolume* aVolume);
   void Set(nsIVolume* aVolume);
 
   void LogState() const;
 
@@ -73,16 +76,17 @@ public:
 
   int32_t State() const               { return mState; }
   const char* StateStr() const        { return NS_VolumeStateStr(mState); }
 
   bool IsFake() const                 { return mIsFake; }
   bool IsMediaPresent() const         { return mIsMediaPresent; }
   bool IsSharing() const              { return mIsSharing; }
   bool IsFormatting() const           { return mIsFormatting; }
+  bool IsUnmounting() const           { return mIsUnmounting; }
 
   typedef nsTArray<nsRefPtr<nsVolume> > Array;
 
 private:
   virtual ~nsVolume() {}  // MozExternalRefCountType complains if this is non-virtual
 
   friend class nsVolumeService; // Calls the following XxxMountLock functions
   void UpdateMountLock(const nsAString& aMountLockState);
@@ -98,14 +102,15 @@ private:
   nsString mMountPoint;
   int32_t  mState;
   int32_t  mMountGeneration;
   bool     mMountLocked;
   bool     mIsFake;
   bool     mIsMediaPresent;
   bool     mIsSharing;
   bool     mIsFormatting;
+  bool     mIsUnmounting;
 };
 
 } // system
 } // mozilla
 
 #endif  // mozilla_system_nsvolume_h__
--- a/dom/system/gonk/nsVolumeService.cpp
+++ b/dom/system/gonk/nsVolumeService.cpp
@@ -203,17 +203,18 @@ nsVolumeService::CreateOrGetVolumeByPath
   // In order to support queries by the updater, we will fabricate a volume
   // from the pathname, so that the caller can determine the volume size.
   nsCOMPtr<nsIVolume> vol = new nsVolume(NS_LITERAL_STRING("fake"),
                                          aPath, nsIVolume::STATE_MOUNTED,
                                          -1    /* generation */,
                                          true  /* isMediaPresent*/,
                                          false /* isSharing */,
                                          false /* isFormatting */,
-                                         true  /* isFake */);
+                                         true  /* isFake */,
+                                         false /* isUnmounting*/);
   vol.forget(aResult);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsVolumeService::GetVolumeNames(nsIArray** aVolNames)
 {
   GetVolumesFromParent();
@@ -264,16 +265,17 @@ nsVolumeService::GetVolumesForIPC(nsTArr
     volInfo->name()             = vol->mName;
     volInfo->mountPoint()       = vol->mMountPoint;
     volInfo->volState()         = vol->mState;
     volInfo->mountGeneration()  = vol->mMountGeneration;
     volInfo->isMediaPresent()   = vol->mIsMediaPresent;
     volInfo->isSharing()        = vol->mIsSharing;
     volInfo->isFormatting()     = vol->mIsFormatting;
     volInfo->isFake()           = vol->mIsFake;
+    volInfo->isUnmounting()     = vol->mIsUnmounting;
   }
 }
 
 void
 nsVolumeService::GetVolumesFromParent()
 {
   if (XRE_GetProcessType() == GeckoProcessType_Default) {
     // We are the parent. Therefore our volumes are already correct.
@@ -291,17 +293,18 @@ nsVolumeService::GetVolumesFromParent()
     const VolumeInfo& volInfo(result[i]);
     nsRefPtr<nsVolume> vol = new nsVolume(volInfo.name(),
                                           volInfo.mountPoint(),
                                           volInfo.volState(),
                                           volInfo.mountGeneration(),
                                           volInfo.isMediaPresent(),
                                           volInfo.isSharing(),
                                           volInfo.isFormatting(),
-                                          volInfo.isFake());
+                                          volInfo.isFake(),
+                                          volInfo.isUnmounting());
     UpdateVolume(vol, false);
   }
 }
 
 NS_IMETHODIMP
 nsVolumeService::CreateMountLock(const nsAString& aVolumeName, nsIVolumeMountLock **aResult)
 {
   nsCOMPtr<nsIVolumeMountLock> mountLock = nsVolumeMountLock::Create(aVolumeName);
@@ -415,17 +418,18 @@ 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,
                                           -1    /* mountGeneration */,
                                           true  /* isMediaPresent */,
                                           false /* isSharing */,
                                           false /* isFormatting */,
-                                          true  /* isFake */);
+                                          true  /* isFake */,
+                                          false /* isUnmounting */);
     vol->LogState();
     UpdateVolume(vol.get());
     return NS_OK;
   }
 
   ContentChild::GetSingleton()->SendCreateFakeVolume(nsString(name), nsString(path));
   return NS_OK;
 }
@@ -465,40 +469,40 @@ public:
   {
     MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   }
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(NS_IsMainThread());
     DBG("UpdateVolumeRunnable::Run '%s' state %s gen %d locked %d "
-        "media %d sharing %d formatting %d",
+        "media %d sharing %d formatting %d unmounting %d",
         mVolume->NameStr().get(), mVolume->StateStr(),
         mVolume->MountGeneration(), (int)mVolume->IsMountLocked(),
         (int)mVolume->IsMediaPresent(), mVolume->IsSharing(),
-        mVolume->IsFormatting());
+        mVolume->IsFormatting(), mVolume->IsUnmounting());
 
     mVolumeService->UpdateVolume(mVolume);
     mVolumeService = nullptr;
     mVolume = nullptr;
     return NS_OK;
   }
 
 private:
   nsRefPtr<nsVolumeService> mVolumeService;
   nsRefPtr<nsVolume>        mVolume;
 };
 
 void
 nsVolumeService::UpdateVolumeIOThread(const Volume* aVolume)
 {
   DBG("UpdateVolumeIOThread: Volume '%s' state %s mount '%s' gen %d locked %d "
-      "media %d sharing %d formatting %d",
+      "media %d sharing %d formatting %d unmounting %d",
       aVolume->NameStr(), aVolume->StateStr(), aVolume->MountPoint().get(),
       aVolume->MountGeneration(), (int)aVolume->IsMountLocked(),
       (int)aVolume->MediaPresent(), (int)aVolume->IsSharing(),
-      (int)aVolume->IsFormatting());
+      (int)aVolume->IsFormatting(), (int)mVolume->IsUnmounting());
   MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   NS_DispatchToMainThread(new UpdateVolumeRunnable(this, aVolume));
 }
 
 } // namespace system
 } // namespace mozilla