|
|
Line 5: |
Line 5: |
| |Feature version=TBD | | |Feature version=TBD |
| |Feature health=OK | | |Feature health=OK |
| |Feature status note=All blocking issues have been addressed. Development is in progress. | | |Feature status note=Development is in progress. |
| }} | | }} |
| {{FeatureTeam | | {{FeatureTeam |
Line 14: |
Line 14: |
| |Feature localization lead=Axel Hecht | | |Feature localization lead=Axel Hecht |
| |Feature qa lead=Tracy Walker | | |Feature qa lead=Tracy Walker |
| |Feature ux lead=Alex Faaborg
| |
| |Feature product marketing lead=Jaclyn Fu | | |Feature product marketing lead=Jaclyn Fu |
| |Feature additional members=Ibai Garcia (SUMO) | | |Feature additional members=Ibai Garcia (SUMO) |
Line 60: |
Line 59: |
|
| |
|
| Sync will ensure that add-ons are installed, uninstalled, disabled,and enabled as they are changed on each client for the same application. This means that desktop to desktop add-ons will sync, and mobile to mobile add-ons will sync. | | Sync will ensure that add-ons are installed, uninstalled, disabled,and enabled as they are changed on each client for the same application. This means that desktop to desktop add-ons will sync, and mobile to mobile add-ons will sync. |
| | |
| | For the initial release, only XPI extensions and themes from addons.mozilla.org will be supported. In a future release, we can expand scope to add-ons not from addons.mozilla.org. |
| |Feature users and use cases=; Desktop browser to browser add-on install | | |Feature users and use cases=; Desktop browser to browser add-on install |
| * A user has sync set up his work and home desktops | | * A user has sync set up his work and home desktops |
Line 91: |
Line 92: |
|
| |
|
| ;Sync will not synchronize non-XPI add-ons such as plugins, lightweight themes, and search engines. Sync will also not synchronize add-ons installed outside of the currently running profile. | | ;Sync will not synchronize non-XPI add-ons such as plugins, lightweight themes, and search engines. Sync will also not synchronize add-ons installed outside of the currently running profile. |
| |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. | | |Feature functional spec=Like other browser primitives that have become syncified, the underlying entities being synced will need a sync identifier. {{bug|682027}} tracks adding a syncGUID to the extensions SQLite database. This properties will need to be documented at https://developer.mozilla.org/en/Addons/Add-on_Manager/Addon. |
| | |
| The solution to this problem will consist of the following:
| |
| | |
| # Optional properties on add-on objects that describe the add-on for purposes of use in Sync
| |
| # Functions on AddonManager that take above properties and perform actions
| |
| | |
| For add-on objects, providers wishing to opt in to Sync will expose the following read-only, optional properties:
| |
|
| |
|
| ; syncData : a string that describes data necessary to synchronize the add-on between clients. The format of the string is opaque to Sync code. From the perspective of add-on providers, it should be a JSON object with a *type* field that is used to route it to the proper provider.
| | New APIs will be added to the AddonManager as appropriate. |
| | |
| ; syncGUID : a GUID used to identify this add-on across Sync client instances. The GUID should be generated using Utils.makeGUID() from the Sync code. It is basically a Base64-encoded representation of 9 random bytes.
| |
| | |
| (These properties will need to be documented at https://developer.mozilla.org/en/Addons/Add-on_Manager/Addon)
| |
| | |
| 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 Sync code smaller by pushing out logic and possibly caching.
| |
| | |
| ; applyRecords(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:
| |
| | |
| *onSuccess - Invoked when an individual record is processed successfully. Arguments are the record itself.
| |
| *onFailure - Invoked when an individual record could not be processed successfully. Arguments are the record itself.
| |
| *onFinished - Invoked when all records have finished processing.
| |
| | |
| Each record is an object containing the following keys:
| |
| | |
| * syncGUID (required) - The Sync GUID for this record
| |
| * syncData (optional) - The .syncData field from an add-on
| |
| * deleted (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 applyRecords() will resemble the following pseudocode:
| |
| | |
| <pre>
| |
| foreach record in records:
| |
| if record.deleted:
| |
| found = this.getAddonBySyncGUID(record.syncGUID)
| |
| if found:
| |
| this.deleteAddon(found)
| |
| # else nothing to do since record not found
| |
|
| |
| callbackObj.onSuccess(record)
| |
| continue
| |
| | |
| existing = this.getExistingAddonBySyncData(record.syncData)
| |
| 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:
| |
| result = this.installAddonFromSyncData(syncGUID, syncData)
| |
| | |
| result ? callbackObj.onSuccess(record) : callbackObj.onFailure(record)
| |
| | |
| 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? | | 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? |
Line 162: |
Line 101: |
|
| |
|
| # Query AddonManager.getAllAddons() | | # Query AddonManager.getAllAddons() |
| # Filter the returned list by items that have the *syncData* and *syncGUID* properties and are installed in the profile add-on scope (addon.scope == AddonManager.SCOPE_PROFILE) | | # Filter the returned list by items that are appropriate for sync |
| #* Open Issue: Should the AddonManager do this filtering? Should the add-on providers only expose syncData on add-ons installed in the proper location?
| |
| | |
| The engine will process incoming records by converting them to the API accepted by AddonManager.applyRecords() and will invoke that function with them. This is similar to how the history engine works.
| |
|
| |
|
| The Sync add-on tracker will install add-on listeners in the AddonManager and will listen for the following events: | | The Sync add-on tracker will install add-on listeners in the AddonManager and will listen for the following events: |
Line 191: |
Line 127: |
|
| |
|
| 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() requires a type as a parameter.) | | 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() requires a type as a parameter.) |
|
| |
| There are some interesting implications for the design proposed above. AddonManager and providers - not Sync - would be responsible for the format of the records containing add-on metadata and this means the onus of ensuring forwards and backwards compatibility (since e.g. a version 9 client could receive a Sync record from a version 14 client and vice-versa) reside in the AddonManager and providers. This might be new territory for AddonManager and providers. Unfortunately, the alternative means lots of AddonManager-specific code in Sync (at best) or giving up provider agnostic add-on sync (at worst).
| |
| |Feature ux design=;May need some for giving the user feedback when an add-on is being updated due to sync. | | |Feature ux design=;May need some for giving the user feedback when an add-on is being updated due to sync. |
| }} | | }} |