summaryrefslogtreecommitdiffstats
path: root/layout/style/SharedStyleSheetCache.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/style/SharedStyleSheetCache.cpp')
-rw-r--r--layout/style/SharedStyleSheetCache.cpp222
1 files changed, 222 insertions, 0 deletions
diff --git a/layout/style/SharedStyleSheetCache.cpp b/layout/style/SharedStyleSheetCache.cpp
new file mode 100644
index 0000000000..96c65efde7
--- /dev/null
+++ b/layout/style/SharedStyleSheetCache.cpp
@@ -0,0 +1,222 @@
+/* -*- 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 (sInstance) {
+ sInstance->ClearInProcess(aForPrincipal, aBaseDomain);
+ }
+}
+
+} // namespace mozilla
+
+#undef LOG