Backed out 10 changesets (bug 1470783, bug 1463587) for causing multiple leakcheck failures on a CLOSED TREE

Backed out changeset cd2080bd727a (bug 1463587)
Backed out changeset 5866137afd9a (bug 1463587)
Backed out changeset d64e1c150db2 (bug 1463587)
Backed out changeset 669f084e8914 (bug 1463587)
Backed out changeset 8074c985095c (bug 1470783)
Backed out changeset 49ed13196e9f (bug 1463587)
Backed out changeset c052042a66cf (bug 1463587)
Backed out changeset cebf1f055d1d (bug 1463587)
Backed out changeset 2ebaf5f8c605 (bug 1463587)
Backed out changeset c27295337b4c (bug 1463587)
This commit is contained in:
Margareta Eliza Balazs 2018-07-12 11:27:45 +03:00
Родитель 2773d9005a
Коммит 7b416abaf1
28 изменённых файлов: 147 добавлений и 1490 удалений

Просмотреть файл

@ -7,10 +7,8 @@
#include "ProcessGlobal.h"
#include "nsContentCID.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/MessageManagerBinding.h"
#include "mozilla/dom/ResolveSystemBinding.h"
#include "mozilla/dom/ipc/SharedMap.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -27,11 +25,6 @@ ProcessGlobal::ProcessGlobal(nsFrameMessageManager* aMessageManager)
ProcessGlobal::~ProcessGlobal()
{
mAnonymousGlobalScopes.Clear();
if (ContentChild* child = ContentChild::GetSingleton()) {
// Clear this now so we can be sure it's destroyed before cycle collector
// shutdown.
child->ClearSharedData();
}
mozilla::DropJSObjects(this);
}
@ -79,16 +72,6 @@ ProcessGlobal::Get()
return global;
}
already_AddRefed<mozilla::dom::ipc::SharedMap>
ProcessGlobal::SharedData()
{
if (ContentChild* child = ContentChild::GetSingleton()) {
return do_AddRef(child->SharedData());
}
auto* ppmm = nsFrameMessageManager::sParentProcessManager;
return do_AddRef(ppmm->SharedData()->GetReadOnly());
}
bool
ProcessGlobal::WasCreated()
{

Просмотреть файл

@ -25,10 +25,6 @@
namespace mozilla {
namespace dom {
namespace ipc {
class SharedMap;
}
class ProcessGlobal :
public nsIMessageSender,
public nsMessageManagerScriptExecutor,
@ -88,8 +84,6 @@ public:
mMessageManager->GetInitialProcessData(aCx, aInitialProcessData, aError);
}
already_AddRefed<ipc::SharedMap> SharedData();
NS_FORWARD_SAFE_NSIMESSAGESENDER(mMessageManager)
virtual void LoadScript(const nsAString& aURL);

Просмотреть файл

@ -48,7 +48,6 @@
#include "mozilla/dom/SameProcessMessageQueue.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/ToJSValue.h"
#include "mozilla/dom/ipc/SharedMap.h"
#include "mozilla/dom/ipc/StructuredCloneData.h"
#include "mozilla/dom/DOMStringList.h"
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
@ -1000,19 +999,6 @@ nsFrameMessageManager::GetInitialProcessData(JSContext* aCx,
aInitialProcessData.set(init);
}
WritableSharedMap*
nsFrameMessageManager::SharedData()
{
if (!mChrome || !mIsProcessManager) {
MOZ_ASSERT(false, "Should only call this binding method on ppmm");
return nullptr;
}
if (!mSharedData) {
mSharedData = new WritableSharedMap();
}
return mSharedData;
}
already_AddRefed<ProcessMessageManager>
nsFrameMessageManager::GetProcessMessageManager(ErrorResult& aError)
{

Просмотреть файл

@ -37,11 +37,6 @@
class nsFrameLoader;
namespace mozilla {
namespace ipc {
class FileDescriptor;
}
namespace dom {
class nsIContentParent;
@ -59,8 +54,6 @@ class ProcessMessageManager;
namespace ipc {
class WritableSharedMap;
// Note: we round the time we spend to the nearest millisecond. So a min value
// of 1 ms actually captures from 500us and above.
static const uint32_t kMinTelemetrySyncMessageManagerLatencyMs = 1;
@ -240,8 +233,6 @@ public:
JS::MutableHandle<JS::Value> aInitialProcessData,
mozilla::ErrorResult& aError);
mozilla::dom::ipc::WritableSharedMap* SharedData();
NS_DECL_NSIMESSAGESENDER
NS_DECL_NSICONTENTFRAMEMESSAGEMANAGER
@ -349,7 +340,6 @@ protected:
nsTArray<nsString> mPendingScripts;
nsTArray<bool> mPendingScriptsGlobalStates;
JS::Heap<JS::Value> mInitialProcessData;
RefPtr<mozilla::dom::ipc::WritableSharedMap> mSharedData;
void LoadPendingScripts(nsFrameMessageManager* aManager,
nsFrameMessageManager* aChildMM);

Просмотреть файл

@ -577,19 +577,6 @@ DOMInterfaces = {
'notflattened': True
},
'MozSharedMap': {
'nativeType': 'mozilla::dom::ipc::SharedMap',
},
'MozWritableSharedMap': {
'headerFile': 'mozilla/dom/ipc/SharedMap.h',
'nativeType': 'mozilla::dom::ipc::WritableSharedMap',
},
'MozSharedMapChangeEvent': {
'nativeType': 'mozilla::dom::ipc::SharedMapChangeEvent',
},
'MozStorageAsyncStatementParams': {
'headerFile': 'mozilla/storage/mozStorageAsyncStatementParams.h',
'nativeType': 'mozilla::storage::AsyncStatementParams',

Просмотреть файл

@ -457,8 +457,6 @@ interface GlobalProcessScriptLoader : ProcessScriptLoader
*/
[Throws]
readonly attribute any initialProcessData;
readonly attribute MozWritableSharedMap sharedData;
};
[ChromeOnly, Global, NeedResolve]
@ -496,8 +494,6 @@ interface ContentProcessMessageManager
*/
[Throws]
readonly attribute any initialProcessData;
readonly attribute MozSharedMap sharedData;
};
// MessageManagerGlobal inherits from SyncMessageSender, which is a real interface, not a
// mixin. This will need to change when we implement mixins according to the current

Просмотреть файл

@ -1,54 +0,0 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/.
*/
typedef any StructuredClonable;
[ChromeOnly]
interface MozSharedMapChangeEvent : Event {
[Cached, Constant]
readonly attribute sequence<DOMString> changedKeys;
};
dictionary MozSharedMapChangeEventInit : EventInit {
required sequence<DOMString> changedKeys;
};
[ChromeOnly]
interface MozSharedMap : EventTarget {
boolean has(DOMString name);
[Throws]
StructuredClonable get(DOMString name);
iterable<DOMString, StructuredClonable>;
};
[ChromeOnly]
interface MozWritableSharedMap : MozSharedMap {
/**
* Sets the given key to the given structured-clonable value. The value is
* synchronously structured cloned, and the serialized value is saved in the
* map.
*
* Unless flush() is called, the new value will be broadcast to content
* processes after a short delay.
*/
[Throws]
void set(DOMString name, StructuredClonable value);
/**
* Removes the given key from the map.
*
* Unless flush() is called, the removal will be broadcast to content
* processes after a short delay.
*/
void delete(DOMString name);
/**
* Broadcasts any pending changes to all content processes.
*/
void flush();
};

Просмотреть файл

@ -37,7 +37,6 @@ WEBIDL_FILES = [
'MatchGlob.webidl',
'MatchPattern.webidl',
'MessageManager.webidl',
'MozSharedMap.webidl',
'MozStorageAsyncStatementParams.webidl',
'MozStorageStatementParams.webidl',
'MozStorageStatementRow.webidl',

Просмотреть файл

@ -48,7 +48,6 @@
#include "mozilla/dom/URLClassifierChild.h"
#include "mozilla/dom/WorkerDebugger.h"
#include "mozilla/dom/WorkerDebuggerManager.h"
#include "mozilla/dom/ipc/SharedMap.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/gfx/Logging.h"
#include "mozilla/psm/PSMContentListener.h"
@ -592,9 +591,7 @@ mozilla::ipc::IPCResult
ContentChild::RecvSetXPCOMProcessAttributes(const XPCOMInitData& aXPCOMInit,
const StructuredCloneData& aInitialData,
nsTArray<LookAndFeelInt>&& aLookAndFeelIntCache,
nsTArray<SystemFontListEntry>&& aFontList,
const FileDescriptor& aSharedDataMapFile,
const uint32_t& aSharedDataMapSize)
nsTArray<SystemFontListEntry>&& aFontList)
{
if (!sShutdownCanary) {
return IPC_OK();
@ -606,18 +603,9 @@ ContentChild::RecvSetXPCOMProcessAttributes(const XPCOMInitData& aXPCOMInit,
InitXPCOM(aXPCOMInit, aInitialData);
InitGraphicsDeviceData(aXPCOMInit.contentDeviceData());
mSharedData = new SharedMap(ProcessGlobal::Get(), aSharedDataMapFile,
aSharedDataMapSize);
return IPC_OK();
}
void
ContentChild::ClearSharedData()
{
mSharedData = nullptr;
}
bool
ContentChild::Init(MessageLoop* aIOLoop,
base::ProcessId aParentPid,
@ -2568,26 +2556,6 @@ ContentChild::RecvRegisterStringBundles(nsTArray<mozilla::dom::StringBundleDescr
return IPC_OK();
}
mozilla::ipc::IPCResult
ContentChild::RecvUpdateSharedData(const FileDescriptor& aMapFile,
const uint32_t& aMapSize,
nsTArray<IPCBlob>&& aBlobs,
nsTArray<nsCString>&& aChangedKeys)
{
if (mSharedData) {
nsTArray<RefPtr<BlobImpl>> blobImpls(aBlobs.Length());
for (auto& ipcBlob : aBlobs) {
blobImpls.AppendElement(IPCBlobUtils::Deserialize(ipcBlob));
}
mSharedData->Update(aMapFile, aMapSize,
std::move(blobImpls),
std::move(aChangedKeys));
}
return IPC_OK();
}
mozilla::ipc::IPCResult
ContentChild::RecvGeolocationUpdate(nsIDOMGeoPosition* aPosition)
{

Просмотреть файл

@ -66,10 +66,6 @@ class URIParams;
namespace dom {
namespace ipc {
class SharedMap;
}
class AlertObserver;
class ConsoleListener;
class ClonedMessageData;
@ -168,10 +164,6 @@ public:
bool IsShuttingDown() const;
ipc::SharedMap* SharedData() { return mSharedData; };
void ClearSharedData();
static void AppendProcessId(nsACString& aName);
static void UpdateCookieStatus(nsIChannel *aChannel);
@ -404,11 +396,6 @@ public:
mozilla::ipc::IPCResult RecvRegisterStringBundles(nsTArray<StringBundleDescriptor>&& stringBundles) override;
mozilla::ipc::IPCResult RecvUpdateSharedData(const FileDescriptor& aMapFile,
const uint32_t& aMapSize,
nsTArray<IPCBlob>&& aBlobs,
nsTArray<nsCString>&& aChangedKeys) override;
virtual mozilla::ipc::IPCResult RecvGeolocationUpdate(nsIDOMGeoPosition* aPosition) override;
virtual mozilla::ipc::IPCResult RecvGeolocationError(const uint16_t& errorCode) override;
@ -628,9 +615,7 @@ public:
RecvSetXPCOMProcessAttributes(const XPCOMInitData& aXPCOMInit,
const StructuredCloneData& aInitialData,
nsTArray<LookAndFeelInt>&& aLookAndFeelIntCache,
nsTArray<SystemFontListEntry>&& aFontList,
const FileDescriptor& aSharedDataMapFile,
const uint32_t& aSharedDataMapSize) override;
nsTArray<SystemFontListEntry>&& aFontList) override;
virtual mozilla::ipc::IPCResult
RecvProvideAnonymousTemporaryFile(const uint64_t& aID, const FileDescOrError& aFD) override;
@ -830,8 +815,6 @@ private:
nsCOMPtr<nsIDomainPolicy> mPolicy;
nsCOMPtr<nsITimer> mForceKillTimer;
RefPtr<ipc::SharedMap> mSharedData;
#ifdef MOZ_GECKO_PROFILER
RefPtr<ChildProfilerController> mProfilerController;
#endif

Просмотреть файл

@ -61,7 +61,6 @@
#include "mozilla/dom/quota/QuotaManagerService.h"
#include "mozilla/dom/ServiceWorkerUtils.h"
#include "mozilla/dom/URLClassifierParent.h"
#include "mozilla/dom/ipc/SharedMap.h"
#include "mozilla/embedding/printingui/PrintingParent.h"
#include "mozilla/extensions/StreamFilterParent.h"
#include "mozilla/gfx/gfxVars.h"
@ -2324,12 +2323,8 @@ ContentParent::InitInternal(ProcessPriority aInitialPriority)
ScreenManager& screenManager = ScreenManager::GetSingleton();
screenManager.CopyScreensToRemote(this);
ipc::WritableSharedMap* sharedData = nsFrameMessageManager::sParentProcessManager->SharedData();
sharedData->Flush();
Unused << SendSetXPCOMProcessAttributes(xpcomInit, initialData, lnfCache,
fontList, sharedData->CloneMapFile(),
sharedData->MapSize());
fontList);
nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService();
nsChromeRegistryChrome* chromeRegistry =

Просмотреть файл

@ -246,7 +246,6 @@ ContentProcess::Init(int aArgc, char* aArgv[])
void
ContentProcess::CleanUp()
{
mContent.ClearSharedData();
mXREEmbed.Stop();
}

Просмотреть файл

@ -466,10 +466,6 @@ child:
async RegisterStringBundles(StringBundleDescriptor[] stringBundles);
async UpdateSharedData(FileDescriptor mapFile, uint32_t aSize,
IPCBlob[] blobs,
nsCString[] changedKeys);
// nsIPermissionManager messages
async AddPermission(Permission permission);
async RemoveAllPermissions();
@ -517,9 +513,7 @@ child:
StructuredCloneData initialData,
LookAndFeelInt[] lookAndFeelIntCache,
/* used on MacOSX and Linux only: */
SystemFontListEntry[] systemFontList,
FileDescriptor sharedDataMapFile,
uint32_t sharedDataMapSize);
SystemFontListEntry[] systemFontList);
// Notify child that last-pb-context-exited notification was observed
async LastPrivateDocShellDestroyed();

Просмотреть файл

@ -1,519 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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 "SharedMap.h"
#include "SharedMapChangeEvent.h"
#include "MemMapSnapshot.h"
#include "ScriptPreloader-inl.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/IPCBlobUtils.h"
#include "mozilla/dom/ProcessGlobal.h"
#include "mozilla/dom/ScriptSettings.h"
using namespace mozilla::loader;
namespace mozilla {
using namespace ipc;
namespace dom {
namespace ipc {
// Align to size of uintptr_t here, to be safe. It's probably not strictly
// necessary, though.
constexpr size_t kStructuredCloneAlign = sizeof(uintptr_t);
static inline void
AlignTo(size_t* aOffset, size_t aAlign)
{
if (auto mod = *aOffset % aAlign) {
*aOffset += aAlign - mod;
}
}
SharedMap::SharedMap()
: DOMEventTargetHelper()
{}
SharedMap::SharedMap(nsIGlobalObject* aGlobal, const FileDescriptor& aMapFile,
size_t aMapSize)
: DOMEventTargetHelper(aGlobal)
{
mMapFile.reset(new FileDescriptor(aMapFile));
mMapSize = aMapSize;
}
bool
SharedMap::Has(const nsACString& aName)
{
return mEntries.Contains(aName);
}
void
SharedMap::Get(JSContext* aCx,
const nsACString& aName,
JS::MutableHandleValue aRetVal,
ErrorResult& aRv)
{
auto res = MaybeRebuild();
if (res.isErr()) {
aRv.Throw(res.unwrapErr());
return;
}
Entry* entry = mEntries.Get(aName);
if (!entry) {
aRetVal.setNull();
return;
}
entry->Read(aCx, aRetVal, aRv);
}
void
SharedMap::Entry::Read(JSContext* aCx,
JS::MutableHandleValue aRetVal,
ErrorResult& aRv)
{
if (mData.is<StructuredCloneData>()) {
// We have a temporary buffer for a key that was changed after the last
// snapshot. Just decode it directly.
auto& holder = mData.as<StructuredCloneData>();
holder.Read(aCx, aRetVal, aRv);
return;
}
// We have a pointer to a shared memory region containing our structured
// clone data. Create a temporary buffer to decode that data, and then
// discard it so that we don't keep a separate process-local copy around any
// longer than necessary.
StructuredCloneData holder;
if (!holder.CopyExternalData(Data(), Size())) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
if (mBlobCount) {
holder.BlobImpls().AppendElements(Blobs());
}
holder.Read(aCx, aRetVal, aRv);
}
FileDescriptor
SharedMap::CloneMapFile()
{
if (mMap.initialized()) {
return mMap.cloneHandle();
}
return *mMapFile;
}
void
SharedMap::Update(const FileDescriptor& aMapFile, size_t aMapSize,
nsTArray<RefPtr<BlobImpl>>&& aBlobs,
nsTArray<nsCString>&& aChangedKeys)
{
MOZ_DIAGNOSTIC_ASSERT(!mWritable);
mMap.reset();
if (mMapFile) {
*mMapFile = aMapFile;
} else {
mMapFile.reset(new FileDescriptor(aMapFile));
}
mMapSize = aMapSize;
mEntries.Clear();
mEntryArray.reset();
mBlobImpls = std::move(aBlobs);
AutoEntryScript aes(GetParentObject(), "SharedMap change event");
JSContext* cx = aes.cx();
RootedDictionary<MozSharedMapChangeEventInit> init(cx);
if (!init.mChangedKeys.SetCapacity(aChangedKeys.Length(), fallible)) {
NS_WARNING("Failed to dispatch SharedMap change event");
return;
}
for (auto& key : aChangedKeys) {
Unused << init.mChangedKeys.AppendElement(NS_ConvertUTF8toUTF16(key),
fallible);
}
RefPtr<SharedMapChangeEvent> event =
SharedMapChangeEvent::Constructor(this, NS_LITERAL_STRING("change"), init);
event->SetTrusted(true);
DispatchEvent(*event);
}
const nsTArray<SharedMap::Entry*>&
SharedMap::EntryArray() const
{
if (mEntryArray.isNothing()) {
MaybeRebuild();
mEntryArray.emplace(mEntries.Count());
auto& array = mEntryArray.ref();
for (auto& entry : IterHash(mEntries)) {
array.AppendElement(entry);
}
}
return mEntryArray.ref();
}
const nsString
SharedMap::GetKeyAtIndex(uint32_t aIndex) const
{
return NS_ConvertUTF8toUTF16(EntryArray()[aIndex]->Name());
}
JS::Value
SharedMap::GetValueAtIndex(uint32_t aIndex) const
{
JSObject* wrapper = GetWrapper();
MOZ_ASSERT(wrapper,
"Should never see GetValueAtIndex on a SharedMap without a live "
"wrapper");
if (!wrapper) {
return JS::NullValue();
}
AutoJSContext cx;
JSAutoRealm ar(cx, wrapper);
JS::RootedValue val(cx);
EntryArray()[aIndex]->Read(cx, &val, IgnoreErrors());
return val;
}
void
SharedMap::Entry::TakeData(StructuredCloneData&& aHolder)
{
mData = AsVariant(std::move(aHolder));
mSize = Holder().Data().Size();
mBlobCount = Holder().BlobImpls().Length();
}
void
SharedMap::Entry::ExtractData(char* aDestPtr, uint32_t aNewOffset, uint16_t aNewBlobOffset)
{
if (mData.is<StructuredCloneData>()) {
char* ptr = aDestPtr;
Holder().Data().ForEachDataChunk([&](const char* aData, size_t aSize) {
memcpy(ptr, aData, aSize);
ptr += aSize;
return true;
});
MOZ_ASSERT(uint32_t(ptr - aDestPtr) == mSize);
} else {
memcpy(aDestPtr, Data(), mSize);
}
mData = AsVariant(aNewOffset);
mBlobOffset = aNewBlobOffset;
}
Result<Ok, nsresult>
SharedMap::MaybeRebuild()
{
if (!mMapFile) {
return Ok();
}
// This function maps a shared memory region created by Serialize() and reads
// its header block to build a new mEntries hashtable of its contents.
//
// The entries created by this function contain a pointer to this SharedMap
// instance, and the offsets and sizes of their structured clone data within
// its shared memory region. When needed, that structured clone data is
// retrieved directly as indexes into the SharedMap's shared memory region.
MOZ_TRY(mMap.initWithHandle(*mMapFile, mMapSize));
mMapFile.reset();
// We should be able to pass this range as an initializer list or an immediate
// param, but gcc currently chokes on that if optimization is enabled, and
// initializes everything to 0.
Range<uint8_t> range(&mMap.get<uint8_t>()[0], mMap.size());
InputBuffer buffer(range);
uint32_t count;
buffer.codeUint32(count);
for (uint32_t i = 0; i < count; i++) {
auto entry = MakeUnique<Entry>(*this);
entry->Code(buffer);
// This buffer was created at runtime, during this session, so any errors
// indicate memory corruption, and are fatal.
MOZ_RELEASE_ASSERT(!buffer.error());
// Note: Order of evaluation of function arguments is not guaranteed, so we
// can't use entry.release() in place of entry.get() without entry->Name()
// sometimes resulting in a null dereference.
mEntries.Put(entry->Name(), entry.get());
Unused << entry.release();
}
return Ok();
}
void
SharedMap::MaybeRebuild() const
{
Unused << const_cast<SharedMap*>(this)->MaybeRebuild();
}
WritableSharedMap::WritableSharedMap()
: SharedMap()
{
mWritable = true;
// Serialize the initial empty contents of the map immediately so that we
// always have a file descriptor to send to callers of CloneMapFile().
Unused << Serialize();
MOZ_RELEASE_ASSERT(mMap.initialized());
}
SharedMap*
WritableSharedMap::GetReadOnly()
{
if (!mReadOnly) {
mReadOnly = new SharedMap(ProcessGlobal::Get(), CloneMapFile(),
MapSize());
}
return mReadOnly;
}
Result<Ok, nsresult>
WritableSharedMap::Serialize()
{
// Serializes a new snapshot of the map, initializes a new read-only shared
// memory region with its contents, and updates all entries to point to that
// new snapshot.
//
// The layout of the snapshot is as follows:
//
// - A header containing a uint32 count field containing the number of
// entries in the map, followed by that number of serialized entry headers,
// as produced by Entry::Code.
//
// - A data block containing structured clone data for each of the entries'
// values. This data is referenced by absolute byte offsets from the start
// of the shared memory region, encoded in each of the entry header values.
// Each entry's data is aligned to kStructuredCloneAlign, and therefore may
// have alignment padding before it.
//
// This serialization format is decoded by the MaybeRebuild() method of
// read-only SharedMap() instances, and used to populate their mEntries
// hashtables.
//
// Writable instances never read the header blocks, but instead directly
// update their Entry instances to point to the appropriate offsets in the
// shared memory region created by this function.
uint32_t count = mEntries.Count();
size_t dataSize = 0;
size_t headerSize = sizeof(count);
size_t blobCount = 0;
for (auto& entry : IterHash(mEntries)) {
headerSize += entry->HeaderSize();
blobCount += entry->BlobCount();
dataSize += entry->Size();
AlignTo(&dataSize, kStructuredCloneAlign);
}
size_t offset = headerSize;
AlignTo(&offset, kStructuredCloneAlign);
OutputBuffer header;
header.codeUint32(count);
MemMapSnapshot mem;
MOZ_TRY(mem.Init(offset + dataSize));
auto ptr = mem.Get<char>();
// We need to build the new array of blobs before we overwrite the existing
// one, since previously-serialized entries will store their blob references
// as indexes into our blobs array.
nsTArray<RefPtr<BlobImpl>> blobImpls(blobCount);
for (auto& entry : IterHash(mEntries)) {
AlignTo(&offset, kStructuredCloneAlign);
entry->ExtractData(&ptr[offset], offset, blobImpls.Length());
entry->Code(header);
offset += entry->Size();
if (entry->BlobCount()) {
mBlobImpls.AppendElements(entry->Blobs());
}
}
mBlobImpls = std::move(blobImpls);
// FIXME: We should create a separate OutputBuffer class which can encode to
// a static memory region rather than dynamically allocating and then
// copying.
MOZ_ASSERT(header.cursor() == headerSize);
memcpy(ptr.get(), header.Get(), header.cursor());
// We've already updated offsets at this point. We need this to succeed.
mMap.reset();
MOZ_RELEASE_ASSERT(mem.Finalize(mMap).isOk());
return Ok();
}
void
WritableSharedMap::BroadcastChanges()
{
if (mChangedKeys.IsEmpty()) {
return;
}
if (!Serialize().isOk()) {
return;
}
nsTArray<ContentParent*> parents;
ContentParent::GetAll(parents);
for (auto& parent : parents) {
nsTArray<IPCBlob> blobs(mBlobImpls.Length());
for (auto& blobImpl : mBlobImpls) {
nsresult rv = IPCBlobUtils::Serialize(blobImpl, parent,
*blobs.AppendElement());
if (NS_WARN_IF(NS_FAILED(rv))) {
continue;
}
}
Unused << parent->SendUpdateSharedData(CloneMapFile(), mMap.size(),
blobs, mChangedKeys);
}
if (mReadOnly) {
nsTArray<RefPtr<BlobImpl>> blobImpls(mBlobImpls);
mReadOnly->Update(CloneMapFile(), mMap.size(),
std::move(blobImpls),
std::move(mChangedKeys));
}
mChangedKeys.Clear();
}
void
WritableSharedMap::Delete(const nsACString& aName)
{
if (mEntries.Remove(aName)) {
KeyChanged(aName);
}
}
void
WritableSharedMap::Set(JSContext* aCx,
const nsACString& aName,
JS::HandleValue aValue,
ErrorResult& aRv)
{
StructuredCloneData holder;
holder.Write(aCx, aValue, aRv);
if (aRv.Failed()) {
return;
}
if (!holder.InputStreams().IsEmpty()) {
aRv.Throw(NS_ERROR_INVALID_ARG);
return;
}
Entry* entry = mEntries.LookupOrAdd(aName, *this, aName);
entry->TakeData(std::move(holder));
KeyChanged(aName);
}
void
WritableSharedMap::Flush()
{
BroadcastChanges();
}
void
WritableSharedMap::IdleFlush()
{
mPendingFlush = false;
Flush();
}
nsresult
WritableSharedMap::KeyChanged(const nsACString& aName)
{
if (!mChangedKeys.ContainsSorted(aName)) {
mChangedKeys.InsertElementSorted(aName);
}
mEntryArray.reset();
if (!mPendingFlush) {
MOZ_TRY(NS_IdleDispatchToCurrentThread(
NewRunnableMethod("WritableSharedMap::IdleFlush",
this,
&WritableSharedMap::IdleFlush)));
mPendingFlush = true;
}
return NS_OK;
}
JSObject*
SharedMap::WrapObject(JSContext* aCx, JS::HandleObject aGivenProto)
{
return MozSharedMap_Binding::Wrap(aCx, this, aGivenProto);
}
JSObject*
WritableSharedMap::WrapObject(JSContext* aCx, JS::HandleObject aGivenProto)
{
return MozWritableSharedMap_Binding::Wrap(aCx, this, aGivenProto);
}
/* static */ already_AddRefed<SharedMapChangeEvent>
SharedMapChangeEvent::Constructor(EventTarget* aEventTarget,
const nsAString& aType,
const MozSharedMapChangeEventInit& aInit)
{
RefPtr<SharedMapChangeEvent> event = new SharedMapChangeEvent(aEventTarget);
bool trusted = event->Init(aEventTarget);
event->InitEvent(aType, aInit.mBubbles, aInit.mCancelable);
event->SetTrusted(trusted);
event->SetComposed(aInit.mComposed);
event->mChangedKeys = aInit.mChangedKeys;
return event.forget();
}
} // ipc
} // dom
} // mozilla

Просмотреть файл

@ -1,393 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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 dom_ipc_SharedMap_h
#define dom_ipc_SharedMap_h
#include "mozilla/dom/MozSharedMapBinding.h"
#include "mozilla/AutoMemMap.h"
#include "mozilla/dom/ipc/StructuredCloneData.h"
#include "mozilla/DOMEventTargetHelper.h"
#include "mozilla/Maybe.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/Variant.h"
#include "nsClassHashtable.h"
#include "nsTArray.h"
class nsIGlobalObject;
namespace mozilla {
namespace dom {
namespace ipc {
/**
* Together, the SharedMap and WritableSharedMap classes allow sharing a
* dynamically-updated, shared-memory key-value store across processes.
*
* The maps may only ever be updated in the parent process, via
* WritableSharedMap instances. When that map changes, its entire contents are
* serialized into a contiguous shared memory buffer, and broadcast to all child
* processes, which in turn update their entire map contents wholesale.
*
* Keys are arbitrary UTF-8 strings (currently exposed to JavaScript as UTF-16),
* and values are structured clone buffers. Values are eagerly encoded whenever
* they are updated, and lazily decoded each time they're read.
*
* Updates are batched. Rather than each key change triggering an immediate
* update, combined updates are broadcast after a delay. Changes are flushed
* immediately any time a new process is created. Additionally, any time a key
* is changed, a flush task is scheduled for the next time the event loop
* becomes idle. Changes can be flushed immediately by calling the flush()
* method.
*
*
* Whenever a read-only SharedMap is updated, it dispatches a "change" event.
* The event contains a "changedKeys" property with a list of all keys which
* were changed in the last update batch. Change events are never dispatched to
* WritableSharedMap instances.
*/
class SharedMap : public DOMEventTargetHelper
{
using FileDescriptor = mozilla::ipc::FileDescriptor;
public:
SharedMap();
SharedMap(nsIGlobalObject* aGlobal, const FileDescriptor&, size_t);
// Returns true if the map contains the given (UTF-8) key.
bool Has(const nsACString& name);
// If the map contains the given (UTF-8) key, decodes and returns a new copy
// of its value. Otherwise returns null.
void Get(JSContext* cx, const nsACString& name, JS::MutableHandleValue aRetVal,
ErrorResult& aRv);
// Conversion helpers for WebIDL callers
bool Has(const nsAString& aName)
{
return Has(NS_ConvertUTF16toUTF8(aName));
}
void Get(JSContext* aCx, const nsAString& aName, JS::MutableHandleValue aRetVal,
ErrorResult& aRv)
{
return Get(aCx, NS_ConvertUTF16toUTF8(aName), aRetVal, aRv);
}
/**
* WebIDL iterator glue.
*/
uint32_t GetIterableLength() const
{
return EntryArray().Length();
}
/**
* These functions return the key or value, respectively, at the given index.
* The index *must* be less than the value returned by GetIterableLength(), or
* the program will crash.
*/
const nsString GetKeyAtIndex(uint32_t aIndex) const;
// Note: This function should only be called if the instance has a live,
// cached wrapper. If it does not, this function will return null, and assert
// in debug builds.
// The returned value will always be in the same Realm as that wrapper.
JS::Value GetValueAtIndex(uint32_t aIndex) const;
/**
* Returns a copy of the read-only file descriptor which backs the shared
* memory region for this map. The file descriptor may be passed between
* processes, and used to update corresponding instances in child processes.
*/
FileDescriptor CloneMapFile();
/**
* Returns the size of the memory mapped region that backs this map. Must be
* passed to the SharedMap() constructor or Update() method along with the
* descriptor returned by CloneMapFile() in order to initialize or update a
* child SharedMap.
*/
size_t MapSize() const { return mMap.size(); }
/**
* Updates this instance to reflect the contents of the shared memory region
* in the given map file, and broadcasts a change event for the given set of
* changed (UTF-8-encoded) keys.
*/
void Update(const FileDescriptor& aMapFile, size_t aMapSize,
nsTArray<RefPtr<BlobImpl>>&& aBlobs,
nsTArray<nsCString>&& aChangedKeys);
JSObject* WrapObject(JSContext* aCx, JS::HandleObject aGivenProto) override;
protected:
~SharedMap() override = default;
class Entry
{
public:
Entry(Entry&&) = delete;
explicit Entry(SharedMap& aMap, const nsACString& aName = EmptyCString())
: mMap(aMap)
, mName(aName)
, mData(AsVariant(uint32_t(0)))
{
}
~Entry() = default;
/**
* Encodes or decodes this entry into or from the given OutputBuffer or
* InputBuffer.
*/
template<typename Buffer>
void Code(Buffer& buffer)
{
DebugOnly<size_t> startOffset = buffer.cursor();
buffer.codeString(mName);
buffer.codeUint32(DataOffset());
buffer.codeUint32(mSize);
buffer.codeUint16(mBlobOffset);
buffer.codeUint16(mBlobCount);
MOZ_ASSERT(buffer.cursor() == startOffset + HeaderSize());
}
/**
* Returns the size that this entry will take up in the map header. This
* must be equal to the number of bytes encoded by Code().
*/
size_t HeaderSize() const
{
return (sizeof(uint16_t) + mName.Length() +
sizeof(DataOffset()) +
sizeof(mSize) +
sizeof(mBlobOffset) +
sizeof(mBlobCount));
}
/**
* Updates the value of this entry to the given structured clone data, of
* which it takes ownership. The passed StructuredCloneData object must not
* be used after this call.
*/
void TakeData(StructuredCloneData&&);
/**
* This is called while building a new snapshot of the SharedMap. aDestPtr
* must point to a buffer within the new snapshot with Size() bytes reserved
* for it, and `aNewOffset` must be the offset of that buffer from the start
* of the snapshot's memory region.
*
* This function copies the raw structured clone data for the entry's value
* to the new buffer, and updates its internal state for use with the new
* data. Its offset is updated to aNewOffset, and any StructuredCloneData
* object it holds is destroyed.
*
* After this call, the entry is only valid in reference to the new
* snapshot, and must not be accessed again until the SharedMap mMap has been
* updated to point to it.
*/
void ExtractData(char* aDestPtr, uint32_t aNewOffset, uint16_t aNewBlobOffset);
// Returns the UTF-8-encoded name of the entry, which is used as its key in
// the map.
const nsCString& Name() const { return mName; }
// Decodes the entry's value into the current Realm of the given JS context
// and puts the result in aRetVal on success.
void Read(JSContext* aCx, JS::MutableHandleValue aRetVal,
ErrorResult& aRv);
// Returns the byte size of the entry's raw structured clone data.
uint32_t Size() const { return mSize; }
private:
// Returns a pointer to the entry value's structured clone data within the
// SharedMap's mapped memory region. This is *only* valid shen mData
// contains a uint32_t.
const char* Data() const
{
return mMap.Data() + DataOffset();
}
// Returns the offset of the entry value's structured clone data within the
// SharedMap's mapped memory region. This is *only* valid shen mData
// contains a uint32_t.
uint32_t& DataOffset()
{
return mData.as<uint32_t>();
}
const uint32_t& DataOffset() const
{
return mData.as<uint32_t>();
}
public:
uint16_t BlobOffset() const { return mBlobOffset; }
uint16_t BlobCount() const { return mBlobCount; }
Span<const RefPtr<BlobImpl>> Blobs()
{
if (mData.is<StructuredCloneData>()) {
return mData.as<StructuredCloneData>().BlobImpls();
}
return {&mMap.mBlobImpls[mBlobOffset], BlobCount()};
}
private:
// Returns the temporary StructuredCloneData object containing the entry's
// value. This is *only* value when mData contains a StructuredCloneDAta
// object.
const StructuredCloneData& Holder() const
{
return mData.as<StructuredCloneData>();
}
SharedMap& mMap;
// The entry's (UTF-8 encoded) name, which serves as its key in the map.
nsCString mName;
/**
* This member provides a reference to the entry's structured clone data.
* Its type varies depending on the state of the entry:
*
* - For entries which have been snapshotted into a shared memory region,
* this is a uint32_t offset into the parent SharedMap's Data() buffer.
*
* - For entries which have been changed in a WritableSharedMap instance,
* but not serialized to a shared memory snapshot yet, this is a
* StructuredCloneData instance, containing a process-local copy of the
* data. This will be discarded the next time the map is serialized, and
* replaced with a buffer offset, as described above.
*/
Variant<uint32_t, StructuredCloneData> mData;
// The size, in bytes, of the entry's structured clone data.
uint32_t mSize = 0;
uint16_t mBlobOffset = 0;
uint16_t mBlobCount = 0;
};
const nsTArray<Entry*>& EntryArray() const;
nsTArray<RefPtr<BlobImpl>> mBlobImpls;
// Rebuilds the entry hashtable mEntries from the values serialized in the
// current snapshot, if necessary. The hashtable is rebuilt lazily after
// construction and after every Update() call, so this function must be called
// before any attempt to access mEntries.
Result<Ok, nsresult> MaybeRebuild();
void MaybeRebuild() const;
// Note: This header is included by WebIDL binding headers, and therefore
// can't include "windows.h". Since FileDescriptor.h does include "windows.h"
// on Windows, we can only forward declare FileDescriptor, and can't include
// it as an inline member.
UniquePtr<FileDescriptor> mMapFile;
// The size of the memory-mapped region backed by mMapFile, in bytes.
size_t mMapSize = 0;
mutable nsClassHashtable<nsCStringHashKey, Entry> mEntries;
mutable Maybe<nsTArray<Entry*>> mEntryArray;
// Manages the memory mapping of the current snapshot. This is initialized
// lazily after each SharedMap construction or updated, based on the values in
// mMapFile and mMapSize.
loader::AutoMemMap mMap;
bool mWritable = false;
// Returns a pointer to the beginning of the memory mapped snapshot. Entry
// offsets are relative to this pointer, and Entry objects access their
// structured clone data by indexing this pointer.
char* Data() { return mMap.get<char>().get(); }
};
class WritableSharedMap final : public SharedMap
{
public:
WritableSharedMap();
// Sets the value of the given (UTF-8 encoded) key to a structured clone
// snapshot of the given value.
void Set(JSContext* cx, const nsACString& name, JS::HandleValue value, ErrorResult& aRv);
// Deletes the given (UTF-8 encoded) key from the map.
void Delete(const nsACString& name);
// Conversion helpers for WebIDL callers
void Set(JSContext* aCx, const nsAString& aName, JS::HandleValue aValue, ErrorResult& aRv)
{
return Set(aCx, NS_ConvertUTF16toUTF8(aName), aValue, aRv);
}
void Delete(const nsAString& aName)
{
return Delete(NS_ConvertUTF16toUTF8(aName));
}
// Flushes any queued changes to a new snapshot, and broadcasts it to all
// child SharedMap instances.
void Flush();
/**
* Returns the read-only SharedMap instance corresponding to this
* WritableSharedMap for use in the parent process.
*/
SharedMap* GetReadOnly();
JSObject* WrapObject(JSContext* aCx, JS::HandleObject aGivenProto) override;
protected:
~WritableSharedMap() override = default;
private:
// The set of (UTF-8 encoded) keys which have changed, or been deleted, since
// the last snapshot.
nsTArray<nsCString> mChangedKeys;
RefPtr<SharedMap> mReadOnly;
bool mPendingFlush = false;
// Creates a new snapshot of the map, and updates all Entry instance to
// reference its data.
Result<Ok, nsresult> Serialize();
void IdleFlush();
// If there have been any changes since the last snapshot, creates a new
// serialization and broadcasts it to all child SharedMap instances.
void BroadcastChanges();
// Marks the given (UTF-8 encoded) key as having changed. This adds it to
// mChangedKeys, if not already present, and schedules a flush for the next
// time the event loop is idle.
nsresult KeyChanged(const nsACString& aName);
};
} // ipc
} // dom
} // mozilla
#endif // dom_ipc_SharedMap_h

Просмотреть файл

@ -1,54 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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 dom_ipc_SharedMapChangeEvent_h
#define dom_ipc_SharedMapChangeEvent_h
#include "mozilla/dom/MozSharedMapBinding.h"
#include "mozilla/dom/Event.h"
#include "nsTArray.h"
namespace mozilla {
namespace dom {
namespace ipc {
class SharedMapChangeEvent final : public Event
{
public:
NS_INLINE_DECL_REFCOUNTING_INHERITED(SharedMapChangeEvent, Event)
JSObject* WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
{
return MozSharedMapChangeEvent_Binding::Wrap(aCx, this, aGivenProto);
}
static already_AddRefed<SharedMapChangeEvent>
Constructor(EventTarget* aEventTarget,
const nsAString& aType,
const MozSharedMapChangeEventInit& aInit);
void GetChangedKeys(nsTArray<nsString>& aChangedKeys) const
{
aChangedKeys.AppendElements(mChangedKeys);
}
protected:
~SharedMapChangeEvent() override = default;
private:
explicit SharedMapChangeEvent(EventTarget* aEventTarget)
: Event(aEventTarget, nullptr, nullptr)
{}
nsTArray<nsString> mChangedKeys;
};
} // ipc
} // dom
} // mozilla
#endif // dom_ipc_SharedMapChangeEvent_h

Просмотреть файл

@ -29,11 +29,6 @@ namespace mozilla {
namespace dom {
namespace ipc {
using mozilla::ipc::AutoIPCStream;
using mozilla::ipc::IPCStream;
using mozilla::ipc::PBackgroundChild;
using mozilla::ipc::PBackgroundParent;
StructuredCloneData::StructuredCloneData()
: StructuredCloneData(StructuredCloneHolder::TransferringSupported)
{}

Просмотреть файл

@ -15,8 +15,6 @@ XPIDL_MODULE = 'dom'
EXPORTS.mozilla.dom.ipc += [
'IdType.h',
'SharedMap.h',
'SharedMapChangeEvent.h',
'SharedStringMap.h',
'StructuredCloneData.h',
]
@ -71,7 +69,6 @@ UNIFIED_SOURCES += [
'PermissionMessageUtils.cpp',
'PreallocatedProcessManager.cpp',
'ProcessPriorityManager.cpp',
'SharedMap.cpp',
'SharedStringMap.cpp',
'StructuredCloneData.cpp',
'TabChild.cpp',
@ -175,7 +172,6 @@ JAR_MANIFESTS += ['jar.mn']
BROWSER_CHROME_MANIFESTS += ['tests/browser.ini']
MOCHITEST_CHROME_MANIFESTS += ['tests/chrome.ini']
MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell.ini']
CXXFLAGS += CONFIG['TK_CFLAGS']

Просмотреть файл

@ -1,160 +0,0 @@
"use strict";
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://testing-common/ExtensionXPCShellUtils.jsm");
ExtensionTestUtils.init(this);
let contentPage;
function getContents(sharedMap = Services.cpmm.sharedData) {
return {
keys: Array.from(sharedMap.keys()),
values: Array.from(sharedMap.values()),
entries: Array.from(sharedMap.entries()),
getValues: Array.from(sharedMap.keys(),
key => sharedMap.get(key)),
};
}
function checkMap(contents, expected) {
expected = Array.from(expected);
equal(contents.keys.length, expected.length,
"Got correct number of keys");
equal(contents.values.length, expected.length,
"Got correct number of values");
equal(contents.entries.length, expected.length,
"Got correct number of entries");
for (let [i, [key, val]] of contents.entries.entries()) {
equal(key, contents.keys[i], `keys()[${i}] matches entries()[${i}]`);
deepEqual(val, contents.values[i], `values()[${i}] matches entries()[${i}]`);
}
expected.sort(([a], [b]) => a.localeCompare(b));
contents.entries.sort(([a], [b]) => a.localeCompare(b));
for (let [i, [key, val]] of contents.entries.entries()) {
equal(key, expected[i][0], `expected[${i}].key matches entries()[${i}].key`);
deepEqual(val, expected[i][1], `expected[${i}].value matches entries()[${i}].value`);
}
}
function checkParentMap(expected) {
info("Checking parent map");
checkMap(getContents(Services.ppmm.sharedData), expected);
}
async function checkContentMaps(expected, parentOnly = false) {
info("Checking in-process content map");
checkMap(getContents(Services.cpmm.sharedData), expected);
if (!parentOnly) {
info("Checking out-of-process content map");
let contents = await contentPage.spawn(undefined, getContents);
checkMap(contents, expected);
}
}
add_task(async function setup() {
contentPage = await ExtensionTestUtils.loadContentPage("about:blank",
{remote: true});
registerCleanupFunction(() => contentPage.close());
});
add_task(async function test_sharedMap() {
let {sharedData} = Services.ppmm;
info("Check that parent and child maps are both initially empty");
checkParentMap([]);
await checkContentMaps([]);
let expected = [
["foo-a", {"foo": "a"}],
["foo-b", {"foo": "b"}],
["bar-c", null],
["bar-d", 42],
];
function setKey(key, val) {
sharedData.set(key, val);
expected = expected.filter(([k]) => k != key);
expected.push([key, val]);
}
function deleteKey(key) {
sharedData.delete(key);
expected = expected.filter(([k]) => k != key);
}
for (let [key, val] of expected) {
sharedData.set(key, val);
}
info("Add some entries, test that they are initially only available in the parent");
checkParentMap(expected);
await checkContentMaps([]);
info("Flush. Check that changes are visible in both parent and children");
sharedData.flush();
checkParentMap(expected);
await checkContentMaps(expected);
info("Add another entry. Check that it is initially only available in the parent");
let oldExpected = Array.from(expected);
setKey("baz-a", {meh: "meh"});
// When we do several checks in a row, we can't check the values in
// the content process, since the async checks may allow the idle
// flush task to run, and update it before we're ready.
checkParentMap(expected);
checkContentMaps(oldExpected, true);
info("Add another entry. Check that both new entries are only available in the parent");
setKey("baz-a", {meh: 12});
checkParentMap(expected);
checkContentMaps(oldExpected, true);
info("Delete an entry. Check that all changes are only visible in the parent");
deleteKey("foo-b");
checkParentMap(expected);
checkContentMaps(oldExpected, true);
info("Flush. Check that all entries are available in both parent and children");
sharedData.flush();
checkParentMap(expected);
await checkContentMaps(expected);
info("Test that entries are automatically flushed on idle:");
info("Add a new entry. Check that it is initially only available in the parent");
// Test the idle flush task.
oldExpected = Array.from(expected);
setKey("thing", "stuff");
checkParentMap(expected);
checkContentMaps(oldExpected, true);
info("Wait for an idle timeout. Check that changes are now visible in all children");
await new Promise(resolve => ChromeUtils.idleDispatch(resolve));
checkParentMap(expected);
await checkContentMaps(expected);
});

Просмотреть файл

@ -1,3 +0,0 @@
[test_sharedMap.js]
skip-if = os == 'android' # Requires OOP

Просмотреть файл

@ -8,7 +8,6 @@
#include "ScriptPreloader-inl.h"
#include "mozilla/Unused.h"
#include "mozilla/ipc/FileDescriptor.h"
#include "nsIFile.h"
#include <private/pprio.h>
@ -143,11 +142,12 @@ AutoMemMap::cloneHandle() const
void
AutoMemMap::reset()
{
if (addr && !persistent_) {
Unused << NS_WARN_IF(PR_MemUnmap(addr, size()) != PR_SUCCESS);
addr = nullptr;
}
if (fileMap) {
if (addr && !persistent_) {
Unused << NS_WARN_IF(PR_MemUnmap(addr, size()) != PR_SUCCESS);
addr = nullptr;
}
Unused << NS_WARN_IF(PR_CloseFileMap(fileMap) != PR_SUCCESS);
fileMap = nullptr;
}

Просмотреть файл

@ -10,6 +10,7 @@
#include "mozilla/MemoryReporting.h"
#include "mozilla/RangedPtr.h"
#include "mozilla/Result.h"
#include "mozilla/ipc/FileDescriptor.h"
#include "nsIMemoryReporter.h"
#include <prio.h>
@ -17,10 +18,6 @@
class nsIFile;
namespace mozilla {
namespace ipc {
class FileDescriptor;
}
namespace loader {
using mozilla::ipc::FileDescriptor;

Просмотреть файл

@ -108,8 +108,6 @@ XPCOMUtils.defineLazyGetter(this, "console", ExtensionCommon.getConsole);
XPCOMUtils.defineLazyGetter(this, "LocaleData", () => ExtensionCommon.LocaleData);
const {sharedData} = Services.ppmm;
// The userContextID reserved for the extension storage (its purpose is ensuring that the IndexedDB
// storage used by the browser.storage.local API is not directly accessible from the extension code).
XPCOMUtils.defineLazyGetter(this, "WEBEXT_STORAGE_USER_CONTEXT_ID", () => {
@ -1247,8 +1245,6 @@ class LangpackBootstrapScope {
}
}
let activeExtensionIDs = new Set();
/**
* This class is the main representation of an active WebExtension
* in the main process.
@ -1258,8 +1254,6 @@ class Extension extends ExtensionData {
constructor(addonData, startupReason) {
super(addonData.resourceURI);
this.sharedDataKeys = new Set();
this.uuid = UUIDMap.get(addonData.id);
this.instanceId = getUniqueId();
@ -1313,8 +1307,6 @@ class Extension extends ExtensionData {
this._optionalOrigins = null;
this.webAccessibleResources = null;
this.registeredContentScripts = new Map();
this.emitter = new EventEmitter();
/* eslint-disable mozilla/balanced-listeners */
@ -1521,15 +1513,6 @@ class Extension extends ExtensionData {
return manifest;
}
get contentSecurityPolicy() {
return this.manifest.content_security_policy;
}
get backgroundScripts() {
return (this.manifest.background &&
this.manifest.background.scripts);
}
// Representation of the extension to send to content
// processes. This should include anything the content process might
// need.
@ -1538,24 +1521,20 @@ class Extension extends ExtensionData {
id: this.id,
uuid: this.uuid,
name: this.name,
contentSecurityPolicy: this.contentSecurityPolicy,
instanceId: this.instanceId,
manifest: this.manifest,
resourceURL: this.resourceURL,
baseURL: this.baseURI.spec,
contentScripts: this.contentScripts,
registeredContentScripts: new Map(),
webAccessibleResources: this.webAccessibleResources.map(res => res.glob),
whiteListedHosts: this.whiteListedHosts.patterns.map(pat => pat.pattern),
permissions: this.permissions,
optionalPermissions: this.manifest.optional_permissions,
};
}
// Extended serialized data which is only needed in the extensions process,
// and is never deserialized in web content processes.
serializeExtended() {
return {
backgroundScripts: this.backgroundScripts,
localeData: this.localeData.serialize(),
childModules: this.modules && this.modules.child,
dependencies: this.dependencies,
permissions: this.permissions,
principal: this.principal,
optionalPermissions: this.manifest.optional_permissions,
schemaURLs: this.schemaURLs,
};
}
@ -1598,30 +1577,6 @@ class Extension extends ExtensionData {
});
}
setSharedData(key, value) {
key = `extension/${this.id}/${key}`;
this.sharedDataKeys.add(key);
sharedData.set(key, value);
}
getSharedData(key, value) {
key = `extension/${this.id}/${key}`;
return sharedData.get(key);
}
initSharedData() {
this.setSharedData("", this.serialize());
this.setSharedData("extendedData", this.serializeExtended());
this.setSharedData("locales", this.localeData.serialize());
this.setSharedData("manifest", this.manifest);
this.updateContentScripts();
}
updateContentScripts() {
this.setSharedData("contentScripts", this.registeredContentScripts);
}
runManifest(manifest) {
let promises = [];
for (let directive in manifest) {
@ -1632,11 +1587,22 @@ class Extension extends ExtensionData {
}
}
activeExtensionIDs.add(this.id);
sharedData.set("extensions/activeIDs", activeExtensionIDs);
let data = Services.ppmm.initialProcessData;
if (!data["Extension:Extensions"]) {
data["Extension:Extensions"] = [];
}
Services.ppmm.sharedData.flush();
return this.broadcast("Extension:Startup", this.id).then(() => {
let serial = this.serialize();
// Map of the programmatically registered content script definitions
// (by string scriptId), used in ext-contentScripts.js to propagate
// the registered content scripts to the child content processes
// (e.g. when a new content process starts after a content process crash).
this.registeredContentScripts = serial.registeredContentScripts;
data["Extension:Extensions"].push(serial);
return this.broadcast("Extension:Startup", serial).then(() => {
return Promise.all(promises);
});
}
@ -1761,8 +1727,6 @@ class Extension extends ExtensionData {
GlobalManager.init(this);
this.initSharedData();
this.policy.active = false;
this.policy = processScript.initExtension(this);
this.policy.extension = this;
@ -1834,12 +1798,8 @@ class Extension extends ExtensionData {
StartupCache.clearAddonData(this.id);
}
activeExtensionIDs.delete(this.id);
sharedData.set("extensions/activeIDs", activeExtensionIDs);
for (let key of this.sharedDataKeys) {
sharedData.delete(key);
}
let data = Services.ppmm.initialProcessData;
data["Extension:Extensions"] = data["Extension:Extensions"].filter(e => e.id !== this.id);
Services.ppmm.removeMessageListener(this.MESSAGE_EMIT_EVENT, this);

Просмотреть файл

@ -59,8 +59,6 @@ const {
withHandlingUserInput,
} = ExtensionCommon;
const {sharedData} = Services.cpmm;
const isContentProcess = Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT;
// Copy an API object from |source| into the scope |dest|.
@ -604,9 +602,9 @@ class BrowserExtensionContent extends EventEmitter {
this.uuid = data.uuid;
this.instanceId = data.instanceId;
if (WebExtensionPolicy.isExtensionProcess) {
Object.assign(this, this.getSharedData("extendedData"));
}
this.childModules = data.childModules;
this.dependencies = data.dependencies;
this.schemaURLs = data.schemaURLs;
this.MESSAGE_EMIT_EVENT = `Extension:EmitEvent:${this.instanceId}`;
Services.cpmm.addMessageListener(this.MESSAGE_EMIT_EVENT, this);
@ -618,6 +616,7 @@ class BrowserExtensionContent extends EventEmitter {
this.webAccessibleResources = data.webAccessibleResources.map(res => new MatchGlob(res));
this.permissions = data.permissions;
this.optionalPermissions = data.optionalPermissions;
this.principal = data.principal;
let restrictSchemes = !this.hasPermission("mozillaAddons");
@ -625,14 +624,11 @@ class BrowserExtensionContent extends EventEmitter {
this.apiManager = this.getAPIManager();
this._manifest = null;
this._localeData = null;
this.localeData = new LocaleData(data.localeData);
this.baseURI = Services.io.newURI(`moz-extension://${this.uuid}/`);
this.baseURL = this.baseURI.spec;
this.principal = Services.scriptSecurityManager.createCodebasePrincipal(
this.baseURI, {});
this.manifest = data.manifest;
this.baseURL = data.baseURL;
this.baseURI = Services.io.newURI(data.baseURL);
// Only used in addon processes.
this.views = new Set();
@ -687,33 +683,13 @@ class BrowserExtensionContent extends EventEmitter {
ExtensionManager.extensions.set(this.id, this);
}
getSharedData(key, value) {
return sharedData.get(`extension/${this.id}/${key}`);
}
get localeData() {
if (!this._localeData) {
this._localeData = new LocaleData(this.getSharedData("locales"));
}
return this._localeData;
}
get manifest() {
if (!this._manifest) {
this._manifest = this.getSharedData("manifest");
}
return this._manifest;
}
getAPIManager() {
let apiManagers = [ExtensionPageChild.apiManager];
if (this.dependencies) {
for (let id of this.dependencies) {
let extension = processScript.getExtensionChild(id);
if (extension) {
apiManagers.push(extension.experimentAPIManager);
}
for (let id of this.dependencies) {
let extension = processScript.getExtensionChild(id);
if (extension) {
apiManagers.push(extension.experimentAPIManager);
}
}

Просмотреть файл

@ -140,7 +140,7 @@ let apiManager = new class extends SchemaAPIManager {
// Load order matters here. The base manifest defines types which are
// extended by other schemas, so needs to be loaded first.
return Schemas.load(BASE_SCHEMA).then(() => {
return Schemas.load(BASE_SCHEMA, AppConstants.DEBUG).then(() => {
let promises = [];
for (let [/* name */, url] of XPCOMUtils.enumerateCategoryEntries(CATEGORY_EXTENSION_SCHEMAS)) {
promises.push(Schemas.load(url));
@ -151,9 +151,7 @@ let apiManager = new class extends SchemaAPIManager {
for (let url of schemaURLs) {
promises.push(Schemas.load(url));
}
return Promise.all(promises).then(() => {
Schemas.updateSharedSchemas();
});
return Promise.all(promises);
});
})();
@ -751,14 +749,19 @@ class DevToolsExtensionPageContextParent extends ExtensionPageContextParent {
ParentAPIManager = {
proxyContexts: new Map(),
parentMessageManagers: new Set(),
init() {
Services.obs.addObserver(this, "message-manager-close");
Services.obs.addObserver(this, "ipc:content-created");
Services.mm.addMessageListener("API:CreateProxyContext", this);
Services.mm.addMessageListener("API:CloseProxyContext", this, true);
Services.mm.addMessageListener("API:Call", this);
Services.mm.addMessageListener("API:AddListener", this);
Services.mm.addMessageListener("API:RemoveListener", this);
this.schemaHook = this.schemaHook.bind(this);
},
attachMessageManager(extension, processMessageManager) {
@ -780,6 +783,23 @@ ParentAPIManager = {
extension.parentMessageManager = null;
}
}
this.parentMessageManagers.delete(mm);
} else if (topic === "ipc:content-created") {
let mm = subject.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIMessageSender);
if (mm.remoteType === E10SUtils.EXTENSION_REMOTE_TYPE) {
this.parentMessageManagers.add(mm);
mm.sendAsyncMessage("Schema:Add", Schemas.schemaJSON);
Schemas.schemaHook = this.schemaHook;
}
}
},
schemaHook(schemas) {
for (let mm of this.parentMessageManagers) {
mm.sendAsyncMessage("Schema:Add", schemas);
}
},

Просмотреть файл

@ -32,9 +32,6 @@ XPCOMUtils.defineLazyGetter(this, "StartupCache", () => ExtensionParent.StartupC
var EXPORTED_SYMBOLS = ["SchemaRoot", "Schemas"];
const KEY_CONTENT_SCHEMAS = "extensions-framework/schemas/content";
const KEY_PRIVILEGED_SCHEMAS = "extensions-framework/schemas/privileged";
const {DEBUG} = AppConstants;
const isParentProcess = Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_DEFAULT;
@ -3060,13 +3057,10 @@ this.Schemas = {
// is useful for sending the JSON across processes.
schemaJSON: new Map(),
// A map of schema JSON which should be available in all content processes.
// A separate map of schema JSON which should be available in all
// content processes.
contentSchemaJSON: new Map(),
// A map of schema JSON which should only be available to extension processes.
privilegedSchemaJSON: new Map(),
_rootSchema: null,
get rootSchema() {
@ -3091,20 +3085,35 @@ this.Schemas = {
this.initialized = true;
if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
let addSchemas = schemas => {
for (let [key, value] of schemas.entries()) {
this.schemaJSON.set(key, value);
}
};
if (WebExtensionPolicy.isExtensionProcess || DEBUG) {
addSchemas(Services.cpmm.sharedData.get(KEY_PRIVILEGED_SCHEMAS));
}
let schemas = Services.cpmm.sharedData.get(KEY_CONTENT_SCHEMAS);
let data = Services.cpmm.initialProcessData;
let schemas = data["Extension:Schemas"];
if (schemas) {
addSchemas(schemas);
this.schemaJSON = schemas;
}
Services.cpmm.addMessageListener("Schema:Add", this);
}
},
receiveMessage(msg) {
let {data} = msg;
switch (msg.name) {
case "Schema:Add":
// If we're given a Map, the ordering of the initial items
// matters, so swap with our current data to make sure its
// entries appear first.
if (typeof data.get === "function") {
// Create a new Map so we're sure it's in the same compartment.
[this.schemaJSON, data] = [new Map(data), this.schemaJSON];
}
for (let [url, schema] of data) {
this.schemaJSON.set(url, schema);
}
if (this._rootSchema) {
throw new Error("Schema loaded after root schema populated");
}
break;
}
},
@ -3124,8 +3133,13 @@ this.Schemas = {
if (content) {
this.contentSchemaJSON.set(url, schema);
} else {
this.privilegedSchemaJSON.set(url, schema);
let data = Services.ppmm.initialProcessData;
data["Extension:Schemas"] = this.contentSchemaJSON;
Services.ppmm.broadcastAsyncMessage("Schema:Add", [[url, schema]]);
} else if (this.schemaHook) {
this.schemaHook([[url, schema]]);
}
if (this._rootSchema) {
@ -3133,13 +3147,6 @@ this.Schemas = {
}
},
updateSharedSchemas() {
let {sharedData} = Services.ppmm;
sharedData.set(KEY_CONTENT_SCHEMAS, this.contentSchemaJSON);
sharedData.set(KEY_PRIVILEGED_SCHEMAS, this.privilegedSchemaJSON);
},
fetch(url) {
return readJSONAndBlobbify(url);
},

Просмотреть файл

@ -30,12 +30,6 @@ const {
getInnerWindowID,
} = ExtensionUtils;
const {sharedData} = Services.cpmm;
function getData(extension, key = "") {
return sharedData.get(`extension/${extension.id}/${key}`);
}
// We need to avoid touching Services.appinfo here in order to prevent
// the wrong version from being cached during xpcshell test startup.
// eslint-disable-next-line mozilla/use-services
@ -307,8 +301,20 @@ ExtensionManager = {
Services.cpmm.addMessageListener("Extension:RegisterContentScript", this);
Services.cpmm.addMessageListener("Extension:UnregisterContentScripts", this);
for (let id of sharedData.get("extensions/activeIDs") || []) {
this.initExtension(getData({id}));
let procData = Services.cpmm.initialProcessData || {};
for (let data of procData["Extension:Extensions"] || []) {
this.initExtension(data);
}
if (isContentProcess) {
// Make sure we handle new schema data until Schemas.jsm is loaded.
if (!procData["Extension:Schemas"]) {
procData["Extension:Schemas"] = new Map();
}
this.schemaJSON = procData["Extension:Schemas"];
Services.cpmm.addMessageListener("Schema:Add", this);
}
},
@ -330,11 +336,6 @@ ExtensionManager = {
webAccessibleResources = extension.webAccessibleResources.map(host => new MatchGlob(host));
}
let {backgroundScripts} = extension;
if (!backgroundScripts && WebExtensionPolicy.isExtensionProcess) {
({backgroundScripts} = getData(extension, "extendedData") || {});
}
policy = new WebExtensionPolicy({
id: extension.id,
mozExtensionHostname: extension.uuid,
@ -345,11 +346,12 @@ ExtensionManager = {
allowedOrigins,
webAccessibleResources,
contentSecurityPolicy: extension.contentSecurityPolicy,
contentSecurityPolicy: extension.manifest.content_security_policy,
localizeCallback,
backgroundScripts,
backgroundScripts: (extension.manifest.background &&
extension.manifest.background.scripts),
contentScripts: extension.contentScripts.map(script => parseScriptOptions(script, restrictSchemes)),
});
@ -361,11 +363,13 @@ ExtensionManager = {
// a content process that crashed and it has been recreated).
const registeredContentScripts = this.registeredContentScripts.get(policy);
for (let [scriptId, options] of getData(extension, "contentScripts") || []) {
const parsedOptions = parseScriptOptions(options, restrictSchemes);
const script = new WebExtensionContentScript(policy, parsedOptions);
policy.registerContentScript(script);
registeredContentScripts.set(scriptId, script);
if (extension.registeredContentScripts) {
for (let [scriptId, options] of extension.registeredContentScripts) {
const parsedOptions = parseScriptOptions(options, restrictSchemes);
const script = new WebExtensionContentScript(policy, parsedOptions);
policy.registerContentScript(script);
registeredContentScripts.set(scriptId, script);
}
}
policy.active = true;
@ -375,9 +379,6 @@ ExtensionManager = {
},
initExtension(data) {
if (typeof data === "string") {
data = getData({id: data});
}
let policy = this.initExtensionPolicy(data);
DocumentManager.initExtension(policy);
@ -414,6 +415,23 @@ ExtensionManager = {
break;
}
case "Schema:Add": {
// If we're given a Map, the ordering of the initial items
// matters, so swap with our current data to make sure its
// entries appear first.
if (typeof data.get === "function") {
[this.schemaJSON, data] = [data, this.schemaJSON];
Services.cpmm.initialProcessData["Extension:Schemas"] =
this.schemaJSON;
}
for (let [url, schema] of data) {
this.schemaJSON.set(url, schema);
}
break;
}
case "Extension:RegisterContentScript": {
let policy = WebExtensionPolicy.getByID(data.id);

Просмотреть файл

@ -131,7 +131,6 @@ this.contentScripts = class extends ExtensionAPI {
for (let scriptId of scriptIds) {
extension.registeredContentScripts.delete(scriptId);
}
extension.updateContentScripts();
extension.broadcast("Extension:UnregisterContentScripts", {
id: extension.id,
@ -163,7 +162,6 @@ this.contentScripts = class extends ExtensionAPI {
});
extension.registeredContentScripts.set(scriptId, scriptOptions);
extension.updateContentScripts();
return scriptId;
},
@ -182,7 +180,6 @@ this.contentScripts = class extends ExtensionAPI {
parentScriptsMap.delete(scriptId);
extension.registeredContentScripts.delete(scriptId);
extension.updateContentScripts();
contentScript.destroy();