This separates the MediaSource storage from the primary BlobImpl storage. The goal here is to separate this simpler process-local storage from the more complex cross-process store. There is very little overlap in how these two types of URLs are used, with a MediaSource URL not being usable anywhere other than in media elements. This makes performing the separation relatively straightforward. The design in this patch attaches the MediaSource storage to the DocGroup, which will limit access to the specific Window Agent. This also makes it simpler to cycle-collect than the previous global-storage based approach. Differential Revision: https://phabricator.services.mozilla.com/D268717
233 lines
7.6 KiB
C++
233 lines
7.6 KiB
C++
/* 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 "mozilla/dom/DocGroup.h"
|
|
|
|
#include "mozilla/AbstractThread.h"
|
|
#include "mozilla/SchedulerGroup.h"
|
|
#include "mozilla/StaticPrefs_dom.h"
|
|
#include "mozilla/ThrottledEventQueue.h"
|
|
#include "mozilla/dom/BlobURLProtocolHandler.h"
|
|
#include "mozilla/dom/BrowsingContext.h"
|
|
#include "mozilla/dom/CustomElementRegistry.h"
|
|
#include "mozilla/dom/DOMTypes.h"
|
|
#include "mozilla/dom/JSExecutionManager.h"
|
|
#include "mozilla/dom/MediaSource.h"
|
|
#include "mozilla/dom/WindowContext.h"
|
|
#include "nsDOMMutationObserver.h"
|
|
#include "nsIDirectTaskDispatcher.h"
|
|
#include "nsIXULRuntime.h"
|
|
#include "nsProxyRelease.h"
|
|
#include "nsThread.h"
|
|
#if defined(XP_WIN)
|
|
# include <processthreadsapi.h> // for GetCurrentProcessId()
|
|
#else
|
|
# include <unistd.h> // for getpid()
|
|
#endif // defined(XP_WIN)
|
|
|
|
namespace mozilla::dom {
|
|
|
|
AutoTArray<RefPtr<DocGroup>, 2>* DocGroup::sPendingDocGroups = nullptr;
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(DocGroup)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DocGroup)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSignalSlotList)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowsingContextGroup)
|
|
|
|
for (const MediaSourceURLEntry& entry : tmp->mMediaSourceURLs.Values()) {
|
|
CycleCollectionNoteChild(cb, entry.mMediaSource.get(),
|
|
"mMediaSourceURLs[].mMediaSource");
|
|
CycleCollectionNoteChild(cb, entry.mOwner.get(),
|
|
"mMediaSourceURLs[].mOwner");
|
|
}
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DocGroup)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSignalSlotList)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowsingContextGroup)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMediaSourceURLs)
|
|
|
|
// If we still have any documents in this array, they were just unlinked, so
|
|
// clear out our weak pointers to them.
|
|
tmp->mDocuments.Clear();
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
/* static */
|
|
already_AddRefed<DocGroup> DocGroup::Create(
|
|
BrowsingContextGroup* aBrowsingContextGroup, const DocGroupKey& aKey) {
|
|
return do_AddRef(new DocGroup(aBrowsingContextGroup, aKey));
|
|
}
|
|
|
|
void DocGroup::AssertMatches(const Document* aDocument) const {
|
|
nsCOMPtr<nsIPrincipal> principal = aDocument->NodePrincipal();
|
|
|
|
// Ensure that this DocGroup is correctly origin keyed / non-origin-keyed.
|
|
Maybe<bool> usesOriginAgentCluster =
|
|
mBrowsingContextGroup->UsesOriginAgentCluster(principal);
|
|
MOZ_RELEASE_ASSERT(
|
|
usesOriginAgentCluster.isSome(),
|
|
"Document principal with unknown OriginAgentCluster behaviour");
|
|
MOZ_RELEASE_ASSERT(*usesOriginAgentCluster == mKey.mOriginKeyed,
|
|
"DocGroup origin keying does not match Principal");
|
|
|
|
// Ensure that the origin is as expected. Note that `GetSiteOrigin` can fail
|
|
// after the TLD service is shut down, and we don't want to assert in that
|
|
// case.
|
|
nsresult rv = NS_ERROR_FAILURE;
|
|
nsAutoCString key;
|
|
if (mKey.mOriginKeyed) {
|
|
rv = principal->GetOrigin(key);
|
|
} else {
|
|
rv = principal->GetSiteOrigin(key);
|
|
}
|
|
if (NS_SUCCEEDED(rv)) {
|
|
MOZ_RELEASE_ASSERT(key == mKey.mKey,
|
|
"DocGroup Key does not match Document");
|
|
}
|
|
}
|
|
|
|
void DocGroup::SetExecutionManager(JSExecutionManager* aManager) {
|
|
mExecutionManager = aManager;
|
|
}
|
|
|
|
mozilla::dom::CustomElementReactionsStack*
|
|
DocGroup::CustomElementReactionsStack() {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
if (!mReactionsStack) {
|
|
mReactionsStack = new mozilla::dom::CustomElementReactionsStack();
|
|
}
|
|
|
|
return mReactionsStack;
|
|
}
|
|
|
|
void DocGroup::AddDocument(Document* aDocument) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(!mDocuments.Contains(aDocument));
|
|
MOZ_ASSERT(mBrowsingContextGroup);
|
|
// If the document is loaded as data it may not have a container, in which
|
|
// case it can be difficult to determine the BrowsingContextGroup it's
|
|
// associated with. XSLT can also add the document to the DocGroup before it
|
|
// gets a container in some cases, in which case this will be asserted
|
|
// elsewhere.
|
|
MOZ_ASSERT_IF(
|
|
aDocument->GetBrowsingContext(),
|
|
aDocument->GetBrowsingContext()->Group() == mBrowsingContextGroup);
|
|
mDocuments.AppendElement(aDocument);
|
|
}
|
|
|
|
void DocGroup::RemoveDocument(Document* aDocument) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(mDocuments.Contains(aDocument));
|
|
mDocuments.RemoveElement(aDocument);
|
|
|
|
if (mDocuments.IsEmpty()) {
|
|
mBrowsingContextGroup = nullptr;
|
|
}
|
|
}
|
|
|
|
DocGroup::DocGroup(BrowsingContextGroup* aBrowsingContextGroup,
|
|
const DocGroupKey& aKey)
|
|
: mKey(aKey),
|
|
mBrowsingContextGroup(aBrowsingContextGroup),
|
|
mAgentClusterId(nsID::GenerateUUID()) {
|
|
// This method does not add itself to
|
|
// mBrowsingContextGroup->mDocGroups as the caller does it for us.
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
mArena = new mozilla::dom::DOMArena(aKey.mKey);
|
|
}
|
|
|
|
DocGroup::~DocGroup() {
|
|
MOZ_RELEASE_ASSERT(NS_IsMainThread());
|
|
MOZ_RELEASE_ASSERT(mDocuments.IsEmpty());
|
|
}
|
|
|
|
void DocGroup::SignalSlotChange(HTMLSlotElement& aSlot) {
|
|
MOZ_ASSERT(!mSignalSlotList.Contains(&aSlot));
|
|
mSignalSlotList.AppendElement(&aSlot);
|
|
|
|
if (!sPendingDocGroups) {
|
|
// Queue a mutation observer compound microtask.
|
|
nsDOMMutationObserver::QueueMutationObserverMicroTask();
|
|
sPendingDocGroups = new AutoTArray<RefPtr<DocGroup>, 2>;
|
|
}
|
|
|
|
sPendingDocGroups->AppendElement(this);
|
|
}
|
|
|
|
nsTArray<RefPtr<HTMLSlotElement>> DocGroup::MoveSignalSlotList() {
|
|
for (const RefPtr<HTMLSlotElement>& slot : mSignalSlotList) {
|
|
slot->RemovedFromSignalSlotList();
|
|
}
|
|
return std::move(mSignalSlotList);
|
|
}
|
|
|
|
bool DocGroup::IsActive() const {
|
|
for (Document* doc : mDocuments) {
|
|
if (doc->IsCurrentActiveDocument()) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
nsresult DocGroup::RegisterMediaSourceURL(nsGlobalWindowInner* aWindow,
|
|
MediaSource* aMediaSource,
|
|
nsACString& aURL) {
|
|
NS_ENSURE_ARG(aWindow);
|
|
NS_ENSURE_ARG(aMediaSource);
|
|
|
|
if (NS_WARN_IF(aWindow->IsDying()) ||
|
|
NS_WARN_IF(aWindow->GetDocGroup() != this)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
nsCOMPtr<nsIPrincipal> principal = aWindow->PrincipalOrNull();
|
|
if (NS_WARN_IF(!principal)) {
|
|
return NS_ERROR_DOM_SECURITY_ERR;
|
|
}
|
|
nsresult rv = BlobURLProtocolHandler::GenerateURIString(principal, aURL);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
MOZ_ASSERT(!mMediaSourceURLs.Contains(aURL), "URL collision?");
|
|
mMediaSourceURLs.InsertOrUpdate(
|
|
aURL,
|
|
MediaSourceURLEntry{.mMediaSource = aMediaSource, .mOwner = aWindow});
|
|
|
|
// Ensure the Window knows about this URL so it can be cleaned up when the
|
|
// Window is destroyed, and can be reported in the memory reporter.
|
|
aWindow->NoteMediaSourceURL(aURL);
|
|
return NS_OK;
|
|
}
|
|
|
|
bool DocGroup::UnregisterMediaSourceURL(const nsACString& aURL,
|
|
bool aNotifyWindow) {
|
|
auto removed = mMediaSourceURLs.Extract(aURL);
|
|
if (aNotifyWindow && removed && removed->mOwner) {
|
|
// Remove this MediaSource from the Window which created the URL, removing
|
|
// it from the memory report and ensuring UnregisterMediaSourceURL won't be
|
|
// called when the window is destroyed.
|
|
removed->mOwner->UnnoteMediaSourceURL(aURL);
|
|
}
|
|
return removed.isSome();
|
|
}
|
|
|
|
already_AddRefed<MediaSource> DocGroup::LookupMediaSourceURL(nsIURI* aURI) {
|
|
MOZ_ASSERT(aURI && aURI->SchemeIs(BLOBURI_SCHEME));
|
|
|
|
nsAutoCString spec;
|
|
if (NS_WARN_IF(NS_FAILED(aURI->GetSpec(spec)))) {
|
|
return nullptr;
|
|
}
|
|
if (auto entry = mMediaSourceURLs.Lookup(spec)) {
|
|
return do_AddRef(entry->mMediaSource);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
} // namespace mozilla::dom
|