|Feature overview=Add-ons are synchronized between sync clients.
|Feature users and use cases=User installs an add-on on one browser. When a sync occurs, the add-on is automagically installed on other sync clients.
|Feature functional spec=The Addon Manager maintainers would like to see Sync support all add-on providers so as to not introduce 1st and 2nd class providers. This will require some APIs.
The solution to this problem will consist of the following:
The AddonManager will support the following APIs:
; getAddonBySyncGUID(syncGUID, callback) : Obtain an add-on from its Sync GUID. Calls the supplied function when that add-on is retrieved. The callback receives null on unknown add-on or the add-on object (generated from the underlying provider) on success. This API technically isn't required, but it makes the Add-on engine's createRecord(syncGUID) API much simpler. Without it, Sync code would probably query for all add-ons and iterate until it finds a match. By smaller by pushing the out logic to AddonManager, optimization can be done there, if neededand possibly caching.
; applySyncDataRecordsapplySyncRecords(records, callbackObj) : This applies an array of records that contain add-on metadata and makes the current state of the world agree with that data as much as possible.
The callbackObj is an object containing the following optional keys:
* isDeleted (optional) - If evaluates to true, indicates that the record was deleted. Application of this record should involve trying to delete this add-on if present or no-op if not present.
The implementation of applySyncDataRecordsapplySyncRecords() will resemble the following pseudocode:
<pre>
result = null
if existing:
# ensure the Sync GUIDs agree. incoming records always win fight
if existing.syncGUID != record.syncGUID:
existing.syncGUID = record.syncGUID
# TODO this probably requires additional API formalization. Each
# provider likely has its own semantics. But, common operations would
# likely include updating common add-on fields (like source and install
# URLs) and upgrading the add-on version.
result = existing.updateFromSyncData(record.syncData)
else:
callbackObj.onFinished()
</pre>
TODO: we should formalize how the Sync engine gets informed of whether a restart is required to finish the record application. Do we catch this in the tracker observers or install observers local to the sync method of the engine?
The add-ons Sync engine will discover the set of add-ons that can be synced via the following procedure:
When these are observed, the tracker will:
# Verify the changed add-on is in the set of Sync-able add-ons (using same heuristics as add-on discovery documented above, if needed)
# Mark the GUID as changed
# This will result in a new record for that GUID being created automagically. The createRecord() procedure will in turn create the necessary record by querying AddonManager which will be queued for upload to the Sync server.
On start-up, the add-on engine will query AddonManager.getStartupChanges() for changes applied on application start-up. These are not observed by Sync because they occur before Sync is registered and running. Even if Sync does catch them in its tracker, it should be safe to mark records as changed. The only side-effect will be a slightly different modified time.
* STARTUP_CHANGE_ENABLED
TODO: Sync should catch all startup change types (the constants above) and react to them. We may need an API or some kind of verification/test to ensure newly-introduced startup changes/constants aren't missed by Sync. (This is all because getStartupChanges() takes requires a type as a parameter.)
}}
{{FeatureInfo