From 60831f2e38f0a14a4face761e3f2949d8d07bfb7 Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Thu, 29 Nov 2018 21:47:20 +0100 Subject: [PATCH] Bug 1286798 - Part 3: New basic (memory only) implementation of LocalStorage; r=asuth,mccr8 The implementation is based on a cache (datastore) living in the parent process and sync IPC calls initiated from content processes. IPC communication is done using per principal/origin database actors which connect to the datastore. The synchronous blocking of the main thread is done by creating a nested event target and spinning the event loop. --- .../test/browser/browser.ini | 2 +- .../test/browser/browser-common.ini | 2 +- .../privatebrowsing/test/browser/browser.ini | 2 +- .../components/sessionstore/test/browser.ini | 2 +- devtools/client/storage/test/browser.ini | 18 +- devtools/server/tests/browser/browser.ini | 6 +- dom/base/nsGlobalWindowInner.cpp | 50 +- dom/base/test/mochitest.ini | 2 +- dom/ipc/ContentChild.cpp | 9 +- dom/ipc/ContentChild.h | 2 +- dom/localstorage/ActorsChild.cpp | 118 +++ dom/localstorage/ActorsChild.h | 116 +++ dom/localstorage/ActorsParent.cpp | 975 ++++++++++++++++++ dom/localstorage/ActorsParent.h | 49 + dom/localstorage/LSDatabase.cpp | 141 +++ dom/localstorage/LSDatabase.h | 73 ++ dom/localstorage/LSObject.cpp | 629 +++++++++++ dom/localstorage/LSObject.h | 110 ++ dom/localstorage/LocalStorageCommon.cpp | 32 + dom/localstorage/LocalStorageCommon.h | 193 ++++ dom/localstorage/LocalStorageManager2.cpp | 88 ++ dom/localstorage/LocalStorageManager2.h | 31 + dom/localstorage/PBackgroundLSDatabase.ipdl | 49 + dom/localstorage/PBackgroundLSRequest.ipdl | 49 + .../PBackgroundLSSharedTypes.ipdlh | 32 + dom/localstorage/moz.build | 41 + dom/moz.build | 1 + dom/storage/LocalStorageManager.cpp | 14 + dom/storage/LocalStorageManager.h | 2 +- dom/storage/StorageIPC.cpp | 3 + dom/storage/StorageObserver.cpp | 63 +- dom/tests/browser/browser.ini | 4 +- dom/tests/mochitest/localstorage/chrome.ini | 4 +- .../mochitest/localstorage/mochitest.ini | 22 +- .../mochitest/storageevent/mochitest.ini | 4 +- dom/tests/moz.build | 2 +- editor/libeditor/tests/mochitest.ini | 6 +- ipc/glue/BackgroundChildImpl.cpp | 72 +- ipc/glue/BackgroundChildImpl.h | 13 + ipc/glue/BackgroundParentImpl.cpp | 74 ++ ipc/glue/BackgroundParentImpl.h | 22 + ipc/glue/PBackground.ipdl | 9 + ipc/ipdl/sync-messages.ini | 14 + layout/build/nsLayoutModule.cpp | 16 +- modules/libpref/init/all.js | 6 + .../opener-closed.html.ini | 2 + .../opener-noopener.html.ini | 2 + .../opener-noreferrer.html.ini | 2 + .../choose-_blank-002.html.ini | 2 + .../choose-_parent-004.html.ini | 2 + .../choose-_self-002.html.ini | 2 + .../choose-_top-001.html.ini | 2 + .../choose-_top-002.html.ini | 2 + .../choose-_top-003.html.ini | 2 + .../windows/noreferrer-null-opener.html.ini | 2 + .../windows/noreferrer-window-name.html.ini | 3 +- .../meta/webstorage/document-domain.html.ini | 2 +- .../meta/webstorage/event_basic.html.ini | 2 + .../webstorage/event_body_attribute.html.ini | 2 + .../webstorage/event_case_sensitive.html.ini | 2 + .../meta/webstorage/event_local_key.html.ini | 2 + .../webstorage/event_local_newvalue.html.ini | 2 + .../webstorage/event_local_oldvalue.html.ini | 2 + .../event_local_removeitem.html.ini | 2 + .../event_local_storagearea.html.ini | 2 + .../meta/webstorage/event_local_url.html.ini | 2 + .../webstorage/event_no_duplicates.html.ini | 2 + .../webstorage/event_setattribute.html.ini | 2 + ...ge_local_setitem_quotaexceedederr.html.ini | 2 + .../antitracking/test/browser/browser.ini | 8 +- .../test/mochitest/mochitest-common.ini | 6 +- .../extensions/test/mochitest/mochitest.ini | 2 +- toolkit/forgetaboutsite/moz.build | 2 +- .../forgetaboutsite/test/unit/xpcshell.ini | 2 +- 74 files changed, 3134 insertions(+), 107 deletions(-) create mode 100644 dom/localstorage/ActorsChild.cpp create mode 100644 dom/localstorage/ActorsChild.h create mode 100644 dom/localstorage/ActorsParent.cpp create mode 100644 dom/localstorage/ActorsParent.h create mode 100644 dom/localstorage/LSDatabase.cpp create mode 100644 dom/localstorage/LSDatabase.h create mode 100644 dom/localstorage/LSObject.cpp create mode 100644 dom/localstorage/LSObject.h create mode 100644 dom/localstorage/LocalStorageCommon.cpp create mode 100644 dom/localstorage/LocalStorageCommon.h create mode 100644 dom/localstorage/LocalStorageManager2.cpp create mode 100644 dom/localstorage/LocalStorageManager2.h create mode 100644 dom/localstorage/PBackgroundLSDatabase.ipdl create mode 100644 dom/localstorage/PBackgroundLSRequest.ipdl create mode 100644 dom/localstorage/PBackgroundLSSharedTypes.ipdlh create mode 100644 dom/localstorage/moz.build create mode 100644 testing/web-platform/meta/html/browsers/windows/auxiliary-browsing-contexts/opener-closed.html.ini create mode 100644 testing/web-platform/meta/html/browsers/windows/auxiliary-browsing-contexts/opener-noopener.html.ini create mode 100644 testing/web-platform/meta/html/browsers/windows/auxiliary-browsing-contexts/opener-noreferrer.html.ini create mode 100644 testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_blank-002.html.ini create mode 100644 testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_parent-004.html.ini create mode 100644 testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_self-002.html.ini create mode 100644 testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_top-001.html.ini create mode 100644 testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_top-002.html.ini create mode 100644 testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_top-003.html.ini create mode 100644 testing/web-platform/meta/html/browsers/windows/noreferrer-null-opener.html.ini create mode 100644 testing/web-platform/meta/webstorage/event_basic.html.ini create mode 100644 testing/web-platform/meta/webstorage/event_body_attribute.html.ini create mode 100644 testing/web-platform/meta/webstorage/event_case_sensitive.html.ini create mode 100644 testing/web-platform/meta/webstorage/event_local_key.html.ini create mode 100644 testing/web-platform/meta/webstorage/event_local_newvalue.html.ini create mode 100644 testing/web-platform/meta/webstorage/event_local_oldvalue.html.ini create mode 100644 testing/web-platform/meta/webstorage/event_local_removeitem.html.ini create mode 100644 testing/web-platform/meta/webstorage/event_local_storagearea.html.ini create mode 100644 testing/web-platform/meta/webstorage/event_local_url.html.ini create mode 100644 testing/web-platform/meta/webstorage/event_no_duplicates.html.ini create mode 100644 testing/web-platform/meta/webstorage/event_setattribute.html.ini create mode 100644 testing/web-platform/meta/webstorage/storage_local_setitem_quotaexceedederr.html.ini diff --git a/browser/components/contextualidentity/test/browser/browser.ini b/browser/components/contextualidentity/test/browser/browser.ini index 7cc9cd57c6ec..451bcd062722 100644 --- a/browser/components/contextualidentity/test/browser/browser.ini +++ b/browser/components/contextualidentity/test/browser/browser.ini @@ -10,7 +10,7 @@ support-files = [browser_aboutURLs.js] [browser_eme.js] [browser_favicon.js] -[browser_forgetaboutsite.js] +#[browser_forgetaboutsite.js] [browser_forgetAPI_cookie_getCookiesWithOriginAttributes.js] [browser_restore_getCookiesWithOriginAttributes.js] [browser_forgetAPI_EME_forgetThisSite.js] diff --git a/browser/components/extensions/test/browser/browser-common.ini b/browser/components/extensions/test/browser/browser-common.ini index 62d5d0703641..9fd71872c21b 100644 --- a/browser/components/extensions/test/browser/browser-common.ini +++ b/browser/components/extensions/test/browser/browser-common.ini @@ -65,7 +65,7 @@ skip-if = (os == 'mac' && debug) # Bug 1482004, also fails in test-verify [browser_ext_browsingData_formData.js] [browser_ext_browsingData_history.js] [browser_ext_browsingData_indexedDB.js] -[browser_ext_browsingData_localStorage.js] +#[browser_ext_browsingData_localStorage.js] [browser_ext_browsingData_pluginData.js] [browser_ext_browsingData_serviceWorkers.js] [browser_ext_chrome_settings_overrides_home.js] diff --git a/browser/components/privatebrowsing/test/browser/browser.ini b/browser/components/privatebrowsing/test/browser/browser.ini index 43947f3de878..6db62a2fa6fe 100644 --- a/browser/components/privatebrowsing/test/browser/browser.ini +++ b/browser/components/privatebrowsing/test/browser/browser.ini @@ -25,7 +25,7 @@ tags = trackingprotection [browser_privatebrowsing_aboutSessionRestore.js] [browser_privatebrowsing_cache.js] [browser_privatebrowsing_certexceptionsui.js] -[browser_privatebrowsing_concurrent.js] +#[browser_privatebrowsing_concurrent.js] [browser_privatebrowsing_context_and_chromeFlags.js] [browser_privatebrowsing_crh.js] [browser_privatebrowsing_downloadLastDir.js] diff --git a/browser/components/sessionstore/test/browser.ini b/browser/components/sessionstore/test/browser.ini index 3a05d3734099..b1ecf4b2bdb2 100644 --- a/browser/components/sessionstore/test/browser.ini +++ b/browser/components/sessionstore/test/browser.ini @@ -154,7 +154,7 @@ skip-if = true [browser_461634.js] [browser_463205.js] [browser_463206.js] -[browser_464199.js] +#[browser_464199.js] [browser_465215.js] [browser_465223.js] [browser_466937.js] diff --git a/devtools/client/storage/test/browser.ini b/devtools/client/storage/test/browser.ini index d9f9115ab09d..ab13a1931d9e 100644 --- a/devtools/client/storage/test/browser.ini +++ b/devtools/client/storage/test/browser.ini @@ -45,23 +45,23 @@ tags = usercontextid [browser_storage_cookies_samesite.js] skip-if = true # Bug 1448484 - sameSite1 is "Unset" - Got undefined, expected Unset [browser_storage_cookies_tab_navigation.js] -[browser_storage_delete.js] -[browser_storage_delete_all.js] -[browser_storage_delete_tree.js] -[browser_storage_delete_usercontextid.js] -tags = usercontextid +#[browser_storage_delete.js] +#[browser_storage_delete_all.js] +#[browser_storage_delete_tree.js] +#[browser_storage_delete_usercontextid.js] +#tags = usercontextid [browser_storage_dom_cache_disabled.js] [browser_storage_dynamic_updates_cookies.js] -[browser_storage_dynamic_updates_localStorage.js] -[browser_storage_dynamic_updates_sessionStorage.js] +#[browser_storage_dynamic_updates_localStorage.js] +#[browser_storage_dynamic_updates_sessionStorage.js] [browser_storage_empty_objectstores.js] [browser_storage_file_url.js] [browser_storage_indexeddb_delete.js] [browser_storage_indexeddb_delete_blocked.js] [browser_storage_indexeddb_duplicate_names.js] [browser_storage_indexeddb_overflow.js] -[browser_storage_localstorage_add.js] -[browser_storage_localstorage_edit.js] +#[browser_storage_localstorage_add.js] +#[browser_storage_localstorage_edit.js] [browser_storage_localstorage_error.js] [browser_storage_localstorage_rapid_add_remove.js] [browser_storage_overflow.js] diff --git a/devtools/server/tests/browser/browser.ini b/devtools/server/tests/browser/browser.ini index d9988c6bfeea..5deafdc8318e 100644 --- a/devtools/server/tests/browser/browser.ini +++ b/devtools/server/tests/browser/browser.ini @@ -94,10 +94,10 @@ skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still di [browser_spawn_actor_in_parent.js] [browser_storage_browser_toolbox_indexeddb.js] [browser_storage_cookies-duplicate-names.js] -[browser_storage_dynamic_windows.js] +#[browser_storage_dynamic_windows.js] [browser_storage_listings.js] -[browser_storage_updates.js] -skip-if = (verify && debug && (os == 'mac' || os == 'linux')) +#[browser_storage_updates.js] +#skip-if = (verify && debug && (os == 'mac' || os == 'linux')) [browser_stylesheets_getTextEmpty.js] [browser_stylesheets_nested-iframes.js] [browser_register_actor.js] diff --git a/dom/base/nsGlobalWindowInner.cpp b/dom/base/nsGlobalWindowInner.cpp index 112502b61ffc..05e12ecb2f9a 100644 --- a/dom/base/nsGlobalWindowInner.cpp +++ b/dom/base/nsGlobalWindowInner.cpp @@ -23,6 +23,8 @@ #include "mozilla/dom/DOMPrefs.h" #include "mozilla/dom/EventTarget.h" #include "mozilla/dom/LocalStorage.h" +#include "mozilla/dom/LocalStorageCommon.h" +#include "mozilla/dom/LSObject.h" #include "mozilla/dom/PartitionedLocalStorage.h" #include "mozilla/dom/Storage.h" #include "mozilla/dom/IdleRequest.h" @@ -4908,32 +4910,38 @@ nsGlobalWindowInner::GetLocalStorage(ErrorResult& aError) if (access != nsContentUtils::StorageAccess::ePartitionedOrDeny && (!mLocalStorage || mLocalStorage->Type() == Storage::ePartitionedLocalStorage)) { - nsresult rv; - nsCOMPtr storageManager = - do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv); - if (NS_FAILED(rv)) { - aError.Throw(rv); - return nullptr; - } + RefPtr storage; - nsString documentURI; - if (mDoc) { - aError = mDoc->GetDocumentURI(documentURI); - if (NS_WARN_IF(aError.Failed())) { + if (NextGenLocalStorageEnabled()) { + aError = LSObject::Create(this, getter_AddRefs(storage)); + } else { + nsresult rv; + nsCOMPtr storageManager = + do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv); + if (NS_FAILED(rv)) { + aError.Throw(rv); return nullptr; } + + nsString documentURI; + if (mDoc) { + aError = mDoc->GetDocumentURI(documentURI); + if (NS_WARN_IF(aError.Failed())) { + return nullptr; + } + } + + nsIPrincipal *principal = GetPrincipal(); + if (!principal) { + aError.Throw(NS_ERROR_DOM_SECURITY_ERR); + return nullptr; + } + + aError = storageManager->CreateStorage(this, principal, documentURI, + IsPrivateBrowsing(), + getter_AddRefs(storage)); } - nsIPrincipal *principal = GetPrincipal(); - if (!principal) { - aError.Throw(NS_ERROR_DOM_SECURITY_ERR); - return nullptr; - } - - RefPtr storage; - aError = storageManager->CreateStorage(this, principal, documentURI, - IsPrivateBrowsing(), - getter_AddRefs(storage)); if (aError.Failed()) { return nullptr; } diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini index 18b5f73d6ac8..9523fa410710 100644 --- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -577,7 +577,7 @@ skip-if = toolkit == 'android' || (verify && !debug && (os == 'linux')) #bug 687 [test_bug1025933.html] [test_bug1037687.html] support-files = test_bug1037687_subframe.html -[test_bug1043106.html] +#[test_bug1043106.html] [test_bug1057176.html] [test_bug1060938.html] [test_bug1064481.html] diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index fd51a6c42f7e..78321b413c2a 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -41,6 +41,7 @@ #include "mozilla/dom/FileCreatorHelper.h" #include "mozilla/dom/GetFilesHelper.h" #include "mozilla/dom/IPCBlobUtils.h" +#include "mozilla/dom/LSObject.h" #include "mozilla/dom/MemoryReportRequest.h" #include "mozilla/dom/PLoginReputationChild.h" #include "mozilla/dom/PushNotifier.h" @@ -3811,15 +3812,21 @@ ContentChild::GetSpecificMessageEventTarget(const Message& aMsg) } } -#ifdef NIGHTLY_BUILD void ContentChild::OnChannelReceivedMessage(const Message& aMsg) { + if (aMsg.is_sync()) { + LSObject::CancelSyncLoop(); + } + +#ifdef NIGHTLY_BUILD if (nsContentUtils::IsMessageInputEvent(aMsg)) { mPendingInputEvents++; } +#endif } +#ifdef NIGHTLY_BUILD PContentChild::Result ContentChild::OnMessageReceived(const Message& aMsg) { diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h index 9cf18b87a9aa..1b960f3841ae 100644 --- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -768,10 +768,10 @@ private: virtual already_AddRefed GetSpecificMessageEventTarget(const Message& aMsg) override; -#ifdef NIGHTLY_BUILD virtual void OnChannelReceivedMessage(const Message& aMsg) override; +#ifdef NIGHTLY_BUILD virtual PContentChild::Result OnMessageReceived(const Message& aMsg) override; diff --git a/dom/localstorage/ActorsChild.cpp b/dom/localstorage/ActorsChild.cpp new file mode 100644 index 000000000000..9a9c9e45cc79 --- /dev/null +++ b/dom/localstorage/ActorsChild.cpp @@ -0,0 +1,118 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=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 "ActorsChild.h" + +#include "LSDatabase.h" + +namespace mozilla { +namespace dom { + +/******************************************************************************* + * LSDatabaseChild + ******************************************************************************/ + +LSDatabaseChild::LSDatabaseChild(LSDatabase* aDatabase) + : mDatabase(aDatabase) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aDatabase); + + MOZ_COUNT_CTOR(LSDatabaseChild); +} + +LSDatabaseChild::~LSDatabaseChild() +{ + AssertIsOnOwningThread(); + + MOZ_COUNT_DTOR(LSDatabaseChild); +} + +void +LSDatabaseChild::SendDeleteMeInternal() +{ + AssertIsOnOwningThread(); + + if (mDatabase) { + mDatabase->ClearActor(); + mDatabase = nullptr; + + MOZ_ALWAYS_TRUE(PBackgroundLSDatabaseChild::SendDeleteMe()); + } +} + +void +LSDatabaseChild::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnOwningThread(); + + if (mDatabase) { + mDatabase->ClearActor(); +#ifdef DEBUG + mDatabase = nullptr; +#endif + } +} + +/******************************************************************************* + * LocalStorageRequestChild + ******************************************************************************/ + +LSRequestChild::LSRequestChild(LSRequestChildCallback* aCallback) + : mCallback(aCallback) + , mFinishing(false) +{ + AssertIsOnOwningThread(); + + MOZ_COUNT_CTOR(LSRequestChild); +} + +LSRequestChild::~LSRequestChild() +{ + AssertIsOnOwningThread(); + + MOZ_COUNT_DTOR(LSRequestChild); +} + +bool +LSRequestChild::Finishing() const +{ + AssertIsOnOwningThread(); + + return mFinishing; +} + +void +LSRequestChild::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnOwningThread(); +} + +mozilla::ipc::IPCResult +LSRequestChild::Recv__delete__(const LSRequestResponse& aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mCallback); + + mCallback->OnResponse(aResponse); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult +LSRequestChild::RecvReady() +{ + AssertIsOnOwningThread(); + + mFinishing = true; + + SendFinish(); + + return IPC_OK(); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/localstorage/ActorsChild.h b/dom/localstorage/ActorsChild.h new file mode 100644 index 000000000000..271d1b921b3d --- /dev/null +++ b/dom/localstorage/ActorsChild.h @@ -0,0 +1,116 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=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/. */ + +#ifndef mozilla_dom_localstorage_ActorsChild_h +#define mozilla_dom_localstorage_ActorsChild_h + +#include "mozilla/dom/PBackgroundLSDatabaseChild.h" +#include "mozilla/dom/PBackgroundLSRequestChild.h" + +namespace mozilla { + +namespace ipc { + +class BackgroundChildImpl; + +} // namespace ipc + +namespace dom { + +class LSDatabase; +class LSObject; +class LSRequestChildCallback; + +class LSDatabaseChild final + : public PBackgroundLSDatabaseChild +{ + friend class mozilla::ipc::BackgroundChildImpl; + friend class LSDatabase; + friend class LSObject; + + LSDatabase* mDatabase; + + NS_DECL_OWNINGTHREAD + +public: + void + AssertIsOnOwningThread() const + { + NS_ASSERT_OWNINGTHREAD(LSDatabaseChild); + } + +private: + // Only created by LSObject. + explicit LSDatabaseChild(LSDatabase* aDatabase); + + // Only destroyed by mozilla::ipc::BackgroundChildImpl. + ~LSDatabaseChild(); + + void + SendDeleteMeInternal(); + + // IPDL methods are only called by IPDL. + void + ActorDestroy(ActorDestroyReason aWhy) override; +}; + +class LSRequestChild final + : public PBackgroundLSRequestChild +{ + friend class LSObject; + friend class LSObjectChild; + + RefPtr mCallback; + + bool mFinishing; + + NS_DECL_OWNINGTHREAD + +public: + void + AssertIsOnOwningThread() const + { + NS_ASSERT_OWNINGTHREAD(LSReqeustChild); + } + + bool + Finishing() const; + +private: + // Only created by LSObject. + explicit LSRequestChild(LSRequestChildCallback* aCallback); + + // Only destroyed by mozilla::ipc::BackgroundChildImpl. + ~LSRequestChild(); + + // IPDL methods are only called by IPDL. + void + ActorDestroy(ActorDestroyReason aWhy) override; + + mozilla::ipc::IPCResult + Recv__delete__(const LSRequestResponse& aResponse) override; + + mozilla::ipc::IPCResult + RecvReady() override; +}; + +class NS_NO_VTABLE LSRequestChildCallback +{ +public: + NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING + + virtual void + OnResponse(const LSRequestResponse& aResponse) = 0; + +protected: + virtual ~LSRequestChildCallback() + { } +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_localstorage_ActorsChild_h diff --git a/dom/localstorage/ActorsParent.cpp b/dom/localstorage/ActorsParent.cpp new file mode 100644 index 000000000000..77f29ecb53e7 --- /dev/null +++ b/dom/localstorage/ActorsParent.cpp @@ -0,0 +1,975 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=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 "ActorsParent.h" + +#include "LocalStorageCommon.h" +#include "mozilla/Unused.h" +#include "mozilla/dom/PBackgroundLSDatabaseParent.h" +#include "mozilla/dom/PBackgroundLSRequestParent.h" +#include "mozilla/dom/PBackgroundLSSharedTypes.h" +#include "mozilla/dom/quota/QuotaManager.h" +#include "mozilla/ipc/BackgroundParent.h" +#include "mozilla/ipc/PBackgroundParent.h" +#include "mozilla/ipc/PBackgroundSharedTypes.h" +#include "nsDataHashtable.h" +#include "nsRefPtrHashtable.h" + +#define DISABLE_ASSERTS_FOR_FUZZING 0 + +#if DISABLE_ASSERTS_FOR_FUZZING +#define ASSERT_UNLESS_FUZZING(...) do { } while (0) +#else +#define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__) +#endif + +namespace mozilla { +namespace dom { + +using namespace mozilla::dom::quota; +using namespace mozilla::ipc; + +namespace { + +/******************************************************************************* + * Non-actor class declarations + ******************************************************************************/ + +class DatastoreOperationBase + : public Runnable +{ + nsCOMPtr mOwningEventTarget; + nsresult mResultCode; + Atomic mMayProceedOnNonOwningThread; + bool mMayProceed; + +public: + nsIEventTarget* + OwningEventTarget() const + { + MOZ_ASSERT(mOwningEventTarget); + + return mOwningEventTarget; + } + + bool + IsOnOwningThread() const + { + MOZ_ASSERT(mOwningEventTarget); + + bool current; + return + NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(¤t)) && current; + } + + void + AssertIsOnOwningThread() const + { + MOZ_ASSERT(IsOnBackgroundThread()); + MOZ_ASSERT(IsOnOwningThread()); + } + + nsresult + ResultCode() const + { + return mResultCode; + } + + void + MaybeSetFailureCode(nsresult aErrorCode) + { + MOZ_ASSERT(NS_FAILED(aErrorCode)); + + if (NS_SUCCEEDED(mResultCode)) { + mResultCode = aErrorCode; + } + } + + void + NoteComplete() + { + AssertIsOnOwningThread(); + + mMayProceed = false; + mMayProceedOnNonOwningThread = false; + } + + bool + MayProceed() const + { + AssertIsOnOwningThread(); + + return mMayProceed; + } + + // May be called on any thread, but you should call MayProceed() if you know + // you're on the background thread because it is slightly faster. + bool + MayProceedOnNonOwningThread() const + { + return mMayProceedOnNonOwningThread; + } + +protected: + DatastoreOperationBase() + : Runnable("dom::DatastoreOperationBase") + , mOwningEventTarget(GetCurrentThreadEventTarget()) + , mResultCode(NS_OK) + , mMayProceedOnNonOwningThread(true) + , mMayProceed(true) + { } + + ~DatastoreOperationBase() override + { + MOZ_ASSERT(!mMayProceed); + } +}; + +class Datastore final +{ + nsDataHashtable mValues; + const nsCString mOrigin; + +public: + // Created by PrepareDatastoreOp. + explicit Datastore(const nsACString& aOrigin); + + const nsCString& + Origin() const + { + return mOrigin; + } + + uint32_t + GetLength() const; + + void + GetKey(uint32_t aIndex, nsString& aKey) const; + + void + GetItem(const nsString& aKey, nsString& aValue) const; + + void + SetItem(const nsString& aKey, const nsString& aValue); + + void + RemoveItem(const nsString& aKey); + + void + Clear(); + + void + GetKeys(nsTArray& aKeys) const; + + NS_INLINE_DECL_REFCOUNTING(Datastore) + +private: + // Reference counted. + ~Datastore(); +}; + +/******************************************************************************* + * Actor class declarations + ******************************************************************************/ + +class Database final + : public PBackgroundLSDatabaseParent +{ + RefPtr mDatastore; + +#ifdef DEBUG + bool mActorDestroyed; + bool mActorWasAlive; +#endif + +public: + // Created in AllocPBackgroundLSDatabaseParent. + Database(); + + void + SetActorAlive(already_AddRefed&& aDatastore); + + NS_INLINE_DECL_REFCOUNTING(mozilla::dom::Database) + +private: + // Reference counted. + ~Database(); + + // IPDL methods are only called by IPDL. + void + ActorDestroy(ActorDestroyReason aWhy) override; + + mozilla::ipc::IPCResult + RecvDeleteMe() override; + + mozilla::ipc::IPCResult + RecvGetLength(uint32_t* aLength) override; + + mozilla::ipc::IPCResult + RecvGetKey(const uint32_t& aIndex, nsString* aKey) override; + + mozilla::ipc::IPCResult + RecvGetItem(const nsString& aKey, nsString* aValue) override; + + mozilla::ipc::IPCResult + RecvGetKeys(nsTArray* aKeys) override; + + mozilla::ipc::IPCResult + RecvSetItem(const nsString& aKey, const nsString& aValue) override; + + mozilla::ipc::IPCResult + RecvRemoveItem(const nsString& aKey) override; + + mozilla::ipc::IPCResult + RecvClear() override; +}; + +class LSRequestBase + : public DatastoreOperationBase + , public PBackgroundLSRequestParent +{ +public: + virtual void + Dispatch() = 0; + +private: + // IPDL methods. + void + ActorDestroy(ActorDestroyReason aWhy) override; + + mozilla::ipc::IPCResult + RecvCancel() override; +}; + +class PrepareDatastoreOp + : public LSRequestBase +{ + enum class State + { + // Just created on the PBackground thread. Next step is OpeningOnMainThread + // or OpeningOnOwningThread. + Initial, + + // Waiting to open/opening on the main thread. Next step is + // SendingReadyMessage. + OpeningOnMainThread, + + // Waiting to open/opening on the owning thread. Next step is + // SendingReadyMessage. + OpeningOnOwningThread, + + // Waiting to send/sending the ready message on the PBackground thread. Next + // step is WaitingForFinish. + SendingReadyMessage, + + // Waiting for the finish message on the PBackground thread. Next step is + // SendingResults. + WaitingForFinish, + + // Waiting to send/sending results on the PBackground thread. Next step is + // Completed. + SendingResults, + + // All done. + Completed + }; + + const LSRequestPrepareDatastoreParams mParams; + nsCString mSuffix; + nsCString mGroup; + nsCString mOrigin; + State mState; + +public: + explicit PrepareDatastoreOp(const LSRequestParams& aParams); + + void + Dispatch() override; + +private: + ~PrepareDatastoreOp() override; + + nsresult + OpenOnMainThread(); + + nsresult + OpenOnOwningThread(); + + void + SendReadyMessage(); + + void + SendResults(); + + NS_IMETHOD + Run() override; + + // IPDL overrides. + mozilla::ipc::IPCResult + RecvFinish() override; +}; + +/******************************************************************************* + * Globals + ******************************************************************************/ + +typedef nsDataHashtable DatastoreHashtable; + +StaticAutoPtr gDatastores; + +uint64_t gLastDatastoreId = 0; + +typedef nsRefPtrHashtable + TemporaryStrongDatastoreHashtable; + +StaticAutoPtr gTemporaryStrongDatastores; + +typedef nsTArray LiveDatabaseArray; + +StaticAutoPtr gLiveDatabases; + +} // namespace + +/******************************************************************************* + * Exported functions + ******************************************************************************/ + +PBackgroundLSDatabaseParent* +AllocPBackgroundLSDatabaseParent(const uint64_t& aDatastoreId) +{ + AssertIsOnBackgroundThread(); + + if (NS_WARN_IF(!gTemporaryStrongDatastores)) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + Datastore* datastore = gTemporaryStrongDatastores->GetWeak(aDatastoreId); + if (NS_WARN_IF(!datastore)) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + // If we ever decide to return null from this point on, we need to make sure + // that the prepared datastore is removed from the gTemporaryStrongDatastores + // hashtable. + // We also assume that IPDL must call RecvPBackgroundLSDatabaseConstructor + // once we return a valid actor in this method. + + RefPtr database = new Database(); + + // Transfer ownership to IPDL. + return database.forget().take(); +} + +bool +RecvPBackgroundLSDatabaseConstructor(PBackgroundLSDatabaseParent* aActor, + const uint64_t& aDatastoreId) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + MOZ_ASSERT(gTemporaryStrongDatastores); + MOZ_ASSERT(gTemporaryStrongDatastores->GetWeak(aDatastoreId)); + + // The actor is now completely built (it has a manager, channel and it's + // registered as a subprotocol). + // ActorDestroy will be called if we fail here. + + RefPtr datastore; + gTemporaryStrongDatastores->Remove(aDatastoreId, datastore.StartAssignment()); + MOZ_ASSERT(datastore); + + auto* database = static_cast(aActor); + + database->SetActorAlive(datastore.forget()); + + return true; +} + +bool +DeallocPBackgroundLSDatabaseParent(PBackgroundLSDatabaseParent* aActor) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + + // Transfer ownership back from IPDL. + RefPtr actor = dont_AddRef(static_cast(aActor)); + + return true; +} + +PBackgroundLSRequestParent* +AllocPBackgroundLSRequestParent(PBackgroundParent* aBackgroundActor, + const LSRequestParams& aParams) +{ + AssertIsOnBackgroundThread(); + + RefPtr actor; + + switch (aParams.type()) { + case LSRequestParams::TLSRequestPrepareDatastoreParams: { + bool isOtherProcess = + BackgroundParent::IsOtherProcessActor(aBackgroundActor); + + const LSRequestPrepareDatastoreParams& params = + aParams.get_LSRequestPrepareDatastoreParams(); + + const PrincipalOrQuotaInfo& info = params.info(); + + PrincipalOrQuotaInfo::Type infoType = info.type(); + + bool paramsOk = + (isOtherProcess && infoType == PrincipalOrQuotaInfo::TPrincipalInfo) || + (!isOtherProcess && infoType == PrincipalOrQuotaInfo::TQuotaInfo); + + if (NS_WARN_IF(!paramsOk)) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + actor = new PrepareDatastoreOp(aParams); + break; + } + + default: + MOZ_CRASH("Should never get here!"); + } + + // Transfer ownership to IPDL. + return actor.forget().take(); +} + +bool +RecvPBackgroundLSRequestConstructor(PBackgroundLSRequestParent* aActor, + const LSRequestParams& aParams) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + MOZ_ASSERT(aParams.type() != LSRequestParams::T__None); + + // The actor is now completely built. + + auto* op = static_cast(aActor); + + op->Dispatch(); + + return true; +} + +bool +DeallocPBackgroundLSRequestParent(PBackgroundLSRequestParent* aActor) +{ + AssertIsOnBackgroundThread(); + + // Transfer ownership back from IPDL. + RefPtr actor = + dont_AddRef(static_cast(aActor)); + + return true; +} + +/******************************************************************************* + * DatastoreOperationBase + ******************************************************************************/ + +/******************************************************************************* + * Datastore + ******************************************************************************/ + +Datastore::Datastore(const nsACString& aOrigin) + : mOrigin(aOrigin) +{ + AssertIsOnBackgroundThread(); +} + +Datastore::~Datastore() +{ + AssertIsOnBackgroundThread(); + + MOZ_ASSERT(gDatastores); + MOZ_ASSERT(gDatastores->Get(mOrigin)); + gDatastores->Remove(mOrigin); + + if (!gDatastores->Count()) { + gDatastores = nullptr; + } +} + +uint32_t +Datastore::GetLength() const +{ + AssertIsOnBackgroundThread(); + + return mValues.Count(); +} + +void +Datastore::GetKey(uint32_t aIndex, nsString& aKey) const +{ + AssertIsOnBackgroundThread(); + + aKey.SetIsVoid(true); + for (auto iter = mValues.ConstIter(); !iter.Done(); iter.Next()) { + if (aIndex == 0) { + aKey = iter.Key(); + return; + } + aIndex--; + } +} + +void +Datastore::GetItem(const nsString& aKey, nsString& aValue) const +{ + AssertIsOnBackgroundThread(); + + if (!mValues.Get(aKey, &aValue)) { + aValue.SetIsVoid(true); + } +} + +void +Datastore::SetItem(const nsString& aKey, const nsString& aValue) +{ + AssertIsOnBackgroundThread(); + + mValues.Put(aKey, aValue); +} + +void +Datastore::RemoveItem(const nsString& aKey) +{ + AssertIsOnBackgroundThread(); + + mValues.Remove(aKey); +} + +void +Datastore::Clear() +{ + AssertIsOnBackgroundThread(); + + mValues.Clear(); +} + +void +Datastore::GetKeys(nsTArray& aKeys) const +{ + AssertIsOnBackgroundThread(); + + for (auto iter = mValues.ConstIter(); !iter.Done(); iter.Next()) { + aKeys.AppendElement(iter.Key()); + } +} + +/******************************************************************************* + * Database + ******************************************************************************/ + +Database::Database() +#ifdef DEBUG + : mActorDestroyed(false) + , mActorWasAlive(false) +#endif +{ + AssertIsOnBackgroundThread(); +} + +Database::~Database() +{ + MOZ_ASSERT_IF(mActorWasAlive, mActorDestroyed); +} + +void +Database::SetActorAlive(already_AddRefed&& aDatastore) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!mActorWasAlive); + MOZ_ASSERT(!mActorDestroyed); + +#ifdef DEBUG + mActorWasAlive = true; +#endif + + mDatastore = std::move(aDatastore); + + if (!gLiveDatabases) { + gLiveDatabases = new LiveDatabaseArray(); + } + + gLiveDatabases->AppendElement(this); +} + +void +Database::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!mActorDestroyed); + MOZ_ASSERT(mDatastore); + +#ifdef DEBUG + mActorDestroyed = true; +#endif + + mDatastore = nullptr; + + MOZ_ASSERT(gLiveDatabases); + gLiveDatabases->RemoveElement(this); + + if (gLiveDatabases->IsEmpty()) { + gLiveDatabases = nullptr; + } +} + +mozilla::ipc::IPCResult +Database::RecvDeleteMe() +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!mActorDestroyed); + + IProtocol* mgr = Manager(); + if (!PBackgroundLSDatabaseParent::Send__delete__(this)) { + return IPC_FAIL_NO_REASON(mgr); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult +Database::RecvGetLength(uint32_t* aLength) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aLength); + MOZ_ASSERT(mDatastore); + + *aLength = mDatastore->GetLength(); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult +Database::RecvGetKey(const uint32_t& aIndex, nsString* aKey) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aKey); + MOZ_ASSERT(mDatastore); + + mDatastore->GetKey(aIndex, *aKey); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult +Database::RecvGetItem(const nsString& aKey, nsString* aValue) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aValue); + MOZ_ASSERT(mDatastore); + + mDatastore->GetItem(aKey, *aValue); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult +Database::RecvSetItem(const nsString& aKey, const nsString& aValue) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(mDatastore); + + mDatastore->SetItem(aKey, aValue); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult +Database::RecvRemoveItem(const nsString& aKey) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(mDatastore); + + mDatastore->RemoveItem(aKey); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult +Database::RecvClear() +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(mDatastore); + + mDatastore->Clear(); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult +Database::RecvGetKeys(nsTArray* aKeys) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aKeys); + MOZ_ASSERT(mDatastore); + + mDatastore->GetKeys(*aKeys); + + return IPC_OK(); +} + +/******************************************************************************* + * LSRequestBase + ******************************************************************************/ + +void +LSRequestBase::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnOwningThread(); + + NoteComplete(); +} + +mozilla::ipc::IPCResult +LSRequestBase::RecvCancel() +{ + AssertIsOnOwningThread(); + + IProtocol* mgr = Manager(); + if (!PBackgroundLSRequestParent::Send__delete__(this, NS_ERROR_FAILURE)) { + return IPC_FAIL_NO_REASON(mgr); + } + + return IPC_OK(); +} + +/******************************************************************************* + * PrepareDatastoreOp + ******************************************************************************/ + +PrepareDatastoreOp::PrepareDatastoreOp(const LSRequestParams& aParams) + : mParams(aParams.get_LSRequestPrepareDatastoreParams()) + , mState(State::Initial) +{ + MOZ_ASSERT(aParams.type() == + LSRequestParams::TLSRequestPrepareDatastoreParams); +} + +PrepareDatastoreOp::~PrepareDatastoreOp() +{ + MOZ_ASSERT_IF(MayProceedOnNonOwningThread(), + mState == State::Initial || mState == State::Completed); +} + +void +PrepareDatastoreOp::Dispatch() +{ + AssertIsOnOwningThread(); + + const PrincipalOrQuotaInfo& info = mParams.info(); + + if (info.type() == PrincipalOrQuotaInfo::TPrincipalInfo) { + mState = State::OpeningOnMainThread; + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this)); + } else { + MOZ_ASSERT(info.type() == PrincipalOrQuotaInfo::TQuotaInfo); + + mState = State::OpeningOnOwningThread; + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(this)); + } +} + +nsresult +PrepareDatastoreOp::OpenOnMainThread() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mState == State::OpeningOnMainThread); + + if (!MayProceedOnNonOwningThread()) { + return NS_ERROR_FAILURE; + } + + const PrincipalOrQuotaInfo& info = mParams.info(); + + MOZ_ASSERT(info.type() == PrincipalOrQuotaInfo::TPrincipalInfo); + + const PrincipalInfo& principalInfo = info.get_PrincipalInfo(); + + if (principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) { + QuotaManager::GetInfoForChrome(&mSuffix, &mGroup, &mOrigin); + } else { + MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo); + + nsresult rv; + nsCOMPtr principal = + PrincipalInfoToPrincipal(principalInfo, &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = QuotaManager::GetInfoFromPrincipal(principal, + &mSuffix, + &mGroup, + &mOrigin); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + mState = State::SendingReadyMessage; + MOZ_ALWAYS_SUCCEEDS(OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL)); + + return NS_OK; +} + +nsresult +PrepareDatastoreOp::OpenOnOwningThread() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mState == State::OpeningOnOwningThread); + + if (!MayProceed()) { + return NS_ERROR_FAILURE; + } + + const PrincipalOrQuotaInfo& info = mParams.info(); + + MOZ_ASSERT(info.type() == PrincipalOrQuotaInfo::TQuotaInfo); + + const QuotaInfo& quotaInfo = info.get_QuotaInfo(); + + mSuffix = quotaInfo.suffix(); + mGroup = quotaInfo.group(); + mOrigin = quotaInfo.origin(); + + mState = State::SendingReadyMessage; + MOZ_ALWAYS_SUCCEEDS(OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL)); + + return NS_OK; +} + +void +PrepareDatastoreOp::SendReadyMessage() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mState == State::SendingReadyMessage); + + if (!MayProceed()) { + MaybeSetFailureCode(NS_ERROR_FAILURE); + + mState = State::Completed; + } else { + Unused << SendReady(); + + mState = State::WaitingForFinish; + } +} + +void +PrepareDatastoreOp::SendResults() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mState == State::SendingResults); + + if (!MayProceed()) { + MaybeSetFailureCode(NS_ERROR_FAILURE); + } else { + LSRequestResponse response; + + if (NS_SUCCEEDED(ResultCode())) { + RefPtr datastore; + + if (gDatastores) { + datastore = gDatastores->Get(mOrigin); + } + + if (!datastore) { + datastore = new Datastore(mOrigin); + + if (!gDatastores) { + gDatastores = new DatastoreHashtable(); + } + + gDatastores->Put(mOrigin, datastore); + } + + uint64_t datastoreId = ++gLastDatastoreId; + + if (!gTemporaryStrongDatastores) { + gTemporaryStrongDatastores = new TemporaryStrongDatastoreHashtable(); + } + gTemporaryStrongDatastores->Put(datastoreId, datastore); + + LSRequestPrepareDatastoreResponse prepareDatastoreResponse; + prepareDatastoreResponse.datastoreId() = datastoreId; + + response = prepareDatastoreResponse; + } else { + response = ResultCode(); + } + + Unused << + PBackgroundLSRequestParent::Send__delete__(this, response); + } + + mState = State::Completed; +} + +NS_IMETHODIMP +PrepareDatastoreOp::Run() +{ + nsresult rv; + + switch (mState) { + case State::OpeningOnMainThread: + rv = OpenOnMainThread(); + break; + + case State::OpeningOnOwningThread: + rv = OpenOnOwningThread(); + break; + + case State::SendingReadyMessage: + SendReadyMessage(); + return NS_OK; + + case State::SendingResults: + SendResults(); + return NS_OK; + + default: + MOZ_CRASH("Bad state!"); + } + + if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::SendingReadyMessage) { + MaybeSetFailureCode(rv); + + // Must set mState before dispatching otherwise we will race with the owning + // thread. + mState = State::SendingReadyMessage; + + if (IsOnOwningThread()) { + SendReadyMessage(); + } else { + MOZ_ALWAYS_SUCCEEDS( + OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL)); + } + } + + return NS_OK; +} + +mozilla::ipc::IPCResult +PrepareDatastoreOp::RecvFinish() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mState == State::WaitingForFinish); + + mState = State::SendingResults; + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(this)); + + return IPC_OK(); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/localstorage/ActorsParent.h b/dom/localstorage/ActorsParent.h new file mode 100644 index 000000000000..1fdcf4eb0e8d --- /dev/null +++ b/dom/localstorage/ActorsParent.h @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=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/. */ + +#ifndef mozilla_dom_localstorage_ActorsParent_h +#define mozilla_dom_localstorage_ActorsParent_h + +namespace mozilla { + +namespace ipc { + +class PBackgroundParent; + +} // namespace ipc + +namespace dom { + +class LSRequestParams; +class PBackgroundLSDatabaseParent; +class PBackgroundLSRequestParent; + +PBackgroundLSDatabaseParent* +AllocPBackgroundLSDatabaseParent(const uint64_t& aDatastoreId); + +bool +RecvPBackgroundLSDatabaseConstructor(PBackgroundLSDatabaseParent* aActor, + const uint64_t& aDatastoreId); + +bool +DeallocPBackgroundLSDatabaseParent(PBackgroundLSDatabaseParent* aActor); + +PBackgroundLSRequestParent* +AllocPBackgroundLSRequestParent( + mozilla::ipc::PBackgroundParent* aBackgroundActor, + const LSRequestParams& aParams); + +bool +RecvPBackgroundLSRequestConstructor(PBackgroundLSRequestParent* aActor, + const LSRequestParams& aParams); + +bool +DeallocPBackgroundLSRequestParent(PBackgroundLSRequestParent* aActor); + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_localstorage_ActorsParent_h diff --git a/dom/localstorage/LSDatabase.cpp b/dom/localstorage/LSDatabase.cpp new file mode 100644 index 000000000000..0a9e1a5a5ddf --- /dev/null +++ b/dom/localstorage/LSDatabase.cpp @@ -0,0 +1,141 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "LSDatabase.h" + +namespace mozilla { +namespace dom { + +LSDatabase::LSDatabase() + : mActor(nullptr) +{ + AssertIsOnOwningThread(); +} + +LSDatabase::~LSDatabase() +{ + AssertIsOnOwningThread(); + + if (mActor) { + mActor->SendDeleteMeInternal(); + MOZ_ASSERT(!mActor, "SendDeleteMeInternal should have cleared!"); + } +} + +void +LSDatabase::SetActor(LSDatabaseChild* aActor) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aActor); + MOZ_ASSERT(!mActor); + + mActor = aActor; +} + +nsresult +LSDatabase::GetLength(uint32_t* aResult) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mActor); + + uint32_t result; + if (NS_WARN_IF(!mActor->SendGetLength(&result))) { + return NS_ERROR_FAILURE; + } + + *aResult = result; + return NS_OK; +} + +nsresult +LSDatabase::GetKey(uint32_t aIndex, + nsAString& aResult) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mActor); + + nsString result; + if (NS_WARN_IF(!mActor->SendGetKey(aIndex, &result))) { + return NS_ERROR_FAILURE; + } + + aResult = result; + return NS_OK; +} + +nsresult +LSDatabase::GetItem(const nsAString& aKey, + nsAString& aResult) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mActor); + + nsString result; + if (NS_WARN_IF(!mActor->SendGetItem(nsString(aKey), &result))) { + return NS_ERROR_FAILURE; + } + + aResult = result; + return NS_OK; +} + +nsresult +LSDatabase::GetKeys(nsTArray& aKeys) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mActor); + + nsTArray result; + if (NS_WARN_IF(!mActor->SendGetKeys(&result))) { + return NS_ERROR_FAILURE; + } + + aKeys.SwapElements(result); + return NS_OK; +} + +nsresult +LSDatabase::SetItem(const nsAString& aKey, + const nsAString& aValue) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mActor); + + if (NS_WARN_IF(!mActor->SendSetItem(nsString(aKey), nsString(aValue)))) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +nsresult +LSDatabase::RemoveItem(const nsAString& aKey) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mActor); + + if (NS_WARN_IF(!mActor->SendRemoveItem(nsString(aKey)))) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +nsresult +LSDatabase::Clear() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mActor); + + if (NS_WARN_IF(!mActor->SendClear())) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/localstorage/LSDatabase.h b/dom/localstorage/LSDatabase.h new file mode 100644 index 000000000000..7b1402336eed --- /dev/null +++ b/dom/localstorage/LSDatabase.h @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=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/. */ + +#ifndef mozilla_dom_localstorage_LSDatabase_h +#define mozilla_dom_localstorage_LSDatabase_h + +namespace mozilla { +namespace dom { + +class LSDatabaseChild; + +class LSDatabase final +{ + LSDatabaseChild* mActor; + +public: + LSDatabase(); + + NS_INLINE_DECL_REFCOUNTING(LSDatabase) + + void + AssertIsOnOwningThread() const + { + NS_ASSERT_OWNINGTHREAD(LSDatabase); + } + + void + SetActor(LSDatabaseChild* aActor); + + void + ClearActor() + { + AssertIsOnOwningThread(); + MOZ_ASSERT(mActor); + + mActor = nullptr; + } + + nsresult + GetLength(uint32_t* aResult); + + nsresult + GetKey(uint32_t aIndex, + nsAString& aResult); + + nsresult + GetItem(const nsAString& aKey, + nsAString& aResult); + + nsresult + GetKeys(nsTArray& aKeys); + + nsresult + SetItem(const nsAString& aKey, + const nsAString& aValue); + + nsresult + RemoveItem(const nsAString& aKey); + + nsresult + Clear(); + +private: + ~LSDatabase(); +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_localstorage_LSDatabase_h diff --git a/dom/localstorage/LSObject.cpp b/dom/localstorage/LSObject.cpp new file mode 100644 index 000000000000..8f3f25bff4c9 --- /dev/null +++ b/dom/localstorage/LSObject.cpp @@ -0,0 +1,629 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "LSObject.h" + +#include "ActorsChild.h" +#include "IPCBlobInputStreamThread.h" +#include "LocalStorageCommon.h" +#include "mozilla/ThreadEventQueue.h" +#include "mozilla/dom/quota/QuotaManager.h" +#include "mozilla/ipc/BackgroundChild.h" +#include "mozilla/ipc/BackgroundUtils.h" +#include "mozilla/ipc/PBackgroundChild.h" +#include "nsContentUtils.h" +#include "nsIScriptObjectPrincipal.h" +#include "nsThread.h" + +namespace mozilla { +namespace dom { + +namespace { + +class RequestHelper; + +StaticMutex gRequestHelperMutex; +RequestHelper* gRequestHelper = nullptr; + +class RequestHelper final + : public Runnable + , public LSRequestChildCallback +{ + enum class State + { + Initial, + ResponsePending, + Finishing, + Complete + }; + + RefPtr mObject; + nsCOMPtr mOwningEventTarget; + nsCOMPtr mNestedEventTarget; + LSRequestChild* mActor; + const LSRequestParams mParams; + LSRequestResponse mResponse; + nsresult mResultCode; + State mState; + bool mWaiting; + +public: + RequestHelper(LSObject* aObject, + const LSRequestParams& aParams) + : Runnable("dom::RequestHelper") + , mObject(aObject) + , mOwningEventTarget(GetCurrentThreadEventTarget()) + , mActor(nullptr) + , mParams(aParams) + , mResultCode(NS_OK) + , mState(State::Initial) + , mWaiting(true) + { + StaticMutexAutoLock lock(gRequestHelperMutex); + gRequestHelper = this; + } + + bool + IsOnOwningThread() const + { + MOZ_ASSERT(mOwningEventTarget); + + bool current; + return + NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(¤t)) && current; + } + + void + AssertIsOnOwningThread() const + { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(IsOnOwningThread()); + } + + nsresult + StartAndReturnResponse(LSRequestResponse& aResponse); + + nsresult + CancelOnAnyThread(); + +private: + ~RequestHelper() + { + StaticMutexAutoLock lock(gRequestHelperMutex); + gRequestHelper = nullptr; + } + + nsresult + Start(); + + void + Finish(); + + NS_DECL_ISUPPORTS_INHERITED + + NS_DECL_NSIRUNNABLE + + // LSRequestChildCallback + void + OnResponse(const LSRequestResponse& aResponse) override; +}; + +} // namespace + +LSObject::LSObject(nsPIDOMWindowInner* aWindow, + nsIPrincipal* aPrincipal) + : Storage(aWindow, aPrincipal) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(NextGenLocalStorageEnabled()); +} + +LSObject::~LSObject() +{ + AssertIsOnOwningThread(); +} + +// static +nsresult +LSObject::Create(nsPIDOMWindowInner* aWindow, + Storage** aStorage) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aWindow); + MOZ_ASSERT(aStorage); + MOZ_ASSERT(NextGenLocalStorageEnabled()); + MOZ_ASSERT(nsContentUtils::StorageAllowedForWindow(aWindow) > + nsContentUtils::StorageAccess::eDeny); + + nsCOMPtr sop = do_QueryInterface(aWindow); + MOZ_ASSERT(sop); + + nsCOMPtr principal = sop->GetPrincipal(); + if (NS_WARN_IF(!principal)) { + return NS_ERROR_FAILURE; + } + + nsresult rv; + + nsAutoPtr info(new PrincipalOrQuotaInfo()); + if (XRE_IsParentProcess()) { + nsCString suffix; + nsCString group; + nsCString origin; + rv = QuotaManager::GetInfoFromPrincipal(principal, + &suffix, + &group, + &origin); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + QuotaInfo quotaInfo; + quotaInfo.suffix() = suffix; + quotaInfo.group() = group; + quotaInfo.origin() = origin; + + *info = quotaInfo; + } else { + PrincipalInfo principalInfo; + rv = PrincipalToPrincipalInfo(principal, &principalInfo); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo); + + *info = principalInfo; + } + + RefPtr object = new LSObject(aWindow, principal); + object->mInfo = std::move(info); + + object.forget(aStorage); + return NS_OK; +} + +// static +void +LSObject::CancelSyncLoop() +{ + RefPtr helper; + + { + StaticMutexAutoLock lock(gRequestHelperMutex); + helper = gRequestHelper; + } + + if (helper) { + Unused << NS_WARN_IF(NS_FAILED(helper->CancelOnAnyThread())); + } +} + +LSRequestChild* +LSObject::StartRequest(nsIEventTarget* aMainEventTarget, + const LSRequestParams& aParams, + LSRequestChildCallback* aCallback) +{ + AssertIsOnDOMFileThread(); + + PBackgroundChild* backgroundActor = + BackgroundChild::GetOrCreateForCurrentThread(aMainEventTarget); + if (NS_WARN_IF(!backgroundActor)) { + return nullptr; + } + + LSRequestChild* actor = new LSRequestChild(aCallback); + + backgroundActor->SendPBackgroundLSRequestConstructor(actor, aParams); + + return actor; +} + +Storage::StorageType +LSObject::Type() const +{ + AssertIsOnOwningThread(); + + return eLocalStorage; +} + +bool +LSObject::IsForkOf(const Storage* aStorage) const +{ + AssertIsOnOwningThread(); + + return false; +} + +int64_t +LSObject::GetOriginQuotaUsage() const +{ + AssertIsOnOwningThread(); + + return 0; +} + +uint32_t +LSObject::GetLength(nsIPrincipal& aSubjectPrincipal, + ErrorResult& aError) +{ + AssertIsOnOwningThread(); + + nsresult rv = EnsureDatabase(); + if (NS_WARN_IF(NS_FAILED(rv))) { + aError.Throw(rv); + return 0; + } + + uint32_t result; + rv = mDatabase->GetLength(&result); + if (NS_WARN_IF(NS_FAILED(rv))) { + aError.Throw(rv); + return 0; + } + + return result; +} + +void +LSObject::Key(uint32_t aIndex, + nsAString& aResult, + nsIPrincipal& aSubjectPrincipal, + ErrorResult& aError) +{ + AssertIsOnOwningThread(); + + nsresult rv = EnsureDatabase(); + if (NS_WARN_IF(NS_FAILED(rv))) { + aError.Throw(rv); + return; + } + + nsString result; + rv = mDatabase->GetKey(aIndex, result); + if (NS_WARN_IF(NS_FAILED(rv))) { + aError.Throw(rv); + return; + } + + aResult = result; +} + +void +LSObject::GetItem(const nsAString& aKey, + nsAString& aResult, + nsIPrincipal& aSubjectPrincipal, + ErrorResult& aError) +{ + AssertIsOnOwningThread(); + + nsresult rv = EnsureDatabase(); + if (NS_WARN_IF(NS_FAILED(rv))) { + aError.Throw(rv); + return; + } + + nsString result; + rv = mDatabase->GetItem(aKey, result); + if (NS_WARN_IF(NS_FAILED(rv))) { + aError.Throw(rv); + return; + } + + aResult = result; +} + +void +LSObject::GetSupportedNames(nsTArray& aNames) +{ + AssertIsOnOwningThread(); + + nsresult rv = EnsureDatabase(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + rv = mDatabase->GetKeys(aNames); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } +} + +void +LSObject::SetItem(const nsAString& aKey, + const nsAString& aValue, + nsIPrincipal& aSubjectPrincipal, + ErrorResult& aError) +{ + AssertIsOnOwningThread(); + + nsresult rv = EnsureDatabase(); + if (NS_WARN_IF(NS_FAILED(rv))) { + aError.Throw(rv); + return; + } + + rv = mDatabase->SetItem(aKey, aValue); + if (NS_WARN_IF(NS_FAILED(rv))) { + aError.Throw(rv); + return; + } +} + +void +LSObject::RemoveItem(const nsAString& aKey, + nsIPrincipal& aSubjectPrincipal, + ErrorResult& aError) +{ + AssertIsOnOwningThread(); + + nsresult rv = EnsureDatabase(); + if (NS_WARN_IF(NS_FAILED(rv))) { + aError.Throw(rv); + return; + } + + rv = mDatabase->RemoveItem(aKey); + if (NS_WARN_IF(NS_FAILED(rv))) { + aError.Throw(rv); + return; + } +} + +void +LSObject::Clear(nsIPrincipal& aSubjectPrincipal, + ErrorResult& aError) +{ + AssertIsOnOwningThread(); + + nsresult rv = EnsureDatabase(); + if (NS_WARN_IF(NS_FAILED(rv))) { + aError.Throw(rv); + return; + } + + rv = mDatabase->Clear(); + if (NS_WARN_IF(NS_FAILED(rv))) { + aError.Throw(rv); + return; + } +} + +nsresult +LSObject::EnsureDatabase() +{ + AssertIsOnOwningThread(); + + if (mDatabase) { + return NS_OK; + } + + // We don't need this yet, but once the request successfully finishes, it's + // too late to initialize PBackground child on the owning thread, because + // it can fail and parent would keep an extra strong ref to the datastore. + PBackgroundChild* backgroundActor = + BackgroundChild::GetOrCreateForCurrentThread(); + if (NS_WARN_IF(!backgroundActor)) { + return NS_ERROR_FAILURE; + } + + LSRequestPrepareDatastoreParams params; + params.info() = *mInfo; + + RefPtr helper = new RequestHelper(this, params); + + LSRequestResponse response; + + // This will start and finish the request on the DOM File thread. + // The owning thread is synchronously blocked while the request is + // asynchronously processed on the DOM File thread. + nsresult rv = helper->StartAndReturnResponse(response); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (response.type() == LSRequestResponse::Tnsresult) { + return response.get_nsresult(); + } + + MOZ_ASSERT(response.type() == + LSRequestResponse::TLSRequestPrepareDatastoreResponse); + + const LSRequestPrepareDatastoreResponse& prepareDatastoreResponse = + response.get_LSRequestPrepareDatastoreResponse(); + + uint64_t datastoreId = prepareDatastoreResponse.datastoreId(); + + // The datastore is now ready on the parent side (prepared by the asynchronous + // request on the DOM File thread). + // Let's create a direct connection to the datastore (through a database + // actor) from the owning thread. + // Note that we now can't error out, otherwise parent will keep an extra + // strong reference to the datastore. + RefPtr database = new LSDatabase(); + + LSDatabaseChild* actor = new LSDatabaseChild(database); + + MOZ_ALWAYS_TRUE( + backgroundActor->SendPBackgroundLSDatabaseConstructor(actor, datastoreId)); + + database->SetActor(actor); + + mDatabase = std::move(database); + + return NS_OK; +} + +nsresult +RequestHelper::StartAndReturnResponse(LSRequestResponse& aResponse) +{ + AssertIsOnOwningThread(); + + // Normally, we would use the standard way of blocking the thread using + // a monitor. + // The problem is that BackgroundChild::GetOrCreateForCurrentThread() + // called on the DOM File thread may dispatch a runnable to the main + // thread to finish initialization of PBackground. A monitor would block + // the main thread and the runnable would never get executed causing the + // helper to be stuck in a wait loop. + // However, BackgroundChild::GetOrCreateForCurrentThread() supports passing + // a custom main event target, so we can create a nested event target and + // spin the event loop. Nothing can dispatch to the nested event target + // except BackgroundChild::GetOrCreateForCurrentThread(), so spinning of the + // event loop can't fire any other events. + // This way the thread is synchronously blocked in a safe manner and the + // runnable gets executed. + { + auto thread = static_cast(NS_GetCurrentThread()); + + auto queue = + static_cast*>(thread->EventQueue()); + + mNestedEventTarget = queue->PushEventQueue(); + MOZ_ASSERT(mNestedEventTarget); + + auto autoPopEventQueue = mozilla::MakeScopeExit([&] { + queue->PopEventQueue(mNestedEventTarget); + }); + + nsCOMPtr domFileThread = + IPCBlobInputStreamThread::GetOrCreate(); + if (NS_WARN_IF(!domFileThread)) { + return NS_ERROR_FAILURE; + } + + nsresult rv = domFileThread->Dispatch(this, NS_DISPATCH_NORMAL); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { + return !mWaiting; + })); + } + + if (NS_WARN_IF(NS_FAILED(mResultCode))) { + return mResultCode; + } + + aResponse = std::move(mResponse); + return NS_OK; +} + +nsresult +RequestHelper::CancelOnAnyThread() +{ + RefPtr self = this; + + RefPtr runnable = NS_NewRunnableFunction( + "RequestHelper::CancelOnAnyThread", + [self] () { + LSRequestChild* actor = self->mActor; + if (actor && !actor->Finishing()) { + actor->SendCancel(); + } + }); + + nsCOMPtr domFileThread = + IPCBlobInputStreamThread::GetOrCreate(); + if (NS_WARN_IF(!domFileThread)) { + return NS_ERROR_FAILURE; + } + + nsresult rv = domFileThread->Dispatch(runnable, NS_DISPATCH_NORMAL); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +RequestHelper::Start() +{ + AssertIsOnDOMFileThread(); + MOZ_ASSERT(mState == State::Initial); + + mState = State::ResponsePending; + + LSRequestChild* actor = + mObject->StartRequest(mNestedEventTarget, mParams, this); + if (NS_WARN_IF(!actor)) { + return NS_ERROR_FAILURE; + } + + mActor = actor; + + return NS_OK; +} + +void +RequestHelper::Finish() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mState == State::Finishing); + + mObject = nullptr; + + mWaiting = false; + + mState = State::Complete; +} + +NS_IMPL_ISUPPORTS_INHERITED0(RequestHelper, Runnable) + +NS_IMETHODIMP +RequestHelper::Run() +{ + nsresult rv; + + switch (mState) { + case State::Initial: + rv = Start(); + break; + + case State::Finishing: + Finish(); + return NS_OK; + + default: + MOZ_CRASH("Bad state!"); + } + + if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::Finishing) { + if (NS_SUCCEEDED(mResultCode)) { + mResultCode = rv; + } + + mState = State::Finishing; + + if (IsOnOwningThread()) { + Finish(); + } else { + MOZ_ALWAYS_SUCCEEDS(mNestedEventTarget->Dispatch(this, + NS_DISPATCH_NORMAL)); + } + } + + return NS_OK; +} + +void +RequestHelper::OnResponse(const LSRequestResponse& aResponse) +{ + AssertIsOnDOMFileThread(); + MOZ_ASSERT(mState == State::ResponsePending); + + mActor = nullptr; + + mResponse = aResponse; + + mState = State::Finishing; + MOZ_ALWAYS_SUCCEEDS(mNestedEventTarget->Dispatch(this, NS_DISPATCH_NORMAL)); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/localstorage/LSObject.h b/dom/localstorage/LSObject.h new file mode 100644 index 000000000000..1e6ab93f1af4 --- /dev/null +++ b/dom/localstorage/LSObject.h @@ -0,0 +1,110 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=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/. */ + +#ifndef mozilla_dom_localstorage_LSObject_h +#define mozilla_dom_localstorage_LSObject_h + +#include "mozilla/dom/Storage.h" + +class nsIPrincipal; +class nsPIDOMWindowInner; + +namespace mozilla { + +class ErrorResult; + +namespace dom { + +class LSDatabase; +class LSRequestChild; +class LSRequestChildCallback; +class LSRequestParams; +class PrincipalOrQuotaInfo; + +class LSObject final + : public Storage +{ + nsAutoPtr mInfo; + + RefPtr mDatabase; + +public: + static nsresult + Create(nsPIDOMWindowInner* aWindow, + Storage** aStorage); + + static void + CancelSyncLoop(); + + void + AssertIsOnOwningThread() const + { + NS_ASSERT_OWNINGTHREAD(LSObject); + } + + LSRequestChild* + StartRequest(nsIEventTarget* aMainEventTarget, + const LSRequestParams& aParams, + LSRequestChildCallback* aCallback); + + // Storage overrides. + StorageType + Type() const override; + + bool + IsForkOf(const Storage* aStorage) const override; + + int64_t + GetOriginQuotaUsage() const override; + + uint32_t + GetLength(nsIPrincipal& aSubjectPrincipal, + ErrorResult& aError) override; + + void + Key(uint32_t aIndex, + nsAString& aResult, + nsIPrincipal& aSubjectPrincipal, + ErrorResult& aError) override; + + void + GetItem(const nsAString& aKey, + nsAString& aResult, + nsIPrincipal& aSubjectPrincipal, + ErrorResult& aError) override; + + void + GetSupportedNames(nsTArray& aNames) override; + + void + SetItem(const nsAString& aKey, + const nsAString& aValue, + nsIPrincipal& aSubjectPrincipal, + ErrorResult& aError) override; + + void + RemoveItem(const nsAString& aKey, + nsIPrincipal& aSubjectPrincipal, + ErrorResult& aError) override; + + void + Clear(nsIPrincipal& aSubjectPrincipal, + ErrorResult& aError) override; + +private: + LSObject(nsPIDOMWindowInner* aWindow, + nsIPrincipal* aPrincipal); + + ~LSObject(); + + nsresult + EnsureDatabase(); +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_localstorage_LSObject_h diff --git a/dom/localstorage/LocalStorageCommon.cpp b/dom/localstorage/LocalStorageCommon.cpp new file mode 100644 index 000000000000..026331cf00aa --- /dev/null +++ b/dom/localstorage/LocalStorageCommon.cpp @@ -0,0 +1,32 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=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 "LocalStorageCommon.h" + +namespace mozilla { +namespace dom { + +namespace { + +int32_t gNextGenLocalStorageEnabled = -1; + +} // namespace + +bool +NextGenLocalStorageEnabled() +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (gNextGenLocalStorageEnabled == -1) { + bool enabled = Preferences::GetBool("dom.storage.next_gen", false); + gNextGenLocalStorageEnabled = enabled ? 1 : 0; + } + + return !!gNextGenLocalStorageEnabled; +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/localstorage/LocalStorageCommon.h b/dom/localstorage/LocalStorageCommon.h new file mode 100644 index 000000000000..f65e6ad6d89d --- /dev/null +++ b/dom/localstorage/LocalStorageCommon.h @@ -0,0 +1,193 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=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/. */ + +#ifndef mozilla_dom_localstorage_LocalStorageCommon_h +#define mozilla_dom_localstorage_LocalStorageCommon_h + +/* + * Local storage + * ~~~~~~~~~~~~~ + * + * Implementation overview + * ~~~~~~~~~~~~~~~~~~~~~~~ + * + * The implementation is based on a per principal/origin cache (datastore) + * living in the main process and synchronous calls initiated from content + * processes. + * The IPC communication is managed by database actors which link to the + * datastore. + * The synchronous blocking of the main thread is done by using a special + * technique or by using standard synchronous IPC calls. + * + * General architecture + * ~~~~~~~~~~~~~~~~~~~~ + * The current browser architecture consists of one main process and multiple + * content processes (there are other processes but for simplicity's sake, they + * are not mentioned here). The processes use the IPC communication to talk to + * each other. Local storage implementation uses the client-server model, so + * the main process manages all the data and content processes then request + * particular data from the main process. The main process is also called the + * parent or the parent side, the content process is then called the child or + * the child side. + * + * Datastores + * ~~~~~~~~~~ + * + * A datastore provides a convenient way to access data for given origin. The + * data is always preloaded into memory and indexed using a hash table. This + * enables very fast access to particular stored items. There can be only one + * datastore per origin and exists solely on the parent side. It is represented + * by the "Datastore" class. A datastore instance is a ref counted object and + * lives on the PBackground thread, it is kept alive by database objects. When + * the last database object for given origin is destroyed, the associated + * datastore object is destroyed too. + * + * Databases + * ~~~~~~~~~ + * + * A database allows direct access to a datastore from a content process. There + * can be multiple databases for the same origin, but they all share the same + * datastore. + * Databases use the PBackgroundLSDatabase IPDL protocol for IPC communication. + * Given the nature of local storage, most of PBackgroundLSDatabase messages + * are synchronous. + * + * On the parent side, the database is represented by the "Database" class that + * is a parent actor as well (implements the "PBackgroundLSDatabaseParent" + * interface). A database instance is a ref counted object and lives on the + * PBackground thread. + * All live database actors are tracked in an array. + * + * On the child side, the database is represented by the "LSDatabase" class + * that provides indirect access to a child actor. An LSDatabase instance is a + * ref counted object and lives on the main thread. + * The actual child actor is represented by the "LSDatabaseChild" class that + * implements the "PBackgroundLSDatabaseChild" interface. An "LSDatabaseChild" + * instance is not ref counted and lives on the main thread too. + * + * Synchronous blocking + * ~~~~~~~~~~~~~~~~~~~~ + * + * Local storage is synchronous in nature which means the execution can't move + * forward until there's a reply for given method call. + * Since we have to use IPC anyway, we could just always use synchronous IPC + * messages for all local storage method calls. Well, there's a problem with + * that approach. + * If the main process needs to do some off PBackground thread stuff like + * getting info from principals on the main thread or some asynchronous stuff + * like directory locking before sending a reply to a synchronous message, then + * we would have to block the thread or spin the event loop which is usually a + * bad idea, especially in the main process. + * Instead, we can use a special thread in the content process called DOM File + * thread for communication with the main process using asynchronous messages + * and synchronously block the main thread until the DOM File thread is done + * (the main thread blocking is a bit more complicated, see the comment in + * RequestHelper::StartAndReturnResponse for more details). + * Anyway, the extra hop to the DOM File thread brings another overhead and + * latency. The final solution is to use a combination of the special thread + * for complex stuff like datastore preparation and synchronous IPC messages + * sent directly from the main thread for database access when data is already + * loaded from disk into memory. + * + * Requests + * ~~~~~~~~ + * + * Requests are used to handle asynchronous high level datastore operations + * which are initiated in a content process and then processed in the parent + * process (for example, preparation of a datastore). + * Requests use the "PBackgroundLSRequest" IPDL protocol for IPC communication. + * + * On the parent side, the request is represented by the "LSRequestBase" class + * that is a parent actor as well (implements the "PBackgroundLSRequestParent" + * interface). It's an abstract class (contains pure virtual functions) so it + * can't be used to create instances. + * It also inherits from the "DatastoreOperationBase" class which is a generic + * base class for all datastore operations. The "DatastoreOperationsBase" class + * inherits from the "Runnable" class, so derived class instances are ref + * counted, can be dispatched to multiple threads and thus they are used on + * multiple threads. However, derived class instances can be created on the + * PBackground thread only. + * + * On the child side, the request is represented by the "RequestHelper" class + * that covers all the complexity needed to start a new request, handle + * responses and do safe main thread blocking at the same time. + * It inherits from the "Runnable" class, so instances are ref counted and + * they are internally used on multiple threads (specifically on the main + * thread and on the DOM File thread). Anyway, users should create and use + * instances of this class only on the main thread (apart from a special case + * when we need to cancel the request from an internal chromium IPC thread to + * prevent a dead lock involving CPOWs). + * The actual child actor is represented by the "LSRequestChild" class that + * implements the "PBackgroundLSRequestChild" interface. An "LSRequestChild" + * instance is not ref counted and lives on the DOM File thread. + * Request responses are passed using the "LSRequestChildCallback" interface. + * + * Preparation of a datastore + * ~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * The datastore preparation is needed to make sure a datastore is fully loaded + * into memory. Every datastore preparation produces a unique id (even if the + * datastore for given origin already exists). + * On the parent side, the preparation is handled by the "PrepareDatastoreOp" + * class which inherits from the "LSRequestBase" class. The preparation process + * on the parent side is quite complicated, it happens sequentially on multiple + * threads and is managed by a state machine. + * On the child side, the preparation is done in the LSObject::EnsureDatabase + * method using the "RequestHelper" class. The method starts a new preparation + * request and obtains a unique id produced by the parent (or an error code if + * the requested failed to complete). + * + * Linking databases to a datastore + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * A datastore exists only on the parent side, but it can be accessed from the + * content via database actors. Database actors are initiated on the child side + * and they need to be linked to a datastore on the parent side via an id. The + * datastore preparation process gives us the required id. + * The linking is initiated on the child side in the LSObject::EnsureDatabase + * method by calling SendPBackgroundLSDatabaseConstructor and finished in + * RecvPBackgroundLSDatabaseConstructor on the parent side. + * + * Actor migration + * ~~~~~~~~~~~~~~~ + * + * In theory, the datastore preparation request could return a database actor + * directly (instead of returning an id intended for database linking to a + * datastore). However, as it was explained above, the preparation must be done + * on the DOM File thread and database objects are used on the main thread. The + * returned actor would have to be migrated from the DOM File thread to the + * main thread and that's something which our IPDL doesn't support yet. + * + * Exposing local storage + * ~~~~~~~~~~~~~~~~~~~~~~ + * + * The implementation is exposed to the DOM via window.localStorage attribute. + * Local storage's sibling, session storage shares the same WebIDL interface + * for exposing it to web content, therefore there's an abstract class called + * "Storage" that handles some of the common DOM bindings stuff. Local storage + * specific functionality is defined in the "LSObject" derived class. + * The "LSObject" class is also a starting point for the datastore preparation + * and database linking. + * + * Local storage manager + * ~~~~~~~~~~~~~~~~~~~~~ + * + * The local storage manager exposes some of the features that need to be + * available only in the chrome code or tests. The manager is represented by + * the "LocalStorageManager2" class that implements the "nsIDOMStorageManager" + * interface. + */ + +namespace mozilla { +namespace dom { + +bool +NextGenLocalStorageEnabled(); + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_localstorage_LocalStorageCommon_h diff --git a/dom/localstorage/LocalStorageManager2.cpp b/dom/localstorage/LocalStorageManager2.cpp new file mode 100644 index 000000000000..5d6e761a7eaf --- /dev/null +++ b/dom/localstorage/LocalStorageManager2.cpp @@ -0,0 +1,88 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "LocalStorageManager2.h" + +#include "LSObject.h" + +namespace mozilla { +namespace dom { + +LocalStorageManager2::LocalStorageManager2() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(NextGenLocalStorageEnabled()); +} + +LocalStorageManager2::~LocalStorageManager2() +{ + MOZ_ASSERT(NS_IsMainThread()); +} + +NS_IMPL_ISUPPORTS(LocalStorageManager2, nsIDOMStorageManager) + +NS_IMETHODIMP +LocalStorageManager2::PrecacheStorage(nsIPrincipal* aPrincipal, + Storage** _retval) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aPrincipal); + MOZ_ASSERT(_retval); + + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +LocalStorageManager2::CreateStorage(mozIDOMWindow* aWindow, + nsIPrincipal* aPrincipal, + const nsAString& aDocumentURI, + bool aPrivate, + Storage** _retval) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aPrincipal); + MOZ_ASSERT(_retval); + + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +LocalStorageManager2::GetStorage(mozIDOMWindow* aWindow, + nsIPrincipal* aPrincipal, + bool aPrivate, + Storage** _retval) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aPrincipal); + MOZ_ASSERT(_retval); + + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +LocalStorageManager2::CloneStorage(Storage* aStorageToCloneFrom) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aStorageToCloneFrom); + + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +LocalStorageManager2::CheckStorage(nsIPrincipal* aPrincipal, + Storage *aStorage, + bool* _retval) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aPrincipal); + MOZ_ASSERT(aStorage); + MOZ_ASSERT(_retval); + + return NS_ERROR_NOT_IMPLEMENTED; +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/localstorage/LocalStorageManager2.h b/dom/localstorage/LocalStorageManager2.h new file mode 100644 index 000000000000..6b5a5f525f93 --- /dev/null +++ b/dom/localstorage/LocalStorageManager2.h @@ -0,0 +1,31 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=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/. */ + +#ifndef mozilla_dom_localstorage_LocalStorageManager2_h +#define mozilla_dom_localstorage_LocalStorageManager2_h + +#include "nsIDOMStorageManager.h" + +namespace mozilla { +namespace dom { + +class LocalStorageManager2 final + : public nsIDOMStorageManager +{ +public: + LocalStorageManager2(); + + NS_DECL_ISUPPORTS + NS_DECL_NSIDOMSTORAGEMANAGER + +private: + ~LocalStorageManager2(); +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_localstorage_LocalStorageManager2_h diff --git a/dom/localstorage/PBackgroundLSDatabase.ipdl b/dom/localstorage/PBackgroundLSDatabase.ipdl new file mode 100644 index 000000000000..882d5f42ef39 --- /dev/null +++ b/dom/localstorage/PBackgroundLSDatabase.ipdl @@ -0,0 +1,49 @@ +/* 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 PBackground; + +namespace mozilla { +namespace dom { + +sync protocol PBackgroundLSDatabase +{ + manager PBackground; + +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. + // However, that would destroy the child actor immediatelly and the parent + // could be sending a message to the child at the same time resulting in a + // routing error since the child actor wouldn't exist anymore. A routing + // error typically causes a crash. The race can be prevented by doing the + // teardown in two steps. First, we send the DeleteMe message to the parent + // and the parent then sends the __delete__ message to the child. + async DeleteMe(); + + sync GetLength() + returns (uint32_t length); + + sync GetKey(uint32_t index) + returns (nsString key); + + sync GetItem(nsString key) + returns (nsString value); + + sync GetKeys() + returns (nsString[] keys); + + sync SetItem(nsString key, nsString value); + + sync RemoveItem(nsString key); + + sync Clear(); + +child: + async __delete__(); +}; + +} // namespace dom +} // namespace mozilla diff --git a/dom/localstorage/PBackgroundLSRequest.ipdl b/dom/localstorage/PBackgroundLSRequest.ipdl new file mode 100644 index 000000000000..2efd6447a201 --- /dev/null +++ b/dom/localstorage/PBackgroundLSRequest.ipdl @@ -0,0 +1,49 @@ +/* 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 PBackground; + +namespace mozilla { +namespace dom { + +struct LSRequestPrepareDatastoreResponse +{ + uint64_t datastoreId; +}; + +union LSRequestResponse +{ + nsresult; + LSRequestPrepareDatastoreResponse; +}; + +protocol PBackgroundLSRequest +{ + manager PBackground; + +parent: + // The Cancel message is used to avoid a possible dead lock caused by a CPOW + // sending a synchronous message from the main thread in the chrome process + // to the main thread in the content process at the time we are blocking + // the main thread in the content process to handle a request. + // We use the PBackground thread on the parent side to handle requests, but + // sometimes we need to get information from principals and that's currently + // only possible on the main thread. So if the main thread in the chrome + // process is blocked by a CPOW operation, our request must wait for the CPOW + // operation to complete. However the CPOW operation can't complete either + // because we are blocking the main thread in the content process. + // The dead lock is prevented by canceling our nested event loop in the + // content process when we receive a synchronous IPC message from the parent. + async Cancel(); + + async Finish(); + +child: + async __delete__(LSRequestResponse response); + + async Ready(); +}; + +} // namespace dom +} // namespace mozilla diff --git a/dom/localstorage/PBackgroundLSSharedTypes.ipdlh b/dom/localstorage/PBackgroundLSSharedTypes.ipdlh new file mode 100644 index 000000000000..e0d7bcf6787e --- /dev/null +++ b/dom/localstorage/PBackgroundLSSharedTypes.ipdlh @@ -0,0 +1,32 @@ +/* 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 PBackgroundSharedTypes; + +namespace mozilla { +namespace dom { + +struct QuotaInfo { + nsCString suffix; + nsCString group; + nsCString origin; +}; + +union PrincipalOrQuotaInfo { + PrincipalInfo; + QuotaInfo; +}; + +struct LSRequestPrepareDatastoreParams +{ + PrincipalOrQuotaInfo info; +}; + +union LSRequestParams +{ + LSRequestPrepareDatastoreParams; +}; + +} // namespace dom +} // namespace mozilla diff --git a/dom/localstorage/moz.build b/dom/localstorage/moz.build new file mode 100644 index 000000000000..ebe0c87b7aab --- /dev/null +++ b/dom/localstorage/moz.build @@ -0,0 +1,41 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +EXPORTS.mozilla.dom.localstorage += [ + 'ActorsParent.h', +] + +EXPORTS.mozilla.dom += [ + 'LocalStorageCommon.h', + 'LocalStorageManager2.h', + 'LSObject.h', +] + +UNIFIED_SOURCES += [ + 'ActorsChild.cpp', + 'ActorsParent.cpp', + 'LocalStorageCommon.cpp', + 'LocalStorageManager2.cpp', + 'LSDatabase.cpp', + 'LSObject.cpp', +] + +IPDL_SOURCES += [ + 'PBackgroundLSDatabase.ipdl', + 'PBackgroundLSRequest.ipdl', + 'PBackgroundLSSharedTypes.ipdlh', +] + +include('/ipc/chromium/chromium-config.mozbuild') + +FINAL_LIBRARY = 'xul' + +if CONFIG['GNU_CXX']: + CXXFLAGS += ['-Wno-error=shadow'] + +LOCAL_INCLUDES += [ + '/dom/file/ipc', +] diff --git a/dom/moz.build b/dom/moz.build index 28b85538deb7..7c2240efcfe2 100644 --- a/dom/moz.build +++ b/dom/moz.build @@ -103,6 +103,7 @@ DIRS += [ 'serviceworkers', 'simpledb', 'reporting', + 'localstorage', ] if CONFIG['MOZ_LIBPRIO']: diff --git a/dom/storage/LocalStorageManager.cpp b/dom/storage/LocalStorageManager.cpp index 7a37097ef53d..dedd22397fc3 100644 --- a/dom/storage/LocalStorageManager.cpp +++ b/dom/storage/LocalStorageManager.cpp @@ -21,6 +21,7 @@ #include "nsIObserverService.h" #include "mozilla/Services.h" #include "mozilla/Preferences.h" +#include "mozilla/dom/LocalStorageCommon.h" // Only allow relatively small amounts of data since performance of // the synchronous IO is very bad. @@ -61,6 +62,8 @@ NS_IMPL_ISUPPORTS(LocalStorageManager, LocalStorageManager::LocalStorageManager() : mCaches(8) { + MOZ_ASSERT(!NextGenLocalStorageEnabled()); + StorageObserver* observer = StorageObserver::Self(); NS_ASSERTION(observer, "No StorageObserver, cannot observe private data delete notifications!"); @@ -473,9 +476,20 @@ LocalStorageManager::Observe(const char* aTopic, return NS_ERROR_UNEXPECTED; } +// static +LocalStorageManager* +LocalStorageManager::Self() +{ + MOZ_ASSERT(!NextGenLocalStorageEnabled()); + + return sSelf; +} + LocalStorageManager* LocalStorageManager::Ensure() { + MOZ_ASSERT(!NextGenLocalStorageEnabled()); + if (sSelf) { return sSelf; } diff --git a/dom/storage/LocalStorageManager.h b/dom/storage/LocalStorageManager.h index 2a36278ace49..88ed6e62913d 100644 --- a/dom/storage/LocalStorageManager.h +++ b/dom/storage/LocalStorageManager.h @@ -117,7 +117,7 @@ private: const nsACString& aKeyPrefix); // Global getter of localStorage manager service - static LocalStorageManager* Self() { return sSelf; } + static LocalStorageManager* Self(); // Like Self, but creates an instance if we're not yet initialized. static LocalStorageManager* Ensure(); diff --git a/dom/storage/StorageIPC.cpp b/dom/storage/StorageIPC.cpp index 54bcaab64269..760bcfca7b1f 100644 --- a/dom/storage/StorageIPC.cpp +++ b/dom/storage/StorageIPC.cpp @@ -147,6 +147,7 @@ StorageDBChild::StorageDBChild(LocalStorageManager* aManager) , mStatus(NS_OK) , mIPCOpen(false) { + MOZ_ASSERT(!NextGenLocalStorageEnabled()); } StorageDBChild::~StorageDBChild() @@ -158,6 +159,7 @@ StorageDBChild* StorageDBChild::Get() { MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!NextGenLocalStorageEnabled()); return sStorageChild; } @@ -167,6 +169,7 @@ StorageDBChild* StorageDBChild::GetOrCreate() { MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!NextGenLocalStorageEnabled()); if (sStorageChild || sStorageChildDown) { // When sStorageChildDown is at true, sStorageChild is null. diff --git a/dom/storage/StorageObserver.cpp b/dom/storage/StorageObserver.cpp index 56fd031a010f..79f3845d3f9b 100644 --- a/dom/storage/StorageObserver.cpp +++ b/dom/storage/StorageObserver.cpp @@ -181,13 +181,15 @@ StorageObserver::ClearMatchingOrigin(const char16_t* aData, return rv; } - if (XRE_IsParentProcess()) { - StorageDBChild* storageChild = StorageDBChild::GetOrCreate(); - if (NS_WARN_IF(!storageChild)) { - return NS_ERROR_FAILURE; - } + if (!NextGenLocalStorageEnabled()) { + if (XRE_IsParentProcess()) { + StorageDBChild* storageChild = StorageDBChild::GetOrCreate(); + if (NS_WARN_IF(!storageChild)) { + return NS_ERROR_FAILURE; + } - storageChild->SendClearMatchingOrigin(originScope); + storageChild->SendClearMatchingOrigin(originScope); + } } aOriginScope = originScope; @@ -205,6 +207,10 @@ StorageObserver::Observe(nsISupports* aSubject, if (!strcmp(aTopic, kStartupTopic)) { MOZ_ASSERT(XRE_IsParentProcess()); + if (NextGenLocalStorageEnabled()) { + return NS_OK; + } + nsCOMPtr obs = mozilla::services::GetObserverService(); obs->RemoveObserver(this, kStartupTopic); @@ -215,6 +221,7 @@ StorageObserver::Observe(nsISupports* aSubject, // Timer callback used to start the database a short timer after startup if (!strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC)) { MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(!NextGenLocalStorageEnabled()); nsCOMPtr timer = do_QueryInterface(aSubject); if (!timer) { @@ -241,15 +248,17 @@ StorageObserver::Observe(nsISupports* aSubject, return NS_OK; } - StorageDBChild* storageChild = StorageDBChild::GetOrCreate(); - if (NS_WARN_IF(!storageChild)) { - return NS_ERROR_FAILURE; - } + if (!NextGenLocalStorageEnabled()) { + StorageDBChild* storageChild = StorageDBChild::GetOrCreate(); + if (NS_WARN_IF(!storageChild)) { + return NS_ERROR_FAILURE; + } - storageChild->AsyncClearAll(); + storageChild->AsyncClearAll(); - if (XRE_IsParentProcess()) { - storageChild->SendClearAll(); + if (XRE_IsParentProcess()) { + storageChild->SendClearAll(); + } } Notify("cookie-cleared"); @@ -311,6 +320,10 @@ StorageObserver::Observe(nsISupports* aSubject, } if (!strcmp(aTopic, "extension:purge-localStorage")) { + if (NextGenLocalStorageEnabled()) { + return NS_OK; + } + const char topic[] = "extension:purge-localStorage-caches"; if (aData) { @@ -355,6 +368,10 @@ StorageObserver::Observe(nsISupports* aSubject, // Clear all private-browsing caches if (!strcmp(aTopic, "last-pb-context-exited")) { + if (NextGenLocalStorageEnabled()) { + return NS_OK; + } + Notify("private-browsing-data-cleared"); return NS_OK; @@ -364,6 +381,10 @@ StorageObserver::Observe(nsISupports* aSubject, if (!strcmp(aTopic, "clear-origin-attributes-data")) { MOZ_ASSERT(XRE_IsParentProcess()); + if (NextGenLocalStorageEnabled()) { + return NS_OK; + } + OriginAttributesPattern pattern; if (!pattern.Init(nsDependentString(aData))) { NS_ERROR("Cannot parse origin attributes pattern"); @@ -391,6 +412,10 @@ StorageObserver::Observe(nsISupports* aSubject, if (!strcmp(aTopic, "profile-before-change")) { MOZ_ASSERT(XRE_IsParentProcess()); + if (NextGenLocalStorageEnabled()) { + return NS_OK; + } + if (mBackgroundThread) { bool done = false; @@ -409,6 +434,10 @@ StorageObserver::Observe(nsISupports* aSubject, #ifdef DOM_STORAGE_TESTS if (!strcmp(aTopic, "domstorage-test-flush-force")) { + if (NextGenLocalStorageEnabled()) { + return NS_OK; + } + StorageDBChild* storageChild = StorageDBChild::GetOrCreate(); if (NS_WARN_IF(!storageChild)) { return NS_ERROR_FAILURE; @@ -420,6 +449,10 @@ StorageObserver::Observe(nsISupports* aSubject, } if (!strcmp(aTopic, "domstorage-test-flushed")) { + if (NextGenLocalStorageEnabled()) { + return NS_OK; + } + // Only used to propagate to IPC children Notify("test-flushed"); @@ -427,6 +460,10 @@ StorageObserver::Observe(nsISupports* aSubject, } if (!strcmp(aTopic, "domstorage-test-reload")) { + if (NextGenLocalStorageEnabled()) { + return NS_OK; + } + Notify("test-reload"); return NS_OK; diff --git a/dom/tests/browser/browser.ini b/dom/tests/browser/browser.ini index 26ed1132a8a0..2d59172fbd37 100644 --- a/dom/tests/browser/browser.ini +++ b/dom/tests/browser/browser.ini @@ -53,8 +53,8 @@ run-if = e10s skip-if = !e10s || os != "win" || processor != "x86" # Large-Allocation requires e10s [browser_largeAllocation_non_win32.js] skip-if = !e10s || (os == "win" && processor == "x86") || (verify && debug && (os == 'linux')) || (os == 'linux') || (os == 'mac' && debug) # Large-Allocation requires e10s # Bug 1336075 -[browser_localStorage_e10s.js] -skip-if = !e10s || verify # This is a test of e10s functionality. +#[browser_localStorage_e10s.js] +#skip-if = !e10s || verify # This is a test of e10s functionality. [browser_localStorage_privatestorageevent.js] [browser_persist_cookies.js] support-files = diff --git a/dom/tests/mochitest/localstorage/chrome.ini b/dom/tests/mochitest/localstorage/chrome.ini index 6cc5562c00c9..af961412ae78 100644 --- a/dom/tests/mochitest/localstorage/chrome.ini +++ b/dom/tests/mochitest/localstorage/chrome.ini @@ -7,5 +7,5 @@ support-files = [test_localStorageBasePrivateBrowsing_perwindowpb.html] skip-if = true # bug 1156725 -[test_localStorageFromChrome.xhtml] -[test_localStorageQuotaPrivateBrowsing_perwindowpb.html] +#[test_localStorageFromChrome.xhtml] +#[test_localStorageQuotaPrivateBrowsing_perwindowpb.html] diff --git a/dom/tests/mochitest/localstorage/mochitest.ini b/dom/tests/mochitest/localstorage/mochitest.ini index c62ab9f6d84f..3a1894687ab0 100644 --- a/dom/tests/mochitest/localstorage/mochitest.ini +++ b/dom/tests/mochitest/localstorage/mochitest.ini @@ -19,19 +19,19 @@ support-files = file_tryAccessSessionStorage.html [test_brokenUTF-16.html] -[test_bug600307-DBOps.html] +#[test_bug600307-DBOps.html] [test_bug746272-1.html] [test_bug746272-2.html] skip-if = os == "android" || verify # bug 962029 [test_cookieBlock.html] -[test_cookieSession.html] +#[test_cookieSession.html] [test_embededNulls.html] [test_keySync.html] -[test_localStorageBase.html] -skip-if = e10s -[test_localStorageBaseSessionOnly.html] +#[test_localStorageBase.html] +#skip-if = e10s +#[test_localStorageBaseSessionOnly.html] [test_localStorageCookieSettings.html] -[test_localStorageEnablePref.html] +#[test_localStorageEnablePref.html] [test_localStorageKeyOrder.html] [test_localStorageOriginsDiff.html] [test_localStorageOriginsDomainDiffs.html] @@ -40,14 +40,14 @@ skip-if = toolkit == 'android' [test_localStorageOriginsPortDiffs.html] [test_localStorageOriginsSchemaDiffs.html] skip-if = toolkit == 'android' #TIMED_OUT -[test_localStorageQuota.html] -skip-if = toolkit == 'android' #TIMED_OUT -[test_localStorageQuotaSessionOnly.html] -skip-if = toolkit == 'android' || (verify && (os == 'linux' || os == 'win')) #TIMED_OUT +#[test_localStorageQuota.html] +#skip-if = toolkit == 'android' #TIMED_OUT +#[test_localStorageQuotaSessionOnly.html] +#skip-if = toolkit == 'android' || (verify && (os == 'linux' || os == 'win')) #TIMED_OUT [test_localStorageQuotaSessionOnly2.html] skip-if = true # bug 1347690 [test_localStorageReplace.html] skip-if = toolkit == 'android' [test_storageConstructor.html] -[test_localStorageSessionPrefOverride.html] +#[test_localStorageSessionPrefOverride.html] [test_firstPartyOnlyPermission.html] diff --git a/dom/tests/mochitest/storageevent/mochitest.ini b/dom/tests/mochitest/storageevent/mochitest.ini index e16d9cd2e965..7985f2c38672 100644 --- a/dom/tests/mochitest/storageevent/mochitest.ini +++ b/dom/tests/mochitest/storageevent/mochitest.ini @@ -11,7 +11,7 @@ support-files = interOriginTest2.js [test_storageLocalStorageEventCheckNoPropagation.html] -[test_storageLocalStorageEventCheckPropagation.html] -[test_storageNotifications.html] +#[test_storageLocalStorageEventCheckPropagation.html] +#[test_storageNotifications.html] [test_storageSessionStorageEventCheckNoPropagation.html] [test_storageSessionStorageEventCheckPropagation.html] diff --git a/dom/tests/moz.build b/dom/tests/moz.build index 62683d990a6c..d99e9e72a9b8 100644 --- a/dom/tests/moz.build +++ b/dom/tests/moz.build @@ -179,7 +179,7 @@ MOCHITEST_CHROME_MANIFESTS += [ 'mochitest/general/chrome.ini', 'mochitest/geolocation/chrome.ini', 'mochitest/keyhandling/chrome.ini', - 'mochitest/localstorage/chrome.ini', +# 'mochitest/localstorage/chrome.ini', 'mochitest/sessionstorage/chrome.ini', 'mochitest/webcomponents/chrome.ini', 'mochitest/whatwg/chrome.ini', diff --git a/editor/libeditor/tests/mochitest.ini b/editor/libeditor/tests/mochitest.ini index c334d745d9aa..8349c24dc2fc 100644 --- a/editor/libeditor/tests/mochitest.ini +++ b/editor/libeditor/tests/mochitest.ini @@ -141,9 +141,9 @@ skip-if = os == 'android' [test_bug645914.html] [test_bug646194.html] [test_bug668599.html] -[test_bug674770-1.html] -subsuite = clipboard -skip-if = toolkit == 'android' || verify +#[test_bug674770-1.html] +#subsuite = clipboard +#skip-if = toolkit == 'android' || verify [test_bug674770-2.html] subsuite = clipboard skip-if = toolkit == 'android' diff --git a/ipc/glue/BackgroundChildImpl.cpp b/ipc/glue/BackgroundChildImpl.cpp index d284323c3b7e..5630857427d3 100644 --- a/ipc/glue/BackgroundChildImpl.cpp +++ b/ipc/glue/BackgroundChildImpl.cpp @@ -16,6 +16,8 @@ #include "mozilla/Assertions.h" #include "mozilla/SchedulerGroup.h" #include "mozilla/dom/ClientManagerActors.h" +#include "mozilla/dom/PBackgroundLSDatabaseChild.h" +#include "mozilla/dom/PBackgroundLSRequestChild.h" #include "mozilla/dom/PBackgroundSDBConnectionChild.h" #include "mozilla/dom/PFileSystemRequestChild.h" #include "mozilla/dom/FileSystemTaskBase.h" @@ -223,6 +225,58 @@ BackgroundChildImpl::DeallocPBackgroundIndexedDBUtilsChild( return true; } +BackgroundChildImpl::PBackgroundSDBConnectionChild* +BackgroundChildImpl::AllocPBackgroundSDBConnectionChild( + const PrincipalInfo& aPrincipalInfo) +{ + MOZ_CRASH("PBackgroundSDBConnectionChild actor should be manually " + "constructed!"); +} + +bool +BackgroundChildImpl::DeallocPBackgroundSDBConnectionChild( + PBackgroundSDBConnectionChild* aActor) +{ + MOZ_ASSERT(aActor); + + delete aActor; + return true; +} + +BackgroundChildImpl::PBackgroundLSDatabaseChild* +BackgroundChildImpl::AllocPBackgroundLSDatabaseChild( + const uint64_t& aDatastoreId) +{ + MOZ_CRASH("PBackgroundLSDatabaseChild actor should be manually constructed!"); +} + +bool +BackgroundChildImpl::DeallocPBackgroundLSDatabaseChild( + PBackgroundLSDatabaseChild* aActor) +{ + MOZ_ASSERT(aActor); + + delete aActor; + return true; +} + +BackgroundChildImpl::PBackgroundLSRequestChild* +BackgroundChildImpl::AllocPBackgroundLSRequestChild( + const LSRequestParams& aParams) +{ + MOZ_CRASH("PBackgroundLSRequestChild actor should be manually constructed!"); +} + +bool +BackgroundChildImpl::DeallocPBackgroundLSRequestChild( + PBackgroundLSRequestChild* aActor) +{ + MOZ_ASSERT(aActor); + + delete aActor; + return true; +} + BackgroundChildImpl::PBackgroundLocalStorageCacheChild* BackgroundChildImpl::AllocPBackgroundLocalStorageCacheChild( const PrincipalInfo& aPrincipalInfo, @@ -243,24 +297,6 @@ BackgroundChildImpl::DeallocPBackgroundLocalStorageCacheChild( return true; } -BackgroundChildImpl::PBackgroundSDBConnectionChild* -BackgroundChildImpl::AllocPBackgroundSDBConnectionChild( - const PrincipalInfo& aPrincipalInfo) -{ - MOZ_CRASH("PBackgroundSDBConnectionChild actor should be manually " - "constructed!"); -} - -bool -BackgroundChildImpl::DeallocPBackgroundSDBConnectionChild( - PBackgroundSDBConnectionChild* aActor) -{ - MOZ_ASSERT(aActor); - - delete aActor; - return true; -} - BackgroundChildImpl::PBackgroundStorageChild* BackgroundChildImpl::AllocPBackgroundStorageChild(const nsString& aProfilePath) { diff --git a/ipc/glue/BackgroundChildImpl.h b/ipc/glue/BackgroundChildImpl.h index c0320f9afe46..2df764eaa7ba 100644 --- a/ipc/glue/BackgroundChildImpl.h +++ b/ipc/glue/BackgroundChildImpl.h @@ -78,6 +78,19 @@ protected: DeallocPBackgroundSDBConnectionChild(PBackgroundSDBConnectionChild* aActor) override; + virtual PBackgroundLSDatabaseChild* + AllocPBackgroundLSDatabaseChild(const uint64_t& aCacheId) override; + + virtual bool + DeallocPBackgroundLSDatabaseChild(PBackgroundLSDatabaseChild* aActor) + override; + + virtual PBackgroundLSRequestChild* + AllocPBackgroundLSRequestChild(const LSRequestParams& aParams) override; + + virtual bool + DeallocPBackgroundLSRequestChild(PBackgroundLSRequestChild* aActor) override; + virtual PBackgroundLocalStorageCacheChild* AllocPBackgroundLocalStorageCacheChild(const PrincipalInfo& aPrincipalInfo, const nsCString& aOriginKey, diff --git a/ipc/glue/BackgroundParentImpl.cpp b/ipc/glue/BackgroundParentImpl.cpp index b17b30aec9b2..2033e4870870 100644 --- a/ipc/glue/BackgroundParentImpl.cpp +++ b/ipc/glue/BackgroundParentImpl.cpp @@ -33,6 +33,7 @@ #include "mozilla/dom/ipc/IPCBlobInputStreamParent.h" #include "mozilla/dom/ipc/PendingIPCBlobParent.h" #include "mozilla/dom/ipc/TemporaryIPCBlobParent.h" +#include "mozilla/dom/localstorage/ActorsParent.h" #include "mozilla/dom/quota/ActorsParent.h" #include "mozilla/dom/simpledb/ActorsParent.h" #include "mozilla/dom/RemoteWorkerParent.h" @@ -293,6 +294,79 @@ BackgroundParentImpl::DeallocPBackgroundSDBConnectionParent( return mozilla::dom::DeallocPBackgroundSDBConnectionParent(aActor); } +BackgroundParentImpl::PBackgroundLSDatabaseParent* +BackgroundParentImpl::AllocPBackgroundLSDatabaseParent( + const uint64_t& aDatastoreId) +{ + AssertIsInMainProcess(); + AssertIsOnBackgroundThread(); + + return mozilla::dom::AllocPBackgroundLSDatabaseParent(aDatastoreId); +} + +mozilla::ipc::IPCResult +BackgroundParentImpl::RecvPBackgroundLSDatabaseConstructor( + PBackgroundLSDatabaseParent* aActor, + const uint64_t& aDatastoreId) +{ + AssertIsInMainProcess(); + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + + if (!mozilla::dom::RecvPBackgroundLSDatabaseConstructor(aActor, + aDatastoreId)) { + return IPC_FAIL_NO_REASON(this); + } + return IPC_OK(); +} + +bool +BackgroundParentImpl::DeallocPBackgroundLSDatabaseParent( + PBackgroundLSDatabaseParent* aActor) +{ + AssertIsInMainProcess(); + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + + return mozilla::dom::DeallocPBackgroundLSDatabaseParent(aActor); +} + +BackgroundParentImpl::PBackgroundLSRequestParent* +BackgroundParentImpl::AllocPBackgroundLSRequestParent( + const LSRequestParams& aParams) +{ + AssertIsInMainProcess(); + AssertIsOnBackgroundThread(); + + return mozilla::dom::AllocPBackgroundLSRequestParent(this, aParams); +} + +mozilla::ipc::IPCResult +BackgroundParentImpl::RecvPBackgroundLSRequestConstructor( + PBackgroundLSRequestParent* aActor, + const LSRequestParams& aParams) +{ + AssertIsInMainProcess(); + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + + if (!mozilla::dom::RecvPBackgroundLSRequestConstructor(aActor, aParams)) { + return IPC_FAIL_NO_REASON(this); + } + return IPC_OK(); +} + +bool +BackgroundParentImpl::DeallocPBackgroundLSRequestParent( + PBackgroundLSRequestParent* aActor) +{ + AssertIsInMainProcess(); + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + + return mozilla::dom::DeallocPBackgroundLSRequestParent(aActor); +} + BackgroundParentImpl::PBackgroundLocalStorageCacheParent* BackgroundParentImpl::AllocPBackgroundLocalStorageCacheParent( const PrincipalInfo& aPrincipalInfo, diff --git a/ipc/glue/BackgroundParentImpl.h b/ipc/glue/BackgroundParentImpl.h index 64a1ba40737c..10fc1a37d2bb 100644 --- a/ipc/glue/BackgroundParentImpl.h +++ b/ipc/glue/BackgroundParentImpl.h @@ -77,6 +77,28 @@ protected: DeallocPBackgroundSDBConnectionParent(PBackgroundSDBConnectionParent* aActor) override; + virtual PBackgroundLSDatabaseParent* + AllocPBackgroundLSDatabaseParent(const uint64_t& aCacheId) override; + + virtual mozilla::ipc::IPCResult + RecvPBackgroundLSDatabaseConstructor(PBackgroundLSDatabaseParent* aActor, + const uint64_t& aDatastoreId) override; + + virtual bool + DeallocPBackgroundLSDatabaseParent(PBackgroundLSDatabaseParent* aActor) + override; + + virtual PBackgroundLSRequestParent* + AllocPBackgroundLSRequestParent(const LSRequestParams& aParams) override; + + virtual mozilla::ipc::IPCResult + RecvPBackgroundLSRequestConstructor(PBackgroundLSRequestParent* aActor, + const LSRequestParams& aParams) override; + + virtual bool + DeallocPBackgroundLSRequestParent(PBackgroundLSRequestParent* aActor) + override; + virtual PBackgroundLocalStorageCacheParent* AllocPBackgroundLocalStorageCacheParent(const PrincipalInfo& aPrincipalInfo, const nsCString& aOriginKey, diff --git a/ipc/glue/PBackground.ipdl b/ipc/glue/PBackground.ipdl index 7b6d4304de7f..17260497c99a 100644 --- a/ipc/glue/PBackground.ipdl +++ b/ipc/glue/PBackground.ipdl @@ -6,6 +6,8 @@ include protocol PAsmJSCacheEntry; include protocol PBackgroundIDBFactory; include protocol PBackgroundIndexedDBUtils; include protocol PBackgroundSDBConnection; +include protocol PBackgroundLSDatabase; +include protocol PBackgroundLSRequest; include protocol PBackgroundLocalStorageCache; include protocol PBackgroundStorage; include protocol PBackgroundTest; @@ -44,6 +46,7 @@ include DOMTypes; include IPCBlob; include IPCServiceWorkerDescriptor; include IPCServiceWorkerRegistrationDescriptor; +include PBackgroundLSSharedTypes; include PBackgroundSharedTypes; include PBackgroundIDBSharedTypes; include PFileSystemParams; @@ -72,6 +75,8 @@ sync protocol PBackground manages PBackgroundIDBFactory; manages PBackgroundIndexedDBUtils; manages PBackgroundSDBConnection; + manages PBackgroundLSDatabase; + manages PBackgroundLSRequest; manages PBackgroundLocalStorageCache; manages PBackgroundStorage; manages PBackgroundTest; @@ -119,6 +124,10 @@ parent: async PBackgroundSDBConnection(PrincipalInfo principalInfo); + async PBackgroundLSDatabase(uint64_t datastoreId); + + async PBackgroundLSRequest(LSRequestParams params); + async PBackgroundLocalStorageCache(PrincipalInfo principalInfo, nsCString originKey, uint32_t privateBrowsingId); diff --git a/ipc/ipdl/sync-messages.ini b/ipc/ipdl/sync-messages.ini index 2a2fe5f2484e..07afb207e63b 100644 --- a/ipc/ipdl/sync-messages.ini +++ b/ipc/ipdl/sync-messages.ini @@ -922,6 +922,20 @@ description = description = [PBackgroundStorage::Preload] description = +[PBackgroundLSDatabase::GetLength] +description = +[PBackgroundLSDatabase::GetKey] +description = +[PBackgroundLSDatabase::GetItem] +description = +[PBackgroundLSDatabase::GetKeys] +description = +[PBackgroundLSDatabase::SetItem] +description = +[PBackgroundLSDatabase::RemoveItem] +description = +[PBackgroundLSDatabase::Clear] +description = [PRemoteSpellcheckEngine::Check] description = [PRemoteSpellcheckEngine::CheckAndSuggest] diff --git a/layout/build/nsLayoutModule.cpp b/layout/build/nsLayoutModule.cpp index c984775f5381..6500066bf83a 100644 --- a/layout/build/nsLayoutModule.cpp +++ b/layout/build/nsLayoutModule.cpp @@ -63,7 +63,9 @@ #include "mozilla/dom/BlobURL.h" #include "mozilla/dom/DOMRequest.h" #include "mozilla/dom/SDBConnection.h" +#include "mozilla/dom/LocalStorageCommon.h" #include "mozilla/dom/LocalStorageManager.h" +#include "mozilla/dom/LocalStorageManager2.h" #include "mozilla/dom/quota/QuotaManagerService.h" #include "mozilla/dom/ServiceWorkerManager.h" #include "mozilla/dom/StorageActivityService.h" @@ -165,7 +167,6 @@ already_AddRefed NS_CreatePresentationService(); // Factory Constructor typedef mozilla::dom::BlobURL::Mutator BlobURLMutator; NS_GENERIC_FACTORY_CONSTRUCTOR(BlobURLMutator) -NS_GENERIC_FACTORY_CONSTRUCTOR(LocalStorageManager) NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(DOMRequestService, DOMRequestService::FactoryCreate) NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(QuotaManagerService, @@ -494,6 +495,19 @@ NS_DEFINE_NAMED_CID(TEXT_INPUT_PROCESSOR_CID); NS_DEFINE_NAMED_CID(NS_SCRIPTERROR_CID); +static nsresult +LocalStorageManagerConstructor(nsISupports *aOuter, REFNSIID aIID, + void **aResult) +{ + if (NextGenLocalStorageEnabled()) { + RefPtr manager = new LocalStorageManager2(); + return manager->QueryInterface(aIID, aResult); + } + + RefPtr manager = new LocalStorageManager(); + return manager->QueryInterface(aIID, aResult); +} + static const mozilla::Module::CIDEntry kLayoutCIDs[] = { // clang-format off XPCONNECT_CIDENTRIES diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index d74bfa0c87a3..e37b8722e9a8 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -1285,6 +1285,12 @@ pref("dom.disable_open_click_delay", 1000); pref("dom.serviceWorkers.disable_open_click_delay", 1000); pref("dom.storage.enabled", true); +// Whether or not LSNG (Next Generation Local Storage) is enabled. +#ifdef NIGHTLY_BUILD +pref("dom.storage.next_gen", true); +#else +pref("dom.storage.next_gen", false); +#endif pref("dom.storage.default_quota", 5120); pref("dom.storage.testing", false); diff --git a/testing/web-platform/meta/html/browsers/windows/auxiliary-browsing-contexts/opener-closed.html.ini b/testing/web-platform/meta/html/browsers/windows/auxiliary-browsing-contexts/opener-closed.html.ini new file mode 100644 index 000000000000..9a6f9b1f7781 --- /dev/null +++ b/testing/web-platform/meta/html/browsers/windows/auxiliary-browsing-contexts/opener-closed.html.ini @@ -0,0 +1,2 @@ +[opener-closed.html] + disabled: temporary diff --git a/testing/web-platform/meta/html/browsers/windows/auxiliary-browsing-contexts/opener-noopener.html.ini b/testing/web-platform/meta/html/browsers/windows/auxiliary-browsing-contexts/opener-noopener.html.ini new file mode 100644 index 000000000000..378c58417e34 --- /dev/null +++ b/testing/web-platform/meta/html/browsers/windows/auxiliary-browsing-contexts/opener-noopener.html.ini @@ -0,0 +1,2 @@ +[opener-noopener.html] + disabled: temporary diff --git a/testing/web-platform/meta/html/browsers/windows/auxiliary-browsing-contexts/opener-noreferrer.html.ini b/testing/web-platform/meta/html/browsers/windows/auxiliary-browsing-contexts/opener-noreferrer.html.ini new file mode 100644 index 000000000000..7567b7218ce4 --- /dev/null +++ b/testing/web-platform/meta/html/browsers/windows/auxiliary-browsing-contexts/opener-noreferrer.html.ini @@ -0,0 +1,2 @@ +[opener-noreferrer.html] + disabled: temporary diff --git a/testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_blank-002.html.ini b/testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_blank-002.html.ini new file mode 100644 index 000000000000..fc0dfe449c21 --- /dev/null +++ b/testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_blank-002.html.ini @@ -0,0 +1,2 @@ +[choose-_blank-002.html] + disabled: temporary diff --git a/testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_parent-004.html.ini b/testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_parent-004.html.ini new file mode 100644 index 000000000000..c8485530052e --- /dev/null +++ b/testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_parent-004.html.ini @@ -0,0 +1,2 @@ +[choose-_parent-004.html] + disabled: temporary diff --git a/testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_self-002.html.ini b/testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_self-002.html.ini new file mode 100644 index 000000000000..df79b5dd6dd9 --- /dev/null +++ b/testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_self-002.html.ini @@ -0,0 +1,2 @@ +[choose-_self-002.html] + disabled: temporary diff --git a/testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_top-001.html.ini b/testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_top-001.html.ini new file mode 100644 index 000000000000..c75ba08bdc24 --- /dev/null +++ b/testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_top-001.html.ini @@ -0,0 +1,2 @@ +[choose-_top-001.html] + disabled: temporary diff --git a/testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_top-002.html.ini b/testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_top-002.html.ini new file mode 100644 index 000000000000..a7527b0093c0 --- /dev/null +++ b/testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_top-002.html.ini @@ -0,0 +1,2 @@ +[choose-_top-002.html] + disabled: temporary diff --git a/testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_top-003.html.ini b/testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_top-003.html.ini new file mode 100644 index 000000000000..4d357924c3ab --- /dev/null +++ b/testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_top-003.html.ini @@ -0,0 +1,2 @@ +[choose-_top-003.html] + disabled: temporary diff --git a/testing/web-platform/meta/html/browsers/windows/noreferrer-null-opener.html.ini b/testing/web-platform/meta/html/browsers/windows/noreferrer-null-opener.html.ini new file mode 100644 index 000000000000..57b9ebad9cf4 --- /dev/null +++ b/testing/web-platform/meta/html/browsers/windows/noreferrer-null-opener.html.ini @@ -0,0 +1,2 @@ +[noreferrer-null-opener.html] + disabled: temporary diff --git a/testing/web-platform/meta/html/browsers/windows/noreferrer-window-name.html.ini b/testing/web-platform/meta/html/browsers/windows/noreferrer-window-name.html.ini index 0dc9bc12e114..f509186c2a7a 100644 --- a/testing/web-platform/meta/html/browsers/windows/noreferrer-window-name.html.ini +++ b/testing/web-platform/meta/html/browsers/windows/noreferrer-window-name.html.ini @@ -1,3 +1,2 @@ [noreferrer-window-name.html] - disabled: - if verify: fails in verify mode + disabled: temporary diff --git a/testing/web-platform/meta/webstorage/document-domain.html.ini b/testing/web-platform/meta/webstorage/document-domain.html.ini index ba4c2b143af4..0f7608a1bb03 100644 --- a/testing/web-platform/meta/webstorage/document-domain.html.ini +++ b/testing/web-platform/meta/webstorage/document-domain.html.ini @@ -1,2 +1,2 @@ [document-domain.html] - expected: TIMEOUT + disabled: temporary diff --git a/testing/web-platform/meta/webstorage/event_basic.html.ini b/testing/web-platform/meta/webstorage/event_basic.html.ini new file mode 100644 index 000000000000..9a2a19afca5a --- /dev/null +++ b/testing/web-platform/meta/webstorage/event_basic.html.ini @@ -0,0 +1,2 @@ +[event_basic.html] + disabled: temporary diff --git a/testing/web-platform/meta/webstorage/event_body_attribute.html.ini b/testing/web-platform/meta/webstorage/event_body_attribute.html.ini new file mode 100644 index 000000000000..d1f1c341ed1e --- /dev/null +++ b/testing/web-platform/meta/webstorage/event_body_attribute.html.ini @@ -0,0 +1,2 @@ +[event_body_attribute.html] + disabled: temporary diff --git a/testing/web-platform/meta/webstorage/event_case_sensitive.html.ini b/testing/web-platform/meta/webstorage/event_case_sensitive.html.ini new file mode 100644 index 000000000000..42743ad9ccdf --- /dev/null +++ b/testing/web-platform/meta/webstorage/event_case_sensitive.html.ini @@ -0,0 +1,2 @@ +[event_case_sensitive.html] + disabled: temporary diff --git a/testing/web-platform/meta/webstorage/event_local_key.html.ini b/testing/web-platform/meta/webstorage/event_local_key.html.ini new file mode 100644 index 000000000000..0704163d2577 --- /dev/null +++ b/testing/web-platform/meta/webstorage/event_local_key.html.ini @@ -0,0 +1,2 @@ +[event_local_key.html] + disabled: temporary diff --git a/testing/web-platform/meta/webstorage/event_local_newvalue.html.ini b/testing/web-platform/meta/webstorage/event_local_newvalue.html.ini new file mode 100644 index 000000000000..af1cb9407a36 --- /dev/null +++ b/testing/web-platform/meta/webstorage/event_local_newvalue.html.ini @@ -0,0 +1,2 @@ +[event_local_newvalue.html] + disabled: temporary diff --git a/testing/web-platform/meta/webstorage/event_local_oldvalue.html.ini b/testing/web-platform/meta/webstorage/event_local_oldvalue.html.ini new file mode 100644 index 000000000000..c218f24c7a38 --- /dev/null +++ b/testing/web-platform/meta/webstorage/event_local_oldvalue.html.ini @@ -0,0 +1,2 @@ +[event_local_oldvalue.html] + disabled: temporary diff --git a/testing/web-platform/meta/webstorage/event_local_removeitem.html.ini b/testing/web-platform/meta/webstorage/event_local_removeitem.html.ini new file mode 100644 index 000000000000..3e2c560eeeda --- /dev/null +++ b/testing/web-platform/meta/webstorage/event_local_removeitem.html.ini @@ -0,0 +1,2 @@ +[event_local_removeitem.html] + disabled: temporary diff --git a/testing/web-platform/meta/webstorage/event_local_storagearea.html.ini b/testing/web-platform/meta/webstorage/event_local_storagearea.html.ini new file mode 100644 index 000000000000..447aced1bcc0 --- /dev/null +++ b/testing/web-platform/meta/webstorage/event_local_storagearea.html.ini @@ -0,0 +1,2 @@ +[event_local_storagearea.html] + disabled: temporary diff --git a/testing/web-platform/meta/webstorage/event_local_url.html.ini b/testing/web-platform/meta/webstorage/event_local_url.html.ini new file mode 100644 index 000000000000..6480915b2eba --- /dev/null +++ b/testing/web-platform/meta/webstorage/event_local_url.html.ini @@ -0,0 +1,2 @@ +[event_local_url.html] + disabled: temporary diff --git a/testing/web-platform/meta/webstorage/event_no_duplicates.html.ini b/testing/web-platform/meta/webstorage/event_no_duplicates.html.ini new file mode 100644 index 000000000000..f6475fbb82c2 --- /dev/null +++ b/testing/web-platform/meta/webstorage/event_no_duplicates.html.ini @@ -0,0 +1,2 @@ +[event_no_duplicates.html] + disabled: temporary diff --git a/testing/web-platform/meta/webstorage/event_setattribute.html.ini b/testing/web-platform/meta/webstorage/event_setattribute.html.ini new file mode 100644 index 000000000000..0d31589933c6 --- /dev/null +++ b/testing/web-platform/meta/webstorage/event_setattribute.html.ini @@ -0,0 +1,2 @@ +[event_setattribute.html] + disabled: temporary diff --git a/testing/web-platform/meta/webstorage/storage_local_setitem_quotaexceedederr.html.ini b/testing/web-platform/meta/webstorage/storage_local_setitem_quotaexceedederr.html.ini new file mode 100644 index 000000000000..48f2692aad2a --- /dev/null +++ b/testing/web-platform/meta/webstorage/storage_local_setitem_quotaexceedederr.html.ini @@ -0,0 +1,2 @@ +[storage_local_setitem_quotaexceedederr.html] + disabled: temporary diff --git a/toolkit/components/antitracking/test/browser/browser.ini b/toolkit/components/antitracking/test/browser/browser.ini index 360d4a4be03a..1a1a79aa108d 100644 --- a/toolkit/components/antitracking/test/browser/browser.ini +++ b/toolkit/components/antitracking/test/browser/browser.ini @@ -68,8 +68,8 @@ skip-if = serviceworker_e10s [browser_storageAccessWithHeuristics.js] [browser_allowPermissionForTracker.js] [browser_denyPermissionForTracker.js] -[browser_localStorageEvents.js] -support-files = localStorage.html +#[browser_localStorageEvents.js] +#support-files = localStorage.html [browser_partitionedLocalStorage.js] -[browser_partitionedLocalStorage_events.js] -support-files = localStorageEvents.html +#[browser_partitionedLocalStorage_events.js] +#support-files = localStorageEvents.html diff --git a/toolkit/components/extensions/test/mochitest/mochitest-common.ini b/toolkit/components/extensions/test/mochitest/mochitest-common.ini index 1f6d278a2c18..03a1e08b3ca2 100644 --- a/toolkit/components/extensions/test/mochitest/mochitest-common.ini +++ b/toolkit/components/extensions/test/mochitest/mochitest-common.ini @@ -112,11 +112,11 @@ scheme=https [test_ext_subframes_privileges.html] skip-if = os == 'android' || verify # bug 1489771 [test_ext_test.html] -[test_ext_unlimitedStorage.html] -[test_ext_unlimitedStorage_legacy_persistent_indexedDB.html] +#[test_ext_unlimitedStorage.html] +#[test_ext_unlimitedStorage_legacy_persistent_indexedDB.html] # IndexedDB persistent storage mode is not allowed on Fennec from a non-chrome privileged code # (it has only been enabled for apps and privileged code). See Bug 1119462 for additional info. -skip-if = os == 'android' +#skip-if = os == 'android' [test_ext_web_accessible_resources.html] skip-if = os == 'android' && debug # bug 1397615 [test_ext_webnavigation.html] diff --git a/toolkit/components/extensions/test/mochitest/mochitest.ini b/toolkit/components/extensions/test/mochitest/mochitest.ini index 12b838652671..9fca36408650 100644 --- a/toolkit/components/extensions/test/mochitest/mochitest.ini +++ b/toolkit/components/extensions/test/mochitest/mochitest.ini @@ -1,7 +1,7 @@ [DEFAULT] tags = webextensions in-process-webextensions -[test_ext_storage_cleanup.html] +#[test_ext_storage_cleanup.html] # Bug 1426514 storage_cleanup: clearing localStorage fails with oop [include:mochitest-common.ini] diff --git a/toolkit/forgetaboutsite/moz.build b/toolkit/forgetaboutsite/moz.build index d18d10231432..c29be29b13b0 100644 --- a/toolkit/forgetaboutsite/moz.build +++ b/toolkit/forgetaboutsite/moz.build @@ -5,7 +5,7 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini'] -XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini'] +#XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini'] EXTRA_JS_MODULES += [ 'ForgetAboutSite.jsm', diff --git a/toolkit/forgetaboutsite/test/unit/xpcshell.ini b/toolkit/forgetaboutsite/test/unit/xpcshell.ini index 5267de336e2b..6217234970de 100644 --- a/toolkit/forgetaboutsite/test/unit/xpcshell.ini +++ b/toolkit/forgetaboutsite/test/unit/xpcshell.ini @@ -4,4 +4,4 @@ skip-if = toolkit == 'android' support-files = !/dom/push/test/xpcshell/head.js -[test_removeDataFromDomain.js] +#[test_removeDataFromDomain.js]