summaryrefslogtreecommitdiffstats
path: root/layout/style/SharedStyleSheetCache.h
blob: 8d134caf6662426333744e4b9d9f72d6237c26ea (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/* -*- 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/. */

#ifndef mozilla_SharedStyleSheetCache_h__
#define mozilla_SharedStyleSheetCache_h__

// The shared style sheet cache is a cache that allows us to share sheets across
// documents.
//
// It's generally a singleton, but it is different from GlobalStyleSheetCache in
// the sense that:
//
//  * It needs to be cycle-collectable, as it can keep alive style sheets from
//    various documents.
//
//  * It is conceptually a singleton, but given its cycle-collectable nature, we
//    might re-create it.

#include "mozilla/PrincipalHashKey.h"
#include "mozilla/WeakPtr.h"
#include "mozilla/css/Loader.h"
#include "nsDataHashtable.h"
#include "nsIMemoryReporter.h"
#include "nsRefPtrHashtable.h"
#include "nsURIHashKey.h"

namespace mozilla {

namespace css {
class SheetLoadData;
}

class SharedStyleSheetCache final : public nsIMemoryReporter {
 public:
  NS_DECL_ISUPPORTS
  NS_DECL_NSIMEMORYREPORTER

  SharedStyleSheetCache(const SharedStyleSheetCache&) = delete;
  SharedStyleSheetCache(SharedStyleSheetCache&&) = delete;

  static already_AddRefed<SharedStyleSheetCache> Get() {
    if (sInstance) {
      return do_AddRef(sInstance);
    }
    return Create();
  }

 public:
  // TODO(emilio): Maybe cache inline sheets here rather than in the loader?
  // But that is a bit dubious, because for inline sheets, the sheet URI is the
  // document URI, so it is basically document-specific... Maybe we can cache
  // the RawServoStyleSheetContents or something, but...

  // A cache hit or miss. It is a miss if the `StyleSheet` is null.
  using CacheResult = std::tuple<RefPtr<StyleSheet>, css::Loader::SheetState>;
  CacheResult Lookup(css::Loader&, const SheetLoadDataHashKey&, bool aSyncLoad);

  // Tries to coalesce with an already existing load. The sheet state must be
  // the one that Lookup returned, if it returned a sheet.
  //
  // TODO(emilio): Maybe try to merge this with the lookup? Most consumers could
  // have a data there already.
  MOZ_MUST_USE bool CoalesceLoad(const SheetLoadDataHashKey&,
                                 css::SheetLoadData& aNewLoad,
                                 css::Loader::SheetState aExistingLoadState);

  size_t SizeOfIncludingThis(MallocSizeOf) const;

  // Puts the load into the "loading" set.
  void LoadStarted(const SheetLoadDataHashKey&, css::SheetLoadData&);
  // Puts a load into the "pending" set.
  void DeferSheetLoad(const SheetLoadDataHashKey&, css::SheetLoadData&);

  enum class StartLoads {
    Always,
    IfNonAlternate,
  };

  void StartDeferredLoadsForLoader(css::Loader&, StartLoads);
  void CancelLoadsForLoader(css::Loader&);

  // This has to be static because it's also called for loaders that don't have
  // a sheet cache (loaders that are not owned by a document).
  static void LoadCompleted(SharedStyleSheetCache*, css::SheetLoadData&,
                            nsresult);

  // Register a loader into the cache. This has the effect of keeping alive all
  // stylesheets for the origin of the loader's document until UnregisterLoader
  // is called.
  void RegisterLoader(css::Loader&);

  // Unregister a loader from the cache.
  //
  // If this is the loader for the last document of a given origin, then all the
  // stylesheets for that document will be removed from the cache. This needs to
  // be called when the document goes away, or when its principal changes.
  void UnregisterLoader(css::Loader&);

  static void Clear(nsIPrincipal* aForPrincipal = nullptr);

 private:
  static already_AddRefed<SharedStyleSheetCache> Create();
  void CancelDeferredLoadsForLoader(css::Loader&);

  static void LoadCompletedInternal(SharedStyleSheetCache*, css::SheetLoadData&,
                                    nsTArray<RefPtr<css::SheetLoadData>>&);
  void InsertIntoCompleteCacheIfNeeded(css::SheetLoadData&);

  SharedStyleSheetCache() = default;

  ~SharedStyleSheetCache();

  struct CompleteSheet {
    uint32_t mExpirationTime = 0;
    UniquePtr<StyleUseCounters> mUseCounters;
    RefPtr<StyleSheet> mSheet;

    bool Expired() const;
  };

  void WillStartPendingLoad(css::SheetLoadData&);

  nsDataHashtable<SheetLoadDataHashKey, CompleteSheet> mCompleteSheets;
  nsRefPtrHashtable<SheetLoadDataHashKey, css::SheetLoadData> mPendingDatas;
  // The SheetLoadData pointers in mLoadingDatas below are weak references that
  // get cleaned up when StreamLoader::OnStopRequest gets called.
  //
  // Note that we hold on to all sheet loads, even if in the end they happen not
  // to be cacheable.
  nsDataHashtable<SheetLoadDataHashKey, WeakPtr<css::SheetLoadData>>
      mLoadingDatas;

  // An origin-to-number-of-registered-documents count, in order to manage cache
  // eviction as described in RegisterLoader / UnregisterLoader.
  nsDataHashtable<PrincipalHashKey, uint32_t> mLoaderPrincipalRefCnt;

  static SharedStyleSheetCache* sInstance;
};

}  // namespace mozilla

#endif