summaryrefslogtreecommitdiffstats
path: root/layout/style/RuleProcessorCache.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/style/RuleProcessorCache.cpp')
-rw-r--r--layout/style/RuleProcessorCache.cpp286
1 files changed, 286 insertions, 0 deletions
diff --git a/layout/style/RuleProcessorCache.cpp b/layout/style/RuleProcessorCache.cpp
new file mode 100644
index 000000000..23832c230
--- /dev/null
+++ b/layout/style/RuleProcessorCache.cpp
@@ -0,0 +1,286 @@
+/* -*- 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/. */
+
+/*
+ * cache of re-usable nsCSSRuleProcessors for given sets of style sheets
+ */
+
+#include "RuleProcessorCache.h"
+
+#include <algorithm>
+#include "nsCSSRuleProcessor.h"
+#include "nsThreadUtils.h"
+
+using namespace mozilla;
+
+NS_IMPL_ISUPPORTS(RuleProcessorCache, nsIMemoryReporter)
+
+MOZ_DEFINE_MALLOC_SIZE_OF(RuleProcessorCacheMallocSizeOf)
+
+NS_IMETHODIMP
+RuleProcessorCache::CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize)
+{
+ MOZ_COLLECT_REPORT(
+ "explicit/layout/rule-processor-cache", KIND_HEAP, UNITS_BYTES,
+ SizeOfIncludingThis(RuleProcessorCacheMallocSizeOf),
+ "Memory used for cached rule processors.");
+
+ return NS_OK;
+}
+
+RuleProcessorCache::~RuleProcessorCache()
+{
+ UnregisterWeakMemoryReporter(this);
+
+ for (Entry& e : mEntries) {
+ for (DocumentEntry& de : e.mDocumentEntries) {
+ if (de.mRuleProcessor->GetExpirationState()->IsTracked()) {
+ mExpirationTracker.RemoveObject(de.mRuleProcessor);
+ }
+ de.mRuleProcessor->SetInRuleProcessorCache(false);
+ }
+ }
+}
+
+void
+RuleProcessorCache::InitMemoryReporter()
+{
+ RegisterWeakMemoryReporter(this);
+}
+
+/* static */ bool
+RuleProcessorCache::EnsureGlobal()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (gShutdown) {
+ return false;
+ }
+
+ if (!gRuleProcessorCache) {
+ gRuleProcessorCache = new RuleProcessorCache;
+ gRuleProcessorCache->InitMemoryReporter();
+ }
+ return true;
+}
+
+/* static */ void
+RuleProcessorCache::RemoveSheet(CSSStyleSheet* aSheet)
+{
+ if (!EnsureGlobal()) {
+ return;
+ }
+ gRuleProcessorCache->DoRemoveSheet(aSheet);
+}
+
+#ifdef DEBUG
+/* static */ bool
+RuleProcessorCache::HasRuleProcessor(nsCSSRuleProcessor* aRuleProcessor)
+{
+ if (!EnsureGlobal()) {
+ return false;
+ }
+ return gRuleProcessorCache->DoHasRuleProcessor(aRuleProcessor);
+}
+#endif
+
+/* static */ void
+RuleProcessorCache::RemoveRuleProcessor(nsCSSRuleProcessor* aRuleProcessor)
+{
+ if (!EnsureGlobal()) {
+ return;
+ }
+ gRuleProcessorCache->DoRemoveRuleProcessor(aRuleProcessor);
+}
+
+/* static */ nsCSSRuleProcessor*
+RuleProcessorCache::GetRuleProcessor(const nsTArray<CSSStyleSheet*>& aSheets,
+ nsPresContext* aPresContext)
+{
+ if (!EnsureGlobal()) {
+ return nullptr;
+ }
+ return gRuleProcessorCache->DoGetRuleProcessor(aSheets, aPresContext);
+}
+
+/* static */ void
+RuleProcessorCache::PutRuleProcessor(
+ const nsTArray<CSSStyleSheet*>& aSheets,
+ nsTArray<css::DocumentRule*>&& aDocumentRulesInSheets,
+ const nsDocumentRuleResultCacheKey& aCacheKey,
+ nsCSSRuleProcessor* aRuleProcessor)
+{
+ if (!EnsureGlobal()) {
+ return;
+ }
+ gRuleProcessorCache->DoPutRuleProcessor(aSheets, Move(aDocumentRulesInSheets),
+ aCacheKey, aRuleProcessor);
+}
+
+/* static */ void
+RuleProcessorCache::StartTracking(nsCSSRuleProcessor* aRuleProcessor)
+{
+ if (!EnsureGlobal()) {
+ return;
+ }
+ return gRuleProcessorCache->DoStartTracking(aRuleProcessor);
+}
+
+/* static */ void
+RuleProcessorCache::StopTracking(nsCSSRuleProcessor* aRuleProcessor)
+{
+ if (!EnsureGlobal()) {
+ return;
+ }
+ return gRuleProcessorCache->DoStopTracking(aRuleProcessor);
+}
+
+void
+RuleProcessorCache::DoRemoveSheet(CSSStyleSheet* aSheet)
+{
+ Entry* last = std::remove_if(mEntries.begin(), mEntries.end(),
+ HasSheet_ThenRemoveRuleProcessors(this, aSheet));
+ mEntries.TruncateLength(last - mEntries.begin());
+}
+
+nsCSSRuleProcessor*
+RuleProcessorCache::DoGetRuleProcessor(const nsTArray<CSSStyleSheet*>& aSheets,
+ nsPresContext* aPresContext)
+{
+ for (Entry& e : mEntries) {
+ if (e.mSheets == aSheets) {
+ for (DocumentEntry& de : e.mDocumentEntries) {
+ if (de.mCacheKey.Matches(aPresContext, e.mDocumentRulesInSheets)) {
+ return de.mRuleProcessor;
+ }
+ }
+ // Entry::mSheets is unique; if we matched aSheets but didn't
+ // find a matching DocumentEntry, we won't find one later in
+ // mEntries.
+ return nullptr;
+ }
+ }
+ return nullptr;
+}
+
+void
+RuleProcessorCache::DoPutRuleProcessor(
+ const nsTArray<CSSStyleSheet*>& aSheets,
+ nsTArray<css::DocumentRule*>&& aDocumentRulesInSheets,
+ const nsDocumentRuleResultCacheKey& aCacheKey,
+ nsCSSRuleProcessor* aRuleProcessor)
+{
+ MOZ_ASSERT(!aRuleProcessor->IsInRuleProcessorCache());
+
+ Entry* entry = nullptr;
+ for (Entry& e : mEntries) {
+ if (e.mSheets == aSheets) {
+ entry = &e;
+ break;
+ }
+ }
+
+ if (!entry) {
+ entry = mEntries.AppendElement();
+ entry->mSheets = aSheets;
+ entry->mDocumentRulesInSheets = aDocumentRulesInSheets;
+ for (CSSStyleSheet* sheet : aSheets) {
+ sheet->SetInRuleProcessorCache();
+ }
+ } else {
+ MOZ_ASSERT(entry->mDocumentRulesInSheets == aDocumentRulesInSheets,
+ "DocumentRule array shouldn't have changed");
+ }
+
+#ifdef DEBUG
+ for (DocumentEntry& de : entry->mDocumentEntries) {
+ MOZ_ASSERT(de.mCacheKey != aCacheKey,
+ "should not have duplicate document cache keys");
+ }
+#endif
+
+ DocumentEntry* documentEntry = entry->mDocumentEntries.AppendElement();
+ documentEntry->mCacheKey = aCacheKey;
+ documentEntry->mRuleProcessor = aRuleProcessor;
+ aRuleProcessor->SetInRuleProcessorCache(true);
+}
+
+#ifdef DEBUG
+bool
+RuleProcessorCache::DoHasRuleProcessor(nsCSSRuleProcessor* aRuleProcessor)
+{
+ for (Entry& e : mEntries) {
+ for (DocumentEntry& de : e.mDocumentEntries) {
+ if (de.mRuleProcessor == aRuleProcessor) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+#endif
+
+void
+RuleProcessorCache::DoRemoveRuleProcessor(nsCSSRuleProcessor* aRuleProcessor)
+{
+ MOZ_ASSERT(aRuleProcessor->IsInRuleProcessorCache());
+
+ aRuleProcessor->SetInRuleProcessorCache(false);
+ mExpirationTracker.RemoveObjectIfTracked(aRuleProcessor);
+ for (Entry& e : mEntries) {
+ for (size_t i = 0; i < e.mDocumentEntries.Length(); i++) {
+ if (e.mDocumentEntries[i].mRuleProcessor == aRuleProcessor) {
+ e.mDocumentEntries.RemoveElementAt(i);
+ return;
+ }
+ }
+ }
+
+ MOZ_ASSERT_UNREACHABLE("should have found rule processor");
+}
+
+void
+RuleProcessorCache::DoStartTracking(nsCSSRuleProcessor* aRuleProcessor)
+{
+ mExpirationTracker.AddObject(aRuleProcessor);
+}
+
+void
+RuleProcessorCache::DoStopTracking(nsCSSRuleProcessor* aRuleProcessor)
+{
+ mExpirationTracker.RemoveObjectIfTracked(aRuleProcessor);
+}
+
+size_t
+RuleProcessorCache::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
+{
+ size_t n = aMallocSizeOf(this);
+
+ int count = 0;
+ n += mEntries.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (Entry& e : mEntries) {
+ n += e.mDocumentEntries.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (DocumentEntry& de : e.mDocumentEntries) {
+ count++;
+ n += de.mRuleProcessor->SizeOfIncludingThis(aMallocSizeOf);
+ }
+ }
+
+ return n;
+}
+
+void
+RuleProcessorCache::ExpirationTracker::RemoveObjectIfTracked(
+ nsCSSRuleProcessor* aRuleProcessor)
+{
+ if (aRuleProcessor->GetExpirationState()->IsTracked()) {
+ RemoveObject(aRuleProcessor);
+ }
+}
+
+bool RuleProcessorCache::gShutdown = false;
+mozilla::StaticRefPtr<RuleProcessorCache> RuleProcessorCache::gRuleProcessorCache;