Bug 1350637 - Part 5: Factor out the parent actor observer sink to work on the PBackground thread, fix the rest of observer handling to use IPC; r=asuth

This commit is contained in:
Jan Varga 2017-08-08 23:01:33 +02:00
parent 75c214b7ea
commit 3acd1f5628
4 changed files with 297 additions and 59 deletions

View File

@ -6,6 +6,11 @@
include protocol PBackground;
include "mozilla/dom/quota/SerializationHelpers.h";
using mozilla::OriginAttributesPattern
from "mozilla/OriginAttributes.h";
namespace mozilla {
namespace dom {
@ -35,7 +40,17 @@ parent:
nsString key);
async AsyncClear(nsCString originSuffix, nsCString originNoSuffix);
async AsyncFlush();
// These are privileged operations for use only by the observer API for
// delayed initialization and clearing origins and will never be used from
// content process children. Ideally these would be guarded by checks or
// exist on a separate, privileged interface, but PBackgroundStorage is
// already insecure.
async Startup();
async ClearAll();
async ClearMatchingOrigin(nsCString originNoSuffix);
async ClearMatchingOriginAttributes(OriginAttributesPattern pattern);
child:
async Observe(nsCString topic,
nsString originAttributesPattern,

View File

@ -303,6 +303,8 @@ StorageDBChild::RecvObserve(const nsCString& aTopic,
const nsString& aOriginAttributesPattern,
const nsCString& aOriginScope)
{
MOZ_ASSERT(!XRE_IsParentProcess());
StorageObserver::Self()->Notify(
aTopic.get(), aOriginAttributesPattern, aOriginScope);
return IPC_OK();
@ -406,6 +408,52 @@ ShutdownObserver::Observe(nsISupports* aSubject,
// Parent
// ----------------------------------------------------------------------------
class StorageDBParent::ObserverSink
: public StorageObserverSink
{
nsCOMPtr<nsIEventTarget> mOwningEventTarget;
// Only touched on the PBackground thread.
StorageDBParent* MOZ_NON_OWNING_REF mActor;
public:
explicit ObserverSink(StorageDBParent* aActor)
: mOwningEventTarget(GetCurrentThreadEventTarget())
, mActor(aActor)
{
AssertIsOnBackgroundThread();
MOZ_ASSERT(aActor);
}
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(StorageDBParent::ObserverSink);
void
Start();
void
Stop();
private:
~ObserverSink() = default;
void
AddSink();
void
RemoveSink();
void
Notify(const nsCString& aTopic,
const nsString& aOriginAttributesPattern,
const nsCString& aOriginScope);
// StorageObserverSink
nsresult
Observe(const char* aTopic,
const nsAString& aOriginAttrPattern,
const nsACString& aOriginScope) override;
};
NS_IMPL_ADDREF(StorageDBParent)
NS_IMPL_RELEASE(StorageDBParent)
@ -442,6 +490,8 @@ private:
return NS_OK;
}
mParent->Init();
StorageDBThread* storageThread = StorageDBThread::Get();
if (storageThread) {
InfallibleTArray<nsCString> scopes;
@ -479,10 +529,7 @@ private:
StorageDBParent::StorageDBParent()
: mIPCOpen(false)
{
StorageObserver* observer = StorageObserver::Self();
if (observer) {
observer->AddSink(this);
}
AssertIsOnBackgroundThread();
// We are always open by IPC only
AddIPDLReference();
@ -496,9 +543,25 @@ StorageDBParent::StorageDBParent()
StorageDBParent::~StorageDBParent()
{
StorageObserver* observer = StorageObserver::Self();
if (observer) {
observer->RemoveSink(this);
AssertIsOnBackgroundThread();
if (mObserverSink) {
mObserverSink->Stop();
mObserverSink = nullptr;
}
}
void
StorageDBParent::Init()
{
AssertIsOnBackgroundThread();
PBackgroundParent* actor = Manager();
MOZ_ASSERT(actor);
if (BackgroundParent::IsOtherProcessActor(actor)) {
mObserverSink = new ObserverSink(this);
mObserverSink->Start();
}
}
@ -744,20 +807,66 @@ StorageDBParent::RecvAsyncFlush()
return IPC_OK();
}
// StorageObserverSink
nsresult
StorageDBParent::Observe(const char* aTopic,
const nsAString& aOriginAttributesPattern,
const nsACString& aOriginScope)
mozilla::ipc::IPCResult
StorageDBParent::RecvStartup()
{
if (mIPCOpen) {
mozilla::Unused << SendObserve(nsDependentCString(aTopic),
nsString(aOriginAttributesPattern),
nsCString(aOriginScope));
StorageDBThread* storageThread = StorageDBThread::GetOrCreate();
if (!storageThread) {
return IPC_FAIL_NO_REASON(this);
}
return NS_OK;
return IPC_OK();
}
mozilla::ipc::IPCResult
StorageDBParent::RecvClearAll()
{
StorageDBThread* storageThread = StorageDBThread::GetOrCreate();
if (!storageThread) {
return IPC_FAIL_NO_REASON(this);
}
storageThread->AsyncClearAll();
return IPC_OK();
}
mozilla::ipc::IPCResult
StorageDBParent::RecvClearMatchingOrigin(const nsCString& aOriginNoSuffix)
{
StorageDBThread* storageThread = StorageDBThread::GetOrCreate();
if (!storageThread) {
return IPC_FAIL_NO_REASON(this);
}
storageThread->AsyncClearMatchingOrigin(aOriginNoSuffix);
return IPC_OK();
}
mozilla::ipc::IPCResult
StorageDBParent::RecvClearMatchingOriginAttributes(
const OriginAttributesPattern& aPattern)
{
StorageDBThread* storageThread = StorageDBThread::GetOrCreate();
if (!storageThread) {
return IPC_FAIL_NO_REASON(this);
}
storageThread->AsyncClearMatchingOriginAttributes(aPattern);
return IPC_OK();
}
void
StorageDBParent::Observe(const nsCString& aTopic,
const nsString& aOriginAttributesPattern,
const nsCString& aOriginScope)
{
if (mIPCOpen) {
mozilla::Unused <<
SendObserve(aTopic, aOriginAttributesPattern, aOriginScope);
}
}
namespace {
@ -1000,6 +1109,96 @@ StorageDBParent::UsageParentBridge::Destroy()
NS_DISPATCH_NORMAL));
}
void
StorageDBParent::
ObserverSink::Start()
{
AssertIsOnBackgroundThread();
RefPtr<Runnable> runnable =
NewRunnableMethod("StorageDBParent::ObserverSink::AddSink",
this,
&StorageDBParent::ObserverSink::AddSink);
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable));
}
void
StorageDBParent::
ObserverSink::Stop()
{
AssertIsOnBackgroundThread();
mActor = nullptr;
RefPtr<Runnable> runnable =
NewRunnableMethod("StorageDBParent::ObserverSink::RemoveSink",
this,
&StorageDBParent::ObserverSink::RemoveSink);
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable));
}
void
StorageDBParent::
ObserverSink::AddSink()
{
MOZ_ASSERT(NS_IsMainThread());
StorageObserver* observer = StorageObserver::Self();
if (observer) {
observer->AddSink(this);
}
}
void
StorageDBParent::
ObserverSink::RemoveSink()
{
MOZ_ASSERT(NS_IsMainThread());
StorageObserver* observer = StorageObserver::Self();
if (observer) {
observer->RemoveSink(this);
}
}
void
StorageDBParent::
ObserverSink::Notify(const nsCString& aTopic,
const nsString& aOriginAttributesPattern,
const nsCString& aOriginScope)
{
AssertIsOnBackgroundThread();
if (mActor) {
mActor->Observe(aTopic, aOriginAttributesPattern, aOriginScope);
}
}
nsresult
StorageDBParent::
ObserverSink::Observe(const char* aTopic,
const nsAString& aOriginAttributesPattern,
const nsACString& aOriginScope)
{
MOZ_ASSERT(NS_IsMainThread());
RefPtr<Runnable> runnable =
NewRunnableMethod<nsCString, nsString, nsCString>(
"StorageDBParent::ObserverSink::Observe2",
this,
&StorageDBParent::ObserverSink::Notify,
aTopic,
aOriginAttributesPattern,
aOriginScope);
MOZ_ALWAYS_SUCCEEDS(
mOwningEventTarget->Dispatch(runnable, NS_DISPATCH_NORMAL));
return NS_OK;
}
/*******************************************************************************
* Exported functions
******************************************************************************/

View File

@ -137,13 +137,17 @@ private:
// Also responsible for forwardning all chrome operation notifications
// such as cookie cleaning etc to the child process.
class StorageDBParent final : public PBackgroundStorageParent
, public StorageObserverSink
{
class ObserverSink;
virtual ~StorageDBParent();
public:
StorageDBParent();
void
Init();
NS_IMETHOD_(MozExternalRefCountType) AddRef(void);
NS_IMETHOD_(MozExternalRefCountType) Release(void);
@ -250,15 +254,23 @@ private:
const nsCString& aOriginNoSuffix) override;
mozilla::ipc::IPCResult RecvAsyncFlush() override;
// StorageObserverSink
virtual nsresult Observe(const char* aTopic,
const nsAString& aOriginAttrPattern,
const nsACString& aOriginScope) override;
mozilla::ipc::IPCResult RecvStartup() override;
mozilla::ipc::IPCResult RecvClearAll() override;
mozilla::ipc::IPCResult RecvClearMatchingOrigin(
const nsCString& aOriginNoSuffix) override;
mozilla::ipc::IPCResult RecvClearMatchingOriginAttributes(
const OriginAttributesPattern& aPattern) override;
void Observe(const nsCString& aTopic,
const nsString& aOriginAttrPattern,
const nsCString& aOriginScope);
private:
CacheParentBridge* NewCache(const nsACString& aOriginSuffix,
const nsACString& aOriginNoSuffix);
RefPtr<ObserverSink> mObserverSink;
ThreadSafeAutoRefCnt mRefCnt;
NS_DECL_OWNINGTHREAD

View File

@ -161,6 +161,8 @@ StorageObserver::Observe(nsISupports* aSubject,
// Start the thread that opens the database.
if (!strcmp(aTopic, kStartupTopic)) {
MOZ_ASSERT(XRE_IsParentProcess());
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
obs->RemoveObserver(this, kStartupTopic);
@ -176,6 +178,8 @@ 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());
nsCOMPtr<nsITimer> timer = do_QueryInterface(aSubject);
if (!timer) {
return NS_ERROR_UNEXPECTED;
@ -184,11 +188,12 @@ StorageObserver::Observe(nsISupports* aSubject,
if (timer == mDBThreadStartDelayTimer) {
mDBThreadStartDelayTimer = nullptr;
// XXX Fix me!
#if 0
StorageDBBridge* db = LocalStorageCache::StartDatabase();
NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
#endif
StorageDBChild* storageChild = StorageDBChild::GetOrCreate();
if (NS_WARN_IF(!storageChild)) {
return NS_ERROR_FAILURE;
}
storageChild->SendStartup();
}
return NS_OK;
@ -200,13 +205,16 @@ StorageObserver::Observe(nsISupports* aSubject,
return NS_OK;
}
// XXX Fix me!
#if 0
StorageDBBridge* db = LocalStorageCache::StartDatabase();
NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
StorageDBChild* storageChild = StorageDBChild::GetOrCreate();
if (NS_WARN_IF(!storageChild)) {
return NS_ERROR_FAILURE;
}
db->AsyncClearAll();
#endif
storageChild->AsyncClearAll();
if (XRE_IsParentProcess()) {
storageChild->SendClearAll();
}
Notify("cookie-cleared");
@ -267,13 +275,16 @@ StorageObserver::Observe(nsISupports* aSubject,
}
if (!strcmp(aTopic, "extension:purge-localStorage")) {
// XXX Fix me!
#if 0
StorageDBBridge* db = LocalStorageCache::StartDatabase();
NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
StorageDBChild* storageChild = StorageDBChild::GetOrCreate();
if (NS_WARN_IF(!storageChild)) {
return NS_ERROR_FAILURE;
}
db->AsyncClearAll();
#endif
storageChild->AsyncClearAll();
if (XRE_IsParentProcess()) {
storageChild->SendClearAll();
}
Notify("extension:purge-localStorage-caches");
@ -303,13 +314,14 @@ StorageObserver::Observe(nsISupports* aSubject,
rv = CreateReversedDomain(aceDomain, originScope);
NS_ENSURE_SUCCESS(rv, rv);
// XXX Fix me!
#if 0
StorageDBBridge* db = LocalStorageCache::StartDatabase();
NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
if (XRE_IsParentProcess()) {
StorageDBChild* storageChild = StorageDBChild::GetOrCreate();
if (NS_WARN_IF(!storageChild)) {
return NS_ERROR_FAILURE;
}
db->AsyncClearMatchingOrigin(originScope);
#endif
storageChild->SendClearMatchingOrigin(originScope);
}
Notify("domain-data-cleared", EmptyString(), originScope);
@ -325,19 +337,20 @@ StorageObserver::Observe(nsISupports* aSubject,
// Clear data of the origins whose prefixes will match the suffix.
if (!strcmp(aTopic, "clear-origin-attributes-data")) {
MOZ_ASSERT(XRE_IsParentProcess());
OriginAttributesPattern pattern;
if (!pattern.Init(nsDependentString(aData))) {
NS_ERROR("Cannot parse origin attributes pattern");
return NS_ERROR_FAILURE;
}
// XXX Fix me!
#if 0
StorageDBBridge* db = LocalStorageCache::StartDatabase();
NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
StorageDBChild* storageChild = StorageDBChild::GetOrCreate();
if (NS_WARN_IF(!storageChild)) {
return NS_ERROR_FAILURE;
}
db->AsyncClearMatchingOriginAttributes(pattern);
#endif
storageChild->SendClearMatchingOriginAttributes(pattern);
Notify("origin-attr-pattern-cleared", nsDependentString(aData));
@ -381,13 +394,12 @@ StorageObserver::Observe(nsISupports* aSubject,
#ifdef DOM_STORAGE_TESTS
if (!strcmp(aTopic, "domstorage-test-flush-force")) {
// XXX Fix me!
#if 0
StorageDBBridge* db = LocalStorageCache::GetDatabase();
if (db) {
db->AsyncFlush();
StorageDBChild* storageChild = StorageDBChild::GetOrCreate();
if (NS_WARN_IF(!storageChild)) {
return NS_ERROR_FAILURE;
}
#endif
storageChild->SendAsyncFlush();
return NS_OK;
}