gecko-dev/dom/localstorage/LocalStorageCommon.h

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

249 lines
11 KiB
C
Raw Normal View History

/* -*- 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
/*
* 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 DOM File
* 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 DOM File 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 DOM File 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 DOM File 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 DOM File thread and database objects are used on the main thread. The
* returned actor would have to be migrated from the DOM File 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.
*/
#include "mozilla/Attributes.h"
#include "nsString.h"
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();
nsresult GenerateOriginKey2(const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
nsACString& aOriginAttrSuffix,
nsACString& aOriginKey);
LogModule* GetLocalStorageLogger();
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_localstorage_LocalStorageCommon_h