gecko-dev/dom/file/ipc/IPCBlobInputStreamStorage.cpp

212 строки
5.3 KiB
C++

/* -*- 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 "IPCBlobInputStreamStorage.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/StaticMutex.h"
#include "mozilla/StaticPtr.h"
#include "nsIPropertyBag2.h"
#include "nsStreamUtils.h"
namespace mozilla {
using namespace hal;
namespace dom {
namespace {
StaticMutex gMutex;
StaticRefPtr<IPCBlobInputStreamStorage> gStorage;
}
NS_INTERFACE_MAP_BEGIN(IPCBlobInputStreamStorage)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
NS_INTERFACE_MAP_ENTRY(nsIObserver)
NS_INTERFACE_MAP_END
NS_IMPL_ADDREF(IPCBlobInputStreamStorage)
NS_IMPL_RELEASE(IPCBlobInputStreamStorage)
IPCBlobInputStreamStorage::IPCBlobInputStreamStorage()
{}
IPCBlobInputStreamStorage::~IPCBlobInputStreamStorage()
{}
/* static */ IPCBlobInputStreamStorage*
IPCBlobInputStreamStorage::Get()
{
return gStorage;
}
/* static */ void
IPCBlobInputStreamStorage::Initialize()
{
MOZ_ASSERT(!gStorage);
gStorage = new IPCBlobInputStreamStorage();
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
obs->AddObserver(gStorage, "xpcom-shutdown", false);
obs->AddObserver(gStorage, "ipc:content-shutdown", false);
}
}
NS_IMETHODIMP
IPCBlobInputStreamStorage::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData)
{
if (!strcmp(aTopic, "xpcom-shutdown")) {
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
obs->RemoveObserver(this, "xpcom-shutdown");
obs->RemoveObserver(this, "ipc:content-shutdown");
}
gStorage = nullptr;
return NS_OK;
}
MOZ_ASSERT(!strcmp(aTopic, "ipc:content-shutdown"));
nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
if (NS_WARN_IF(!props)) {
return NS_ERROR_FAILURE;
}
uint64_t childID = CONTENT_PROCESS_ID_UNKNOWN;
props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), &childID);
if (NS_WARN_IF(childID == CONTENT_PROCESS_ID_UNKNOWN)) {
return NS_ERROR_FAILURE;
}
mozilla::StaticMutexAutoLock lock(gMutex);
for (auto iter = mStorage.Iter(); !iter.Done(); iter.Next()) {
if (iter.Data()->mChildID == childID) {
iter.Remove();
}
}
return NS_OK;
}
void
IPCBlobInputStreamStorage::AddStream(nsIInputStream* aInputStream,
const nsID& aID,
uint64_t aSize,
uint64_t aChildID)
{
MOZ_ASSERT(aInputStream);
StreamData* data = new StreamData();
data->mInputStream = aInputStream;
data->mChildID = aChildID;
data->mSize = aSize;
mozilla::StaticMutexAutoLock lock(gMutex);
mStorage.Put(aID, data);
}
void
IPCBlobInputStreamStorage::ForgetStream(const nsID& aID)
{
mozilla::StaticMutexAutoLock lock(gMutex);
mStorage.Remove(aID);
}
void
IPCBlobInputStreamStorage::GetStream(const nsID& aID,
uint64_t aStart, uint64_t aLength,
nsIInputStream** aInputStream)
{
*aInputStream = nullptr;
nsCOMPtr<nsIInputStream> inputStream;
uint64_t size;
// NS_CloneInputStream cannot be called when the mutex is locked because it
// can, recursively call GetStream() in case the child actor lives on the
// parent process.
{
mozilla::StaticMutexAutoLock lock(gMutex);
StreamData* data = mStorage.Get(aID);
if (!data) {
return;
}
inputStream = data->mInputStream;
size = data->mSize;
}
MOZ_ASSERT(inputStream);
// We cannot return always the same inputStream because not all of them are
// able to be reused. Better to clone them.
nsCOMPtr<nsIInputStream> clonedStream;
nsCOMPtr<nsIInputStream> replacementStream;
nsresult rv =
NS_CloneInputStream(inputStream, getter_AddRefs(clonedStream),
getter_AddRefs(replacementStream));
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
if (replacementStream) {
mozilla::StaticMutexAutoLock lock(gMutex);
StreamData* data = mStorage.Get(aID);
// data can be gone in the meantime.
if (!data) {
return;
}
data->mInputStream = replacementStream;
}
// Now it's the right time to apply a slice if needed.
if (aStart > 0 || aLength < size) {
clonedStream =
new SlicedInputStream(clonedStream.forget(), aStart, aLength);
}
clonedStream.forget(aInputStream);
}
void
IPCBlobInputStreamStorage::StoreCallback(const nsID& aID,
IPCBlobInputStreamParentCallback* aCallback)
{
MOZ_ASSERT(aCallback);
mozilla::StaticMutexAutoLock lock(gMutex);
StreamData* data = mStorage.Get(aID);
if (data) {
MOZ_ASSERT(!data->mCallback);
data->mCallback = aCallback;
}
}
already_AddRefed<IPCBlobInputStreamParentCallback>
IPCBlobInputStreamStorage::TakeCallback(const nsID& aID)
{
mozilla::StaticMutexAutoLock lock(gMutex);
StreamData* data = mStorage.Get(aID);
if (!data) {
return nullptr;
}
RefPtr<IPCBlobInputStreamParentCallback> callback;
data->mCallback.swap(callback);
return callback.forget();
}
} // namespace dom
} // namespace mozilla