summaryrefslogtreecommitdiffstats
path: root/dom/localstorage/LSObject.h
blob: d394816fa49dd83be8b68b4e2244b2a5f57515e1 (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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
/* -*- 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_dom_localstorage_LSObject_h
#define mozilla_dom_localstorage_LSObject_h

#include <cstdint>
#include "ErrorList.h"
#include "mozilla/AlreadyAddRefed.h"
#include "mozilla/Assertions.h"
#include "mozilla/Maybe.h"
#include "mozilla/RefPtr.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/dom/Storage.h"
#include "mozilla/ipc/PBackgroundSharedTypes.h"
#include "nsCycleCollectionParticipant.h"
#include "nsID.h"
#include "nsISupports.h"
#include "nsStringFwd.h"
#include "nsTArrayForwardDeclare.h"

class nsGlobalWindowInner;
class nsIEventTarget;
class nsIPrincipal;
class nsISerialEventTarget;
class nsPIDOMWindowInner;

namespace mozilla {

class ErrorResult;

namespace dom {

class LSDatabase;
class LSObjectChild;
class LSObserver;
class LSRequestChild;
class LSRequestChildCallback;
class LSRequestParams;
class LSRequestResponse;

/**
 * Backs the WebIDL `Storage` binding; all content LocalStorage calls are
 * handled by this class.
 *
 * ## Semantics under e10s / multi-process ##
 *
 * A snapshot mechanism used in conjuction with stable points ensures that JS
 * run-to-completion semantics are experienced even if the same origin is
 * concurrently accessing LocalStorage across multiple content processes.
 *
 * ### Snapshot Consistency ###
 *
 * An LSSnapshot is created locally whenever the contents of LocalStorage are
 * about to be read or written (including length).  This synchronously
 * establishes a corresponding Snapshot in PBackground in the parent process.
 * An effort is made to send as much data from the parent process as possible,
 * so sites using a small/reasonable amount of LocalStorage data will have it
 * sent to the content process for immediate access.  Sites with greater
 * LocalStorage usage may only have some of the information relayed.  In that
 * case, the parent Snapshot will ensure that it retains the exact state of the
 * parent Datastore at the moment the Snapshot was created.
 */
class LSObject final : public Storage {
  using PrincipalInfo = mozilla::ipc::PrincipalInfo;

  friend nsGlobalWindowInner;

  UniquePtr<PrincipalInfo> mPrincipalInfo;
  UniquePtr<PrincipalInfo> mStoragePrincipalInfo;

  RefPtr<LSDatabase> mDatabase;
  RefPtr<LSObserver> mObserver;

  uint32_t mPrivateBrowsingId;
  Maybe<nsID> mClientId;
  Maybe<PrincipalInfo> mClientPrincipalInfo;
  nsCString mOrigin;
  nsCString mOriginKey;
  nsString mDocumentURI;

  bool mInExplicitSnapshot;

 public:
  /**
   * The normal creation path invoked by nsGlobalWindowInner.
   */
  static nsresult CreateForWindow(nsPIDOMWindowInner* aWindow,
                                  Storage** aStorage);

  /**
   * nsIDOMStorageManager creation path for use in testing logic.  Supports the
   * system principal where CreateForWindow does not.  This is also why aPrivate
   * exists separate from the principal; because the system principal can never
   * be mutated to have a private browsing id even though it can be used in a
   * window/document marked as private browsing.  That's a legacy issue that is
   * being dealt with, but it's why it exists here.
   */
  static nsresult CreateForPrincipal(nsPIDOMWindowInner* aWindow,
                                     nsIPrincipal* aPrincipal,
                                     nsIPrincipal* aStoragePrincipal,
                                     const nsAString& aDocumentURI,
                                     bool aPrivate, LSObject** aObject);

  void AssertIsOnOwningThread() const { NS_ASSERT_OWNINGTHREAD(LSObject); }

  const RefPtr<LSDatabase>& DatabaseStrongRef() const { return mDatabase; }

  const nsString& DocumentURI() const { return mDocumentURI; }

  bool InExplicitSnapshot() const { return mInExplicitSnapshot; }

  LSRequestChild* StartRequest(const LSRequestParams& aParams,
                               LSRequestChildCallback* aCallback);

  // Storage overrides.
  StorageType Type() const override;

  bool IsForkOf(const Storage* aStorage) const override;

  int64_t GetOriginQuotaUsage() const override;

  void Disconnect() override;

  uint32_t GetLength(nsIPrincipal& aSubjectPrincipal,
                     ErrorResult& aError) override;

  void Key(uint32_t aIndex, nsAString& aResult, nsIPrincipal& aSubjectPrincipal,
           ErrorResult& aError) override;

  void GetItem(const nsAString& aKey, nsAString& aResult,
               nsIPrincipal& aSubjectPrincipal, ErrorResult& aError) override;

  void GetSupportedNames(nsTArray<nsString>& aNames) override;

  void SetItem(const nsAString& aKey, const nsAString& aValue,
               nsIPrincipal& aSubjectPrincipal, ErrorResult& aError) override;

  void RemoveItem(const nsAString& aKey, nsIPrincipal& aSubjectPrincipal,
                  ErrorResult& aError) override;

  void Clear(nsIPrincipal& aSubjectPrincipal, ErrorResult& aError) override;

  //////////////////////////////////////////////////////////////////////////////
  // Testing Methods: See Storage.h
  void Open(nsIPrincipal& aSubjectPrincipal, ErrorResult& aError) override;

  void Close(nsIPrincipal& aSubjectPrincipal, ErrorResult& aError) override;

  void BeginExplicitSnapshot(nsIPrincipal& aSubjectPrincipal,
                             ErrorResult& aError) override;

  void CheckpointExplicitSnapshot(nsIPrincipal& aSubjectPrincipal,
                                  ErrorResult& aError) override;

  void EndExplicitSnapshot(nsIPrincipal& aSubjectPrincipal,
                           ErrorResult& aError) override;

  bool GetHasSnapshot(nsIPrincipal& aSubjectPrincipal,
                      ErrorResult& aError) override;

  int64_t GetSnapshotUsage(nsIPrincipal& aSubjectPrincipal,
                           ErrorResult& aError) override;

  //////////////////////////////////////////////////////////////////////////////

  NS_DECL_ISUPPORTS_INHERITED
  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(LSObject, Storage)

 private:
  LSObject(nsPIDOMWindowInner* aWindow, nsIPrincipal* aPrincipal,
           nsIPrincipal* aStoragePrincipal);

  ~LSObject();

  nsresult DoRequestSynchronously(const LSRequestParams& aParams,
                                  LSRequestResponse& aResponse);

  nsresult EnsureDatabase();

  void DropDatabase();

  /**
   * Invoked by nsGlobalWindowInner whenever a new "storage" event listener is
   * added to the window in order to ensure that "storage" events are received
   * from other processes.  (`LSObject::OnChange` directly invokes
   * `Storage::NotifyChange` to notify in-process listeners.)
   *
   * If this is the first request in the process for an observer for this
   * origin, this will trigger a RequestHelper-mediated synchronous LSRequest
   * to prepare a new observer in the parent process and also construction of
   * corresponding actors, which will result in the observer being fully
   * registered in the parent process.
   */
  nsresult EnsureObserver();

  /**
   * Invoked by nsGlobalWindowInner whenever its last "storage" event listener
   * is removed.
   */
  void DropObserver();

  /**
   * Internal helper method used by mutation methods that wraps the call to
   * Storage::NotifyChange to generate same-process "storage" events.
   */
  void OnChange(const nsAString& aKey, const nsAString& aOldValue,
                const nsAString& aNewValue);

  // Storage overrides.
  void LastRelease() override;
};

}  // namespace dom
}  // namespace mozilla

#endif  // mozilla_dom_localstorage_LSObject_h