RuleProcessorCache.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. /*
  6. * cache of re-usable nsCSSRuleProcessors for given sets of style sheets
  7. */
  8. #include "RuleProcessorCache.h"
  9. #include <algorithm>
  10. #include "nsCSSRuleProcessor.h"
  11. #include "nsThreadUtils.h"
  12. #include "CSSStyleSheet.h"
  13. using namespace mozilla;
  14. NS_IMPL_ISUPPORTS(RuleProcessorCache, nsIMemoryReporter)
  15. MOZ_DEFINE_MALLOC_SIZE_OF(RuleProcessorCacheMallocSizeOf)
  16. NS_IMETHODIMP
  17. RuleProcessorCache::CollectReports(nsIHandleReportCallback* aHandleReport,
  18. nsISupports* aData, bool aAnonymize)
  19. {
  20. MOZ_COLLECT_REPORT(
  21. "explicit/layout/rule-processor-cache", KIND_HEAP, UNITS_BYTES,
  22. SizeOfIncludingThis(RuleProcessorCacheMallocSizeOf),
  23. "Memory used for cached rule processors.");
  24. return NS_OK;
  25. }
  26. RuleProcessorCache::~RuleProcessorCache()
  27. {
  28. UnregisterWeakMemoryReporter(this);
  29. for (Entry& e : mEntries) {
  30. for (DocumentEntry& de : e.mDocumentEntries) {
  31. if (de.mRuleProcessor->GetExpirationState()->IsTracked()) {
  32. mExpirationTracker.RemoveObject(de.mRuleProcessor);
  33. }
  34. de.mRuleProcessor->SetInRuleProcessorCache(false);
  35. }
  36. }
  37. }
  38. void
  39. RuleProcessorCache::InitMemoryReporter()
  40. {
  41. RegisterWeakMemoryReporter(this);
  42. }
  43. /* static */ bool
  44. RuleProcessorCache::EnsureGlobal()
  45. {
  46. MOZ_ASSERT(NS_IsMainThread());
  47. if (gShutdown) {
  48. return false;
  49. }
  50. if (!gRuleProcessorCache) {
  51. gRuleProcessorCache = new RuleProcessorCache;
  52. gRuleProcessorCache->InitMemoryReporter();
  53. }
  54. return true;
  55. }
  56. /* static */ void
  57. RuleProcessorCache::RemoveSheet(CSSStyleSheet* aSheet)
  58. {
  59. if (!EnsureGlobal()) {
  60. return;
  61. }
  62. gRuleProcessorCache->DoRemoveSheet(aSheet);
  63. }
  64. #ifdef DEBUG
  65. /* static */ bool
  66. RuleProcessorCache::HasRuleProcessor(nsCSSRuleProcessor* aRuleProcessor)
  67. {
  68. if (!EnsureGlobal()) {
  69. return false;
  70. }
  71. return gRuleProcessorCache->DoHasRuleProcessor(aRuleProcessor);
  72. }
  73. #endif
  74. /* static */ void
  75. RuleProcessorCache::RemoveRuleProcessor(nsCSSRuleProcessor* aRuleProcessor)
  76. {
  77. if (!EnsureGlobal()) {
  78. return;
  79. }
  80. gRuleProcessorCache->DoRemoveRuleProcessor(aRuleProcessor);
  81. }
  82. /* static */ nsCSSRuleProcessor*
  83. RuleProcessorCache::GetRuleProcessor(const nsTArray<CSSStyleSheet*>& aSheets,
  84. nsPresContext* aPresContext)
  85. {
  86. if (!EnsureGlobal()) {
  87. return nullptr;
  88. }
  89. return gRuleProcessorCache->DoGetRuleProcessor(aSheets, aPresContext);
  90. }
  91. /* static */ void
  92. RuleProcessorCache::PutRuleProcessor(
  93. const nsTArray<CSSStyleSheet*>& aSheets,
  94. nsTArray<css::DocumentRule*>&& aDocumentRulesInSheets,
  95. const nsDocumentRuleResultCacheKey& aCacheKey,
  96. nsCSSRuleProcessor* aRuleProcessor)
  97. {
  98. if (!EnsureGlobal()) {
  99. return;
  100. }
  101. gRuleProcessorCache->DoPutRuleProcessor(aSheets, Move(aDocumentRulesInSheets),
  102. aCacheKey, aRuleProcessor);
  103. }
  104. /* static */ void
  105. RuleProcessorCache::StartTracking(nsCSSRuleProcessor* aRuleProcessor)
  106. {
  107. if (!EnsureGlobal()) {
  108. return;
  109. }
  110. return gRuleProcessorCache->DoStartTracking(aRuleProcessor);
  111. }
  112. /* static */ void
  113. RuleProcessorCache::StopTracking(nsCSSRuleProcessor* aRuleProcessor)
  114. {
  115. if (!EnsureGlobal()) {
  116. return;
  117. }
  118. return gRuleProcessorCache->DoStopTracking(aRuleProcessor);
  119. }
  120. void
  121. RuleProcessorCache::DoRemoveSheet(CSSStyleSheet* aSheet)
  122. {
  123. Entry* last = std::remove_if(mEntries.begin(), mEntries.end(),
  124. HasSheet_ThenRemoveRuleProcessors(this, aSheet));
  125. mEntries.TruncateLength(last - mEntries.begin());
  126. }
  127. nsCSSRuleProcessor*
  128. RuleProcessorCache::DoGetRuleProcessor(const nsTArray<CSSStyleSheet*>& aSheets,
  129. nsPresContext* aPresContext)
  130. {
  131. for (Entry& e : mEntries) {
  132. if (e.mSheets == aSheets) {
  133. for (DocumentEntry& de : e.mDocumentEntries) {
  134. if (de.mCacheKey.Matches(aPresContext, e.mDocumentRulesInSheets)) {
  135. return de.mRuleProcessor;
  136. }
  137. }
  138. // Entry::mSheets is unique; if we matched aSheets but didn't
  139. // find a matching DocumentEntry, we won't find one later in
  140. // mEntries.
  141. return nullptr;
  142. }
  143. }
  144. return nullptr;
  145. }
  146. void
  147. RuleProcessorCache::DoPutRuleProcessor(
  148. const nsTArray<CSSStyleSheet*>& aSheets,
  149. nsTArray<css::DocumentRule*>&& aDocumentRulesInSheets,
  150. const nsDocumentRuleResultCacheKey& aCacheKey,
  151. nsCSSRuleProcessor* aRuleProcessor)
  152. {
  153. MOZ_ASSERT(!aRuleProcessor->IsInRuleProcessorCache());
  154. Entry* entry = nullptr;
  155. for (Entry& e : mEntries) {
  156. if (e.mSheets == aSheets) {
  157. entry = &e;
  158. break;
  159. }
  160. }
  161. if (!entry) {
  162. entry = mEntries.AppendElement();
  163. entry->mSheets = aSheets;
  164. entry->mDocumentRulesInSheets = aDocumentRulesInSheets;
  165. for (CSSStyleSheet* sheet : aSheets) {
  166. sheet->SetInRuleProcessorCache();
  167. }
  168. } else {
  169. MOZ_ASSERT(entry->mDocumentRulesInSheets == aDocumentRulesInSheets,
  170. "DocumentRule array shouldn't have changed");
  171. }
  172. #ifdef DEBUG
  173. for (DocumentEntry& de : entry->mDocumentEntries) {
  174. MOZ_ASSERT(de.mCacheKey != aCacheKey,
  175. "should not have duplicate document cache keys");
  176. }
  177. #endif
  178. DocumentEntry* documentEntry = entry->mDocumentEntries.AppendElement();
  179. documentEntry->mCacheKey = aCacheKey;
  180. documentEntry->mRuleProcessor = aRuleProcessor;
  181. aRuleProcessor->SetInRuleProcessorCache(true);
  182. }
  183. #ifdef DEBUG
  184. bool
  185. RuleProcessorCache::DoHasRuleProcessor(nsCSSRuleProcessor* aRuleProcessor)
  186. {
  187. for (Entry& e : mEntries) {
  188. for (DocumentEntry& de : e.mDocumentEntries) {
  189. if (de.mRuleProcessor == aRuleProcessor) {
  190. return true;
  191. }
  192. }
  193. }
  194. return false;
  195. }
  196. #endif
  197. void
  198. RuleProcessorCache::DoRemoveRuleProcessor(nsCSSRuleProcessor* aRuleProcessor)
  199. {
  200. MOZ_ASSERT(aRuleProcessor->IsInRuleProcessorCache());
  201. aRuleProcessor->SetInRuleProcessorCache(false);
  202. mExpirationTracker.RemoveObjectIfTracked(aRuleProcessor);
  203. for (Entry& e : mEntries) {
  204. for (size_t i = 0; i < e.mDocumentEntries.Length(); i++) {
  205. if (e.mDocumentEntries[i].mRuleProcessor == aRuleProcessor) {
  206. e.mDocumentEntries.RemoveElementAt(i);
  207. return;
  208. }
  209. }
  210. }
  211. MOZ_ASSERT_UNREACHABLE("should have found rule processor");
  212. }
  213. void
  214. RuleProcessorCache::DoStartTracking(nsCSSRuleProcessor* aRuleProcessor)
  215. {
  216. mExpirationTracker.AddObject(aRuleProcessor);
  217. }
  218. void
  219. RuleProcessorCache::DoStopTracking(nsCSSRuleProcessor* aRuleProcessor)
  220. {
  221. mExpirationTracker.RemoveObjectIfTracked(aRuleProcessor);
  222. }
  223. size_t
  224. RuleProcessorCache::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
  225. {
  226. size_t n = aMallocSizeOf(this);
  227. int count = 0;
  228. n += mEntries.ShallowSizeOfExcludingThis(aMallocSizeOf);
  229. for (Entry& e : mEntries) {
  230. n += e.mDocumentEntries.ShallowSizeOfExcludingThis(aMallocSizeOf);
  231. for (DocumentEntry& de : e.mDocumentEntries) {
  232. count++;
  233. n += de.mRuleProcessor->SizeOfIncludingThis(aMallocSizeOf);
  234. }
  235. }
  236. return n;
  237. }
  238. void
  239. RuleProcessorCache::ExpirationTracker::RemoveObjectIfTracked(
  240. nsCSSRuleProcessor* aRuleProcessor)
  241. {
  242. if (aRuleProcessor->GetExpirationState()->IsTracked()) {
  243. RemoveObject(aRuleProcessor);
  244. }
  245. }
  246. bool RuleProcessorCache::gShutdown = false;
  247. mozilla::StaticRefPtr<RuleProcessorCache> RuleProcessorCache::gRuleProcessorCache;