2015-06-26 06:52:47 +03:00
|
|
|
/* -*- 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>
|
2016-12-01 21:40:04 +03:00
|
|
|
#include "mozilla/CSSStyleSheet.h"
|
2015-06-26 06:52:47 +03:00
|
|
|
#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)
|
|
|
|
{
|
2016-08-24 08:23:45 +03:00
|
|
|
MOZ_COLLECT_REPORT(
|
2015-06-26 06:52:47 +03:00
|
|
|
"explicit/layout/rule-processor-cache", KIND_HEAP, UNITS_BYTES,
|
|
|
|
SizeOfIncludingThis(RuleProcessorCacheMallocSizeOf),
|
|
|
|
"Memory used for cached rule processors.");
|
2016-08-24 08:23:45 +03:00
|
|
|
|
|
|
|
return NS_OK;
|
2015-06-26 06:52:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2016-09-01 01:42:12 +03:00
|
|
|
auto last = std::remove_if(mEntries.begin(), mEntries.end(),
|
|
|
|
HasSheet_ThenRemoveRuleProcessors(this, aSheet));
|
2015-06-26 06:52:47 +03:00
|
|
|
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;
|
2015-07-29 09:24:24 +03:00
|
|
|
n += mEntries.ShallowSizeOfExcludingThis(aMallocSizeOf);
|
2015-06-26 06:52:47 +03:00
|
|
|
for (Entry& e : mEntries) {
|
2015-07-29 09:24:24 +03:00
|
|
|
n += e.mDocumentEntries.ShallowSizeOfExcludingThis(aMallocSizeOf);
|
2015-06-26 06:52:47 +03:00
|
|
|
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;
|