From 7b416abaf12a5e2391716613dc83a31d0bbf0ea3 Mon Sep 17 00:00:00 2001 From: Margareta Eliza Balazs Date: Thu, 12 Jul 2018 11:27:45 +0300 Subject: [PATCH] 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) --- dom/base/ProcessGlobal.cpp | 17 - dom/base/ProcessGlobal.h | 6 - dom/base/nsFrameMessageManager.cpp | 14 - dom/base/nsFrameMessageManager.h | 10 - dom/bindings/Bindings.conf | 13 - dom/chrome-webidl/MessageManager.webidl | 4 - dom/chrome-webidl/MozSharedMap.webidl | 54 -- dom/chrome-webidl/moz.build | 1 - dom/ipc/ContentChild.cpp | 34 +- dom/ipc/ContentChild.h | 19 +- dom/ipc/ContentParent.cpp | 7 +- dom/ipc/ContentProcess.cpp | 1 - dom/ipc/PContent.ipdl | 8 +- dom/ipc/SharedMap.cpp | 519 ------------------ dom/ipc/SharedMap.h | 393 ------------- dom/ipc/SharedMapChangeEvent.h | 54 -- dom/ipc/StructuredCloneData.cpp | 5 - dom/ipc/moz.build | 4 - dom/ipc/tests/test_sharedMap.js | 160 ------ dom/ipc/tests/xpcshell.ini | 3 - js/xpconnect/loader/AutoMemMap.cpp | 10 +- js/xpconnect/loader/AutoMemMap.h | 5 +- toolkit/components/extensions/Extension.jsm | 88 +-- .../components/extensions/ExtensionChild.jsm | 48 +- .../components/extensions/ExtensionParent.jsm | 28 +- toolkit/components/extensions/Schemas.jsm | 65 ++- .../extensions/extension-process-script.js | 64 ++- .../extensions/parent/ext-contentScripts.js | 3 - 28 files changed, 147 insertions(+), 1490 deletions(-) delete mode 100644 dom/chrome-webidl/MozSharedMap.webidl delete mode 100644 dom/ipc/SharedMap.cpp delete mode 100644 dom/ipc/SharedMap.h delete mode 100644 dom/ipc/SharedMapChangeEvent.h delete mode 100644 dom/ipc/tests/test_sharedMap.js delete mode 100644 dom/ipc/tests/xpcshell.ini diff --git a/dom/base/ProcessGlobal.cpp b/dom/base/ProcessGlobal.cpp index c9f33abeba07..ae2068a3e40e 100644 --- a/dom/base/ProcessGlobal.cpp +++ b/dom/base/ProcessGlobal.cpp @@ -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 -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() { diff --git a/dom/base/ProcessGlobal.h b/dom/base/ProcessGlobal.h index 9de3a5264b7b..9e47f7725773 100644 --- a/dom/base/ProcessGlobal.h +++ b/dom/base/ProcessGlobal.h @@ -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 SharedData(); - NS_FORWARD_SAFE_NSIMESSAGESENDER(mMessageManager) virtual void LoadScript(const nsAString& aURL); diff --git a/dom/base/nsFrameMessageManager.cpp b/dom/base/nsFrameMessageManager.cpp index 42df460e456f..3dbc40b8eb0a 100644 --- a/dom/base/nsFrameMessageManager.cpp +++ b/dom/base/nsFrameMessageManager.cpp @@ -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 nsFrameMessageManager::GetProcessMessageManager(ErrorResult& aError) { diff --git a/dom/base/nsFrameMessageManager.h b/dom/base/nsFrameMessageManager.h index 74a206b79c40..87e4a2fff2f6 100644 --- a/dom/base/nsFrameMessageManager.h +++ b/dom/base/nsFrameMessageManager.h @@ -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 aInitialProcessData, mozilla::ErrorResult& aError); - mozilla::dom::ipc::WritableSharedMap* SharedData(); - NS_DECL_NSIMESSAGESENDER NS_DECL_NSICONTENTFRAMEMESSAGEMANAGER @@ -349,7 +340,6 @@ protected: nsTArray mPendingScripts; nsTArray mPendingScriptsGlobalStates; JS::Heap mInitialProcessData; - RefPtr mSharedData; void LoadPendingScripts(nsFrameMessageManager* aManager, nsFrameMessageManager* aChildMM); diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index 59dc95076608..af34c3d8e6b8 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -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', diff --git a/dom/chrome-webidl/MessageManager.webidl b/dom/chrome-webidl/MessageManager.webidl index 733462c1382f..1ecc0651e247 100644 --- a/dom/chrome-webidl/MessageManager.webidl +++ b/dom/chrome-webidl/MessageManager.webidl @@ -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 diff --git a/dom/chrome-webidl/MozSharedMap.webidl b/dom/chrome-webidl/MozSharedMap.webidl deleted file mode 100644 index 178dc82a948f..000000000000 --- a/dom/chrome-webidl/MozSharedMap.webidl +++ /dev/null @@ -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 changedKeys; -}; - -dictionary MozSharedMapChangeEventInit : EventInit { - required sequence changedKeys; -}; - -[ChromeOnly] -interface MozSharedMap : EventTarget { - boolean has(DOMString name); - - [Throws] - StructuredClonable get(DOMString name); - - iterable; -}; - -[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(); -}; diff --git a/dom/chrome-webidl/moz.build b/dom/chrome-webidl/moz.build index 96cd020a97d0..b7b90fd8cc6b 100644 --- a/dom/chrome-webidl/moz.build +++ b/dom/chrome-webidl/moz.build @@ -37,7 +37,6 @@ WEBIDL_FILES = [ 'MatchGlob.webidl', 'MatchPattern.webidl', 'MessageManager.webidl', - 'MozSharedMap.webidl', 'MozStorageAsyncStatementParams.webidl', 'MozStorageStatementParams.webidl', 'MozStorageStatementRow.webidl', diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 9ce9b87484cd..6ed5d4f63f73 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -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&& aLookAndFeelIntCache, - nsTArray&& aFontList, - const FileDescriptor& aSharedDataMapFile, - const uint32_t& aSharedDataMapSize) + nsTArray&& 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&& aBlobs, - nsTArray&& aChangedKeys) -{ - if (mSharedData) { - nsTArray> 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) { diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h index 3456110afeb7..17ca8cc892af 100644 --- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -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&& stringBundles) override; - mozilla::ipc::IPCResult RecvUpdateSharedData(const FileDescriptor& aMapFile, - const uint32_t& aMapSize, - nsTArray&& aBlobs, - nsTArray&& 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&& aLookAndFeelIntCache, - nsTArray&& aFontList, - const FileDescriptor& aSharedDataMapFile, - const uint32_t& aSharedDataMapSize) override; + nsTArray&& aFontList) override; virtual mozilla::ipc::IPCResult RecvProvideAnonymousTemporaryFile(const uint64_t& aID, const FileDescOrError& aFD) override; @@ -830,8 +815,6 @@ private: nsCOMPtr mPolicy; nsCOMPtr mForceKillTimer; - RefPtr mSharedData; - #ifdef MOZ_GECKO_PROFILER RefPtr mProfilerController; #endif diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index b30f778209ae..ab1fbbec6e2c 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -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 registrySvc = nsChromeRegistry::GetService(); nsChromeRegistryChrome* chromeRegistry = diff --git a/dom/ipc/ContentProcess.cpp b/dom/ipc/ContentProcess.cpp index 71728afe634d..a2750a9c77d8 100644 --- a/dom/ipc/ContentProcess.cpp +++ b/dom/ipc/ContentProcess.cpp @@ -246,7 +246,6 @@ ContentProcess::Init(int aArgc, char* aArgv[]) void ContentProcess::CleanUp() { - mContent.ClearSharedData(); mXREEmbed.Stop(); } diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index e43e5daeecb5..e50c4c1c15ea 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -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(); diff --git a/dom/ipc/SharedMap.cpp b/dom/ipc/SharedMap.cpp deleted file mode 100644 index f644d631e2d9..000000000000 --- a/dom/ipc/SharedMap.cpp +++ /dev/null @@ -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()) { - // We have a temporary buffer for a key that was changed after the last - // snapshot. Just decode it directly. - auto& holder = mData.as(); - 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>&& aBlobs, - nsTArray&& 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 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 event = - SharedMapChangeEvent::Constructor(this, NS_LITERAL_STRING("change"), init); - event->SetTrusted(true); - - DispatchEvent(*event); -} - - -const nsTArray& -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()) { - 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 -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 range(&mMap.get()[0], mMap.size()); - InputBuffer buffer(range); - - uint32_t count; - buffer.codeUint32(count); - - for (uint32_t i = 0; i < count; i++) { - auto entry = MakeUnique(*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(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 -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(); - - // 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> 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 parents; - ContentParent::GetAll(parents); - for (auto& parent : parents) { - nsTArray 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> 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::Constructor(EventTarget* aEventTarget, - const nsAString& aType, - const MozSharedMapChangeEventInit& aInit) -{ - RefPtr 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 diff --git a/dom/ipc/SharedMap.h b/dom/ipc/SharedMap.h deleted file mode 100644 index 212204f75b7a..000000000000 --- a/dom/ipc/SharedMap.h +++ /dev/null @@ -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>&& aBlobs, - nsTArray&& 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 - void Code(Buffer& buffer) - { - DebugOnly 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(); - } - const uint32_t& DataOffset() const - { - return mData.as(); - } - - public: - uint16_t BlobOffset() const { return mBlobOffset; } - uint16_t BlobCount() const { return mBlobCount; } - - Span> Blobs() - { - if (mData.is()) { - return mData.as().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(); - } - - 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 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& EntryArray() const; - - nsTArray> 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 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 mMapFile; - // The size of the memory-mapped region backed by mMapFile, in bytes. - size_t mMapSize = 0; - - mutable nsClassHashtable mEntries; - mutable Maybe> 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().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 mChangedKeys; - - RefPtr mReadOnly; - - bool mPendingFlush = false; - - // Creates a new snapshot of the map, and updates all Entry instance to - // reference its data. - Result 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 diff --git a/dom/ipc/SharedMapChangeEvent.h b/dom/ipc/SharedMapChangeEvent.h deleted file mode 100644 index 7f9e164efc88..000000000000 --- a/dom/ipc/SharedMapChangeEvent.h +++ /dev/null @@ -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 aGivenProto) override - { - return MozSharedMapChangeEvent_Binding::Wrap(aCx, this, aGivenProto); - } - - static already_AddRefed - Constructor(EventTarget* aEventTarget, - const nsAString& aType, - const MozSharedMapChangeEventInit& aInit); - - void GetChangedKeys(nsTArray& aChangedKeys) const - { - aChangedKeys.AppendElements(mChangedKeys); - } - -protected: - ~SharedMapChangeEvent() override = default; - -private: - explicit SharedMapChangeEvent(EventTarget* aEventTarget) - : Event(aEventTarget, nullptr, nullptr) - {} - - nsTArray mChangedKeys; -}; - -} // ipc -} // dom -} // mozilla - -#endif // dom_ipc_SharedMapChangeEvent_h diff --git a/dom/ipc/StructuredCloneData.cpp b/dom/ipc/StructuredCloneData.cpp index 7e15fc7d79cb..1b9f10585768 100644 --- a/dom/ipc/StructuredCloneData.cpp +++ b/dom/ipc/StructuredCloneData.cpp @@ -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) {} diff --git a/dom/ipc/moz.build b/dom/ipc/moz.build index 13f64017a1bb..9d3270faa078 100644 --- a/dom/ipc/moz.build +++ b/dom/ipc/moz.build @@ -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'] diff --git a/dom/ipc/tests/test_sharedMap.js b/dom/ipc/tests/test_sharedMap.js deleted file mode 100644 index df33fc15717a..000000000000 --- a/dom/ipc/tests/test_sharedMap.js +++ /dev/null @@ -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); -}); diff --git a/dom/ipc/tests/xpcshell.ini b/dom/ipc/tests/xpcshell.ini deleted file mode 100644 index 8c001845b66f..000000000000 --- a/dom/ipc/tests/xpcshell.ini +++ /dev/null @@ -1,3 +0,0 @@ - -[test_sharedMap.js] -skip-if = os == 'android' # Requires OOP diff --git a/js/xpconnect/loader/AutoMemMap.cpp b/js/xpconnect/loader/AutoMemMap.cpp index c325a15d2cfe..f4d8278ce8de 100644 --- a/js/xpconnect/loader/AutoMemMap.cpp +++ b/js/xpconnect/loader/AutoMemMap.cpp @@ -8,7 +8,6 @@ #include "ScriptPreloader-inl.h" #include "mozilla/Unused.h" -#include "mozilla/ipc/FileDescriptor.h" #include "nsIFile.h" #include @@ -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; } diff --git a/js/xpconnect/loader/AutoMemMap.h b/js/xpconnect/loader/AutoMemMap.h index e1b0bf6d75c4..716e408dabd7 100644 --- a/js/xpconnect/loader/AutoMemMap.h +++ b/js/xpconnect/loader/AutoMemMap.h @@ -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 @@ -17,10 +18,6 @@ class nsIFile; namespace mozilla { -namespace ipc { - class FileDescriptor; -} - namespace loader { using mozilla::ipc::FileDescriptor; diff --git a/toolkit/components/extensions/Extension.jsm b/toolkit/components/extensions/Extension.jsm index 208ebe57dfce..ac147703ac8e 100644 --- a/toolkit/components/extensions/Extension.jsm +++ b/toolkit/components/extensions/Extension.jsm @@ -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); diff --git a/toolkit/components/extensions/ExtensionChild.jsm b/toolkit/components/extensions/ExtensionChild.jsm index 3ac1af8717c1..33de509f96f7 100644 --- a/toolkit/components/extensions/ExtensionChild.jsm +++ b/toolkit/components/extensions/ExtensionChild.jsm @@ -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); } } diff --git a/toolkit/components/extensions/ExtensionParent.jsm b/toolkit/components/extensions/ExtensionParent.jsm index 0131492832e3..1a3647b9e2a4 100644 --- a/toolkit/components/extensions/ExtensionParent.jsm +++ b/toolkit/components/extensions/ExtensionParent.jsm @@ -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); } }, diff --git a/toolkit/components/extensions/Schemas.jsm b/toolkit/components/extensions/Schemas.jsm index db0e090b5a0b..45fdd6607bbe 100644 --- a/toolkit/components/extensions/Schemas.jsm +++ b/toolkit/components/extensions/Schemas.jsm @@ -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); }, diff --git a/toolkit/components/extensions/extension-process-script.js b/toolkit/components/extensions/extension-process-script.js index 73a80047662d..53d1e44e0eeb 100644 --- a/toolkit/components/extensions/extension-process-script.js +++ b/toolkit/components/extensions/extension-process-script.js @@ -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); diff --git a/toolkit/components/extensions/parent/ext-contentScripts.js b/toolkit/components/extensions/parent/ext-contentScripts.js index bca0e9faaf80..717a60f51faa 100644 --- a/toolkit/components/extensions/parent/ext-contentScripts.js +++ b/toolkit/components/extensions/parent/ext-contentScripts.js @@ -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();