diff options
Diffstat (limited to 'bandwagon/components/bandwagon-service.js')
-rw-r--r-- | bandwagon/components/bandwagon-service.js | 585 |
1 files changed, 100 insertions, 485 deletions
diff --git a/bandwagon/components/bandwagon-service.js b/bandwagon/components/bandwagon-service.js index 3e22c3b..8c484cd 100644 --- a/bandwagon/components/bandwagon-service.js +++ b/bandwagon/components/bandwagon-service.js @@ -59,31 +59,13 @@ const nsIFile = Ci.nsIFile; const nsIObserverService = Ci.nsIObserverService; const nsICookieManager = Ci.nsICookieManager; + var Bandwagon; var bandwagonService; -var gEmGUID; -var gUninstallObserverInited = false; - -/* Restore settings added or changed by the extension: - * - extension preferences - * - logins stored in the Login Manager? - */ -function cleanupSettings() -{ - // Cleanup preferences - var prefs = Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); - try { - prefs.deleteBranch("extensions.bandwagon"); - } - catch(e) {} -} - function BandwagonService() { this.wrappedJSObject = this; - gEmGUID = "sharing@addons.mozilla.org"; } BandwagonService.prototype = { @@ -94,7 +76,6 @@ BandwagonService.prototype = { _service: null, _collectionUpdateObservers: [], _collectionListChangeObservers: [], - _authenticationStatusChangeObservers: [], _storageConnection: null, _collectionFactory: null, _collectionUpdateTimer: null, @@ -121,8 +102,6 @@ BandwagonService.prototype = { Bandwagon.Logger.info("Initializing Bandwagon"); - this._initAMOHost(); - // init rpc service this._service = new Bandwagon.RPC.Service(); @@ -131,6 +110,7 @@ BandwagonService.prototype = { this._service.registerObserver(this._getServiceDocumentObserver); this.registerCollectionUpdateObserver(this._collectionUpdateObserver); + // init sqlite storage (also creating tables in sqlite if needed). create factory objects. this._initStorage(); @@ -143,60 +123,14 @@ BandwagonService.prototype = { this.firstrun(); } - // storage initialized, tables created - open the collections and service document - - this._initCollections(); - - // start the update timer + // storage initialized, tables created - open the collections - this._initUpdateTimer(); - - // observe when the app shuts down so we can uninit - - ObserverService.getService(nsIObserverService).addObserver(this._bwObserver, "quit-application", false); - - // kick off the auto-publish functionality - - this.autopublishExtensions(); - - this._initialized = true; - - Bandwagon.Logger.info("Bandwagon has been initialized"); - }, - - /** - * Update "constants" to reflect amo_host in preferences - */ - _initAMOHost: function() - { - var amoHost = Bandwagon.Preferences.getPreference("amo_host"); - - Bandwagon.RPC.Constants.BANDWAGON_RPC_SERVICE_DOCUMENT = Bandwagon.RPC.Constants.BANDWAGON_RPC_SERVICE_DOCUMENT.replace("%%AMO_HOST%%", amoHost); - Bandwagon.LOGINPANE_DO_NEW_ACCOUNT = Bandwagon.LOGINPANE_DO_NEW_ACCOUNT.replace("%%AMO_HOST%%", amoHost); - Bandwagon.COLLECTIONSPANE_DO_SUBSCRIBE_URL = Bandwagon.COLLECTIONSPANE_DO_SUBSCRIBE_URL.replace("%%AMO_HOST%%", amoHost); - Bandwagon.COLLECTIONSPANE_DO_NEW_COLLECTION_URL = Bandwagon.COLLECTIONSPANE_DO_NEW_COLLECTION_URL.replace("%%AMO_HOST%%", amoHost); - Bandwagon.FIRSTRUN_LANDING_PAGE = Bandwagon.FIRSTRUN_LANDING_PAGE.replace("%%AMO_HOST%%", amoHost); - Bandwagon.AMO_AUTH_COOKIE_HOST = Bandwagon.AMO_AUTH_COOKIE_HOST.replace("%%AMO_HOST%%", amoHost); - }, - - _initCollections: function() - { var storageCollections = this._collectionFactory.openCollections(); for (var id in storageCollections) { this.collections[id] = storageCollections[id]; this.collections[id].setAllNotified(); - - if (this.collections[id].isLocalAutoPublisher()) - { - this.collections[id].autoPublishExtensions = Bandwagon.Preferences.getPreference("local.autopublisher.publish.extensions"); - this.collections[id].autoPublishThemes = Bandwagon.Preferences.getPreference("local.autopublisher.publish.themes"); - this.collections[id].autoPublishDicts = Bandwagon.Preferences.getPreference("local.autopublisher.publish.dictionaries"); - this.collections[id].autoPublishLangPacks = Bandwagon.Preferences.getPreference("local.autopublisher.publish.language.packs"); - this.collections[id].autoPublishDisabled = !Bandwagon.Preferences.getPreference("local.autopublisher.only.publish.enabled"); - } - Bandwagon.Logger.debug("opened collection from storage: " + id); } @@ -208,10 +142,9 @@ BandwagonService.prototype = { // no service document in storage, we never had it or we've lost it - go fetch it this.updateCollectionsList(); } - }, - _initUpdateTimer: function() - { + // start the update timer + this._bwObserver = { observe: function(aSubject, aTopic, aData) @@ -230,19 +163,27 @@ BandwagonService.prototype = { this._collectionUpdateTimer = Timer.createInstance(nsITimer); this._collectionUpdateTimer.init( this._bwObserver, - (Bandwagon.Preferences.getPreference("debug")?120*1000:Bandwagon.COLLECTION_UPDATE_TIMER_DELAY*1000), + (Bandwagon.Preferences.getPreference("debug")?30*1000:Bandwagon.COLLECTION_UPDATE_TIMER_DELAY*1000), nsITimer.TYPE_REPEATING_SLACK ); + + // observe when the app shuts down so we can uninit + + ObserverService.getService(nsIObserverService).addObserver(this._bwObserver, "quit-application", false); + + // kick off the auto-publish functionality + + this._autopublishExtensions(); + + this._initialized = true; + + Bandwagon.Logger.info("Bandwagon has been initialized"); }, uninit: function() { this._collectionUpdateTimer = null; this.commitAll(); - this._service = null; - this._collectionFactory = null; - Bandwagon = null; - bandwagonService = null; }, getLocalAutoPublisher: function() @@ -258,10 +199,8 @@ BandwagonService.prototype = { return null; }, - autopublishExtensions: function(callback) + _autopublishExtensions: function() { - Bandwagon.Logger.debug("in autopublishExtensions()"); - var localAutoPublisher = bandwagonService.getLocalAutoPublisher(); if (localAutoPublisher == null) @@ -270,74 +209,12 @@ BandwagonService.prototype = { return; } - var internalCallback = function(event) - { - if (!event.isError()) - { - bandwagonService._notifyCollectionUpdateObservers(localAutoPublisher); - } - - if (callback) - { - callback(event); - } - } - var installedExtensions = Bandwagon.Util.getInstalledExtensions(); var autopublishedExtensions = Bandwagon.Preferences.getPreferenceList("autopublished.extensions"); var willAutopublishExtensions = []; for (var i=0; i<installedExtensions.length; i++) { - //Bandwagon.Logger.debug("checking addon '" + installedExtensions[i].id + "' against user auto pub prefs (type=" + installedExtensions[i].type + ")"); - - // check if user wants to publish this extension (enabled, type) - - if (( - Bandwagon.Util.getExtensionProperty(installedExtensions[i].id, "isDisabled") == "true" - || - Bandwagon.Util.getExtensionProperty(installedExtensions[i].id, "appDisabled") == "true" - || - Bandwagon.Util.getExtensionProperty(installedExtensions[i].id, "userDisabled") == "true" - ) - && !localAutoPublisher.autoPublishDisabled) - { - //Bandwagon.Logger.debug("addon '" + installedExtensions[i].id + "' is disabled, so won't publish"); - continue; - } - - if (installedExtensions[i].type & installedExtensions[i].TYPE_EXTENSION - && !localAutoPublisher.autoPublishExtensions) - { - //Bandwagon.Logger.debug("addon '" + installedExtensions[i].id + "' is an extension, so won't publish"); - continue; - } - - if (installedExtensions[i].type & installedExtensions[i].TYPE_THEME - && !localAutoPublisher.autoPublishThemes) - { - //Bandwagon.Logger.debug("addon '" + installedExtensions[i].id + "' is a theme, so won't publish"); - continue; - } - - if (installedExtensions[i].type & installedExtensions[i].TYPE_LOCALE - && !localAutoPublisher.autoPublishLangPacks) - { - //Bandwagon.Logger.debug("addon '" + installedExtensions[i].id + "' is a locale, so won't publish"); - continue; - } - - /** TODO - if (installedExtensions[i].type & installedExtensions[i].TYPE_DICT - && !localAutoPublisher.autoPublishDicts) - { - Bandwagon.Logger.debug("addon '" + installedExtensions[i].id + "' is a dict, so won't publish"); - continue; - } - */ - - // check if we have already published this extension - var hasPublished = false; for (var j=0; j<autopublishedExtensions.length; j++) @@ -351,20 +228,15 @@ BandwagonService.prototype = { if (hasPublished == false) { - //Bandwagon.Logger.debug("addon '" + installedExtensions[i].id + "' added to auto-publish queue"); willAutopublishExtensions.push(installedExtensions[i]); } - else - { - //Bandwagon.Logger.debug("addon '" + installedExtensions[i].id + "' has already been published"); - } } if (willAutopublishExtensions.length > 0) { for (var i=0; i<willAutopublishExtensions.length; i++) { - //Bandwagon.Logger.debug("Will autopublish extension '" + willAutopublishExtensions[i].id + "' to collection '" + localAutoPublisher.resourceURL + "'"); + Bandwagon.Logger.debug("Will autopublish extension '" + willAutopublishExtensions[i].id + "' to collection '" + localAutoPublisher.resourceURL + "'"); var extension = { @@ -372,7 +244,7 @@ BandwagonService.prototype = { name: willAutopublishExtensions[i].name } - bandwagonService.publishToCollection(extension, localAutoPublisher, "", internalCallback); + bandwagonService.publishToCollection(extension, localAutoPublisher, "", null); // add to autopublish autopublishedExtensions.push(willAutopublishExtensions[i].id); @@ -393,12 +265,6 @@ BandwagonService.prototype = { if (event.isError()) { Bandwagon.Logger.error("RPC error: '" + event.getError().getMessage() + "'"); - - if (event.getError().getCode() == Bandwagon.RPC.Constants.BANDWAGON_RPC_SERVICE_ERROR_UNAUTHORIZED) - { - bandwagonService.deauthenticate(); - } - // otherwise ignore for now } else @@ -425,11 +291,6 @@ BandwagonService.prototype = { if (event.isError()) { Bandwagon.Logger.error("Could not update collections list: " + event.getError().toString()); - - if (event.getError().getCode() == Bandwagon.RPC.Constants.BANDWAGON_RPC_SERVICE_ERROR_UNAUTHORIZED) - { - bandwagonService.deauthenticate(); - } } else { @@ -520,38 +381,6 @@ BandwagonService.prototype = { } }, - _notifyAuthenticationStatusChangeObservers: function() - { - Bandwagon.Logger.debug("Notifying authentication status change observers"); - - for (var i=0; i<bandwagonService._authenticationStatusChangeObservers.length; i++) - { - if (bandwagonService._authenticationStatusChangeObservers[i]) - { - bandwagonService._authenticationStatusChangeObservers[i](); - } - } - }, - - registerAuthenticationStatusChangeObserver: function(observer) - { - Bandwagon.Logger.debug("Registering authentication status change observer"); - this._authenticationStatusChangeObservers.push(observer); - }, - - unregisterAuthenticationStatusChangeObserver: function(observer) - { - Bandwagon.Logger.debug("Unregistering authentication status change observer"); - - for (var i=0; i<this._authenticationStatusChangeObservers.length; i++) - { - if (this._authenticationStatusChangeObservers[i] == observer) - { - delete this._authenticationStatusChangeObservers[i]; - } - } - }, - _notifyListChangeObservers: function() { Bandwagon.Logger.debug("Notifying collection list change observers"); @@ -584,72 +413,27 @@ BandwagonService.prototype = { } }, - authenticate: function(login, password, callback) - { - Bandwagon.Logger.debug("in authenticate()"); - - var service = this; - - Bandwagon.Preferences.setPreference(Bandwagon.PREF_AUTH_TOKEN, ""); - Bandwagon.Preferences.setPreference("login", ""); - - // The following is a workaround to allow auth-token based - // authentication to work when an AMO cookie is also present. Full - // description in bug 496612. - // XXX. Comment out when bug 496612 is addressed. - //this.deleteAMOCookie(); - - var internalCallback = function(event) - { - if (!event.isError()) - { - Bandwagon.Preferences.setPreference("login", login); - - service._notifyAuthenticationStatusChangeObservers(); - } - - if (callback) - callback(event); - } - - this._service.authenticate(login, password, internalCallback); - }, - - deauthenticate: function(callback) - { - Bandwagon.Preferences.setPreference(Bandwagon.PREF_AUTH_TOKEN, ""); - Bandwagon.Preferences.setPreference("login", ""); - - this._notifyAuthenticationStatusChangeObservers(); - - if (callback) - callback(); - }, - - updateCollectionsList: function(callback) + updateCollectionsList: function() { Bandwagon.Logger.debug("Updating collections list..."); - this.updateServiceDocument(callback); + this.updateServiceDocument(); }, - updateServiceDocument: function(callback) + updateServiceDocument: function() { - if (!this.isAuthenticated()) + if (!this.isAMOAuthenticated()) + { + Bandwagon.Logger.debug("Not authenticated in AMO"); return; - - this._service.getServiceDocument(callback); + } + + this._service.getServiceDocument(); }, checkForUpdates: function(collection) { - if (!this.isAuthenticated()) - return; - this._service.getCollection(collection); - - var now = new Date(); - collection.dateLastCheck = now; }, @@ -712,9 +496,6 @@ BandwagonService.prototype = { forceCheckForUpdates: function(collection) { - if (!this.isAuthenticated()) - return; - this._service.getCollection(collection); collection.dateLastCheck = new Date(); }, @@ -730,19 +511,54 @@ BandwagonService.prototype = { } }, - forceCheckAllForUpdatesAndUpdateCollectionsList: function(callback) + forceCheckAllForUpdatesAndUpdateCollectionsList: function() { // All updates to the collections list are forced, i.e. they are always // caused by *some* user interaction, never in the background. // Updating the collections list also forces the collections to be updated. - this.updateCollectionsList(callback); + this.updateCollectionsList(); + }, + + /** OBSOLETE + subscribe: function(collection) + { + collection.preview = false; + collection.setAllNotified(); + + this._service.subscribeCollection(collection); }, + */ + + /** OBSOLETE + unsubscribe: function(collection) + { + this._service.unsubscribeCollection(collection); + }, + */ firstrun: function() { Bandwagon.Logger.info("This is bandwagon's firstrun. Welcome!"); + // set up and save default collections + + // FIXME temporarily disabling this + //this._addDefaultCollection(Bandwagon.DEFAULT_COLLECTION1_URL, Bandwagon.DEFAULT_COLLECTION1_NAME); + //this._addDefaultCollection("http://www.33eels.com/clients/briks/bandwagon/testcollection.xml", "test collection"); + + /** OBSOLETE + // check for cookie to see if we have to add a collection like that + var addCollectionCookieValue = Bandwagon.Util.getCookie(Bandwagon.MAGIC_ADD_COLLECTION_COOKIE_HOST, Bandwagon.MAGIC_ADD_COLLECTION_COOKIE_NAME); + if (addCollectionCookieValue) + { + Bandwagon.Logger.info("Found magic 'add collection' cookie. Adding the collection '" + addCollectionCookieValue + "'."); + this.addPreviewCollection(addCollectionCookieValue); + + // TODO we don't have to because we're in firstrun, but should we delete cookie to be neat? + } + */ + // the last check date is now var now = new Date(); @@ -769,6 +585,22 @@ BandwagonService.prototype = { this.subscribe(collection); }, + /** OBSOLETE + addPreviewCollection: function(url) + { + var collection = this._collectionFactory.newCollection(); + collection.resourceURL = url; + collection.preview = true; + this.collections[collection.resourceURL] = collection; + + this.forceCheckForUpdates(collection); + + bandwagonService._notifyListChangeObservers(); + + return collection; + }, + */ + uninstall: function() { // TODO @@ -779,6 +611,11 @@ BandwagonService.prototype = { if (!bandwagonService._collectionFactory) return; + /** OBSOLETE + if (collection.preview) + return; + */ + Bandwagon.Logger.debug("In commit() with collection: " + collection.resourceURL); bandwagonService._collectionFactory.commitCollection(collection); @@ -799,23 +636,18 @@ BandwagonService.prototype = { bandwagonService._collectionFactory.commitServiceDocument(bandwagonService._serviceDocument); }, - removeAddonFromCollection: function(guid, collection, callback) + removeAddonFromCollection: function(guid, collection) { Bandwagon.Logger.debug("In removeAddonFromCollection()"); - if (!this.isAuthenticated()) - return; - - this._service.removeAddonFromCollection(guid, collection, callback); + this._service.removeAddonFromCollection(guid, collection); }, newCollection: function(collection, callback) { Bandwagon.Logger.debug("In newCollection()"); - if (!this.isAuthenticated()) - return; - + /* var internalCallback = function(event) { if (!event.isError()) @@ -834,6 +666,9 @@ BandwagonService.prototype = { } this._service.newCollection(collection, internalCallback); + */ + + this._service.newCollection(collection, callback); }, unlinkCollection: function(collection) @@ -855,114 +690,9 @@ BandwagonService.prototype = { deleteCollection: function(collection, callback) { - if (!this.isAuthenticated()) - return; - this._service.deleteCollection(collection, callback); }, - subscribeToCollection: function(collection, callback) - { - if (!this.isAuthenticated()) - return; - - var internalCallback = function(event) - { - if (!event.isError()) - { - collection.subscribed = true; - bandwagonService._notifyListChangeObservers(); - } - - if (callback) - { - callback(event); - } - } - - this._service.subscribeToCollection(collection, internalCallback); - }, - - unsubscribeFromCollection: function(collection, callback) - { - if (!this.isAuthenticated()) - return; - - var internalCallback = function(event) - { - if (!event.isError()) - { - bandwagonService.unlinkCollection(collection); - } - - if (callback) - { - callback(event); - } - } - - this._service.unsubscribeFromCollection(collection, internalCallback); - }, - - updateCollectionDetails: function(collection, callback) - { - if (!this.isAuthenticated()) - return; - - this._service.updateCollectionDetails(collection, callback); - }, - - /** This function is called when a 3rd party extension is uninstalled by - * the user. If this extension is part of a local auto publisher, we - * remove it from that autopublisher. - */ - processOtherExtensionUninstall: function(guid, callback) - { - Bandwagon.Logger.debug("In processOtherExtensionUninstall() with guid = " + guid); - - var localAutoPublisher = bandwagonService.getLocalAutoPublisher(); - - if (!localAutoPublisher) - return; - - var internalCallback = function(event) - { - if (!event.isError()) - { - // update list of autopublished extensions to remove this one - var autopublishedExtensions = Bandwagon.Preferences.getPreferenceList("autopublished.extensions"); - - for (var j=0; j<autopublishedExtensions.length; j++) - { - if (autopublishedExtensions[j] == guid) - { - delete autopublishedExtensions[j]; - break; - } - } - - Bandwagon.Preferences.setPreferenceList("autopublished.extensions", autopublishedExtensions); - - bandwagonService.forceCheckForUpdates(localAutoPublisher); - - if (callback) - callback(event); - } - } - - for (var id in localAutoPublisher.addons) - { - if (localAutoPublisher.addons[id].guid == guid) - { - Bandwagon.Logger.debug("Found this extension in local auto publisher: '" + localAutoPublisher.addons[id].guid + "' vs '" + guid + "', will remove."); - - this.removeAddonFromCollection(guid, localAutoPublisher, internalCallback); - - break; - } - } - }, - getAddonsPerPage: function(collection) { // returns this collection's custom items per page, or else the global value @@ -1000,8 +730,6 @@ BandwagonService.prototype = { { emailAddress = emailAddress.replace(/^\s+/, ""); emailAddress = emailAddress.replace(/\s+$/, ""); - emailAddress = emailAddress.replace(/^,/, ""); - emailAddress = emailAddress.replace(/,$/, ""); var previouslySharedEmailAddresses = this.getPreviouslySharedEmailAddresses(); @@ -1024,44 +752,21 @@ BandwagonService.prototype = { for (var i=0; i<bits.length; i++) { - if (bits[i].match(/.*@.*/)) - { - this.addPreviouslySharedEmailAddress(bits[i]); - } + this.addPreviouslySharedEmailAddress(bits[i]); } }, publishToCollection: function(extension, collection, personalNote, callback) { - if (!this.isAuthenticated()) - return; - this._service.publishToCollection(extension, collection, personalNote, callback); }, shareToEmail: function(extension, emailAddress, personalNote, callback) { - if (!this.isAuthenticated()) - return; - - // trim any commas from a multi-email string - - emailAddress = emailAddress.replace(/^,/, ""); - emailAddress = emailAddress.replace(/,$/, ""); - this._service.shareToEmail(extension, emailAddress, personalNote, callback); }, - /** - * Performs a 'soft' check for authenication. I.e. do we have a token from a previous auth. This method doesn't - * check if that token is still valid on the server. - */ - isAuthenticated: function() - { - return (Bandwagon.Preferences.getPreference(Bandwagon.PREF_AUTH_TOKEN) != ""); - }, - - deleteAMOCookie: function() + isAMOAuthenticated: function() { var cm = CookieManager.getService(nsICookieManager); @@ -1074,12 +779,11 @@ BandwagonService.prototype = { if (cookie instanceof Ci.nsICookie) { if (cookie.host == Bandwagon.AMO_AUTH_COOKIE_HOST && cookie.name == Bandwagon.AMO_AUTH_COOKIE_NAME) - { - // KILL! - cm.remove(cookie.host, cookie.name, cookie.path, false); - } + return true; } } + + return false; }, _collectionUpdateObserver: function(collection) @@ -1139,7 +843,7 @@ BandwagonService.prototype = { try { - this._storageConnection = storageService.openUnsharedDatabase(file); + this._storageConnection = storageService.openDatabase(file); } catch (e) { @@ -1159,8 +863,6 @@ BandwagonService.prototype = { // create tables (if they're not already created) - this._storageConnection.beginTransaction(); - try { this._storageConnection.executeSimpleSQL( @@ -1257,93 +959,6 @@ BandwagonService.prototype = { catch (e) { Bandwagon.Logger.error("Error creating sqlite table: " + e); - this._storageConnection.rollbackTransaction(); - return; - } - - this._storageConnection.commitTransaction(); - }, - - startUninstallObserver : function () - { - if (gUninstallObserverInited) return; - - var extService = Components.classes["@mozilla.org/extensions/manager;1"] - .getService(Components.interfaces.nsIExtensionManager); - - if (extService && ("uninstallItem" in extService)) - { - var observerService = Components.classes["@mozilla.org/observer-service;1"] - .getService(Components.interfaces.nsIObserverService); - observerService.addObserver(this.addonsAction, "em-action-requested", false); - gUninstallObserverInited = true; - } - else - { - try - { - extService.datasource.AddObserver(this.addonsObserver); - gUninstallObserverInited = true; - } - catch (e) { } - } - }, - - addonsObserver: - { - onAssert: function (ds, subject, predicate, target) - { - if ((predicate.Value == "http://www.mozilla.org/2004/em-rdf#toBeUninstalled") - && - (target instanceof Components.interfaces.nsIRDFLiteral) - && - (target.Value == "true")) - { - if (subject.Value == "urn:mozilla:extension:" + gEmGUID) - { - // This is case where bandwagon is being uninstalled - clean up - - cleanupSettings(); - } - else - { - // This is case where some other extension is being uninstalled - - var val = subject.Value; - val = val.replace(/urn:mozilla:extension:/, ""); - - bandwagonService.processOtherExtensionUninstall(val); - } - } - }, - - onUnassert: function (ds, subject, predicate, target) {}, - onChange: function (ds, subject, predicate, oldtarget, newtarget) {}, - onMove: function (ds, oldsubject, newsubject, predicate, target) {}, - onBeginUpdateBatch: function() {}, - onEndUpdateBatch: function() {} - }, - - addonsAction: - { - observe: function (subject, topic, data) - { - if ((data == "item-uninstalled") && - (subject instanceof Components.interfaces.nsIUpdateItem)) - { - if (subject.id == gEmGUID) - { - // This is case where bandwagon is being uninstalled - clean up - - cleanupSettings(); - } - else - { - // This is case where some other extension is being uninstalled - - bandwagonService.processOtherExtensionUninstall(subject.id); - } - } } }, |