diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /dom/localstorage/LocalStorageCommon.h | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/localstorage/LocalStorageCommon.h')
-rw-r--r-- | dom/localstorage/LocalStorageCommon.h | 286 |
1 files changed, 286 insertions, 0 deletions
diff --git a/dom/localstorage/LocalStorageCommon.h b/dom/localstorage/LocalStorageCommon.h new file mode 100644 index 0000000000..b9c185bcaf --- /dev/null +++ b/dom/localstorage/LocalStorageCommon.h @@ -0,0 +1,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 |