summaryrefslogtreecommitdiffstats
path: root/dom/xul/nsXULPrototypeCache.h
blob: 657bf9251131a2a89160d9a6d49e15800930c676 (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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 nsXULPrototypeCache_h__
#define nsXULPrototypeCache_h__

#include "nsBaseHashtable.h"
#include "nsCOMPtr.h"
#include "nsIObserver.h"
#include "nsInterfaceHashtable.h"
#include "nsRefPtrHashtable.h"
#include "nsURIHashKey.h"
#include "nsXULPrototypeDocument.h"
#include "nsIStorageStream.h"

#include "mozilla/scache/StartupCache.h"
#include "js/experimental/JSStencil.h"
#include "mozilla/RefPtr.h"

class nsIHandleReportCallback;
namespace mozilla {
class StyleSheet;
}  // namespace mozilla

/**
 * The XUL prototype cache can be used to store and retrieve shared data for
 * XUL documents, style sheets, XBL, and scripts.
 *
 * The cache has two levels:
 *  1. In-memory hashtables
 *  2. The on-disk cache file.
 */
class nsXULPrototypeCache : public nsIObserver {
 public:
  enum class CacheType { Prototype, Script };

  // nsISupports
  NS_DECL_THREADSAFE_ISUPPORTS
  NS_DECL_NSIOBSERVER

  bool IsCached(nsIURI* aURI) { return GetPrototype(aURI) != nullptr; }
  void AbortCaching();

  /**
   * Whether the prototype cache is enabled.
   */
  bool IsEnabled();

  /**
   * Flush the cache; remove all XUL prototype documents, style
   * sheets, and scripts.
   */
  void Flush();

  // The following methods are used to put and retrive various items into and
  // from the cache.

  nsXULPrototypeDocument* GetPrototype(nsIURI* aURI);
  nsresult PutPrototype(nsXULPrototypeDocument* aDocument);
  void RemovePrototype(nsIURI* aURI) { mPrototypeTable.Remove(aURI); }

  JS::Stencil* GetStencil(nsIURI* aURI);
  nsresult PutStencil(nsIURI* aURI, JS::Stencil* aStencil);

  /**
   * Write the XUL prototype document to a cache file. The proto must be
   * fully loaded.
   */
  nsresult WritePrototype(nsXULPrototypeDocument* aPrototypeDocument);

  /**
   * This interface allows partial reads and writes from the buffers in the
   * startupCache.
   */

  inline nsresult GetPrototypeInputStream(nsIURI* aURI,
                                          nsIObjectInputStream** objectInput) {
    return GetInputStream(CacheType::Prototype, aURI, objectInput);
  }
  inline nsresult GetScriptInputStream(nsIURI* aURI,
                                       nsIObjectInputStream** objectInput) {
    return GetInputStream(CacheType::Script, aURI, objectInput);
  }
  inline nsresult FinishScriptInputStream(nsIURI* aURI) {
    return FinishInputStream(aURI);
  }

  inline nsresult GetPrototypeOutputStream(
      nsIURI* aURI, nsIObjectOutputStream** objectOutput) {
    return GetOutputStream(aURI, objectOutput);
  }
  inline nsresult GetScriptOutputStream(nsIURI* aURI,
                                        nsIObjectOutputStream** objectOutput) {
    return GetOutputStream(aURI, objectOutput);
  }

  inline nsresult FinishPrototypeOutputStream(nsIURI* aURI) {
    return FinishOutputStream(CacheType::Prototype, aURI);
  }
  inline nsresult FinishScriptOutputStream(nsIURI* aURI) {
    return FinishOutputStream(CacheType::Script, aURI);
  }

  inline nsresult HasPrototype(nsIURI* aURI, bool* exists) {
    return HasData(CacheType::Prototype, aURI, exists);
  }
  inline nsresult HasScript(nsIURI* aURI, bool* exists) {
    return HasData(CacheType::Script, aURI, exists);
  }

 private:
  nsresult GetInputStream(CacheType cacheType, nsIURI* uri,
                          nsIObjectInputStream** stream);
  nsresult FinishInputStream(nsIURI* aURI);

  nsresult GetOutputStream(nsIURI* aURI, nsIObjectOutputStream** objectOutput);
  nsresult FinishOutputStream(CacheType cacheType, nsIURI* aURI);
  nsresult HasData(CacheType cacheType, nsIURI* aURI, bool* exists);

 public:
  static nsXULPrototypeCache* GetInstance();
  static nsXULPrototypeCache* MaybeGetInstance() { return sInstance; }

  static void ReleaseGlobals() { NS_IF_RELEASE(sInstance); }

  void MarkInCCGeneration(uint32_t aGeneration);

  static void CollectMemoryReports(nsIHandleReportCallback* aHandleReport,
                                   nsISupports* aData);

 protected:
  friend nsresult NS_NewXULPrototypeCache(REFNSIID aIID, void** aResult);

  nsXULPrototypeCache();
  virtual ~nsXULPrototypeCache() = default;

  static nsXULPrototypeCache* sInstance;

  nsRefPtrHashtable<nsURIHashKey, nsXULPrototypeDocument>
      mPrototypeTable;  // owns the prototypes

  class StencilHashKey : public nsURIHashKey {
   public:
    explicit StencilHashKey(const nsIURI* aKey) : nsURIHashKey(aKey) {}
    StencilHashKey(StencilHashKey&&) = default;

    RefPtr<JS::Stencil> mStencil;
  };

  nsTHashtable<StencilHashKey> mStencilTable;

  // URIs already written to the startup cache, to prevent double-caching.
  nsTHashtable<nsURIHashKey> mStartupCacheURITable;

  nsInterfaceHashtable<nsURIHashKey, nsIStorageStream> mOutputStreamTable;
  nsInterfaceHashtable<nsURIHashKey, nsIObjectInputStream> mInputStreamTable;

  // Bootstrap caching service
  nsresult BeginCaching(nsIURI* aDocumentURI);
};

#endif  // nsXULPrototypeCache_h__