summaryrefslogtreecommitdiffstats
path: root/dom/localstorage/LocalStorageCommon.h
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /dom/localstorage/LocalStorageCommon.h
parentInitial commit. (diff)
downloadfirefox-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.h286
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