Closed Bug 555349 Opened 15 years ago Closed 15 years ago

Finalize the restartless add-on format

Categories

(Toolkit :: Add-ons Manager, defect, P1)

defect

Tracking

()

VERIFIED FIXED
mozilla1.9.3a5

People

(Reporter: mossop, Assigned: mossop)

References

()

Details

(Keywords: dev-doc-complete, Whiteboard: [rewrite])

Attachments

(1 file, 4 obsolete files)

A couple of things are striking me as incorrect about the current form of restartless add-ons. These should be fixed before we land on trunk.
Blocks: 542385
Blocks: 549324
Attached patch patch rev 1 (obsolete) — Splinter Review
This addresses the basics of the new spec. It is slightly wrong in that the install method will get called even when the add-on is installed disabled, but I think that this is good enough for trunk and I've filed bug 560608 to fix that afterward.
Attachment #440280 - Flags: review?(robert.bugzilla)
Whiteboard: [rewrite] → [rewrite][fixed-in-addonsmgr][needs-review]
Status: NEW → ASSIGNED
Attached patch patch rev 2 (obsolete) — Splinter Review
Had to make a couple of small changes to this as I was testing locally with the crash reporter disabled. The only changes are: http://hg.mozilla.org/projects/addonsmgr/rev/78f54b9bf8ca http://hg.mozilla.org/projects/addonsmgr/rev/439285fc775d
Attachment #440280 - Attachment is obsolete: true
Attachment #440601 - Flags: review?(robert.bugzilla)
Attachment #440280 - Flags: review?(robert.bugzilla)
Comment on attachment 440601 [details] [diff] [review] patch rev 2 >diff --git a/toolkit/mozapps/extensions/XPIProvider.jsm b/toolkit/mozapps/extensions/XPIProvider.jsm >--- a/toolkit/mozapps/extensions/XPIProvider.jsm >+++ b/toolkit/mozapps/extensions/XPIProvider.jsm >@@ -96,28 +96,36 @@ const PREFIX_NS_EM = > > const TOOLKIT_ID = "toolkit@mozilla.org"; > > const BRANCH_REGEXP = /^([^\.]+\.[0-9]+[a-z]*).*/gi; > > const DB_SCHEMA = 1; > const REQ_VERSION = 2; > >+const APP_STARTUP = 1; >+const APP_SHUTDOWN = 2; >+const ADDON_ENABLE = 3; >+const ADDON_DISABLE = 4; >+const ADDON_INSTALL = 5; >+const ADDON_UNINSTALL = 6; >+const ADDON_UPGRADE = 7; >+const ADDON_DOWNGRADE = 8; >+ >@@ -1886,165 +1901,158 @@ var XPIProvider = { >... >+ /** >+ * Loads a bootstrapped add-on's bootstrap.js into a sandbox and adds the >+ * constants to it. This will also add information about the add-on to the >+ * bootstrappedAddons dictionary and notify the crash reporter that new >+ * add-ons have been loaded. nit: "adds the constants to it" leaves me wanting to know what constants these are. Could you please make this more specific? >+ * >+ * @param aId >+ * The add-on's ID >+ * @param aDir >+ * The add-on's install directory nit: "The nsILocalFile for the directory containing the add-on" or something similar. > * @param aVersion >- * The version of the add-on being activated >- * @param aDir >- * The directory containing the add-on >- * @param aStartup >- * true if the add-on is being activated during startup >- * @param aInstall >- * true if the add-on is being activated during installation >- */ >- activateAddon: function XPI_activateAddon(aId, aVersion, aDir, aStartup, aInstall) { >- let methods = ["enable"]; >- if (aStartup) >- methods.unshift("startup"); >- if (aInstall) >- methods.unshift("install"); >+ * The add-on's version >+ * @return a JavaScript scope >+ */ >+ loadBootstrapScope: function XPI_loadBootstrapScope(aId, aDir, aVersion) { >+ LOG("Loading bootstrap scope from " + aDir.path); >+ // Mark the add-on as active for the crash reporter before loading >+ this.bootstrappedAddons[aId] = { >+ version: aVersion, >+ descriptor: aDir.persistentDescriptor >+ }; >+ this.addAddonsToCrashReporter(); >+ >+ let principal = Cc["@mozilla.org/systemprincipal;1"]. >+ createInstance(Ci.nsIPrincipal); >+ this.bootstrapScopes[aId] = new Components.utils.Sandbox(principal); >+ > let bootstrap = aDir.clone(); > bootstrap.append("bootstrap.js"); > if (bootstrap.exists()) { > let uri = Services.io.newFileURI(bootstrap); >- let principal = Cc["@mozilla.org/systemprincipal;1"]. >- createInstance(Ci.nsIPrincipal); >- let scope = new Components.utils.Sandbox(principal); > let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]. > createInstance(Ci.mozIJSSubScriptLoader); > >- // Mark the add-on as active for the crash reporter before loading >- this.bootstrappedAddons[aId] = { >- version: aVersion, >- descriptor: aDir.persistentDescriptor >- }; >- this.bootstrapScopes[aId] = scope; >- this.addAddonsToCrashReporter(); >- >- try { >- loader.loadSubScript(uri.spec, scope); >+ try { >+ loader.loadSubScript(uri.spec, this.bootstrapScopes[aId]); > } > catch (e) { > WARN("Error loading bootstrap.js for " + aId + ": " + e); > } > >- this.callBootstrapMethod(aId, scope, methods); >+ let consts = ["APP_STARTUP", "APP_SHUTDOWN", "ADDON_ENABLE", >+ "ADDON_DISABLE","ADDON_INSTALLED", "ADDON_UNINSTALLED", >+ "ADDON_UPGRADED", "ADDON_DOWNGRADED"]; >+ for (let i = 0; i < consts.length; i++) >+ this.bootstrapScopes[aId][consts[i]] = i; This is ugly. Not sure of a better solution but I suspect there is one. > } > else { > WARN("Bootstrap missing for " + aId); > } > }, > > /** >- * Dectivates a bootstrapped add-on by by calling the appropriate method on >- * the cached JS scope. Removes the add-on and its scope from the >- * bootstrapScopes and bootstrappedAddons dictionaries. >- * >- * @param aId >- * The ID of the add-on being deactivated >- * @param aShutdown >- * true if the add-on is being deactivated during shutdown >- * @param aUninstall >- * true if the add-on is being deactivated during uninstallation >- */ >- deactivateAddon: function XPI_deactivateAddon(aId, aShutdown, aUninstall) { >- if (!(aId in this.bootstrappedAddons)) { >- ERROR("Attempted to deactivate an add-on that was never activated"); >- return; >- } >- let scope = this.bootstrapScopes[aId]; >+ * Unloads a bootstrap scope by dropping all references to it and then >+ * updating the list of active add-ons with the crash reporter. >+ * >+ * @param aId >+ * The add-on's ID >+ */ >+ unloadBootstrapScope: function XPI_unloadBootstrapScope(aId) { >+ delete this.bootstrapScopes[aId]; > delete this.bootstrappedAddons[aId]; >- delete this.bootstrapScopes[aId]; >- >- let methods = ["disable"]; >- if (aShutdown) >- methods.unshift("shutdown"); >- if (aUninstall) >- methods.unshift("uninstall"); >- this.callBootstrapMethod(aId, scope, methods); >- > this.addAddonsToCrashReporter(); > }, > > /** >+ * Calls a bootstrap method for an add-on. >+ * >+ * @param aId >+ * The ID of the add-on >+ * @param aVersion >+ * The version of the add-on >+ * @param aDir >+ * The directory containing the add-on nit: "The nsILocalFile for the directory containing the add-on" or something similar. This should be consistent with the previous aDir comment. >+ * @param aMethod >+ * The name of the bootstrap method to call >+ * @param aReason >+ * The reason flag to pass to the bootstrap's startup method >+ */ >+ callBootstrapMethod: function XPI_callBootstrapMethod(aId, aVersion, aDir, >+ aMethod, aReason) { >+ // Load the scope if it hasn't already been loaded >+ if (!(aId in this.bootstrapScopes)) >+ this.loadBootstrapScope(aId, aDir, aVersion); >+ >+ let params = { >+ id: aId, >+ version: aVersion, >+ installPath: aDir.clone() >+ }; nit: put this after the early return below >+ >+ if (!(aMethod in this.bootstrapScopes[aId])) { >+ WARN("Add-on " + aId + " is missing bootstrap method " + aMethod); >+ return; >+ } >+ >+ LOG("Calling bootstrap method " + aMethod + " on " + aId + " version " + aVersion); >+ try { >+ this.bootstrapScopes[aId][aMethod](params, aReason); >+ } >+ catch (e) { >+ WARN("Exception running bootstrap method " + aMethods + " on " + >+ aId + ": " + e); >+ } >+ }, >+ >+ /** > * Updates the appDisabled property for all add-ons. > */ > updateAllAddonDisabledStates: function XPI_updateAllAddonDisabledStates() { > let addons = XPIDatabase.getAddons(); > addons.forEach(function(aAddon) { > this.updateAddonDisabledState(aAddon); > }, this); > },
Comment on attachment 440601 [details] [diff] [review] patch rev 2 >diff --git a/toolkit/mozapps/extensions/XPIProvider.jsm b/toolkit/mozapps/extensions/XPIProvider.jsm >--- a/toolkit/mozapps/extensions/XPIProvider.jsm >+++ b/toolkit/mozapps/extensions/XPIProvider.jsm >... >@@ -2613,17 +2634,17 @@ var XPIDatabase = { > addon._internal_id = aRow.internal_id; > addon._installLocation = XPIProvider.installLocationsByName[aRow.location]; > addon._descriptor = aRow.descriptor; > copyProperties(aRow, PROP_METADATA, addon); > addon._defaultLocale = aRow.defaultLocale; > addon.installDate = aRow.installDate; > addon.updateDate = aRow.updateDate; > ["visible", "active", "userDisabled", "appDisabled", >- "pendingUninstall", "applyBackgroundUpdates"].forEach(function(aProp) { >+ "pendingUninstall", "applyBackgroundUpdates", "bootstrap"].forEach(function(aProp) { nit: move "bootstrap" to the next line > addon[aProp] = aRow[aProp] != 0; > }); > this.addonCache[aRow.internal_id] = Components.utils.getWeakReference(addon); > return addon; > }, > > /** > * Asynchronously fetches additional metadata for a DBAddonInternal. >@@ -2763,17 +2784,17 @@ var XPIDatabase = { > let location = aRow.getResultByName("location"); > addon._installLocation = XPIProvider.installLocationsByName[location]; > addon._descriptor = aRow.getResultByName("descriptor"); > copyRowProperties(aRow, PROP_METADATA, addon); > addon._defaultLocale = aRow.getResultByName("defaultLocale"); > addon.installDate = aRow.getResultByName("installDate"); > addon.updateDate = aRow.getResultByName("updateDate"); > ["visible", "active", "userDisabled", "appDisabled", >- "pendingUninstall", "applyBackgroundUpdates"].forEach(function(aProp) { >+ "pendingUninstall", "applyBackgroundUpdates", "bootstrap"].forEach(function(aProp) { nit: move "bootstrap" to the next line > addon[aProp] = aRow.getResultByName(aProp) != 0; > }); > this.addonCache[internal_id] = Components.utils.getWeakReference(addon); > return addon; > }, > > /** > * Synchronously reads all install locations known about by the database. This >@@ -3038,17 +3059,17 @@ var XPIDatabase = { > > stmt.params.locale = insertLocale(aAddon.defaultLocale); > stmt.params.location = aAddon._installLocation.name; > stmt.params.descriptor = aDescriptor; > stmt.params.installDate = aAddon.installDate; > stmt.params.updateDate = aAddon.updateDate; > copyProperties(aAddon, PROP_METADATA, stmt.params); > ["visible", "userDisabled", "appDisabled", >- "applyBackgroundUpdates"].forEach(function(aProp) { >+ "applyBackgroundUpdates", "bootstrap"].forEach(function(aProp) { nit: put applyBackgroundUpdates on the previous line > stmt.params[aProp] = aAddon[aProp] ? 1 : 0; > }); > stmt.params.active = (aAddon.visible && !aAddon.userDisabled && > !aAddon.appDisabled) ? 1 : 0; > stmt.execute(); > let internal_id = this.connection.lastInsertRowID; > > stmt = this.getStatement("addAddonMetadata_addon_locale");
Comment on attachment 440601 [details] [diff] [review] patch rev 2 >diff --git a/toolkit/mozapps/extensions/test/addons/test_bootstrap1_1/bootstrap.js b/toolkit/mozapps/extensions/test/addons/test_bootstrap1_1/bootstrap.js >--- a/toolkit/mozapps/extensions/test/addons/test_bootstrap1_1/bootstrap.js >+++ b/toolkit/mozapps/extensions/test/addons/test_bootstrap1_1/bootstrap.js >@@ -1,9 +1,19 @@ > Components.utils.import("resource://gre/modules/Services.jsm"); > >-function enable() { >- Services.prefs.setIntPref("bootstraptest.version", 1); >+function install(data, reason) { >+ Services.prefs.setIntPref("bootstraptest.install", 1); > } Per in person discussion please use bootstraptest.version here and elsewhere > >-function disable() { >+function startup(data, reason) { >+ Services.prefs.setIntPref("bootstraptest.version", 1); >+ Services.prefs.setIntPref("bootstraptest.startup", reason); >+} >+ >+function shutdown(data, reason) { > Services.prefs.setIntPref("bootstraptest.version", 0); >+ Services.prefs.setIntPref("bootstraptest.shutdown", reason); > } >+ >+function uninstall(data, reason) { >+ Services.prefs.setIntPref("bootstraptest.install", 0); >+} Per in person discussion please use bootstraptest.version... I think? >diff --git a/toolkit/mozapps/extensions/test/addons/test_bootstrap1_1/install.rdf b/toolkit/mozapps/extensions/test/addons/test_bootstrap1_1/install.rdf >--- a/toolkit/mozapps/extensions/test/addons/test_bootstrap1_1/install.rdf >+++ b/toolkit/mozapps/extensions/test/addons/test_bootstrap1_1/install.rdf >@@ -1,17 +1,17 @@ > <?xml version="1.0"?> > > <RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" > xmlns:em="http://www.mozilla.org/2004/em-rdf#"> > > <Description about="urn:mozilla:install-manifest"> > <em:id>bootstrap1@tests.mozilla.org</em:id> > <em:version>1.0</em:version> >- <em:type>64</em:type> >+ <em:bootstrap>true</em:bootstrap> nit: add the type for extension here and elsewhere. > > <!-- Front End MetaData --> > <em:name>Test Bootstrap 1</em:name> > <em:description>Test Description</em:description> > > <em:targetApplication> > <Description> > <em:id>xpcshell@tests.mozilla.org</em:id>
Comment on attachment 440601 [details] [diff] [review] patch rev 2 r=me with nits fixed. If you come up with a better way to deal with adding the constants either request review in this bug or a followup bug... I don't think making that better should hold this up.
Attachment #440601 - Flags: review?(robert.bugzilla) → review+
Whiteboard: [rewrite][fixed-in-addonsmgr][needs-review] → [rewrite][fixed-in-addonsmgr]
Attached patch patch rev 3 (obsolete) — Splinter Review
This addresses the nits but I have done a slight better way (I think) of passing hte constants through to the bootstrap scope and also changes the preferences used to check the bootstrap script was called correctly.
Attachment #440601 - Attachment is obsolete: true
Attachment #441598 - Flags: review?(robert.bugzilla)
Attached patch patch rev 4 (obsolete) — Splinter Review
From the discussion IRL, put the codes into a single global object.
Attachment #441598 - Attachment is obsolete: true
Attachment #441901 - Flags: review?(robert.bugzilla)
Attachment #441598 - Flags: review?(robert.bugzilla)
Attached patch patch rev 4Splinter Review
Attached the right patch this time
Attachment #441901 - Attachment is obsolete: true
Attachment #441904 - Flags: review?(robert.bugzilla)
Attachment #441901 - Flags: review?(robert.bugzilla)
Comment on attachment 441904 [details] [diff] [review] patch rev 4 >diff --git a/toolkit/mozapps/extensions/XPIProvider.jsm b/toolkit/mozapps/extensions/XPIProvider.jsm >--- a/toolkit/mozapps/extensions/XPIProvider.jsm >+++ b/toolkit/mozapps/extensions/XPIProvider.jsm >@@ -1886,165 +1905,156 @@ var XPIProvider = { >... >+ loadBootstrapScope: function XPI_loadBootstrapScope(aId, aDir, aVersion) { >+ LOG("Loading bootstrap scope from " + aDir.path); >+ // Mark the add-on as active for the crash reporter before loading >+ this.bootstrappedAddons[aId] = { >+ version: aVersion, >+ descriptor: aDir.persistentDescriptor >+ }; >+ this.addAddonsToCrashReporter(); >+ >+ let principal = Cc["@mozilla.org/systemprincipal;1"]. >+ createInstance(Ci.nsIPrincipal); >+ this.bootstrapScopes[aId] = new Components.utils.Sandbox(principal); >+ > let bootstrap = aDir.clone(); > bootstrap.append("bootstrap.js"); > if (bootstrap.exists()) { > let uri = Services.io.newFileURI(bootstrap); >- let principal = Cc["@mozilla.org/systemprincipal;1"]. >- createInstance(Ci.nsIPrincipal); >- let scope = new Components.utils.Sandbox(principal); > let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]. > createInstance(Ci.mozIJSSubScriptLoader); > >- // Mark the add-on as active for the crash reporter before loading >- this.bootstrappedAddons[aId] = { >- version: aVersion, >- descriptor: aDir.persistentDescriptor >- }; >- this.bootstrapScopes[aId] = scope; >- this.addAddonsToCrashReporter(); >- >- try { >- loader.loadSubScript(uri.spec, scope); >+ try { >+ loader.loadSubScript(uri.spec, this.bootstrapScopes[aId]); > } > catch (e) { > WARN("Error loading bootstrap.js for " + aId + ": " + e); > } Seems wrong to continue when there is an error loading the bootstrap? I suspect that if loadSubScript fails none of the script will be loaded but it wouldn't hurt to verify that. > >- this.callBootstrapMethod(aId, scope, methods); >+ // Copy the reason values from the global object into the bootstrap scope. >+ for (let name in BOOTSTRAP_REASONS) >+ this.bootstrapScopes[aId][name] = BOOTSTRAP_REASONS[name]; > } > else { > WARN("Bootstrap missing for " + aId); > } > }, >... > /** >+ * Calls a bootstrap method for an add-on. >+ * >+ * @param aId >+ * The ID of the add-on >+ * @param aVersion >+ * The version of the add-on >+ * @param aDir >+ * The nsILocalFile for the directory containing the add-on >+ * @param aMethod >+ * The name of the bootstrap method to call >+ * @param aReason >+ * The reason flag to pass to the bootstrap's startup method >+ */ >+ callBootstrapMethod: function XPI_callBootstrapMethod(aId, aVersion, aDir, >+ aMethod, aReason) { >+ // Load the scope if it hasn't already been loaded >+ if (!(aId in this.bootstrapScopes)) >+ this.loadBootstrapScope(aId, aDir, aVersion); >+ >+ if (!(aMethod in this.bootstrapScopes[aId])) { >+ WARN("Add-on " + aId + " is missing bootstrap method " + aMethod); >+ return; >+ } >+ >+ let params = { >+ id: aId, >+ version: aVersion, >+ installPath: aDir.clone() >+ }; >+ >+ LOG("Calling bootstrap method " + aMethod + " on " + aId + " version " + aVersion); nit: over 80 and easy to fix >+ try { >+ this.bootstrapScopes[aId][aMethod](params, aReason); >+ } >+ catch (e) { >+ WARN("Exception running bootstrap method " + aMethods + " on " + >+ aId + ": " + e); >+ } >+ }, >@@ -2476,17 +2499,17 @@ var XPIDatabase = { > "id TEXT, location TEXT, version TEXT, " + > "type TEXT, internalName TEXT, updateURL TEXT, " + > "updateKey TEXT, optionsURL TEXT, aboutURL TEXT, " + > "iconURL TEXT, defaultLocale INTEGER, " + > "visible INTEGER, active INTEGER, " + > "userDisabled INTEGER, appDisabled INTEGER, " + > "pendingUninstall INTEGER, descriptor TEXT, " + > "installDate INTEGER, updateDate INTEGER, " + >- "applyBackgroundUpdates INTEGER, " + >+ "applyBackgroundUpdates INTEGER, bootstrap INTEGER, " + > "UNIQUE (id, location)"); > this.connection.createTable("targetApplication", > "addon_internal_id INTEGER, " + > "id TEXT, minVersion TEXT, maxVersion TEXT, " + > "UNIQUE (addon_internal_id, id)"); nit: a few of these creep over 80 and are easy to fix >@@ -3837,29 +3862,43 @@ AddonInstall.prototype = { > createWrapper(this.addon)); > } > else { > // TODO We can probably reduce the number of DB operations going on here > // We probably also want to support rolling back failed upgrades etc. > // See bug 553015. > > // Deactivate and remove the old add-on as necessary >+ let reason = BOOTSTRAP_REASONS.ADDON_INSTALL; > if (this.existingAddon) { >- if (this.existingAddon.active) { >- if (this.existingAddon.type == "bootstrapped") >- XPIProvider.deactivateAddon(this.existingAddon.id, false, >- isUpgrade); >- // If this is an upgrade its metadata will be removed below >- if (!isUpgrade) { >- this.existingAddon.active = false; >- XPIDatabase.updateAddonActive(this.existingAddon); >- } >- } >- if (isUpgrade) >+ if (Services.vc.compare(this.existingAddon.version, this.addon.version) < 0) >+ reason = BOOTSTRAP_REASONS.ADDON_UPGRADE; >+ else >+ reason = BOOTSTRAP_REASONS.ADDON_DOWNGRADE; >+ >+ if (this.existingAddon.bootstrap) { >+ let dir = this.existingAddon._installLocation.getLocationForID(this.existingAddon.id); nit: well over 80
Comment on attachment 441904 [details] [diff] [review] patch rev 4 r=me with nits fixed
Attachment #441904 - Flags: review?(robert.bugzilla) → review+
No longer blocks: 549324
Status: ASSIGNED → RESOLVED
Closed: 15 years ago
Flags: in-testsuite+
Flags: in-litmus-
Resolution: --- → FIXED
Whiteboard: [rewrite][fixed-in-addonsmgr] → [rewrite]
Target Milestone: --- → mozilla1.9.3a5
do we a sample extension XPI file for Bootstrapped Extensions ?
Marking as verified fixed. If further changes are needed, a new bug should handle that work. Jetpacks work just fine with the new add-ons manager. Tested with builds on all platforms like Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.3a5pre) Gecko/20100517 Minefield/3.7a5pre (In reply to comment #14) > do we a sample extension XPI file for Bootstrapped Extensions ? You can check https://secure.toolness.com/xpi/restartless/ which contains some examples of Jetpacks.
Status: RESOLVED → VERIFIED
We have the docs at https://wiki.mozilla.org/Extension_Manager:Bootstrapped_Extensions but they really need to be nicified and put on MDC
Keywords: dev-doc-needed
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: