Project Fission/BrowsingContext
Synced Contexts
Using the base class SyncedContex
, classes that need to replicate
state across content processes can do so. SyncedContex
is not
intended to be modified in the general case, but only for improving
the behavior of synced contexts in general. SyncedContex
isn't
inherited, but is declared as a has-a in a class that needs to keep
state synced.
State is kept in "synced fields". A synced field is basically a named piece of state. It is important that the value stored in a synced field is immutable, so that the syncing framework can make sure that the value is actually synced. Synced fields are accessed through getters and setters exposed on the owning class, where the setters ensure that the state is synced.
To add content process synced state to a class you use
MOZ_DECL_SYNCED_CONTEXT
defiend in
SyncedContex.h
. MOZ_DECL_SYNCED_CONTEXT
takes two arguments
- the name of the class that owns the synced state.
- a macro that lists all the data that should be synced.
The macro listing all synced fields in turn should take one argument, which is used to do the actual field declaration. A field declaration consists of a name and a type. Remember, that type should denote an immutable value, and needs to be default initializable.
This way MOZ_DECL_SYNCED_CONTEXT
will declare and define the setters
and getters, and type indices used to access the synced state by name.
Getters will be of the form:
type GetFieldName() const;
Setters will be of the form:
MOZ_MUST_USE nsresult SetFieldName(type&& value)
Note that setters can fail, and you need to handle failure! (This isn't currently true, but will be soon!)
It also defines CanSet
method for every field that you need to
implement in the owning class to decide if it is OK to modify a
field. CanSet
takes three arguments:
- The field index that names it (for overloading)
- The value being set
- A
ContentParent
, if and when it is receiving a transaction from itsContentChild
.
Finally, there will also be field setter callbacks available as DidSet
methods. DidSet
is supposed to be overridden, and can either take only the field index that names it, or the field index and the previous value.
Here, 3 will only be non-null if in the parent process, and if the field change originated in a child process.
With this we know everything needed to add synced state to a class, so we will show a small contrived example:
#define MOZ_EACH_FOO_FIELD(FIELD) \
FIELD(First, int) \
FIELD(Second, double) \
FIELD(Third, Maybe<bool>)
class Foo {
MOZ_DECL_SYNCED_CONTEXT(Foo, MOZ_EACH_FOO_FIELD)
public:
bool CanSet(FieldIndex<IDX_First>, const int& value);
bool CanSet(FieldIndex<IDX_Second>, const double& value);
bool CanSet(FieldIndex<IDX_Third>, const Maybe<bool>& value);
};
This adds three fields, First, Second, Third with declared
types. Foo will now have the getters and setters GetFirst
,
SetFirst
, etc. To add a new field you need to add a new FIELD
line, and a corresponding CanSet
.
SyncedContext
is currently only available in two classes:
BrowsingContext
and WindowContext
.
Declared fields are default initialized on construction of the owner class. If you need to initialize the fields to something else before first sync, you can use SetWithoutSyncing
.
Whenever a field is synced a number of IPC messages will be sent. To keep this number down it is possible to batch field modifications using the underlying framework on which synced fields are built upon. MOZ_DECL_SYNCED_CONTEXT
defines Transaction
in the owner class which can be used to accomplish this.
Continuing with our contrieved example from before we can do:
nsresult Foo::SetFirstAndSecond() {
Tranxaction txn;
txn.SetFirst(42);
txn.SetSecond(3.14);
return txt.Commit();
}
(Again note that the returning nsresult
isn't actually in tree yet).