Bug 77999 - Part 2: Add RuleProcessorCache. r=dbaron

This commit is contained in:
Cameron McCormack 2015-06-26 13:52:47 +10:00
Родитель ea9aba1e83
Коммит 7c894d7982
8 изменённых файлов: 488 добавлений и 2 удалений

Просмотреть файл

@ -30,6 +30,7 @@
#include "nsGkAtoms.h"
#include "nsImageFrame.h"
#include "nsLayoutStylesheetCache.h"
#include "mozilla/RuleProcessorCache.h"
#include "nsPrincipal.h"
#include "nsRange.h"
#include "nsRegion.h"
@ -376,6 +377,7 @@ nsLayoutStatics::Shutdown()
nsAttrValue::Shutdown();
nsContentUtils::Shutdown();
nsLayoutStylesheetCache::Shutdown();
RuleProcessorCache::Shutdown();
ShutdownJSEnvironment();
nsGlobalWindow::ShutDown();

Просмотреть файл

@ -46,6 +46,7 @@
#include "mozilla/dom/CSSStyleSheetBinding.h"
#include "nsComponentManagerUtils.h"
#include "nsNullPrincipal.h"
#include "mozilla/RuleProcessorCache.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -1075,6 +1076,7 @@ CSSStyleSheet::CSSStyleSheet(CORSMode aCORSMode, ReferrerPolicy aReferrerPolicy)
mOwningNode(nullptr),
mDisabled(false),
mDirty(false),
mInRuleProcessorCache(false),
mScopeElement(nullptr),
mRuleProcessors(nullptr)
{
@ -1093,6 +1095,7 @@ CSSStyleSheet::CSSStyleSheet(const CSSStyleSheet& aCopy,
mOwningNode(aOwningNodeToUse),
mDisabled(aCopy.mDisabled),
mDirty(aCopy.mDirty),
mInRuleProcessorCache(false),
mScopeElement(nullptr),
mInner(aCopy.mInner),
mRuleProcessors(nullptr)
@ -1135,6 +1138,9 @@ CSSStyleSheet::~CSSStyleSheet()
NS_ASSERTION(mRuleProcessors->Length() == 0, "destructing sheet with rule processor reference");
delete mRuleProcessors; // weak refs, should be empty here anyway
}
if (mInRuleProcessorCache) {
RuleProcessorCache::RemoveSheet(this);
}
}
void
@ -1703,10 +1709,18 @@ CSSStyleSheet::List(FILE* out, int32_t aIndent) const
void
CSSStyleSheet::ClearRuleCascades()
{
bool removedSheetFromRuleProcessorCache = false;
if (mRuleProcessors) {
nsCSSRuleProcessor **iter = mRuleProcessors->Elements(),
**end = iter + mRuleProcessors->Length();
for(; iter != end; ++iter) {
if (!removedSheetFromRuleProcessorCache && (*iter)->IsShared()) {
// Since the sheet has been modified, we need to remove all
// RuleProcessorCache entries that contain this sheet, as the
// list of @-moz-document rules might have changed.
RuleProcessorCache::RemoveSheet(this);
removedSheetFromRuleProcessorCache = true;
}
(*iter)->ClearRuleCascades();
}
}

Просмотреть файл

@ -239,6 +239,8 @@ public:
nsresult ParseSheet(const nsAString& aInput);
void SetInRuleProcessorCache() { mInRuleProcessorCache = true; }
// nsIDOMStyleSheet interface
NS_DECL_NSIDOMSTYLESHEET
@ -357,6 +359,7 @@ protected:
nsINode* mOwningNode; // weak ref
bool mDisabled;
bool mDirty; // has been modified
bool mInRuleProcessorCache;
nsRefPtr<dom::Element> mScopeElement;
CSSStyleSheetInner* mInner;

Просмотреть файл

@ -0,0 +1,284 @@
/* -*- 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)
{
return MOZ_COLLECT_REPORT(
"explicit/layout/rule-processor-cache", KIND_HEAP, UNITS_BYTES,
SizeOfIncludingThis(RuleProcessorCacheMallocSizeOf),
"Memory used for cached rule processors.");
}
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.SizeOfExcludingThis(aMallocSizeOf);
for (Entry& e : mEntries) {
n += e.mDocumentEntries.SizeOfExcludingThis(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;

Просмотреть файл

@ -0,0 +1,149 @@
/* -*- 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
*/
#ifndef mozilla_RuleProcessorCache_h
#define mozilla_RuleProcessorCache_h
#include "mozilla/MemoryReporting.h"
#include "mozilla/StaticPtr.h"
#include "nsCSSRuleProcessor.h"
#include "nsExpirationTracker.h"
#include "nsIMediaList.h"
#include "nsIMemoryReporter.h"
#include "nsTArray.h"
class nsCSSRuleProcessor;
namespace mozilla {
class CSSStyleSheet;
namespace css {
class DocumentRule;
}
}
namespace mozilla {
/**
* The RuleProcessorCache is a singleton object that caches
* nsCSSRuleProcessors keyed off a list of style sheets and the result of
* evaluating all @-moz-documents in the style sheets. nsStyleSet gets and
* puts nsCSSRuleProcessors from/to the RuleProcessorCache.
*
* State bits on CSSStyleSheet and nsCSSRuleProcessor track whether they are in
* the RuleProcessorCache. This lets us remove them from the RuleProcessorCache
* when they're going away.
*/
class RuleProcessorCache final : public nsIMemoryReporter
{
NS_DECL_ISUPPORTS
NS_DECL_NSIMEMORYREPORTER
public:
static nsCSSRuleProcessor* GetRuleProcessor(
const nsTArray<CSSStyleSheet*>& aSheets,
nsPresContext* aPresContext);
static void PutRuleProcessor(
const nsTArray<CSSStyleSheet*>& aSheets,
nsTArray<css::DocumentRule*>&& aDocumentRulesInSheets,
const nsDocumentRuleResultCacheKey& aCacheKey,
nsCSSRuleProcessor* aRuleProcessor);
static void StartTracking(nsCSSRuleProcessor* aRuleProcessor);
static void StopTracking(nsCSSRuleProcessor* aRuleProcessor);
#ifdef DEBUG
static bool HasRuleProcessor(nsCSSRuleProcessor* aRuleProcessor);
#endif
static void RemoveRuleProcessor(nsCSSRuleProcessor* aRuleProcessor);
static void RemoveSheet(CSSStyleSheet* aSheet);
static void Shutdown() { gShutdown = true; gRuleProcessorCache = nullptr; }
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
private:
class ExpirationTracker : public nsExpirationTracker<nsCSSRuleProcessor,3>
{
public:
ExpirationTracker(RuleProcessorCache* aCache)
: nsExpirationTracker<nsCSSRuleProcessor,3>(10000)
, mCache(aCache) {}
void RemoveObjectIfTracked(nsCSSRuleProcessor* aRuleProcessor);
virtual void NotifyExpired(nsCSSRuleProcessor* aRuleProcessor) override {
mCache->RemoveRuleProcessor(aRuleProcessor);
}
private:
RuleProcessorCache* mCache;
};
RuleProcessorCache() : mExpirationTracker(this) {}
~RuleProcessorCache();
void InitMemoryReporter();
static bool EnsureGlobal();
static StaticRefPtr<RuleProcessorCache> gRuleProcessorCache;
static bool gShutdown;
void DoRemoveSheet(CSSStyleSheet* aSheet);
nsCSSRuleProcessor* DoGetRuleProcessor(
const nsTArray<CSSStyleSheet*>& aSheets,
nsPresContext* aPresContext);
void DoPutRuleProcessor(const nsTArray<CSSStyleSheet*>& aSheets,
nsTArray<css::DocumentRule*>&& aDocumentRulesInSheets,
const nsDocumentRuleResultCacheKey& aCacheKey,
nsCSSRuleProcessor* aRuleProcessor);
#ifdef DEBUG
bool DoHasRuleProcessor(nsCSSRuleProcessor* aRuleProcessor);
#endif
void DoRemoveRuleProcessor(nsCSSRuleProcessor* aRuleProcessor);
void DoStartTracking(nsCSSRuleProcessor* aRuleProcessor);
void DoStopTracking(nsCSSRuleProcessor* aRuleProcessor);
struct DocumentEntry {
nsDocumentRuleResultCacheKey mCacheKey;
nsRefPtr<nsCSSRuleProcessor> mRuleProcessor;
};
struct Entry {
nsTArray<CSSStyleSheet*> mSheets;
nsTArray<css::DocumentRule*> mDocumentRulesInSheets;
nsTArray<DocumentEntry> mDocumentEntries;
};
// Function object to test whether an Entry object has a given sheet
// in its mSheets array. If it does, removes all of its rule processors
// before returning true.
struct HasSheet_ThenRemoveRuleProcessors {
HasSheet_ThenRemoveRuleProcessors(RuleProcessorCache* aCache,
CSSStyleSheet* aSheet)
: mCache(aCache), mSheet(aSheet) {}
bool operator()(Entry& aEntry) {
if (aEntry.mSheets.Contains(mSheet)) {
for (DocumentEntry& de : aEntry.mDocumentEntries) {
de.mRuleProcessor->SetInRuleProcessorCache(false);
mCache->mExpirationTracker.RemoveObjectIfTracked(de.mRuleProcessor);
}
return true;
}
return false;
}
RuleProcessorCache* mCache;
CSSStyleSheet* mSheet;
};
ExpirationTracker mExpirationTracker;
nsTArray<Entry> mEntries;
};
} // namespace mozilla
#endif // mozilla_RuleProcessorCache_h

Просмотреть файл

@ -86,6 +86,7 @@ EXPORTS.mozilla += [
'CSSVariableValues.h',
'IncrementalClearCOMRuleArray.h',
'RuleNodeCacheConditions.h',
'RuleProcessorCache.h',
'StyleAnimationValue.h',
]
@ -165,6 +166,7 @@ UNIFIED_SOURCES += [
'nsStyleUtil.cpp',
'nsTransitionManager.cpp',
'RuleNodeCacheConditions.cpp',
'RuleProcessorCache.cpp',
'StyleAnimationValue.cpp',
'StyleRule.cpp',
'SVGAttrAnimationRuleProcessor.cpp',

Просмотреть файл

@ -54,6 +54,7 @@
#include "mozilla/LookAndFeel.h"
#include "mozilla/Likely.h"
#include "mozilla/TypedEnumBits.h"
#include "RuleProcessorCache.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -1005,7 +1006,8 @@ nsCSSRuleProcessor::nsCSSRuleProcessor(const sheet_array_type& aSheets,
uint8_t aSheetType,
Element* aScopeElement,
nsCSSRuleProcessor*
aPreviousCSSRuleProcessor)
aPreviousCSSRuleProcessor,
bool aIsShared)
: mSheets(aSheets)
, mRuleCascades(nullptr)
, mPreviousCacheKey(aPreviousCSSRuleProcessor
@ -1014,6 +1016,8 @@ nsCSSRuleProcessor::nsCSSRuleProcessor(const sheet_array_type& aSheets,
, mLastPresContext(nullptr)
, mScopeElement(aScopeElement)
, mSheetType(aSheetType)
, mIsShared(aIsShared)
, mInRuleProcessorCache(false)
{
NS_ASSERTION(!!mScopeElement == (aSheetType == nsStyleSet::eScopedDocSheet),
"aScopeElement must be specified iff aSheetType is "
@ -1025,6 +1029,10 @@ nsCSSRuleProcessor::nsCSSRuleProcessor(const sheet_array_type& aSheets,
nsCSSRuleProcessor::~nsCSSRuleProcessor()
{
if (mInRuleProcessorCache) {
RuleProcessorCache::RemoveRuleProcessor(this);
}
MOZ_ASSERT(!mExpirationState.IsTracked());
ClearSheets();
ClearRuleCascades();
}
@ -2990,6 +2998,13 @@ nsCSSRuleProcessor::ClearRuleCascades()
mPreviousCacheKey = CloneMQCacheKey();
}
// No need to remove the rule processor from the RuleProcessorCache here,
// since CSSStyleSheet::ClearRuleCascades will have called
// RuleProcessorCache::RemoveSheet() passing itself, which will catch
// this rule processor (and any others for different @-moz-document
// cache key results).
MOZ_ASSERT(!RuleProcessorCache::HasRuleProcessor(this));
// We rely on our caller (perhaps indirectly) to do something that
// will rebuild style data and the user font set (either
// nsIPresShell::ReconstructStyleData or

Просмотреть файл

@ -18,6 +18,7 @@
#include "nsIStyleRuleProcessor.h"
#include "nsTArray.h"
#include "nsAutoPtr.h"
#include "nsExpirationTracker.h"
#include "nsRuleWalker.h"
#include "mozilla/UniquePtr.h"
@ -59,7 +60,8 @@ public:
nsCSSRuleProcessor(const sheet_array_type& aSheets,
uint8_t aSheetType,
mozilla::dom::Element* aScopeElement,
nsCSSRuleProcessor* aPreviousCSSRuleProcessor);
nsCSSRuleProcessor* aPreviousCSSRuleProcessor,
bool aIsShared = false);
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(nsCSSRuleProcessor)
@ -161,6 +163,16 @@ public:
*/
mozilla::dom::Element* GetScopeElement() const { return mScopeElement; }
bool IsShared() const { return mIsShared; }
nsExpirationState* GetExpirationState() { return &mExpirationState; }
void SetInRuleProcessorCache(bool aVal) {
MOZ_ASSERT(mIsShared);
printf("%p SetInRuleProcessorCache %d\n", this, aVal);
mInRuleProcessorCache = aVal;
}
bool IsInRuleProcessorCache() const { return mInRuleProcessorCache; }
#ifdef XP_WIN
// Cached theme identifier for the moz-windows-theme media query.
static uint8_t GetWindowsThemeIdentifier();
@ -214,9 +226,14 @@ private:
// Only used if mSheetType == nsStyleSet::eScopedDocSheet.
nsRefPtr<mozilla::dom::Element> mScopeElement;
nsExpirationState mExpirationState;
// type of stylesheet using this processor
uint8_t mSheetType; // == nsStyleSet::sheetType
const bool mIsShared;
bool mInRuleProcessorCache;
#ifdef XP_WIN
static uint8_t sWinThemeId;
#endif