1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
|
/* -*- 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;
auto* networkMetadata = aData.GetNetworkMetadata();
do {
MOZ_RELEASE_ASSERT(!data->mSheetCompleteCalled);
data->mSheetCompleteCalled = true;
if (!data->mNetworkMetadata) {
data->mNetworkMetadata = networkMetadata;
}
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);
}
}
size_t SharedStyleSheetCache::SizeOfIncludingThis(
MallocSizeOf aMallocSizeOf) const {
size_t n = aMallocSizeOf(this);
n += Base::SizeOfExcludingThis(aMallocSizeOf);
n += mInlineSheets.ShallowSizeOfExcludingThis(aMallocSizeOf);
for (const auto& sheetMap : mInlineSheets) {
for (const auto& entry : sheetMap.GetData()) {
n += entry.GetKey().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
n += entry.GetData()->SizeOfIncludingThis(aMallocSizeOf);
}
}
return n;
}
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::ClearInProcess(
const Maybe<bool>& aChrome, const Maybe<nsCOMPtr<nsIPrincipal>>& aPrincipal,
const Maybe<nsCString>& aSchemelessSite,
const Maybe<OriginAttributesPattern>& aPattern,
const Maybe<nsCString>& aURL) {
Base::ClearInProcess(aChrome, aPrincipal, aSchemelessSite, aPattern, aURL);
if (!aChrome && !aPrincipal && !aSchemelessSite && !aURL) {
mInlineSheets.Clear();
}
if (aURL) {
// Inline sheets don't have a URL.
return;
}
for (auto iter = mInlineSheets.Iter(); !iter.Done(); iter.Next()) {
if (SharedSubResourceCacheUtils::ShouldClearEntry(
nullptr, iter.Key(), iter.Key(), aChrome, aPrincipal,
aSchemelessSite, aPattern, aURL)) {
iter.Remove();
}
}
}
void SharedStyleSheetCache::Clear(
const Maybe<bool>& aChrome, const Maybe<nsCOMPtr<nsIPrincipal>>& aPrincipal,
const Maybe<nsCString>& aSchemelessSite,
const Maybe<OriginAttributesPattern>& aPattern,
const Maybe<nsCString>& aURL) {
using ContentParent = dom::ContentParent;
if (XRE_IsParentProcess()) {
for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
Unused << cp->SendClearStyleSheetCache(aChrome, aPrincipal,
aSchemelessSite, aPattern, aURL);
}
}
if (sSingleton) {
sSingleton->ClearInProcess(aChrome, aPrincipal, aSchemelessSite, aPattern,
aURL);
}
}
} // namespace mozilla
#undef LOG
|