Backed out changeset e5c32a502e0f (bug 1915419) Backed out changeset 8cbcbb25fc99 (bug 1915419) Backed out changeset 947379a2f167 (bug 1915419) Backed out changeset 4cfe3a954ade (bug 1915419) Backed out changeset 94fba1a2786b (bug 1915419) Backed out changeset 477b4f7c2760 (bug 1915419) Backed out changeset ed6c8d9d39d4 (bug 1915419) Backed out changeset e534c390101c (bug 1915419) Backed out changeset bc3fe74a8b03 (bug 1915419) Backed out changeset afefdce605f9 (bug 1915419) Backed out changeset e58887b90d39 (bug 1915419) Backed out changeset 114298b7b855 (bug 1915419)
223 lines
8.2 KiB
C++
223 lines
8.2 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "SharedStyleSheetCache.h"
|
|
|
|
#include "mozilla/MemoryReporting.h"
|
|
#include "mozilla/StoragePrincipalHelper.h"
|
|
#include "mozilla/StyleSheet.h"
|
|
#include "mozilla/css/SheetLoadData.h"
|
|
#include "mozilla/dom/ContentParent.h"
|
|
#include "mozilla/dom/Document.h"
|
|
#include "mozilla/ServoBindings.h"
|
|
#include "nsContentUtils.h"
|
|
#include "nsXULPrototypeCache.h"
|
|
|
|
extern mozilla::LazyLogModule sCssLoaderLog;
|
|
|
|
#define LOG(...) MOZ_LOG(sCssLoaderLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
|
|
|
|
namespace mozilla {
|
|
|
|
NS_IMPL_ISUPPORTS(SharedStyleSheetCache, nsIMemoryReporter)
|
|
|
|
MOZ_DEFINE_MALLOC_SIZE_OF(SharedStyleSheetCacheMallocSizeOf)
|
|
|
|
SharedStyleSheetCache::SharedStyleSheetCache() = default;
|
|
|
|
void SharedStyleSheetCache::Init() { RegisterWeakMemoryReporter(this); }
|
|
|
|
SharedStyleSheetCache::~SharedStyleSheetCache() {
|
|
UnregisterWeakMemoryReporter(this);
|
|
}
|
|
|
|
void SharedStyleSheetCache::LoadCompleted(SharedStyleSheetCache* aCache,
|
|
StyleSheetLoadData& aData,
|
|
nsresult aStatus) {
|
|
// If aStatus is a failure we need to mark this data failed. We also need to
|
|
// mark any ancestors of a failing data as failed and any sibling of a
|
|
// failing data as failed. Note that SheetComplete is never called on a
|
|
// SheetLoadData that is the mNext of some other SheetLoadData.
|
|
nsresult cancelledStatus = aStatus;
|
|
if (NS_FAILED(aStatus)) {
|
|
css::Loader::MarkLoadTreeFailed(aData);
|
|
} else {
|
|
cancelledStatus = NS_BINDING_ABORTED;
|
|
css::SheetLoadData* data = &aData;
|
|
do {
|
|
if (data->IsCancelled()) {
|
|
// We only need to mark loads for this loader as cancelled, so as to not
|
|
// fire error events in unrelated documents.
|
|
css::Loader::MarkLoadTreeFailed(*data, data->mLoader);
|
|
}
|
|
} while ((data = data->mNext));
|
|
}
|
|
|
|
// 8 is probably big enough for all our common cases. It's not likely that
|
|
// imports will nest more than 8 deep, and multiple sheets with the same URI
|
|
// are rare.
|
|
AutoTArray<RefPtr<css::SheetLoadData>, 8> datasToNotify;
|
|
LoadCompletedInternal(aCache, aData, datasToNotify);
|
|
|
|
// Now it's safe to go ahead and notify observers
|
|
for (RefPtr<css::SheetLoadData>& data : datasToNotify) {
|
|
auto status = data->IsCancelled() ? cancelledStatus : aStatus;
|
|
data->mLoader->NotifyObservers(*data, status);
|
|
}
|
|
}
|
|
|
|
void SharedStyleSheetCache::InsertIfNeeded(css::SheetLoadData& aData) {
|
|
MOZ_ASSERT(aData.mLoader->IsDocumentAssociated(),
|
|
"We only cache document-associated sheets");
|
|
LOG("SharedStyleSheetCache::InsertIfNeeded");
|
|
// If we ever start doing this for failed loads, we'll need to adjust the
|
|
// PostLoadEvent code that thinks anything already complete must have loaded
|
|
// succesfully.
|
|
if (aData.mLoadFailed) {
|
|
LOG(" Load failed, bailing");
|
|
return;
|
|
}
|
|
|
|
// If this sheet came from the cache already, there's no need to override
|
|
// anything.
|
|
if (aData.mSheetAlreadyComplete) {
|
|
LOG(" Sheet came from the cache, bailing");
|
|
return;
|
|
}
|
|
|
|
if (!aData.mURI) {
|
|
LOG(" Inline or constructable style sheet, bailing");
|
|
// Inline sheet caching happens in Loader::mInlineSheets, where we still
|
|
// have the input text available.
|
|
// Constructable sheets are not worth caching, they're always unique.
|
|
return;
|
|
}
|
|
|
|
LOG(" Putting style sheet in shared cache: %s",
|
|
aData.mURI->GetSpecOrDefault().get());
|
|
Insert(aData);
|
|
}
|
|
|
|
void SharedStyleSheetCache::LoadCompletedInternal(
|
|
SharedStyleSheetCache* aCache, css::SheetLoadData& aData,
|
|
nsTArray<RefPtr<css::SheetLoadData>>& aDatasToNotify) {
|
|
if (aCache) {
|
|
aCache->LoadCompleted(aData);
|
|
}
|
|
|
|
// Go through and deal with the whole linked list.
|
|
auto* data = &aData;
|
|
do {
|
|
MOZ_RELEASE_ASSERT(!data->mSheetCompleteCalled);
|
|
data->mSheetCompleteCalled = true;
|
|
|
|
if (!data->mSheetAlreadyComplete) {
|
|
// If mSheetAlreadyComplete, then the sheet could well be modified between
|
|
// when we posted the async call to SheetComplete and now, since the sheet
|
|
// was page-accessible during that whole time.
|
|
|
|
// HasForcedUniqueInner() is okay if the sheet is constructed, because
|
|
// constructed sheets are always unique and they may be set to complete
|
|
// multiple times if their rules are replaced via Replace()
|
|
MOZ_ASSERT(data->mSheet->IsConstructed() ||
|
|
!data->mSheet->HasForcedUniqueInner(),
|
|
"should not get a forced unique inner during parsing");
|
|
// Insert the sheet into the tree now the sheet has loaded, but only if
|
|
// the sheet is still relevant, and if this is a top-level sheet.
|
|
const bool needInsertIntoTree = [&] {
|
|
if (!data->mLoader->GetDocument()) {
|
|
// Not a document load, nothing to do.
|
|
return false;
|
|
}
|
|
if (data->IsPreload()) {
|
|
// Preloads are not supposed to be observable.
|
|
return false;
|
|
}
|
|
if (data->mSheet->IsConstructed()) {
|
|
// Constructable sheets are not in the regular stylesheet tree.
|
|
return false;
|
|
}
|
|
if (data->mIsChildSheet) {
|
|
// A child sheet, those will get exposed from the parent, no need to
|
|
// insert them into the tree.
|
|
return false;
|
|
}
|
|
if (data->mHadOwnerNode != !!data->mSheet->GetOwnerNode()) {
|
|
// The sheet was already removed from the tree and is no longer the
|
|
// current sheet of the owning node, we can bail.
|
|
return false;
|
|
}
|
|
return true;
|
|
}();
|
|
|
|
if (needInsertIntoTree) {
|
|
data->mLoader->InsertSheetInTree(*data->mSheet);
|
|
}
|
|
data->mSheet->SetComplete();
|
|
} else if (data->mSheet->IsApplicable()) {
|
|
if (dom::Document* doc = data->mLoader->GetDocument()) {
|
|
// We post these events for devtools, even though the applicable state
|
|
// has not actually changed, to make the cache not observable.
|
|
doc->PostStyleSheetApplicableStateChangeEvent(*data->mSheet);
|
|
}
|
|
}
|
|
aDatasToNotify.AppendElement(data);
|
|
|
|
NS_ASSERTION(!data->mParentData || data->mParentData->mPendingChildren != 0,
|
|
"Broken pending child count on our parent");
|
|
|
|
// If we have a parent, our parent is no longer being parsed, and
|
|
// we are the last pending child, then our load completion
|
|
// completes the parent too. Note that the parent _can_ still be
|
|
// being parsed (eg if the child (us) failed to open the channel
|
|
// or some such).
|
|
if (data->mParentData && --(data->mParentData->mPendingChildren) == 0 &&
|
|
!data->mParentData->mIsBeingParsed) {
|
|
LoadCompletedInternal(aCache, *data->mParentData, aDatasToNotify);
|
|
}
|
|
|
|
data = data->mNext;
|
|
} while (data);
|
|
|
|
if (aCache) {
|
|
aCache->InsertIfNeeded(aData);
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
SharedStyleSheetCache::CollectReports(nsIHandleReportCallback* aHandleReport,
|
|
nsISupports* aData, bool aAnonymize) {
|
|
MOZ_COLLECT_REPORT("explicit/layout/style-sheet-cache/document-shared",
|
|
KIND_HEAP, UNITS_BYTES,
|
|
SizeOfIncludingThis(SharedStyleSheetCacheMallocSizeOf),
|
|
"Memory used for SharedStyleSheetCache to share style "
|
|
"sheets across documents (not to be confused with "
|
|
"GlobalStyleSheetCache)");
|
|
return NS_OK;
|
|
}
|
|
|
|
void SharedStyleSheetCache::Clear(nsIPrincipal* aForPrincipal,
|
|
const nsACString* aBaseDomain) {
|
|
using ContentParent = dom::ContentParent;
|
|
|
|
if (XRE_IsParentProcess()) {
|
|
auto forPrincipal = aForPrincipal ? Some(RefPtr(aForPrincipal)) : Nothing();
|
|
auto baseDomain = aBaseDomain ? Some(nsCString(*aBaseDomain)) : Nothing();
|
|
|
|
for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
|
|
Unused << cp->SendClearStyleSheetCache(forPrincipal, baseDomain);
|
|
}
|
|
}
|
|
|
|
if (sSingleton) {
|
|
sSingleton->ClearInProcess(aForPrincipal, aBaseDomain);
|
|
}
|
|
}
|
|
|
|
} // namespace mozilla
|
|
|
|
#undef LOG
|