зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1286798 - Part 53: Review code comments; r=janv,mrbkap,mccr8
This commit is contained in:
Родитель
35d317c919
Коммит
6c6e230a77
|
@ -49,6 +49,11 @@ interface nsIDOMStorageManager : nsISupports
|
|||
in AString aDocumentURI,
|
||||
[optional] in bool aPrivate);
|
||||
/**
|
||||
* DEPRECATED. The only good reason to use this was if you were writing a
|
||||
* test and wanted to hackily determine if a preload happened. That's now
|
||||
* covered by `nsILocalStorageManager.isPreloaded` and you should use that if
|
||||
* that's what you want. If LSNG is in use, this will throw.
|
||||
*
|
||||
* Returns instance of DOM storage object for given principal.
|
||||
* If there is no storage managed for the scope, then null is returned and
|
||||
* no object is created. Otherwise, an object (new) for the existing storage
|
||||
|
|
|
@ -31,6 +31,24 @@ class LSRequestChildCallback;
|
|||
class LSSimpleRequestChildCallback;
|
||||
class LSSnapshot;
|
||||
|
||||
/**
|
||||
* Minimal glue actor with standard IPC-managed new/delete existence that exists
|
||||
* primarily to track the continued existence of the LSDatabase in the child.
|
||||
* Most of the interesting bits happen via PBackgroundLSSnapshot.
|
||||
*
|
||||
* Mutual raw pointers are maintained between LSDatabase and this class that are
|
||||
* cleared at either (expected) when the child starts the deletion process
|
||||
* (SendDeleteMeInternal) or unexpected actor death (ActorDestroy).
|
||||
*
|
||||
* See `PBackgroundLSDatabase.ipdl` for more information.
|
||||
*
|
||||
*
|
||||
* ## Low-Level Lifecycle ##
|
||||
* - Created by LSObject::EnsureDatabase if it had to create a database.
|
||||
* - Deletion begun by LSDatabase's destructor invoking SendDeleteMeInternal
|
||||
* which will result in the parent sending __delete__ which destroys the
|
||||
* actor.
|
||||
*/
|
||||
class LSDatabaseChild final
|
||||
: public PBackgroundLSDatabaseChild
|
||||
{
|
||||
|
@ -78,6 +96,14 @@ private:
|
|||
override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Minimal IPC-managed (new/delete) actor that exists to receive and relay
|
||||
* "storage" events from changes to LocalStorage that take place in other
|
||||
* processes as their Snapshots are checkpointed to the canonical Datastore in
|
||||
* the parent process.
|
||||
*
|
||||
* See `PBackgroundLSObserver.ipdl` for more info.
|
||||
*/
|
||||
class LSObserverChild final
|
||||
: public PBackgroundLSObserverChild
|
||||
{
|
||||
|
@ -119,6 +145,17 @@ private:
|
|||
const nsString& aNewValue) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Minimal glue IPC-managed (new/delete) actor that is used by LSObject and its
|
||||
* RequestHelper to perform synchronous requests on top of an asynchronous
|
||||
* protocol.
|
||||
*
|
||||
* Takes an `LSReuestChildCallback` to be invoked when a response is received
|
||||
* via __delete__.
|
||||
*
|
||||
* See `PBackgroundLSRequest.ipdl`, `LSObject`, and `RequestHelper` for more
|
||||
* info.
|
||||
*/
|
||||
class LSRequestChild final
|
||||
: public PBackgroundLSRequestChild
|
||||
{
|
||||
|
@ -172,6 +209,15 @@ protected:
|
|||
{ }
|
||||
};
|
||||
|
||||
/**
|
||||
* Minimal glue IPC-managed (new/delete) actor used by `LocalStorageManager2` to
|
||||
* issue asynchronous requests in an asynchronous fashion.
|
||||
*
|
||||
* Takes an `LSSimpleRequestChildCallback` to be invoked when a response is
|
||||
* received via __delete__.
|
||||
*
|
||||
* See `PBackgroundLSSimpleRequest.ipdl` for more info.
|
||||
*/
|
||||
class LSSimpleRequestChild final
|
||||
: public PBackgroundLSSimpleRequestChild
|
||||
{
|
||||
|
@ -216,6 +262,16 @@ protected:
|
|||
{ }
|
||||
};
|
||||
|
||||
/**
|
||||
* Minimal IPC-managed (new/delete) actor that lasts as long as its owning
|
||||
* LSSnapshot.
|
||||
*
|
||||
* Mutual raw pointers are maintained between LSSnapshot and this class that are
|
||||
* cleared at either (expected) when the child starts the deletion process
|
||||
* (SendDeleteMeInternal) or unexpected actor death (ActorDestroy).
|
||||
*
|
||||
* See `PBackgroundLSSnapshot.ipdl` and `LSSnapshot` for more info.
|
||||
*/
|
||||
class LSSnapshotChild final
|
||||
: public PBackgroundLSSnapshotChild
|
||||
{
|
||||
|
|
|
@ -119,9 +119,25 @@ static_assert(kSQLiteGrowthIncrement >= 0 &&
|
|||
kSQLiteGrowthIncrement < uint32_t(INT32_MAX),
|
||||
"Must be 0 (disabled) or a positive multiple of the page size!");
|
||||
|
||||
/**
|
||||
* The database name for LocalStorage data in a per-origin directory.
|
||||
*/
|
||||
#define DATA_FILE_NAME "data.sqlite"
|
||||
/**
|
||||
* The journal corresponding to DATA_FILE_NAME. (We don't use WAL mode.)
|
||||
*/
|
||||
#define JOURNAL_FILE_NAME "data.sqlite-journal"
|
||||
|
||||
/**
|
||||
* How long between the first moment we know we have data to be written on a
|
||||
* `Connection` and when we should actually perform the write. This helps
|
||||
* limit disk churn under silly usage patterns and is historically consistent
|
||||
* with the previous, legacy implementation.
|
||||
*
|
||||
* Note that flushing happens downstream of Snapshot checkpointing and its
|
||||
* batch mechanism which helps avoid wasteful IPC in the case of silly content
|
||||
* code.
|
||||
*/
|
||||
const uint32_t kFlushTimeoutMs = 5000;
|
||||
|
||||
const char kPrivateBrowsingObserverTopic[] = "last-pb-context-exited";
|
||||
|
@ -129,13 +145,55 @@ const char kPrivateBrowsingObserverTopic[] = "last-pb-context-exited";
|
|||
const uint32_t kDefaultOriginLimitKB = 5 * 1024;
|
||||
const uint32_t kDefaultShadowWrites = true;
|
||||
const uint32_t kDefaultSnapshotPrefill = 4096;
|
||||
/**
|
||||
* LocalStorage data limit as determined by summing up the lengths of all string
|
||||
* keys and values. This is consistent with the legacy implementation and other
|
||||
* browser engines. This value should really only ever change in unit testing
|
||||
* where being able to lower it makes it easier for us to test certain edge
|
||||
* cases.
|
||||
*/
|
||||
const char kDefaultQuotaPref[] = "dom.storage.default_quota";
|
||||
/**
|
||||
* Should all mutations also be reflected in the "shadow" database, which is
|
||||
* the legacy webappsstore.sqlite database. When this is enabled, users can
|
||||
* downgrade their version of Firefox and/or otherwise fall back to the legacy
|
||||
* implementation without loss of data. (Older versions of Firefox will
|
||||
* recognize the presence of ls-archive.sqlite and purge it and the other
|
||||
* LocalStorage directories so privacy is maintained.)
|
||||
*/
|
||||
const char kShadowWritesPref[] = "dom.storage.shadow_writes";
|
||||
/**
|
||||
* Byte budget for sending data down to the LSSnapshot instance when it is first
|
||||
* created. If there is less data than this (measured by tallying the string
|
||||
* length of the keys and values), all data is sent, otherwise partial data is
|
||||
* sent. See `Snapshot`.
|
||||
*/
|
||||
const char kSnapshotPrefillPref[] = "dom.storage.snapshot_prefill";
|
||||
|
||||
/**
|
||||
* The amount of time a PreparedDatastore instance should stick around after a
|
||||
* preload is triggered in order to give time for the page to use LocalStorage
|
||||
* without triggering worst-case synchronous jank.
|
||||
*/
|
||||
const uint32_t kPreparedDatastoreTimeoutMs = 20000;
|
||||
|
||||
/**
|
||||
* Cold storage for LocalStorage data extracted from webappsstore.sqlite at
|
||||
* LSNG first-run that has not yet been migrated to its own per-origin directory
|
||||
* by use.
|
||||
*
|
||||
* In other words, at first run, LSNG copies the contents of webappsstore.sqlite
|
||||
* into this database. As requests are made for that LocalStorage data, the
|
||||
* contents are removed from this database and placed into per-origin QM
|
||||
* storage. So the contents of this database are always old, unused
|
||||
* LocalStorage data that we can potentially get rid of at some point in the
|
||||
* future.
|
||||
*/
|
||||
#define LS_ARCHIVE_FILE_NAME "ls-archive.sqlite"
|
||||
/**
|
||||
* The legacy LocalStorage database. Its contents are maintained as our
|
||||
* "shadow" database so that LSNG can be disabled without loss of user data.
|
||||
*/
|
||||
#define WEB_APPS_STORE_FILE_NAME "webappsstore.sqlite"
|
||||
|
||||
// Shadow database Write Ahead Log's maximum size is 512KB
|
||||
|
@ -948,6 +1006,16 @@ DetachShadowDatabase(mozIStorageConnection* aConnection)
|
|||
* Non-actor class declarations
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Coalescing manipulation queue used by `Connection` and `DataStore`. Used by
|
||||
* `Connection` to buffer and coalesce manipulations applied to the Datastore
|
||||
* in batches by Snapshot Checkpointing until flushed to disk. Used by
|
||||
* `Datastore` to update `DataStore::mOrderedItems` efficiently/for code
|
||||
* simplification. (DataStore does not actually depend on the coalescing, as
|
||||
* mutations are applied atomically when a Snapshot Checkpoints, and with
|
||||
* `Datastore::mValues` being updated at the same time the mutations are applied
|
||||
* to Datastore's mWriteOptimizer.)
|
||||
*/
|
||||
class WriteOptimizer final
|
||||
{
|
||||
class WriteInfo;
|
||||
|
@ -1001,6 +1069,12 @@ public:
|
|||
PerformWrites(Connection* aConnection, bool aShadowWrites);
|
||||
};
|
||||
|
||||
/**
|
||||
* Base class for specific mutations. Each subclass knows how to `Perform` the
|
||||
* manipulation against a `Connection` and the "shadow" database (legacy
|
||||
* webappsstore.sqlite database that exists so LSNG can be disabled/safely
|
||||
* downgraded from.)
|
||||
*/
|
||||
class WriteOptimizer::WriteInfo
|
||||
{
|
||||
public:
|
||||
|
@ -1020,6 +1094,9 @@ public:
|
|||
virtual ~WriteInfo() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* SetItem mutation where the key did not previously exist.
|
||||
*/
|
||||
class WriteOptimizer::AddItemInfo
|
||||
: public WriteInfo
|
||||
{
|
||||
|
@ -1056,6 +1133,9 @@ private:
|
|||
Perform(Connection* aConnection, bool aShadowWrites) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* SetItem mutation where the key already existed.
|
||||
*/
|
||||
class WriteOptimizer::UpdateItemInfo final
|
||||
: public AddItemInfo
|
||||
{
|
||||
|
@ -1100,6 +1180,9 @@ private:
|
|||
Perform(Connection* aConnection, bool aShadowWrites) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear mutation.
|
||||
*/
|
||||
class WriteOptimizer::ClearInfo final
|
||||
: public WriteInfo
|
||||
{
|
||||
|
@ -1300,6 +1383,7 @@ public:
|
|||
return mArchivedOriginScope;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Methods which can only be called on the owning thread.
|
||||
|
||||
// This method is used to asynchronously execute a connection datastore
|
||||
|
@ -1332,6 +1416,7 @@ public:
|
|||
void
|
||||
EndUpdateBatch();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Methods which can only be called on the connection thread.
|
||||
|
||||
nsresult
|
||||
|
@ -1471,17 +1556,54 @@ private:
|
|||
~ConnectionThread();
|
||||
};
|
||||
|
||||
/**
|
||||
* Canonical state of Storage for an origin, containing all keys and their
|
||||
* values in the parent process. Specifically, this is the state that will
|
||||
* be handed out to freshly created Snapshots and that will be persisted to disk
|
||||
* when the Connection's flush completes. State is mutated in batches as
|
||||
* Snapshot instances Checkpoint their mutations locally accumulated in the
|
||||
* child LSSnapshots.
|
||||
*/
|
||||
class Datastore final
|
||||
{
|
||||
RefPtr<DirectoryLock> mDirectoryLock;
|
||||
RefPtr<Connection> mConnection;
|
||||
RefPtr<QuotaObject> mQuotaObject;
|
||||
nsCOMPtr<nsIRunnable> mCompleteCallback;
|
||||
/**
|
||||
* PrepareDatastoreOps register themselves with the Datastore at
|
||||
* and unregister in PrepareDatastoreOp::Cleanup.
|
||||
*/
|
||||
nsTHashtable<nsPtrHashKey<PrepareDatastoreOp>> mPrepareDatastoreOps;
|
||||
/**
|
||||
* PreparedDatastore instances register themselves with their associated
|
||||
* Datastore at construction time and unregister at destruction time. They
|
||||
* hang around for kPreparedDatastoreTimeoutMs in order to keep the Datastore
|
||||
* from closing itself via MaybeClose(), thereby giving the document enough
|
||||
* time to load and access LocalStorage.
|
||||
*/
|
||||
nsTHashtable<nsPtrHashKey<PreparedDatastore>> mPreparedDatastores;
|
||||
/**
|
||||
* A database is live (and in this hashtable) if it has a live LSDatabase
|
||||
* actor. There is at most one Database per origin per content process. Each
|
||||
* Database corresponds to an LSDatabase in its associated content process.
|
||||
*/
|
||||
nsTHashtable<nsPtrHashKey<Database>> mDatabases;
|
||||
/**
|
||||
* A database is active if it has a non-null `mSnapshot`. As long as there
|
||||
* are any active databases final deltas can't be calculated and
|
||||
* `UpdateUsage()` can't be invoked.
|
||||
*/
|
||||
nsTHashtable<nsPtrHashKey<Database>> mActiveDatabases;
|
||||
/**
|
||||
* Non-authoritative hashtable representation of mOrderedItems for efficient
|
||||
* lookup.
|
||||
*/
|
||||
nsDataHashtable<nsStringHashKey, nsString> mValues;
|
||||
/**
|
||||
* The authoritative ordered state of the Datastore; mValue also exists as an
|
||||
* unordered hashtable for efficient lookup.
|
||||
*/
|
||||
nsTArray<LSItemInfo> mOrderedItems;
|
||||
nsTArray<int64_t> mPendingUsageDeltas;
|
||||
WriteOptimizer mWriteOptimizer;
|
||||
|
@ -1524,6 +1646,9 @@ public:
|
|||
bool
|
||||
IsPersistent() const
|
||||
{
|
||||
// Private-browsing is forbidden from touching disk, but
|
||||
// StorageAccess::eSessionScoped is allowed to touch disk because
|
||||
// QuotaManager's storage for such origins is wiped at shutdown.
|
||||
return mPrivateBrowsingId == 0;
|
||||
}
|
||||
|
||||
|
@ -1589,6 +1714,15 @@ public:
|
|||
void
|
||||
GetKeys(nsTArray<nsString>& aKeys) const;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Mutation Methods
|
||||
//
|
||||
// These are only called during Snapshot::RecvCheckpoint
|
||||
|
||||
/**
|
||||
* Used by Snapshot::RecvCheckpoint to set a key/value pair as part of a an
|
||||
* explicit batch.
|
||||
*/
|
||||
void
|
||||
SetItem(Database* aDatabase,
|
||||
const nsString& aDocumentURI,
|
||||
|
@ -1878,24 +2012,103 @@ private:
|
|||
override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Attempts to capture the state of the underlying Datastore at the time of its
|
||||
* creation so run-to-completion semantics can be honored.
|
||||
*
|
||||
* Rather than simply duplicate the contents of `DataStore::mValues` and
|
||||
* `Datastore::mOrderedItems` at the time of their creation, the Snapshot tracks
|
||||
* mutations to the Datastore as they happen, saving off the state of values as
|
||||
* they existed when the Snapshot was created. In other words, given an initial
|
||||
* Datastore state of { foo: 'bar', bar: 'baz' }, the Snapshot won't store those
|
||||
* values until it hears via `SaveItem` that "foo" is being over-written. At
|
||||
* that time, it will save off foo='bar' in mValues.
|
||||
*
|
||||
* ## Quota Allocation ##
|
||||
*
|
||||
* ## States ##
|
||||
*
|
||||
*/
|
||||
class Snapshot final
|
||||
: public PBackgroundLSSnapshotParent
|
||||
{
|
||||
/**
|
||||
* The Database that owns this snapshot. There is a 1:1 relationship between
|
||||
* snapshots and databases.
|
||||
*/
|
||||
RefPtr<Database> mDatabase;
|
||||
RefPtr<Datastore> mDatastore;
|
||||
/**
|
||||
* The set of keys for which values have been sent to the child LSSnapshot.
|
||||
* Cleared once all values have been sent as indicated by
|
||||
* mLoadedItems.Count()==mTotalLength and therefore mLoadedAllItems should be
|
||||
* true. No requests should be received for keys already in this set, and
|
||||
* this is enforced by fatal IPC error (unless fuzzing).
|
||||
*/
|
||||
nsTHashtable<nsStringHashKey> mLoadedItems;
|
||||
/**
|
||||
* The set of keys for which a RecvLoadItem request was received but there
|
||||
* was no such key, and so null was returned. The child LSSnapshot will also
|
||||
* cache these values, so redundant requests are also handled with fatal
|
||||
* process termination just like for mLoadedItems. Also cleared when
|
||||
* mLoadedAllItems becomes true because then the child can infer that all
|
||||
* other values must be null. (Note: this could also be done when
|
||||
* mLoadKeysReceived is true as a further optimization, but is not.)
|
||||
*/
|
||||
nsTHashtable<nsStringHashKey> mUnknownItems;
|
||||
/**
|
||||
* Values that have changed in mDatastore as reported by SaveItem
|
||||
* notifications that are not yet known to the child LSSnapshot.
|
||||
*
|
||||
* The naive way to snapshot the state of mDatastore would be to duplicate its
|
||||
* internal mValues at the time of our creation, but that is wasteful if few
|
||||
* changes are made to the Datastore's state. So we only track values that
|
||||
* are changed/evicted from the Datastore as they happen, as reported to us by
|
||||
* SaveItem notifications.
|
||||
*/
|
||||
nsDataHashtable<nsStringHashKey, nsString> mValues;
|
||||
/**
|
||||
* Latched state of mDatastore's keys during a SaveItem notification with
|
||||
* aAffectsOrder=true. The ordered keys needed to be saved off so that a
|
||||
* consistent ordering could be presented to the child LSSnapshot when it asks
|
||||
* for them via RecvLoadKeys.
|
||||
*/
|
||||
nsTArray<nsString> mKeys;
|
||||
nsString mDocumentURI;
|
||||
/**
|
||||
* The number of key/value pairs that were present in the Datastore at the
|
||||
* time the snapshot was created. Once we have sent this many values to the
|
||||
* child LSSnapshot, we can infer that it has received all of the keys/values
|
||||
* and set mLoadedAllItems to true and clear mLoadedItems and mUnknownItems.
|
||||
* Note that knowing the keys/values is not the same as knowing their ordering
|
||||
* and so mKeys may be retained.
|
||||
*/
|
||||
uint32_t mTotalLength;
|
||||
int64_t mUsage;
|
||||
int64_t mPeakUsage;
|
||||
/**
|
||||
* True if SaveItem has saved mDatastore's keys into mKeys because a SaveItem
|
||||
* notification with aAffectsOrder=true was received.
|
||||
*/
|
||||
bool mSavedKeys;
|
||||
bool mActorDestroyed;
|
||||
bool mFinishReceived;
|
||||
bool mLoadedReceived;
|
||||
/**
|
||||
* True if LSSnapshot's mLoadState should be LoadState::AllOrderedItems or
|
||||
* LoadState::AllUnorderedItems. It will be AllOrderedItems if the initial
|
||||
* snapshot contained all the data or if the state was AllOrderedKeys and
|
||||
* successive RecvLoadItem requests have resulted in the LSSnapshot being told
|
||||
* all of the key/value pairs. It will be AllUnorderedItems if the state was
|
||||
* LoadState::Partial and successive RecvLoadItem requests got all the
|
||||
* keys/values but the key ordering was not retrieved.
|
||||
*/
|
||||
bool mLoadedAllItems;
|
||||
/**
|
||||
* True if LSSnapshot's mLoadState should be LoadState::AllOrderedItems or
|
||||
* AllOrderedKeys. This can occur because of the initial snapshot, or because
|
||||
* a RecvLoadKeys request was received.
|
||||
*/
|
||||
bool mLoadKeysReceived;
|
||||
bool mSentMarkDirty;
|
||||
|
||||
|
@ -1933,6 +2146,11 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called via NotifySnapshots by Datastore whenever it is updating its
|
||||
* internal state so that snapshots can save off the state of a value at the
|
||||
* time of their creation.
|
||||
*/
|
||||
void
|
||||
SaveItem(const nsAString& aKey,
|
||||
const nsAString& aOldValue,
|
||||
|
@ -4984,6 +5202,8 @@ Datastore::NotifyObservers(Database* aDatabase,
|
|||
|
||||
MOZ_ASSERT(array);
|
||||
|
||||
// We do not want to send information about events back to the content process
|
||||
// that caused the change.
|
||||
PBackgroundParent* databaseBackgroundActor = aDatabase->Manager();
|
||||
|
||||
for (Observer* observer : *array) {
|
||||
|
|
|
@ -28,26 +28,85 @@ class RequestHelper;
|
|||
StaticMutex gRequestHelperMutex;
|
||||
RequestHelper* gRequestHelper = nullptr;
|
||||
|
||||
/**
|
||||
* Main-thread helper that implements the blocking logic required by
|
||||
* LocalStorage's synchronous semantics. StartAndReturnResponse pushes an
|
||||
* event queue which is a new event target and spins its nested event loop until
|
||||
* a result is received or an abort is necessary due to a PContent-managed sync
|
||||
* IPC message being received. Note that because the event queue is its own
|
||||
* event target, there is no re-entrancy. Normal main-thread runnables will not
|
||||
* get a chance to run. See StartAndReturnResponse() for info on this choice.
|
||||
*
|
||||
* The normal life-cycle of this method looks like:
|
||||
* - Main Thread: LSObject::DoRequestSynchronously creates a RequestHelper and
|
||||
* invokes StartAndReturnResponse(). It pushes the event queue and Dispatches
|
||||
* the RequestHelper to the DOM File Thread.
|
||||
* - DOM File Thread: RequestHelper::Run is called, invoking Start() which
|
||||
* invokes LSObject::StartRequest, which gets-or-creates the PBackground actor
|
||||
* if necessary (which may dispatch a runnable to the nested event queue on
|
||||
* the main thread), sends LSRequest constructor which is provided with a
|
||||
* callback reference to the RequestHelper. State advances to ResponsePending.
|
||||
* - DOM File Thread:: LSRequestChild::Recv__delete__ is received, which invokes
|
||||
* RequestHelepr::OnResponse, advancing the state to Finishing and dispatching
|
||||
* RequestHelper to its own nested event target.
|
||||
* - Main Thread: RequestHelper::Run is called, invoking Finish() which advances
|
||||
* the state to Complete and sets mWaiting to false, allowing the nested event
|
||||
* loop being spun by StartAndReturnResponse to cease spinning and return the
|
||||
* received response.
|
||||
*
|
||||
* See LocalStorageCommon.h for high-level context and method comments for
|
||||
* low-level details.
|
||||
*/
|
||||
class RequestHelper final
|
||||
: public Runnable
|
||||
, public LSRequestChildCallback
|
||||
{
|
||||
enum class State
|
||||
{
|
||||
/**
|
||||
* The RequestHelper has been created and dispatched to the DOM File Thread.
|
||||
*/
|
||||
Initial,
|
||||
/**
|
||||
* Start() has been invoked on the DOM File Thread and
|
||||
* LSObject::StartRequest has been invoked from there, sending an IPC
|
||||
* message to PBackground to service the request. We stay in this state
|
||||
* until a response is received.
|
||||
*/
|
||||
ResponsePending,
|
||||
/**
|
||||
* A response has been received and RequestHelper has been dispatched back
|
||||
* to the nested event loop to call Finish().
|
||||
*/
|
||||
Finishing,
|
||||
/**
|
||||
* Finish() has been called on the main thread. The nested event loop will
|
||||
* terminate imminently and the received response returned to the caller of
|
||||
* StartAndReturnResponse.
|
||||
*/
|
||||
Complete
|
||||
};
|
||||
|
||||
// The object we are issuing a request on behalf of. Present because of the
|
||||
// need to invoke LSObject::StartRequest off the main thread. Dropped on
|
||||
// return to the main-thread in Finish().
|
||||
RefPtr<LSObject> mObject;
|
||||
// The thread the RequestHelper was created on. This should be the main
|
||||
// thread.
|
||||
nsCOMPtr<nsIEventTarget> mOwningEventTarget;
|
||||
// The pushed event queue that we use to spin the event loop without
|
||||
// processing any of the events dispatched at the mOwningEventTarget (which
|
||||
// would result in re-entrancy and violate LocalStorage semantics).
|
||||
nsCOMPtr<nsIEventTarget> mNestedEventTarget;
|
||||
// The IPC actor handling the request with standard IPC allocation rules.
|
||||
// Our reference is nulled in OnResponse which corresponds to the actor's
|
||||
// __destroy__ method.
|
||||
LSRequestChild* mActor;
|
||||
const LSRequestParams mParams;
|
||||
LSRequestResponse mResponse;
|
||||
nsresult mResultCode;
|
||||
State mState;
|
||||
// Control flag for the nested event loop; once set to false, the loop ends.
|
||||
bool mWaiting;
|
||||
|
||||
public:
|
||||
|
@ -350,6 +409,14 @@ LSObject::GetOriginQuotaUsage() const
|
|||
{
|
||||
AssertIsOnOwningThread();
|
||||
|
||||
// It's not necessary to return an actual value here. This method is
|
||||
// implemented only because the SessionStore currently needs it to cap the
|
||||
// amount of data it persists to disk (via nsIDOMWindowUtils.getStorageUsage).
|
||||
// Any callers that want to know about storage usage should be asking
|
||||
// QuotaManager directly.
|
||||
//
|
||||
// Note: This may change as LocalStorage is repurposed to be the new
|
||||
// SessionStorage backend.
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,28 @@ 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
|
||||
{
|
||||
|
@ -52,10 +74,21 @@ class LSObject final
|
|||
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,
|
||||
|
@ -71,6 +104,16 @@ public:
|
|||
static already_AddRefed<nsIEventTarget>
|
||||
GetSyncLoopEventTarget();
|
||||
|
||||
/**
|
||||
* Helper invoked by ContentChild::OnChannelReceivedMessage when a sync IPC
|
||||
* message is received. This will be invoked on the IPC I/O thread and it's
|
||||
* necessary to unblock the main thread when this happens to avoid the
|
||||
* potential for browser deadlock. This should only occur in (ugly) testing
|
||||
* scenarios where CPOWs are in use.
|
||||
*
|
||||
* Cancellation will result in the underlying LSRequest being explicitly
|
||||
* canceled, resulting in the parent sending an NS_ERROR_FAILURE result.
|
||||
*/
|
||||
static void
|
||||
CancelSyncLoop();
|
||||
|
||||
|
@ -135,6 +178,8 @@ public:
|
|||
Clear(nsIPrincipal& aSubjectPrincipal,
|
||||
ErrorResult& aError) override;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Testing Methods: See Storage.h
|
||||
void
|
||||
Open(nsIPrincipal& aSubjectPrincipal,
|
||||
ErrorResult& aError) override;
|
||||
|
@ -151,6 +196,8 @@ public:
|
|||
EndExplicitSnapshot(nsIPrincipal& aSubjectPrincipal,
|
||||
ErrorResult& aError) override;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(LSObject, Storage)
|
||||
|
||||
|
@ -170,12 +217,32 @@ private:
|
|||
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,
|
||||
|
|
|
@ -12,6 +12,24 @@ namespace dom {
|
|||
|
||||
class LSObserverChild;
|
||||
|
||||
/**
|
||||
* Effectively just a refcounted life-cycle management wrapper around
|
||||
* LSObserverChild which exists to receive "storage" event information from
|
||||
* other processes. (Same-process events are handled within the process, see
|
||||
* `LSObject::OnChange`.)
|
||||
*
|
||||
* ## Lifecycle ##
|
||||
* - Created by LSObject::EnsureObserver via synchronous LSRequest idiom
|
||||
* whenever the first window's origin adds a "storage" event. Placed in the
|
||||
* gLSObservers LSObserverHashtable for subsequent LSObject's via
|
||||
* LSObserver::Get lookup.
|
||||
* - The LSObserverChild directly handles "Observe" messages, shunting them
|
||||
* directly to Storage::NotifyChange which does all the legwork of notifying
|
||||
* windows about "storage" events.
|
||||
* - Destroyed when refcount goes to zero due to all owning LSObjects being
|
||||
* destroyed or having their `LSObject::DropObserver` methods invoked due to
|
||||
* the last "storage" event listener being removed from the owning window.
|
||||
*/
|
||||
class LSObserver final
|
||||
{
|
||||
friend class LSObject;
|
||||
|
|
|
@ -20,12 +20,50 @@ class LSSnapshot final
|
|||
: public nsIRunnable
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* The LoadState expresses what subset of information a snapshot has from the
|
||||
* authoritative Datastore in the parent process. The initial snapshot is
|
||||
* populated heuristically based on the size of the keys and size of the items
|
||||
* (inclusive of the key value; item is key+value, not just value) of the
|
||||
* entire datastore relative to the configured prefill limit (via pref
|
||||
* "dom.storage.snapshot_prefill" exposed as gSnapshotPrefill in bytes).
|
||||
*
|
||||
* If there's less data than the limit, we send both keys and values and end
|
||||
* up as AllOrderedItems. If there's enough room for all the keys but not
|
||||
* all the values, we end up as AllOrderedKeys with as many values present as
|
||||
* would fit. If there's not enough room for all the keys, then we end up as
|
||||
* Partial with as many key-value pairs as will fit.
|
||||
*
|
||||
* The state AllUnorderedItems can only be reached by code getting items one
|
||||
* by one.
|
||||
*/
|
||||
enum class LoadState
|
||||
{
|
||||
/**
|
||||
* Class constructed, Init(LSSnapshotInitInfo) has not been invoked yet.
|
||||
*/
|
||||
Initial,
|
||||
/**
|
||||
* Some keys and their values are known.
|
||||
*/
|
||||
Partial,
|
||||
/**
|
||||
* All the keys are known in order, but some values are unknown.
|
||||
*/
|
||||
AllOrderedKeys,
|
||||
/**
|
||||
* All keys and their values are known, but in an arbitrary order.
|
||||
*/
|
||||
AllUnorderedItems,
|
||||
/**
|
||||
* All keys and their values are known and are present in their canonical
|
||||
* order. This is everything, and is the preferred case. The initial
|
||||
* population will send this info when the size of all items is less than
|
||||
* the prefill threshold.
|
||||
*
|
||||
* mValues will contain all keys and values, mLoadedItems and mUnknownItems
|
||||
* are unused.
|
||||
*/
|
||||
AllOrderedItems,
|
||||
EndGuard
|
||||
};
|
||||
|
|
|
@ -186,6 +186,13 @@ 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;
|
||||
|
@ -221,9 +228,16 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Main-thread-only check of LSNG being enabled, the value is latched once
|
||||
* initialized so changing the preference during runtime has no effect.
|
||||
*/
|
||||
bool
|
||||
NextGenLocalStorageEnabled();
|
||||
|
||||
/**
|
||||
* Cached any-thread version of NextGenLocalStorageEnabled().
|
||||
*/
|
||||
bool
|
||||
CachedNextGenLocalStorageEnabled();
|
||||
|
||||
|
|
|
@ -133,6 +133,11 @@ LocalStorageManager2::PrecacheStorage(nsIPrincipal* aPrincipal,
|
|||
MOZ_ASSERT(aPrincipal);
|
||||
MOZ_ASSERT(_retval);
|
||||
|
||||
// This method was created as part of the e10s-ification of the old LS
|
||||
// implementation to perform a preload in the content/current process. That's
|
||||
// not how things work in LSNG. Instead everything happens in the parent
|
||||
// process, triggered by the official preloading spot,
|
||||
// ContentParent::AboutToLoadHttpFtpWyciwygDocumentForChild.
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
|
@ -182,6 +187,8 @@ LocalStorageManager2::CloneStorage(Storage* aStorageToCloneFrom)
|
|||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aStorageToCloneFrom);
|
||||
|
||||
// Cloning is specific to sessionStorage; state is forked when a new tab is
|
||||
// opened from an existing tab.
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
|
@ -195,6 +202,7 @@ LocalStorageManager2::CheckStorage(nsIPrincipal* aPrincipal,
|
|||
MOZ_ASSERT(aStorage);
|
||||
MOZ_ASSERT(_retval);
|
||||
|
||||
// Only used by sessionStorage.
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,19 @@ class LSRequestParams;
|
|||
class LSSimpleRequestParams;
|
||||
class Promise;
|
||||
|
||||
/**
|
||||
* Under LSNG this exposes nsILocalStorageManager::Preload to ContentParent to
|
||||
* trigger preloading. Otherwise, this is basically just a place for test logic
|
||||
* that doesn't make sense to put directly on the Storage WebIDL interface.
|
||||
*
|
||||
* Previously, the nsIDOMStorageManager XPCOM interface was also used by
|
||||
* nsGlobalWindowInner to interact with LocalStorage, but in these de-XPCOM
|
||||
* days, we've moved to just directly reference the relevant concrete classes
|
||||
* (ex: LSObject) directly.
|
||||
*
|
||||
* Note that testing methods are now also directly exposed on the Storage WebIDL
|
||||
* interface for simplicity/sanity.
|
||||
*/
|
||||
class LocalStorageManager2 final
|
||||
: public nsIDOMStorageManager
|
||||
, public nsILocalStorageManager
|
||||
|
@ -31,10 +44,20 @@ public:
|
|||
private:
|
||||
~LocalStorageManager2();
|
||||
|
||||
/**
|
||||
* Helper to trigger an LSRequest and resolve/reject the provided promise when
|
||||
* the result comes in. This routine is notable because the LSRequest
|
||||
* mechanism is normally used synchronously from content, but here it's
|
||||
* exposed asynchronously.
|
||||
*/
|
||||
nsresult
|
||||
StartRequest(Promise* aPromise,
|
||||
const LSRequestParams& aParams);
|
||||
|
||||
/**
|
||||
* Helper to trigger an LSSimpleRequst and resolve/reject the provided promise
|
||||
* when the result comes in.
|
||||
*/
|
||||
nsresult
|
||||
StartSimpleRequest(Promise* aPromise,
|
||||
const LSSimpleRequestParams& aParams);
|
||||
|
|
|
@ -13,21 +13,67 @@ using mozilla::dom::LSSnapshot::LoadState
|
|||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
/**
|
||||
* LocalStorage key/value pair wire representations. `value` may be void in
|
||||
* cases where there is a value but it is not being sent for memory/bandwidth
|
||||
* conservation purposes. (It's not possible to have a null/undefined `value`
|
||||
* as Storage is defined explicitly as a String store.)
|
||||
*/
|
||||
struct LSItemInfo
|
||||
{
|
||||
nsString key;
|
||||
nsString value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initial LSSnapshot state as produced by Datastore::GetSnapshotInitInfo. See
|
||||
* `LSSnapshot::LoadState` for more details about the possible states and a
|
||||
* high level overview.
|
||||
*/
|
||||
struct LSSnapshotInitInfo
|
||||
{
|
||||
/**
|
||||
* As many key/value or key/void pairs as the snapshot prefill byte budget
|
||||
* allowed.
|
||||
*/
|
||||
LSItemInfo[] itemInfos;
|
||||
/**
|
||||
* The total number of key/value pairs in LocalStorage for this origin at the
|
||||
* time the snapshot was created. (And the point of the snapshot is to
|
||||
* conceptually freeze the state of the Datastore in time, so this value does
|
||||
* not change despite what other LSDatabase objects get up to in other
|
||||
* processes.)
|
||||
*/
|
||||
uint32_t totalLength;
|
||||
/**
|
||||
* The current amount of LocalStorage usage as measured by the summing the
|
||||
* nsString Length() of both the key and the value over all stored pairs.
|
||||
*/
|
||||
int64_t initialUsage;
|
||||
/**
|
||||
* The amount of storage allowed to be used by the Snapshot without requesting
|
||||
* more storage space via IncreasePeakUsage. This is the `initialUsage` plus
|
||||
* 0 or more bytes of space. If space was available, the increase will be the
|
||||
* `requestedSize` from the PBackgroundLSSnapshot constructor. If the
|
||||
* LocalStorage usage was already close to the limit, then the fallback is the
|
||||
* `minSize` requested, or 0 if there wasn't space for that.
|
||||
*/
|
||||
int64_t peakUsage;
|
||||
// See `LSSnapshot::LoadState` in `LSSnapshot.h`
|
||||
LoadState loadState;
|
||||
};
|
||||
|
||||
/**
|
||||
* This protocol is asynchronously created via constructor on PBackground but
|
||||
* has synchronous semantics from the perspective of content on the main thread.
|
||||
* The construction potentially involves waiting for disk I/O to load the
|
||||
* LocalStorage data from disk as well as related QuotaManager checks, so async
|
||||
* calls to PBackground are the only viable mechanism because blocking
|
||||
* PBackground is not acceptable. (Note that an attempt is made to minimize any
|
||||
* I/O latency by triggering preloading from
|
||||
* ContentParent::AboutToLoadHttpFtpWyciwygDocumentForChild, the central place
|
||||
* for pre-loading.)
|
||||
*/
|
||||
sync protocol PBackgroundLSDatabase
|
||||
{
|
||||
manager PBackground;
|
||||
|
@ -45,8 +91,28 @@ parent:
|
|||
// and the parent then sends the __delete__ message to the child.
|
||||
async DeleteMe();
|
||||
|
||||
/**
|
||||
* Sent in response to a `RequestAllowToClose` message once the snapshot
|
||||
* cleanup has happened OR from LSDatabase's destructor if AllowToClose has
|
||||
* not already been reported.
|
||||
*/
|
||||
async AllowToClose();
|
||||
|
||||
/**
|
||||
* Invoked to create an LSSnapshot backed by a Snapshot in PBackground that
|
||||
* presents an atomic and consistent view of the state of the authoritative
|
||||
* Datastore state in the parent.
|
||||
*
|
||||
* This needs to be synchronous because LocalStorage's semantics are
|
||||
* synchronous. Note that the Datastore in the PBackground parent already
|
||||
* has the answers to this request immediately available without needing to
|
||||
* consult any other threads or perform any I/O. Additionally, the response
|
||||
* is explicitly bounded in size by the tunable snapshot prefill byte limit.
|
||||
*
|
||||
* @param increasePeakUsage
|
||||
* Whether the parent should attempt to pre-allocate some amount of quota
|
||||
* usage to the Snapshot.
|
||||
*/
|
||||
sync PBackgroundLSSnapshot(nsString documentURI,
|
||||
bool increasePeakUsage,
|
||||
int64_t requestedSize,
|
||||
|
@ -54,8 +120,27 @@ parent:
|
|||
returns (LSSnapshotInitInfo initInfo);
|
||||
|
||||
child:
|
||||
/**
|
||||
* Only sent by the parent in response to the child's DeleteMe request.
|
||||
*/
|
||||
async __delete__();
|
||||
|
||||
/**
|
||||
* Request to close the LSDatabase, checkpointing and finishing any
|
||||
* outstanding snapshots so no state is lost. This request is issued when
|
||||
* QuotaManager is shutting down or is aborting operations for an origin or
|
||||
* process. Once the snapshot has cleaned up, AllowToClose will be sent to
|
||||
* the parent.
|
||||
*
|
||||
* Note that the QuotaManager shutdown process is more likely to happen in
|
||||
* unit tests where we explicitly reset the QuotaManager. At runtime, we
|
||||
* expect windows to be closed and content processes terminated well before
|
||||
* QuotaManager shutdown would actually occur.
|
||||
*
|
||||
* Also, Operations are usually aborted for an origin due to privacy API's
|
||||
* clearing data for an origin. Operations are aborted for a process by
|
||||
* ContentParent::ShutDownProcess.
|
||||
*/
|
||||
async RequestAllowToClose();
|
||||
};
|
||||
|
||||
|
|
|
@ -9,16 +9,42 @@ include PBackgroundSharedTypes;
|
|||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
/**
|
||||
* The observer protocol sends "storage" event notifications for changes to
|
||||
* LocalStorage that take place in other processes as their Snapshots are
|
||||
* Checkpointed to the canonical Datastore in the parent process. Same-process
|
||||
* notifications are generated as mutations happen.
|
||||
*
|
||||
* Note that mutations are never generated for redundant mutations. Setting the
|
||||
* key "foo" to have value "bar" when it already has value "bar" will never
|
||||
* result in a "storage" event.
|
||||
*/
|
||||
async protocol PBackgroundLSObserver
|
||||
{
|
||||
manager PBackground;
|
||||
|
||||
parent:
|
||||
/**
|
||||
* Sent by the LSObserver's destructor when it's going away. Any Observe
|
||||
* messages received after this is sent will be ignored. Which is fine,
|
||||
* because there should be nothing around left to hear. In the event a new
|
||||
* page came into existence, its Observer creation will happen (effectively)
|
||||
* synchronously.
|
||||
*/
|
||||
async DeleteMe();
|
||||
|
||||
child:
|
||||
/**
|
||||
* Only sent by the parent in response to a deletion request.
|
||||
*/
|
||||
async __delete__();
|
||||
|
||||
/**
|
||||
* Sent by the parent process as Snapshots from other processes are
|
||||
* Checkpointed, applying their mutations. The child actor currently directly
|
||||
* shunts these to Storage::NotifyChange to generate "storage" events for
|
||||
* immediate dispatch.
|
||||
*/
|
||||
async Observe(PrincipalInfo principalInfo,
|
||||
uint32_t privateBrowsingId,
|
||||
nsString documentURI,
|
||||
|
|
|
@ -26,6 +26,10 @@ struct LSRequestPrepareObserverResponse
|
|||
uint64_t observerId;
|
||||
};
|
||||
|
||||
/**
|
||||
* Discriminated union which can contain an error code (`nsresult`) or
|
||||
* particular request response.
|
||||
*/
|
||||
union LSRequestResponse
|
||||
{
|
||||
nsresult;
|
||||
|
@ -33,6 +37,15 @@ union LSRequestResponse
|
|||
LSRequestPrepareObserverResponse;
|
||||
};
|
||||
|
||||
/**
|
||||
* An asynchronous protocol for issuing requests that are used in a synchronous
|
||||
* fashion by LocalStorage via LSObject's RequestHelper mechanism. This differs
|
||||
* from LSSimpleRequest which is implemented and used asynchronously.
|
||||
*
|
||||
* See `PBackgroundLSSharedTypes.ipdlh` for more on the request types, the
|
||||
* response types above for their corresponding responses, and `RequestHelper`
|
||||
* for more on the usage and lifecycle of this mechanism.
|
||||
*/
|
||||
protocol PBackgroundLSRequest
|
||||
{
|
||||
manager PBackground;
|
||||
|
@ -50,13 +63,39 @@ parent:
|
|||
// because we are blocking the main thread in the content process.
|
||||
// The dead lock is prevented by canceling our nested event loop in the
|
||||
// content process when we receive a synchronous IPC message from the parent.
|
||||
//
|
||||
// Note that cancellation isn't instantaneous. It's just an asynchronous flow
|
||||
// that definitely doesn't involve the main thread in the parent process, so
|
||||
// we're guaranteed to unblock the main-thread in the content process and
|
||||
// allow the sync IPC to make progress. When Cancel() is received by the
|
||||
// parent, it will Send__delete__. The child will either send Cancel or
|
||||
// Finish, but not both.
|
||||
async Cancel();
|
||||
|
||||
/**
|
||||
* Sent by the child in response to Ready, requesting that __delete__ be sent
|
||||
* with the result. The child will either send Finish or Cancel, but not
|
||||
* both. No further message will be sent from the child after invoking one.
|
||||
*/
|
||||
async Finish();
|
||||
|
||||
child:
|
||||
/**
|
||||
* The deletion is sent with the result of the request directly in response to
|
||||
* either Cancel or Finish.
|
||||
*/
|
||||
async __delete__(LSRequestResponse response);
|
||||
|
||||
/**
|
||||
* Sent by the parent when it has completed whatever async stuff it needs to
|
||||
* do and is ready to send the results. It then awaits the Finish() call to
|
||||
* send the results. This may seem redundant, but it's not. If the
|
||||
* __delete__ was sent directly, it's possible there could be a race where
|
||||
* Cancel() would be received by the parent after it had already sent
|
||||
* __delete__. (Which may no longer be fatal thanks to improvements to the
|
||||
* IPC layer, but it would still lead to warnings, etc. And we don't
|
||||
* expect PBackground to be highly contended nor the DOM File thread.)
|
||||
*/
|
||||
async Ready();
|
||||
};
|
||||
|
||||
|
|
|
@ -7,17 +7,37 @@ include protocol PBackground;
|
|||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
/**
|
||||
* Response to a `LSSimpleRequestPreloadedParams` request indicating whether the
|
||||
* origin was preloaded.
|
||||
*/
|
||||
struct LSSimpleRequestPreloadedResponse
|
||||
{
|
||||
bool preloaded;
|
||||
};
|
||||
|
||||
/**
|
||||
* Discriminated union which can contain an error code (`nsresult`) or
|
||||
* particular simple request response.
|
||||
*/
|
||||
union LSSimpleRequestResponse
|
||||
{
|
||||
nsresult;
|
||||
LSSimpleRequestPreloadedResponse;
|
||||
};
|
||||
|
||||
/**
|
||||
* Simple requests are async-only from both a protocol perspective and the
|
||||
* manner in which they're used. In comparison, PBackgroundLSRequests are
|
||||
* async only from a protocol perspective; they are used synchronously from the
|
||||
* main thread via LSObject's RequestHelper mechanism. (With the caveat that
|
||||
* nsILocalStorageManager does expose LSRequests asynchronously.)
|
||||
*
|
||||
* These requests use the common idiom where the arguments to the request are
|
||||
* sent in the constructor and the result is sent in the __delete__ response.
|
||||
* Request types are indicated by the Params variant used and those live in
|
||||
* `PBackgroundLSSharedTypes.ipdlh`.
|
||||
*/
|
||||
protocol PBackgroundLSSimpleRequest
|
||||
{
|
||||
manager PBackground;
|
||||
|
|
|
@ -25,6 +25,9 @@ struct LSClearInfo
|
|||
{
|
||||
};
|
||||
|
||||
/**
|
||||
* Union of LocalStorage mutation types.
|
||||
*/
|
||||
union LSWriteInfo
|
||||
{
|
||||
LSSetItemInfo;
|
||||
|
@ -45,12 +48,42 @@ parent:
|
|||
|
||||
async Loaded();
|
||||
|
||||
/**
|
||||
* Invoked on demand to load an item that didn't fit into the initial
|
||||
* snapshot prefill.
|
||||
*
|
||||
* This needs to be synchronous because LocalStorage's semantics are
|
||||
* synchronous. Note that the Snapshot in the PBackground parent already
|
||||
* has the answers to this request immediately available without needing to
|
||||
* consult any other threads or perform any I/O.
|
||||
*/
|
||||
sync LoadItem(nsString key)
|
||||
returns (nsString value);
|
||||
|
||||
/**
|
||||
* Invoked on demand to load all keys in in their canonical order if they
|
||||
* didn't fit into the initial snapshot prefill.
|
||||
*
|
||||
* This needs to be synchronous because LocalStorage's semantics are
|
||||
* synchronous. Note that the Snapshot in the PBackground parent already
|
||||
* has the answers to this request immediately available without needing to
|
||||
* consult any other threads or perform any I/O.
|
||||
*/
|
||||
sync LoadKeys()
|
||||
returns (nsString[] keys);
|
||||
|
||||
/**
|
||||
* This needs to be synchronous because LocalStorage's semantics are
|
||||
* synchronous. Note that the Snapshot in the PBackground parent typically
|
||||
* doesn't need to consult any other threads or perform any I/O to handle
|
||||
* this request. However, it has to call a quota manager method that can
|
||||
* potentially do I/O directly on the PBackground thread. It can only happen
|
||||
* rarely in a storage pressure (low storage space) situation. Specifically,
|
||||
* after we get a list of origin directories for eviction, we will delete
|
||||
* them directly on the PBackground thread. This doesn't cause any
|
||||
* performance problems, but avoiding I/O completely might need to be done as
|
||||
* a futher optimization.
|
||||
*/
|
||||
sync IncreasePeakUsage(int64_t requestedSize, int64_t minSize)
|
||||
returns (int64_t size);
|
||||
|
||||
|
@ -60,6 +93,18 @@ parent:
|
|||
sync Ping();
|
||||
|
||||
child:
|
||||
/**
|
||||
* Compels the child LSSnapshot to Checkpoint() and Finish(), effectively
|
||||
* compelling the snapshot to flush any issued mutations and close itself.
|
||||
* The child LSSnapshot does that either immediately if it's just waiting
|
||||
* to be reused or when it gets into a stable state.
|
||||
*
|
||||
* This message is expected to be sent in the following two cases only:
|
||||
* 1. The state of the underlying Datastore starts to differ from the state
|
||||
* captured at the time of snapshot creation.
|
||||
* 2. The last private browsing context exits. And in that case we expect
|
||||
* all private browsing globals to already have been destroyed.
|
||||
*/
|
||||
async MarkDirty();
|
||||
|
||||
async __delete__();
|
||||
|
|
|
@ -8,11 +8,25 @@
|
|||
|
||||
interface nsIPrincipal;
|
||||
|
||||
/**
|
||||
* Methods specific to LocalStorage, see nsIDOMStorageManager for methods shared
|
||||
* with SessionStorage. Methods may migrate there as SessionStorage is
|
||||
* overhauled.
|
||||
*/
|
||||
[scriptable, builtinclass, uuid(d4f534da-2744-4db3-8774-8b187c64ade9)]
|
||||
interface nsILocalStorageManager : nsISupports
|
||||
{
|
||||
readonly attribute boolean nextGenLocalStorageEnabled;
|
||||
|
||||
/**
|
||||
* Trigger preload of LocalStorage for the given principal. For use by
|
||||
* ContentParent::AboutToLoadHttpFtpWyciwygDocumentForChild to maximize the
|
||||
* amount of time we have to load the data off disk before the page might
|
||||
* attempt to touch LocalStorage.
|
||||
*
|
||||
* This method will not create a QuotaManager-managed directory on disk if
|
||||
* one does not already exist for the principal.
|
||||
*/
|
||||
[implicit_jscontext] nsISupports
|
||||
preload(in nsIPrincipal aPrincipal);
|
||||
|
||||
|
|
|
@ -110,6 +110,13 @@ public:
|
|||
|
||||
bool IsSessionOnly() const { return mIsSessionOnly; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Testing Methods:
|
||||
//
|
||||
// These methods are exposed on the `Storage` WebIDL interface behind a
|
||||
// preference for the benefit of automated-tests. They are not exposed to
|
||||
// content. See `Storage.webidl` for more details.
|
||||
|
||||
virtual void
|
||||
Open(nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv)
|
||||
{ }
|
||||
|
@ -126,11 +133,18 @@ public:
|
|||
EndExplicitSnapshot(nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv)
|
||||
{ }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Dispatch storage notification events on all impacted pages in the current
|
||||
// process as well as for consumption by devtools. Pages receive the
|
||||
// notification via StorageNotifierService (not observers like in the past),
|
||||
// while devtools does receive the notification via the observer service.
|
||||
//
|
||||
// aStorage can be null if this method is called by LocalStorageCacheChild.
|
||||
//
|
||||
// aImmediateDispatch is for use by child IPC code (LocalStorageCacheChild)
|
||||
// so that PBackground ordering can be maintained. Without this, the event
|
||||
// would be/ enqueued and run in a future turn of the event loop, potentially
|
||||
// would be enqueued and run in a future turn of the event loop, potentially
|
||||
// allowing other PBackground Recv* methods to trigger script that wants to
|
||||
// assume our localstorage changes have already been applied. This is the
|
||||
// case for message manager messages which are used by ContentTask testing
|
||||
|
|
|
@ -14,6 +14,13 @@ namespace dom {
|
|||
|
||||
class StorageEvent;
|
||||
|
||||
/**
|
||||
* Enables the StorageNotifierService to check whether an observer is interested
|
||||
* in receiving events for the given principal before calling the method, an
|
||||
* optimization refactoring.
|
||||
*
|
||||
* Expected to only be implemented by nsGlobalWindowObserver or its succesor.
|
||||
*/
|
||||
class StorageNotificationObserver
|
||||
{
|
||||
public:
|
||||
|
@ -34,6 +41,15 @@ public:
|
|||
GetEventTarget() const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* A specialized version of the observer service that uses the custom
|
||||
* StorageNotificationObserver so that principal checks can happen in this class
|
||||
* rather than in the nsIObserver::observe method where they used to happen.
|
||||
*
|
||||
* The only expected consumers are nsGlobalWindowInner instances via their
|
||||
* nsGlobalWindowObserver helper that avoids being able to use the window as an
|
||||
* nsIObserver.
|
||||
*/
|
||||
class StorageNotifierService final
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -34,17 +34,40 @@ interface Storage {
|
|||
readonly attribute boolean isSessionOnly;
|
||||
};
|
||||
|
||||
// Testing only.
|
||||
/**
|
||||
* Testing methods that exist only for the benefit of automated glass-box
|
||||
* testing. Will never be exposed to content at large and unlikely to be useful
|
||||
* in a WebDriver context.
|
||||
*/
|
||||
partial interface Storage {
|
||||
/**
|
||||
* Does a security-check and ensures the underlying database has been opened
|
||||
* without actually calling any database methods. (Read-only methods will
|
||||
* have a similar effect but also impact the state of the snapshot.)
|
||||
*/
|
||||
[Throws, NeedsSubjectPrincipal, Pref="dom.storage.testing"]
|
||||
void open();
|
||||
|
||||
/**
|
||||
* Automatically ends any explicit snapshot and drops the reference to the
|
||||
* underlying database, but does not otherwise perturb the database.
|
||||
*/
|
||||
[Throws, NeedsSubjectPrincipal, Pref="dom.storage.testing"]
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Ensures the database has been opened and initiates an explicit snapshot.
|
||||
* Snapshots are normally automatically ended and checkpointed back to the
|
||||
* parent, but explicitly opened snapshots must be explicitly ended via
|
||||
* `endExplicitSnapshot` or `close`.
|
||||
*/
|
||||
[Throws, NeedsSubjectPrincipal, Pref="dom.storage.testing"]
|
||||
void beginExplicitSnapshot();
|
||||
|
||||
/**
|
||||
* Ends the explicitly begun snapshot and retains the underlying database.
|
||||
* Compare with `close` which also drops the reference to the database.
|
||||
*/
|
||||
[Throws, NeedsSubjectPrincipal, Pref="dom.storage.testing"]
|
||||
void endExplicitSnapshot();
|
||||
};
|
||||
|
|
|
@ -134,8 +134,19 @@ parent:
|
|||
|
||||
async PBackgroundLSObserver(uint64_t observerId);
|
||||
|
||||
/**
|
||||
* Issue an asynchronous request that will be used in a synchronous fashion
|
||||
* through complex machinations described in `PBackgroundLSRequest.ipdl` and
|
||||
* `LSObject.h`.
|
||||
*/
|
||||
async PBackgroundLSRequest(LSRequestParams params);
|
||||
|
||||
/**
|
||||
* Issues a simple, non-cancelable asynchronous request that's used in an
|
||||
* asynchronous fashion by callers. (LSRequest is not simple because it used
|
||||
* in a synchronous fashion which leads to complexities regarding cancelation,
|
||||
* see `PBackgroundLSRequest.ipdl` for details.)
|
||||
*/
|
||||
async PBackgroundLSSimpleRequest(LSSimpleRequestParams params);
|
||||
|
||||
async PBackgroundLocalStorageCache(PrincipalInfo principalInfo,
|
||||
|
|
|
@ -923,13 +923,13 @@ description =
|
|||
[PBackgroundStorage::Preload]
|
||||
description =
|
||||
[PBackgroundLSDatabase::PBackgroundLSSnapshot]
|
||||
description =
|
||||
description = See corresponding comment in PBackgroundLSDatabase.ipdl
|
||||
[PBackgroundLSSnapshot::LoadItem]
|
||||
description =
|
||||
description = See corresponding comment in PBackgroundLSSnapshot.ipdl
|
||||
[PBackgroundLSSnapshot::LoadKeys]
|
||||
description =
|
||||
description = See corresponding comment in PBackgroundLSSnapshot.ipdl
|
||||
[PBackgroundLSSnapshot::IncreasePeakUsage]
|
||||
description =
|
||||
description = See corresponding comment in PBackgroundLSSnapshot.ipdl
|
||||
[PBackgroundLSSnapshot::Ping]
|
||||
description = See corresponding comment in PBackgroundLSSnapshot.ipdl
|
||||
[PRemoteSpellcheckEngine::Check]
|
||||
|
|
Загрузка…
Ссылка в новой задаче