Bug 1286798 - Part 13: Preparation for quota checks; r=asuth
authorJan Varga <jan.varga@gmail.com>
Thu, 29 Nov 2018 21:47:55 +0100
changeset 508011 c63189db48cb7b515e53c33bc3203a92761e1d32
parent 508010 a4324eecfb06d326abe06677339bf153c627af33
child 508012 20606f9de28fd7dff30f30b3862b2bdc68df85b3
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersasuth
bugs1286798
milestone65.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 1286798 - Part 13: Preparation for quota checks; r=asuth
dom/localstorage/ActorsParent.cpp
dom/localstorage/LSDatabase.cpp
dom/localstorage/LSDatabase.h
dom/localstorage/LSObject.cpp
dom/localstorage/LSObject.h
dom/localstorage/PBackgroundLSDatabase.ipdl
--- a/dom/localstorage/ActorsParent.cpp
+++ b/dom/localstorage/ActorsParent.cpp
@@ -100,16 +100,18 @@ static_assert(kSQLiteGrowthIncrement >= 
               kSQLiteGrowthIncrement < uint32_t(INT32_MAX),
               "Must be 0 (disabled) or a positive multiple of the page size!");
 
 #define DATA_FILE_NAME "data.sqlite"
 #define JOURNAL_FILE_NAME "data.sqlite-journal"
 
 const uint32_t kAutoCommitTimeoutMs = 5000;
 
+const char kPrivateBrowsingObserverTopic[] = "last-pb-context-exited";
+
 bool
 IsOnConnectionThread();
 
 void
 AssertIsOnConnectionThread();
 
 /*******************************************************************************
  * SQLite functions
@@ -916,28 +918,26 @@ public:
 
   void
   GetItem(const nsString& aKey, nsString& aValue) const;
 
   void
   SetItem(Database* aDatabase,
           const nsString& aKey,
           const nsString& aValue,
-          bool* aChanged,
-          nsString& aOldValue);
+          LSWriteOpResponse& aResponse);
 
   void
   RemoveItem(Database* aDatabase,
              const nsString& aKey,
-             bool* aChanged,
-             nsString& aOldValue);
+             LSWriteOpResponse& aResponse);
 
   void
   Clear(Database* aDatabase,
-        bool* aChanged);
+        LSWriteOpResponse& aResponse);
 
   void
   GetKeys(nsTArray<nsString>& aKeys) const;
 
   NS_INLINE_DECL_REFCOUNTING(Datastore)
 
 private:
   // Reference counted.
@@ -1143,26 +1143,24 @@ private:
   RecvGetItem(const nsString& aKey, nsString* aValue) override;
 
   mozilla::ipc::IPCResult
   RecvGetKeys(nsTArray<nsString>* aKeys) override;
 
   mozilla::ipc::IPCResult
   RecvSetItem(const nsString& aKey,
               const nsString& aValue,
-              bool* aChanged,
-              nsString* aOldValue) override;
+              LSWriteOpResponse* aResponse) override;
 
   mozilla::ipc::IPCResult
   RecvRemoveItem(const nsString& aKey,
-                 bool* aChanged,
-                 nsString* aOldValue) override;
+                 LSWriteOpResponse* aResponse) override;
 
   mozilla::ipc::IPCResult
-  RecvClear(bool* aChanged) override;
+  RecvClear(LSWriteOpResponse* aResponse) override;
 };
 
 class Observer final
   : public PBackgroundLSObserverParent
 {
   nsCString mOrigin;
   bool mActorDestroyed;
 
@@ -1499,20 +1497,20 @@ private:
 /*******************************************************************************
  * Other class declarations
  ******************************************************************************/
 
 class QuotaClient final
   : public mozilla::dom::quota::Client
 {
   class ClearPrivateBrowsingRunnable;
-  class PrivateBrowsingObserver;
+  class Observer;
 
   static QuotaClient* sInstance;
-  static bool sPrivateBrowsingObserverRegistered;
+  static bool sObserversRegistered;
 
   bool mShutdownRequested;
 
 public:
   QuotaClient();
 
   static bool
   IsShuttingDownOnBackgroundThread()
@@ -1602,32 +1600,32 @@ public:
   }
 
 private:
   ~ClearPrivateBrowsingRunnable() = default;
 
   NS_DECL_NSIRUNNABLE
 };
 
-class QuotaClient::PrivateBrowsingObserver final
+class QuotaClient::Observer final
   : public nsIObserver
 {
   nsCOMPtr<nsIEventTarget> mBackgroundEventTarget;
 
 public:
-  explicit PrivateBrowsingObserver(nsIEventTarget* aBackgroundEventTarget)
+  explicit Observer(nsIEventTarget* aBackgroundEventTarget)
     : mBackgroundEventTarget(aBackgroundEventTarget)
   {
     MOZ_ASSERT(NS_IsMainThread());
   }
 
   NS_DECL_ISUPPORTS
 
 private:
-  ~PrivateBrowsingObserver()
+  ~Observer()
   {
     MOZ_ASSERT(NS_IsMainThread());
   }
 
   NS_DECL_NSIOBSERVER
 };
 
 /*******************************************************************************
@@ -2542,22 +2540,20 @@ Datastore::GetItem(const nsString& aKey,
     aValue.SetIsVoid(true);
   }
 }
 
 void
 Datastore::SetItem(Database* aDatabase,
                    const nsString& aKey,
                    const nsString& aValue,
-                   bool* aChanged,
-                   nsString& aOldValue)
+                   LSWriteOpResponse& aResponse)
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aDatabase);
-  MOZ_ASSERT(aChanged);
   MOZ_ASSERT(!mClosed);
 
   nsString oldValue;
   if (!mValues.Get(aKey, &oldValue)) {
     oldValue.SetIsVoid(true);
   }
 
   bool changed;
@@ -2573,25 +2569,26 @@ Datastore::SetItem(Database* aDatabase,
     if (IsPersistent()) {
       EnsureTransaction();
 
       RefPtr<SetItemOp> op = new SetItemOp(mConnection, aKey, aValue);
       mConnection->Dispatch(op);
     }
   }
 
-  *aChanged = changed;
-  aOldValue = oldValue;
+  LSNotifyInfo info;
+  info.changed() = changed;
+  info.oldValue() = oldValue;
+  aResponse = info;
 }
 
 void
 Datastore::RemoveItem(Database* aDatabase,
                       const nsString& aKey,
-                      bool* aChanged,
-                      nsString& aOldValue)
+                      LSWriteOpResponse& aResponse)
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aDatabase);
   MOZ_ASSERT(!mClosed);
 
   bool changed;
   nsString oldValue;
   if (!mValues.Get(aKey, &oldValue)) {
@@ -2607,26 +2604,27 @@ Datastore::RemoveItem(Database* aDatabas
     if (IsPersistent()) {
       EnsureTransaction();
 
       RefPtr<RemoveItemOp> op = new RemoveItemOp(mConnection, aKey);
       mConnection->Dispatch(op);
     }
   }
 
-  *aChanged = changed;
-  aOldValue = oldValue;
+  LSNotifyInfo info;
+  info.changed() = changed;
+  info.oldValue() = oldValue;
+  aResponse = info;
 }
 
 void
 Datastore::Clear(Database* aDatabase,
-                 bool* aChanged)
+                 LSWriteOpResponse& aResponse)
 {
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(aChanged);
   MOZ_ASSERT(!mClosed);
 
   bool changed;
   if (!mValues.Count()) {
     changed = false;
   } else {
     changed = true;
 
@@ -2639,17 +2637,19 @@ Datastore::Clear(Database* aDatabase,
     if (IsPersistent()) {
       EnsureTransaction();
 
       RefPtr<ClearOp> op = new ClearOp(mConnection);
       mConnection->Dispatch(op);
     }
   }
 
-  *aChanged = changed;
+  LSNotifyInfo info;
+  info.changed() = changed;
+  aResponse = info;
 }
 
 void
 Datastore::GetKeys(nsTArray<nsString>& aKeys) const
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(!mClosed);
 
@@ -3058,67 +3058,63 @@ Database::RecvGetItem(const nsString& aK
   mDatastore->GetItem(aKey, *aValue);
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 Database::RecvSetItem(const nsString& aKey,
                       const nsString& aValue,
-                      bool* aChanged,
-                      nsString* aOldValue)
+                      LSWriteOpResponse* aResponse)
 {
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(aChanged);
-  MOZ_ASSERT(aOldValue);
+  MOZ_ASSERT(aResponse);
   MOZ_ASSERT(mDatastore);
 
   if (NS_WARN_IF(mAllowedToClose)) {
     ASSERT_UNLESS_FUZZING();
     return IPC_FAIL_NO_REASON(this);
   }
 
-  mDatastore->SetItem(this, aKey, aValue, aChanged, *aOldValue);
+  mDatastore->SetItem(this, aKey, aValue, *aResponse);
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 Database::RecvRemoveItem(const nsString& aKey,
-                         bool* aChanged,
-                         nsString* aOldValue)
+                         LSWriteOpResponse* aResponse)
 {
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(aChanged);
-  MOZ_ASSERT(aOldValue);
+  MOZ_ASSERT(aResponse);
   MOZ_ASSERT(mDatastore);
 
   if (NS_WARN_IF(mAllowedToClose)) {
     ASSERT_UNLESS_FUZZING();
     return IPC_FAIL_NO_REASON(this);
   }
 
-  mDatastore->RemoveItem(this, aKey, aChanged, *aOldValue);
+  mDatastore->RemoveItem(this, aKey, *aResponse);
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
-Database::RecvClear(bool* aChanged)
+Database::RecvClear(LSWriteOpResponse* aResponse)
 {
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(aChanged);
+  MOZ_ASSERT(aResponse);
   MOZ_ASSERT(mDatastore);
 
   if (NS_WARN_IF(mAllowedToClose)) {
     ASSERT_UNLESS_FUZZING();
     return IPC_FAIL_NO_REASON(this);
   }
 
-  mDatastore->Clear(this, aChanged);
+  mDatastore->Clear(this, *aResponse);
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 Database::RecvGetKeys(nsTArray<nsString>* aKeys)
 {
   AssertIsOnBackgroundThread();
@@ -4231,17 +4227,17 @@ PrepareObserverOp::GetResponse(LSRequest
   aResponse = prepareObserverResponse;
 }
 
 /*******************************************************************************
  * QuotaClient
  ******************************************************************************/
 
 QuotaClient* QuotaClient::sInstance = nullptr;
-bool QuotaClient::sPrivateBrowsingObserverRegistered = false;
+bool QuotaClient::sObserversRegistered = false;
 
 QuotaClient::QuotaClient()
   : mShutdownRequested(false)
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(!sInstance, "We expect this to be a singleton!");
 
   sInstance = this;
@@ -4263,31 +4259,31 @@ QuotaClient::GetType()
 
 // static
 nsresult
 QuotaClient::RegisterObservers(nsIEventTarget* aBackgroundEventTarget)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aBackgroundEventTarget);
 
-  if (!sPrivateBrowsingObserverRegistered) {
+  if (!sObserversRegistered) {
     nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
     if (NS_WARN_IF(!obs)) {
       return NS_ERROR_FAILURE;
     }
 
-    nsCOMPtr<nsIObserver> observer =
-      new PrivateBrowsingObserver(aBackgroundEventTarget);
-
-    nsresult rv = obs->AddObserver(observer, "last-pb-context-exited", false);
+    nsCOMPtr<nsIObserver> observer = new Observer(aBackgroundEventTarget);
+
+    nsresult rv =
+      obs->AddObserver(observer, kPrivateBrowsingObserverTopic, false);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
-    sPrivateBrowsingObserverRegistered = true;
+    sObserversRegistered = true;
   }
 
   return NS_OK;
 }
 
 nsresult
 QuotaClient::InitOrigin(PersistenceType aPersistenceType,
                         const nsACString& aGroup,
@@ -4604,39 +4600,43 @@ ClearPrivateBrowsingRunnable::Run()
   AssertIsOnBackgroundThread();
 
   if (gDatastores) {
     for (auto iter = gDatastores->ConstIter(); !iter.Done(); iter.Next()) {
       Datastore* datastore = iter.Data();
       MOZ_ASSERT(datastore);
 
       if (datastore->PrivateBrowsingId()) {
-        bool dummy;
-        datastore->Clear(nullptr, &dummy);
+        LSWriteOpResponse dummy;
+        datastore->Clear(nullptr, dummy);
       }
     }
   }
 
   return NS_OK;
 }
 
-NS_IMPL_ISUPPORTS(QuotaClient::PrivateBrowsingObserver, nsIObserver)
+NS_IMPL_ISUPPORTS(QuotaClient::Observer, nsIObserver)
 
 NS_IMETHODIMP
 QuotaClient::
-PrivateBrowsingObserver::Observe(nsISupports* aSubject,
-                                 const char* aTopic,
-                                 const char16_t* aData)
+Observer::Observe(nsISupports* aSubject,
+                  const char* aTopic,
+                  const char16_t* aData)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(!strcmp(aTopic, "last-pb-context-exited"));
-
-  RefPtr<ClearPrivateBrowsingRunnable> runnable =
-    new ClearPrivateBrowsingRunnable();
-
-  MOZ_ALWAYS_SUCCEEDS(
-    mBackgroundEventTarget->Dispatch(runnable, NS_DISPATCH_NORMAL));
-
+
+  if (!strcmp(aTopic, kPrivateBrowsingObserverTopic)) {
+    RefPtr<ClearPrivateBrowsingRunnable> runnable =
+      new ClearPrivateBrowsingRunnable();
+
+    MOZ_ALWAYS_SUCCEEDS(
+      mBackgroundEventTarget->Dispatch(runnable, NS_DISPATCH_NORMAL));
+
+    return NS_OK;
+  }
+
+  NS_WARNING("Unknown observer topic!");
   return NS_OK;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/localstorage/LSDatabase.cpp
+++ b/dom/localstorage/LSDatabase.cpp
@@ -117,72 +117,60 @@ LSDatabase::GetKeys(nsTArray<nsString>& 
 
   aKeys.SwapElements(result);
   return NS_OK;
 }
 
 nsresult
 LSDatabase::SetItem(const nsAString& aKey,
                     const nsAString& aValue,
-                    bool* aChanged,
-                    nsAString& aOldValue)
+                    LSWriteOpResponse& aResponse)
 {
   AssertIsOnOwningThread();
-  MOZ_ASSERT(aChanged);
   MOZ_ASSERT(mActor);
   MOZ_ASSERT(!mAllowedToClose);
 
-  bool changed;
-  nsString oldValue;
+  LSWriteOpResponse response;
   if (NS_WARN_IF(!mActor->SendSetItem(nsString(aKey),
                                       nsString(aValue),
-                                      &changed,
-                                      &oldValue))) {
+                                      &response))) {
     return NS_ERROR_FAILURE;
   }
 
-  *aChanged = changed;
-  aOldValue = oldValue;
+  aResponse = response;
   return NS_OK;
 }
 
 nsresult
 LSDatabase::RemoveItem(const nsAString& aKey,
-                       bool* aChanged,
-                       nsAString& aOldValue)
+                       LSWriteOpResponse& aResponse)
 {
   AssertIsOnOwningThread();
-  MOZ_ASSERT(aChanged);
   MOZ_ASSERT(mActor);
   MOZ_ASSERT(!mAllowedToClose);
 
-  bool changed;
-  nsString oldValue;
-  if (NS_WARN_IF(!mActor->SendRemoveItem(nsString(aKey),
-                                         &changed,
-                                         &oldValue))) {
+  LSWriteOpResponse response;
+  if (NS_WARN_IF(!mActor->SendRemoveItem(nsString(aKey), &response))) {
     return NS_ERROR_FAILURE;
   }
 
-  *aChanged = changed;
-  aOldValue = oldValue;
+  aResponse = response;
   return NS_OK;
 }
 
 nsresult
-LSDatabase::Clear(bool* aChanged)
+LSDatabase::Clear(LSWriteOpResponse& aResponse)
 {
   AssertIsOnOwningThread();
-  MOZ_ASSERT(aChanged);
   MOZ_ASSERT(mActor);
   MOZ_ASSERT(!mAllowedToClose);
 
-  bool changed;
-  if (NS_WARN_IF(!mActor->SendClear(&changed))) {
+  LSWriteOpResponse response;
+  if (NS_WARN_IF(!mActor->SendClear(&response))) {
     return NS_ERROR_FAILURE;
   }
 
-  *aChanged = changed;
+  aResponse = response;
   return NS_OK;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/localstorage/LSDatabase.h
+++ b/dom/localstorage/LSDatabase.h
@@ -6,16 +6,17 @@
 
 #ifndef mozilla_dom_localstorage_LSDatabase_h
 #define mozilla_dom_localstorage_LSDatabase_h
 
 namespace mozilla {
 namespace dom {
 
 class LSDatabaseChild;
+class LSWriteOpResponse;
 
 class LSDatabase final
 {
   LSDatabaseChild* mActor;
 
   bool mAllowedToClose;
 
 public:
@@ -64,26 +65,24 @@ public:
           nsAString& aResult);
 
   nsresult
   GetKeys(nsTArray<nsString>& aKeys);
 
   nsresult
   SetItem(const nsAString& aKey,
           const nsAString& aValue,
-          bool* aChanged,
-          nsAString& aOldValue);
+          LSWriteOpResponse& aResponse);
 
   nsresult
   RemoveItem(const nsAString& aKey,
-             bool* aChanged,
-             nsAString& aOldValue);
+             LSWriteOpResponse& aResponse);
 
   nsresult
-  Clear(bool* aChanged);
+  Clear(LSWriteOpResponse& aResponse);
 
 private:
   ~LSDatabase();
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/localstorage/LSObject.cpp
+++ b/dom/localstorage/LSObject.cpp
@@ -411,26 +411,32 @@ LSObject::SetItem(const nsAString& aKey,
   }
 
   nsresult rv = EnsureDatabase();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aError.Throw(rv);
     return;
   }
 
-  bool changed;
-  nsString oldValue;
-  rv = mDatabase->SetItem(aKey, aValue, &changed, oldValue);
+  LSWriteOpResponse response;
+  rv = mDatabase->SetItem(aKey, aValue, response);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aError.Throw(rv);
     return;
   }
 
-  if (changed) {
-    OnChange(aKey, oldValue, aValue);
+  LSNotifyInfo info;
+  rv = GetInfoFromResponse(response, info);
+  if (NS_FAILED(rv)) {
+    aError.Throw(rv);
+    return;
+  }
+
+  if (info.changed()) {
+    OnChange(aKey, info.oldValue(), aValue);
   }
 }
 
 void
 LSObject::RemoveItem(const nsAString& aKey,
                      nsIPrincipal& aSubjectPrincipal,
                      ErrorResult& aError)
 {
@@ -442,26 +448,32 @@ LSObject::RemoveItem(const nsAString& aK
   }
 
   nsresult rv = EnsureDatabase();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aError.Throw(rv);
     return;
   }
 
-  bool changed;
-  nsString oldValue;
-  rv = mDatabase->RemoveItem(aKey, &changed, oldValue);
+  LSWriteOpResponse response;
+  rv = mDatabase->RemoveItem(aKey, response);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aError.Throw(rv);
     return;
   }
 
-  if (changed) {
-    OnChange(aKey, oldValue, VoidString());
+  LSNotifyInfo info;
+  rv = GetInfoFromResponse(response, info);
+  if (NS_FAILED(rv)) {
+    aError.Throw(rv);
+    return;
+  }
+
+  if (info.changed()) {
+    OnChange(aKey, info.oldValue(), VoidString());
   }
 }
 
 void
 LSObject::Clear(nsIPrincipal& aSubjectPrincipal,
                 ErrorResult& aError)
 {
   AssertIsOnOwningThread();
@@ -472,24 +484,31 @@ LSObject::Clear(nsIPrincipal& aSubjectPr
   }
 
   nsresult rv = EnsureDatabase();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aError.Throw(rv);
     return;
   }
 
-  bool changed;
-  rv = mDatabase->Clear(&changed);
+  LSWriteOpResponse response;
+  rv = mDatabase->Clear(response);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aError.Throw(rv);
     return;
   }
 
-  if (changed) {
+  LSNotifyInfo info;
+  rv = GetInfoFromResponse(response, info);
+  if (NS_FAILED(rv)) {
+    aError.Throw(rv);
+    return;
+  }
+
+  if (info.changed()) {
     OnChange(VoidString(), VoidString(), VoidString());
   }
 }
 
 NS_IMPL_ADDREF_INHERITED(LSObject, Storage)
 NS_IMPL_RELEASE_INHERITED(LSObject, Storage)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(LSObject)
@@ -689,16 +708,38 @@ LSObject::DropObserver()
 {
   AssertIsOnOwningThread();
 
   if (mObserver) {
     mObserver = nullptr;
   }
 }
 
+nsresult
+LSObject::GetInfoFromResponse(const LSWriteOpResponse& aResponse,
+                              LSNotifyInfo& aInfo)
+{
+  AssertIsOnOwningThread();
+
+  if (aResponse.type() == LSWriteOpResponse::Tnsresult) {
+    nsresult errorCode = aResponse.get_nsresult();
+
+    if (errorCode == NS_ERROR_FILE_NO_DEVICE_SPACE) {
+      errorCode = NS_ERROR_DOM_QUOTA_EXCEEDED_ERR;
+    }
+
+    return errorCode;
+  }
+
+  MOZ_ASSERT(aResponse.type() == LSWriteOpResponse::TLSNotifyInfo);
+
+  aInfo = aResponse.get_LSNotifyInfo();
+  return NS_OK;
+}
+
 void
 LSObject::OnChange(const nsAString& aKey,
                    const nsAString& aOldValue,
                    const nsAString& aNewValue)
 {
   AssertIsOnOwningThread();
 
   NotifyChange(/* aStorage */ this,
--- a/dom/localstorage/LSObject.h
+++ b/dom/localstorage/LSObject.h
@@ -21,22 +21,24 @@ namespace ipc {
 
 class PrincipalInfo;
 
 } // namespace ipc
 
 namespace dom {
 
 class LSDatabase;
+class LSNotifyInfo;
 class LSObjectChild;
 class LSObserver;
 class LSRequestChild;
 class LSRequestChildCallback;
 class LSRequestParams;
 class LSRequestResponse;
+class LSWriteOpResponse;
 
 class LSObject final
   : public Storage
 {
   typedef mozilla::ipc::PrincipalInfo PrincipalInfo;
 
   friend nsGlobalWindowInner;
 
@@ -151,16 +153,20 @@ private:
   DropDatabase();
 
   nsresult
   EnsureObserver();
 
   void
   DropObserver();
 
+  nsresult
+  GetInfoFromResponse(const LSWriteOpResponse& aResponse,
+                      LSNotifyInfo& aInfo);
+
   void
   OnChange(const nsAString& aKey,
            const nsAString& aOldValue,
            const nsAString& aNewValue);
 
   // Storage overrides.
   void
   LastRelease() override;
--- a/dom/localstorage/PBackgroundLSDatabase.ipdl
+++ b/dom/localstorage/PBackgroundLSDatabase.ipdl
@@ -2,16 +2,28 @@
  * 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 PBackgroundLSObject;
 
 namespace mozilla {
 namespace dom {
 
+struct LSNotifyInfo
+{
+  bool changed;
+  nsString oldValue;
+};
+
+union LSWriteOpResponse
+{
+  nsresult;
+  LSNotifyInfo;
+};
+
 sync protocol PBackgroundLSDatabase
 {
   manager PBackgroundLSObject;
 
 parent:
   // The DeleteMe message is used to avoid a race condition between the parent
   // actor and the child actor. The PBackgroundLSDatabase protocol could be
   // simply destroyed by sending the __delete__ message from the child side.
@@ -33,23 +45,23 @@ parent:
 
   sync GetItem(nsString key)
     returns (nsString value);
 
   sync GetKeys()
     returns (nsString[] keys);
 
   sync SetItem(nsString key, nsString value)
-    returns (bool changed, nsString oldValue);
+    returns (LSWriteOpResponse response);
 
   sync RemoveItem(nsString key)
-    returns (bool changed, nsString oldValue);
+    returns (LSWriteOpResponse response);
 
   sync Clear()
-    returns (bool changed);
+    returns (LSWriteOpResponse response);
 
 child:
   async __delete__();
 
   async RequestAllowToClose();
 };
 
 } // namespace dom