summaryrefslogtreecommitdiffstats
path: root/dom/localstorage/LocalStorageCommon.h
blob: b9c185bcaf85cea339ac5e679245dea546c05727 (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
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
/* -*- 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_LocalStorageCommon_h
#define mozilla_dom_localstorage_LocalStorageCommon_h

#include <cstdint>
#include "ErrorList.h"
#include "mozilla/Attributes.h"
#include "mozilla/dom/quota/QuotaCommon.h"
#include "nsLiteralString.h"
#include "nsStringFwd.h"

/*
 * Local storage
 * ~~~~~~~~~~~~~
 *
 * Implementation overview
 * ~~~~~~~~~~~~~~~~~~~~~~~
 *
 * The implementation is based on a per principal/origin cache (datastore)
 * living in the main process and synchronous calls initiated from content
 * processes.
 * The IPC communication is managed by database actors which link to the
 * datastore.
 * The synchronous blocking of the main thread is done by using a special
 * technique or by using standard synchronous IPC calls.
 *
 * General architecture
 * ~~~~~~~~~~~~~~~~~~~~
 * The current browser architecture consists of one main process and multiple
 * content processes (there are other processes but for simplicity's sake, they
 * are not mentioned here). The processes use the IPC communication to talk to
 * each other. Local storage implementation uses the client-server model, so
 * the main process manages all the data and content processes then request
 * particular data from the main process. The main process is also called the
 * parent or the parent side, the content process is then called the child or
 * the child side.
 *
 * Datastores
 * ~~~~~~~~~~
 *
 * A datastore provides a convenient way to access data for given origin. The
 * data is always preloaded into memory and indexed using a hash table. This
 * enables very fast access to particular stored items. There can be only one
 * datastore per origin and exists solely on the parent side. It is represented
 * by the "Datastore" class. A datastore instance is a ref counted object and
 * lives on the PBackground thread, it is kept alive by database objects. When
 * the last database object for given origin is destroyed, the associated
 * datastore object is destroyed too.
 *
 * Databases
 * ~~~~~~~~~
 *
 * A database allows direct access to a datastore from a content process. There
 * can be multiple databases for the same origin, but they all share the same
 * datastore.
 * Databases use the PBackgroundLSDatabase IPDL protocol for IPC communication.
 * Given the nature of local storage, most of PBackgroundLSDatabase messages
 * are synchronous.
 *
 * On the parent side, the database is represented by the "Database" class that
 * is a parent actor as well (implements the "PBackgroundLSDatabaseParent"
 * interface). A database instance is a ref counted object and lives on the
 * PBackground thread.
 * All live database actors are tracked in an array.
 *
 * On the child side, the database is represented by the "LSDatabase" class
 * that provides indirect access to a child actor. An LSDatabase instance is a
 * ref counted object and lives on the main thread.
 * The actual child actor is represented by the "LSDatabaseChild" class that
 * implements the "PBackgroundLSDatabaseChild" interface. An "LSDatabaseChild"
 * instance is not ref counted and lives on the main thread too.
 *
 * Synchronous blocking
 * ~~~~~~~~~~~~~~~~~~~~
 *
 * Local storage is synchronous in nature which means the execution can't move
 * forward until there's a reply for given method call.
 * Since we have to use IPC anyway, we could just always use synchronous IPC
 * messages for all local storage method calls. Well, there's a problem with
 * that approach.
 * If the main process needs to do some off PBackground thread stuff like
 * getting info from principals on the main thread or some asynchronous stuff
 * like directory locking before sending a reply to a synchronous message, then
 * we would have to block the thread or spin the event loop which is usually a
 * bad idea, especially in the main process.
 * Instead, we can use a special thread in the content process called
 * RemoteLazyInputStream thread for communication with the main process using
 * asynchronous messages and synchronously block the main thread until the DOM
 * File thread is done (the main thread blocking is a bit more complicated, see
 * the comment in RequestHelper::StartAndReturnResponse for more details).
 * Anyway, the extra hop to the RemoteLazyInputStream thread brings another
 * overhead and latency. The final solution is to use a combination of the
 * special thread for complex stuff like datastore preparation and synchronous
 * IPC messages sent directly from the main thread for database access when data
 * is already loaded from disk into memory.
 *
 * Requests
 * ~~~~~~~~
 *
 * Requests are used to handle asynchronous high level datastore operations
 * which are initiated in a content process and then processed in the parent
 * process (for example, preparation of a datastore).
 * Requests use the "PBackgroundLSRequest" IPDL protocol for IPC communication.
 *
 * On the parent side, the request is represented by the "LSRequestBase" class
 * that is a parent actor as well (implements the "PBackgroundLSRequestParent"
 * interface). It's an abstract class (contains pure virtual functions) so it
 * can't be used to create instances.
 * It also inherits from the "DatastoreOperationBase" class which is a generic
 * base class for all datastore operations. The "DatastoreOperationsBase" class
 * inherits from the "Runnable" class, so derived class instances are ref
 * counted, can be dispatched to multiple threads and thus they are used on
 * multiple threads. However, derived class instances can be created on the
 * PBackground thread only.
 *
 * On the child side, the request is represented by the "RequestHelper" class
 * that covers all the complexity needed to start a new request, handle
 * responses and do safe main thread blocking at the same time.
 * It inherits from the "Runnable" class, so instances are ref counted and
 * they are internally used on multiple threads (specifically on the main
 * thread and on the RemoteLazyInputStream thread). Anyway, users should create
 * and use instances of this class only on the main thread (apart from a special
 * case when we need to cancel the request from an internal chromium IPC thread
 * to prevent a dead lock involving CPOWs).
 * The actual child actor is represented by the "LSRequestChild" class that
 * implements the "PBackgroundLSRequestChild" interface. An "LSRequestChild"
 * instance is not ref counted and lives on the RemoteLazyInputStream thread.
 * Request responses are passed using the "LSRequestChildCallback" interface.
 *
 * Preparation of a datastore
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * The datastore preparation is needed to make sure a datastore is fully loaded
 * into memory. Every datastore preparation produces a unique id (even if the
 * datastore for given origin already exists).
 * On the parent side, the preparation is handled by the "PrepareDatastoreOp"
 * class which inherits from the "LSRequestBase" class. The preparation process
 * on the parent side is quite complicated, it happens sequentially on multiple
 * threads and is managed by a state machine.
 * On the child side, the preparation is done in the LSObject::EnsureDatabase
 * method using the "RequestHelper" class. The method starts a new preparation
 * request and obtains a unique id produced by the parent (or an error code if
 * the requested failed to complete).
 *
 * Linking databases to a datastore
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * A datastore exists only on the parent side, but it can be accessed from the
 * content via database actors. Database actors are initiated on the child side
 * and they need to be linked to a datastore on the parent side via an id. The
 * datastore preparation process gives us the required id.
 * The linking is initiated on the child side in the LSObject::EnsureDatabase
 * method by calling SendPBackgroundLSDatabaseConstructor and finished in
 * RecvPBackgroundLSDatabaseConstructor on the parent side.
 *
 * Actor migration
 * ~~~~~~~~~~~~~~~
 *
 * In theory, the datastore preparation request could return a database actor
 * directly (instead of returning an id intended for database linking to a
 * datastore). However, as it was explained above, the preparation must be done
 * on the RemoteLazyInputStream thread and database objects are used on the main
 * thread. The returned actor would have to be migrated from the
 * RemoteLazyInputStream thread to the main thread and that's something which
 * our IPDL doesn't support yet.
 *
 * Exposing local storage
 * ~~~~~~~~~~~~~~~~~~~~~~
 *
 * The implementation is exposed to the DOM via window.localStorage attribute.
 * Local storage's sibling, session storage shares the same WebIDL interface
 * for exposing it to web content, therefore there's an abstract class called
 * "Storage" that handles some of the common DOM bindings stuff. Local storage
 * specific functionality is defined in the "LSObject" derived class.
 * The "LSObject" class is also a starting point for the datastore preparation
 * and database linking.
 *
 * Local storage manager
 * ~~~~~~~~~~~~~~~~~~~~~
 *
 * The local storage manager exposes some of the features that need to be
 * available only in the chrome code or tests. The manager is represented by
 * the "LocalStorageManager2" class that implements the "nsIDOMStorageManager"
 * interface.
 */

// LocalStorage equivalents of QM_TRY.
#define LS_TRY_GLUE(...)                                             \
  QM_TRY_META(mozilla::dom::localstorage, MOZ_UNIQUE_VAR(tryResult), \
              ##__VA_ARGS__)
#define LS_TRY(...) LS_TRY_GLUE(__VA_ARGS__)

// LocalStorage equivalents of QM_TRY_UNWRAP and QM_TRY_INSPECT.
#define LS_TRY_ASSIGN_GLUE(accessFunction, ...)                             \
  QM_TRY_ASSIGN_META(mozilla::dom::localstorage, MOZ_UNIQUE_VAR(tryResult), \
                     accessFunction, ##__VA_ARGS__)
#define LS_TRY_UNWRAP(...) LS_TRY_ASSIGN_GLUE(unwrap, __VA_ARGS__)
#define LS_TRY_INSPECT(...) LS_TRY_ASSIGN_GLUE(inspect, __VA_ARGS__)

// LocalStorage equivalents of QM_TRY_RETURN.
#define LS_TRY_RETURN_GLUE(...)                                             \
  QM_TRY_RETURN_META(mozilla::dom::localstorage, MOZ_UNIQUE_VAR(tryResult), \
                     ##__VA_ARGS__)
#define LS_TRY_RETURN(...) LS_TRY_RETURN_GLUE(__VA_ARGS__)

// LocalStorage equivalents of QM_FAIL.
#define LS_FAIL_GLUE(...) \
  QM_FAIL_META(mozilla::dom::localstorage, ##__VA_ARGS__)
#define LS_FAIL(...) LS_FAIL_GLUE(__VA_ARGS__)

namespace mozilla {

class LogModule;

namespace ipc {

class PrincipalInfo;

}  // namespace ipc

namespace dom {

extern const char16_t* kLocalStorageType;

/**
 * Convenience data-structure to make it easier to track whether a value has
 * changed and what its previous value was for notification purposes.  Instances
 * are created on the stack by LSObject and passed to LSDatabase which in turn
 * passes them onto LSSnapshot for final updating/population.  LSObject then
 * generates an event, if appropriate.
 */
class MOZ_STACK_CLASS LSNotifyInfo {
  bool mChanged;
  nsString mOldValue;

 public:
  LSNotifyInfo() : mChanged(false) {}

  bool changed() const { return mChanged; }

  bool& changed() { return mChanged; }

  const nsString& oldValue() const { return mOldValue; }

  nsString& oldValue() { return mOldValue; }
};

/**
 * A check of LSNG being enabled, the value is latched once initialized so
 * changing the preference during runtime has no effect.
 * May be called on any thread in the parent process, but you should call
 * CachedNextGenLocalStorageEnabled if you know that NextGenLocalStorageEnabled
 * was already called because it is faster.
 * May be called on the main thread only in a content process.
 */
bool NextGenLocalStorageEnabled();

/**
 * Cached any-thread version of NextGenLocalStorageEnabled().
 */
bool CachedNextGenLocalStorageEnabled();

/**
 * Returns a success value containing a pair of origin attribute suffix and
 * origin key.
 */
Result<std::pair<nsCString, nsCString>, nsresult> GenerateOriginKey2(
    const mozilla::ipc::PrincipalInfo& aPrincipalInfo);

LogModule* GetLocalStorageLogger();

namespace localstorage {

QM_META_HANDLE_ERROR("LocalStorage"_ns)

}  // namespace localstorage

}  // namespace dom
}  // namespace mozilla

#endif  // mozilla_dom_localstorage_LocalStorageCommon_h