diff options
Diffstat (limited to 'toolkit/mozapps/extensions/internal/AddonRepository.jsm')
-rw-r--r-- | toolkit/mozapps/extensions/internal/AddonRepository.jsm | 301 |
1 files changed, 172 insertions, 129 deletions
diff --git a/toolkit/mozapps/extensions/internal/AddonRepository.jsm b/toolkit/mozapps/extensions/internal/AddonRepository.jsm index 7f88d44ad..adcecbee7 100644 --- a/toolkit/mozapps/extensions/internal/AddonRepository.jsm +++ b/toolkit/mozapps/extensions/internal/AddonRepository.jsm @@ -11,7 +11,6 @@ const Cr = Components.results; Components.utils.import("resource://gre/modules/Services.jsm"); Components.utils.import("resource://gre/modules/AddonManager.jsm"); -/* globals AddonManagerPrivate*/ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", @@ -24,8 +23,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "AddonRepository_SQLiteMigrator", "resource://gre/modules/addons/AddonRepository_SQLiteMigrator.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Promise", "resource://gre/modules/Promise.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "ServiceRequest", - "resource://gre/modules/ServiceRequest.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm"); @@ -67,13 +64,15 @@ const BLANK_DB = function() { } const TOOLKIT_ID = "toolkit@mozilla.org"; - +#ifdef MOZ_PHOENIX_EXTENSIONS +const FIREFOX_ID = "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"; +#endif Cu.import("resource://gre/modules/Log.jsm"); const LOGGER_ID = "addons.repository"; // Create a new logger for use by the Addons Repository // (Requires AddonManager.jsm) -var logger = Log.repository.getLogger(LOGGER_ID); +let logger = Log.repository.getLogger(LOGGER_ID); // A map between XML keys to AddonSearchResult keys for string values // that require no extra parsing from XML @@ -101,6 +100,10 @@ const INTEGER_KEY_MAP = { daily_users: "dailyUsers" }; +// Wrap the XHR factory so that tests can override with a mock +let XHRequest = Components.Constructor("@mozilla.org/xmlextras/xmlhttprequest;1", + "nsIXMLHttpRequest"); + function convertHTMLToPlainText(html) { if (!html) return html; @@ -130,14 +133,14 @@ function getAddonsToCache(aIds, aCallback) { types = types.split(","); - AddonManager.getAddonsByIDs(aIds, function(aAddons) { + AddonManager.getAddonsByIDs(aIds, function getAddonsToCache_getAddonsByIDs(aAddons) { let enabledIds = []; for (var i = 0; i < aIds.length; i++) { var preference = PREF_GETADDONS_CACHE_ID_ENABLED.replace("%ID%", aIds[i]); try { if (!Services.prefs.getBoolPref(preference)) continue; - } catch (e) { + } catch(e) { // If the preference doesn't exist caching is enabled by default } @@ -398,7 +401,7 @@ AddonSearchResult.prototype = { * A platform version to test against * @return Boolean representing if the add-on is compatible */ - isCompatibleWith: function(aAppVersion, aPlatformVersion) { + isCompatibleWith: function ASR_isCompatibleWith(aAppVerison, aPlatformVersion) { return true; }, @@ -415,7 +418,7 @@ AddonSearchResult.prototype = { * @param aPlatformVersion * A platform version to check for updates for */ - findUpdates: function(aListener, aReason, aAppVersion, aPlatformVersion) { + findUpdates: function ASR_findUpdates(aListener, aReason, aAppVersion, aPlatformVersion) { if ("onNoCompatibilityUpdateAvailable" in aListener) aListener.onNoCompatibilityUpdateAvailable(this); if ("onNoUpdateAvailable" in aListener) @@ -427,8 +430,7 @@ AddonSearchResult.prototype = { toJSON: function() { let json = {}; - for (let property of Object.keys(this)) { - let value = this[property]; + for (let [property, value] of Iterator(this)) { if (property.startsWith("_") || typeof(value) === "function") continue; @@ -451,8 +453,7 @@ AddonSearchResult.prototype = { } } - for (let property of Object.keys(this._unsupportedProperties)) { - let value = this._unsupportedProperties[property]; + for (let [property, value] of Iterator(this._unsupportedProperties)) { if (!property.startsWith("_")) json[property] = value; } @@ -477,11 +478,18 @@ this.AddonRepository = { * Whether caching is currently enabled */ get cacheEnabled() { + // Act as though caching is disabled if there was an unrecoverable error + // openning the database. + if (!AddonDatabase.databaseOk) { + logger.warn("Cache is disabled because database is not OK"); + return false; + } + let preference = PREF_GETADDONS_CACHE_ENABLED; let enabled = false; try { enabled = Services.prefs.getBoolPref(preference); - } catch (e) { + } catch(e) { logger.warn("cacheEnabled: Couldn't get pref: " + preference); } @@ -524,7 +532,7 @@ this.AddonRepository = { * return: promise{integer} resolves with the result of flushing * the AddonRepository database */ - shutdown: function() { + shutdown: function AddonRepo_shutdown() { this.cancelSearch(); this._addons = null; @@ -546,7 +554,7 @@ this.AddonRepository = { return now - lastUpdate; }, - isMetadataStale: function() { + isMetadataStale: function AddonRepo_isMetadataStale() { let threshold = DEFAULT_METADATA_UPDATETHRESHOLD_SEC; try { threshold = Services.prefs.getIntPref(PREF_METADATA_UPDATETHRESHOLD_SEC); @@ -564,7 +572,7 @@ this.AddonRepository = { * @param aCallback * The callback to pass the result back to */ - getCachedAddonByID: Task.async(function*(aId, aCallback) { + getCachedAddonByID: Task.async(function* (aId, aCallback) { if (!aId || !this.cacheEnabled) { aCallback(null); return; @@ -605,7 +613,7 @@ this.AddonRepository = { * Clear and delete the AddonRepository database * @return Promise{null} resolves when the database is deleted */ - _clearCache: function() { + _clearCache: function () { this._addons = null; return AddonDatabase.delete().then(() => new Promise((resolve, reject) => @@ -613,43 +621,44 @@ this.AddonRepository = { ); }, - _repopulateCacheInternal: Task.async(function*(aSendPerformance, aTimeout) { + _repopulateCacheInternal: Task.async(function* (aSendPerformance, aTimeout) { let allAddons = yield new Promise((resolve, reject) => AddonManager.getAllAddons(resolve)); - // Filter the hotfix out of our list of add-ons - allAddons = allAddons.filter(a => a.id != AddonManager.hotfixID); - // Completely remove cache if caching is not enabled if (!this.cacheEnabled) { logger.debug("Clearing cache because it is disabled"); - yield this._clearCache(); - return; + return this._clearCache(); } - let ids = allAddons.map(a => a.id); + // Tycho: let ids = [a.id for (a of allAddons)]; + let ids = []; + for (let a of allAddons) { + ids.push(a.id); + } + logger.debug("Repopulate add-on cache with " + ids.toSource()); + let self = this; let addonsToCache = yield new Promise((resolve, reject) => getAddonsToCache(ids, resolve)); // Completely remove cache if there are no add-ons to cache if (addonsToCache.length == 0) { logger.debug("Clearing cache because 0 add-ons were requested"); - yield this._clearCache(); - return; + return this._clearCache(); } yield new Promise((resolve, reject) => - this._beginGetAddons(addonsToCache, { - searchSucceeded: aAddons => { - this._addons = new Map(); + self._beginGetAddons(addonsToCache, { + searchSucceeded: function repopulateCacheInternal_searchSucceeded(aAddons) { + self._addons = new Map(); for (let addon of aAddons) { - this._addons.set(addon.id, addon); + self._addons.set(addon.id, addon); } AddonDatabase.repopulate(aAddons, resolve); }, - searchFailed: () => { + searchFailed: function repopulateCacheInternal_searchFailed() { logger.warn("Search failed when repopulating cache"); resolve(); } @@ -670,7 +679,7 @@ this.AddonRepository = { * @param aCallback * The optional callback to call once complete */ - cacheAddons: function(aIds, aCallback) { + cacheAddons: function AddonRepo_cacheAddons(aIds, aCallback) { logger.debug("cacheAddons: enabled " + this.cacheEnabled + " IDs " + aIds.toSource()); if (!this.cacheEnabled) { if (aCallback) @@ -678,7 +687,8 @@ this.AddonRepository = { return; } - getAddonsToCache(aIds, aAddons => { + let self = this; + getAddonsToCache(aIds, function cacheAddons_getAddonsToCache(aAddons) { // If there are no add-ons to cache, act as if caching is disabled if (aAddons.length == 0) { if (aCallback) @@ -686,14 +696,14 @@ this.AddonRepository = { return; } - this.getAddonsByIDs(aAddons, { - searchSucceeded: aAddons => { + self.getAddonsByIDs(aAddons, { + searchSucceeded: function cacheAddons_searchSucceeded(aAddons) { for (let addon of aAddons) { - this._addons.set(addon.id, addon); + self._addons.set(addon.id, addon); } AddonDatabase.insertAddons(aAddons, aCallback); }, - searchFailed: () => { + searchFailed: function cacheAddons_searchFailed() { logger.warn("Search failed when adding add-ons to cache"); if (aCallback) aCallback(); @@ -723,7 +733,7 @@ this.AddonRepository = { * The url that can be visited to see recommended add-ons in this repository. * If the corresponding preference is not defined, defaults to about:blank. */ - getRecommendedURL: function() { + getRecommendedURL: function AddonRepo_getRecommendedURL() { let url = this._formatURLPref(PREF_GETADDONS_BROWSERECOMMENDED, {}); return (url != null) ? url : "about:blank"; }, @@ -736,7 +746,7 @@ this.AddonRepository = { * @param aSearchTerms * Search terms used to search the repository */ - getSearchURL: function(aSearchTerms) { + getSearchURL: function AddonRepo_getSearchURL(aSearchTerms) { let url = this._formatURLPref(PREF_GETADDONS_BROWSESEARCHRESULTS, { TERMS : encodeURIComponent(aSearchTerms) }); @@ -747,7 +757,7 @@ this.AddonRepository = { * Cancels the search in progress. If there is no search in progress this * does nothing. */ - cancelSearch: function() { + cancelSearch: function AddonRepo_cancelSearch() { this._searching = false; if (this._request) { this._request.abort(); @@ -765,7 +775,7 @@ this.AddonRepository = { * @param aCallback * The callback to pass results to */ - getAddonsByIDs: function(aIDs, aCallback) { + getAddonsByIDs: function AddonRepo_getAddonsByIDs(aIDs, aCallback) { return this._beginGetAddons(aIDs, aCallback, false); }, @@ -823,12 +833,13 @@ this.AddonRepository = { let url = this._formatURLPref(pref, params); - let handleResults = (aElements, aTotalResults, aCompatData) => { + let self = this; + function handleResults(aElements, aTotalResults, aCompatData) { // Don't use this._parseAddons() so that, for example, // incompatible add-ons are not filtered out let results = []; - for (let i = 0; i < aElements.length && results.length < this._maxResults; i++) { - let result = this._parseAddon(aElements[i], null, aCompatData); + for (let i = 0; i < aElements.length && results.length < self._maxResults; i++) { + let result = self._parseAddon(aElements[i], null, aCompatData); if (result == null) continue; @@ -849,8 +860,7 @@ this.AddonRepository = { // Include any compatibility overrides for addons not hosted by the // remote repository. - for (let id in aCompatData) { - let addonCompat = aCompatData[id]; + for each (let addonCompat in aCompatData) { if (addonCompat.hosted) continue; @@ -867,7 +877,7 @@ this.AddonRepository = { } // aTotalResults irrelevant - this._reportSuccess(results, -1); + self._reportSuccess(results, -1); } this._beginSearch(url, ids.length, aCallback, handleResults, aTimeout); @@ -882,7 +892,7 @@ this.AddonRepository = { * * @return Promise{null} Resolves when the metadata update is complete. */ - backgroundUpdateCheck: function() { + backgroundUpdateCheck: function () { return this._repopulateCacheInternal(true); }, @@ -895,7 +905,7 @@ this.AddonRepository = { * @param aCallback * The callback to pass results to */ - retrieveRecommendedAddons: function(aMaxResults, aCallback) { + retrieveRecommendedAddons: function AddonRepo_retrieveRecommendedAddons(aMaxResults, aCallback) { let url = this._formatURLPref(PREF_GETADDONS_GETRECOMMENDED, { API_VERSION : API_VERSION, @@ -903,10 +913,11 @@ this.AddonRepository = { MAX_RESULTS : 2 * aMaxResults }); - let handleResults = (aElements, aTotalResults) => { - this._getLocalAddonIds(aLocalAddonIds => { + let self = this; + function handleResults(aElements, aTotalResults) { + self._getLocalAddonIds(function retrieveRecommendedAddons_getLocalAddonIds(aLocalAddonIds) { // aTotalResults irrelevant - this._parseAddons(aElements, -1, aLocalAddonIds); + self._parseAddons(aElements, -1, aLocalAddonIds); }); } @@ -924,7 +935,7 @@ this.AddonRepository = { * @param aCallback * The callback to pass results to */ - searchAddons: function(aSearchTerms, aMaxResults, aCallback) { + searchAddons: function AddonRepo_searchAddons(aSearchTerms, aMaxResults, aCallback) { let compatMode = "normal"; if (!AddonManager.checkCompatibility) compatMode = "ignore"; @@ -941,9 +952,10 @@ this.AddonRepository = { let url = this._formatURLPref(PREF_GETADDONS_GETSEARCHRESULTS, substitutions); - let handleResults = (aElements, aTotalResults) => { - this._getLocalAddonIds(aLocalAddonIds => { - this._parseAddons(aElements, aTotalResults, aLocalAddonIds); + let self = this; + function handleResults(aElements, aTotalResults) { + self._getLocalAddonIds(function searchAddons_getLocalAddonIds(aLocalAddonIds) { + self._parseAddons(aElements, aTotalResults, aLocalAddonIds); }); } @@ -951,18 +963,23 @@ this.AddonRepository = { }, // Posts results to the callback - _reportSuccess: function(aResults, aTotalResults) { + _reportSuccess: function AddonRepo_reportSuccess(aResults, aTotalResults) { this._searching = false; this._request = null; // The callback may want to trigger a new search so clear references early - let addons = aResults.map(result => result.addon); + // Tycho: let addons = [result.addon for each(result in aResults)]; + let addons = []; + for each(let result in aResults) { + addons.push(result.addon); + } + let callback = this._callback; this._callback = null; callback.searchSucceeded(addons, addons.length, aTotalResults); }, // Notifies the callback of a failure - _reportFailure: function() { + _reportFailure: function AddonRepo_reportFailure() { this._searching = false; this._request = null; // The callback may want to trigger a new search so clear references early @@ -972,28 +989,28 @@ this.AddonRepository = { }, // Get descendant by unique tag name. Returns null if not unique tag name. - _getUniqueDescendant: function(aElement, aTagName) { + _getUniqueDescendant: function AddonRepo_getUniqueDescendant(aElement, aTagName) { let elementsList = aElement.getElementsByTagName(aTagName); return (elementsList.length == 1) ? elementsList[0] : null; }, // Get direct descendant by unique tag name. // Returns null if not unique tag name. - _getUniqueDirectDescendant: function(aElement, aTagName) { + _getUniqueDirectDescendant: function AddonRepo_getUniqueDirectDescendant(aElement, aTagName) { let elementsList = Array.filter(aElement.children, - aChild => aChild.tagName == aTagName); + function arrayFiltering(aChild) aChild.tagName == aTagName); return (elementsList.length == 1) ? elementsList[0] : null; }, // Parse out trimmed text content. Returns null if text content empty. - _getTextContent: function(aElement) { + _getTextContent: function AddonRepo_getTextContent(aElement) { let textContent = aElement.textContent.trim(); return (textContent.length > 0) ? textContent : null; }, // Parse out trimmed text content of a descendant with the specified tag name // Returns null if the parsing unsuccessful. - _getDescendantTextContent: function(aElement, aTagName) { + _getDescendantTextContent: function AddonRepo_getDescendantTextContent(aElement, aTagName) { let descendant = this._getUniqueDescendant(aElement, aTagName); return (descendant != null) ? this._getTextContent(descendant) : null; }, @@ -1001,7 +1018,7 @@ this.AddonRepository = { // Parse out trimmed text content of a direct descendant with the specified // tag name. // Returns null if the parsing unsuccessful. - _getDirectDescendantTextContent: function(aElement, aTagName) { + _getDirectDescendantTextContent: function AddonRepo_getDirectDescendantTextContent(aElement, aTagName) { let descendant = this._getUniqueDirectDescendant(aElement, aTagName); return (descendant != null) ? this._getTextContent(descendant) : null; }, @@ -1019,7 +1036,7 @@ this.AddonRepository = { * @return Result object containing the parsed AddonSearchResult, xpiURL and * xpiHash if the parsing was successful. Otherwise returns null. */ - _parseAddon: function(aElement, aSkip, aCompatData) { + _parseAddon: function AddonRepo_parseAddon(aElement, aSkip, aCompatData) { let skipIDs = (aSkip && aSkip.ids) ? aSkip.ids : []; let skipSourceURIs = (aSkip && aSkip.sourceURIs) ? aSkip.sourceURIs : []; @@ -1037,6 +1054,7 @@ this.AddonRepository = { if (aCompatData && guid in aCompatData) addon.compatibilityOverrides = aCompatData[guid].compatRanges; + let self = this; for (let node = aElement.firstChild; node; node = node.nextSibling) { if (!(node instanceof Ci.nsIDOMElement)) continue; @@ -1107,8 +1125,8 @@ this.AddonRepository = { case "authors": let authorNodes = node.getElementsByTagName("author"); for (let authorNode of authorNodes) { - let name = this._getDescendantTextContent(authorNode, "name"); - let link = this._getDescendantTextContent(authorNode, "link"); + let name = self._getDescendantTextContent(authorNode, "name"); + let link = self._getDescendantTextContent(authorNode, "link"); if (name == null || link == null) continue; @@ -1126,22 +1144,22 @@ this.AddonRepository = { case "previews": let previewNodes = node.getElementsByTagName("preview"); for (let previewNode of previewNodes) { - let full = this._getUniqueDescendant(previewNode, "full"); + let full = self._getUniqueDescendant(previewNode, "full"); if (full == null) continue; - let fullURL = this._getTextContent(full); + let fullURL = self._getTextContent(full); let fullWidth = full.getAttribute("width"); let fullHeight = full.getAttribute("height"); let thumbnailURL, thumbnailWidth, thumbnailHeight; - let thumbnail = this._getUniqueDescendant(previewNode, "thumbnail"); + let thumbnail = self._getUniqueDescendant(previewNode, "thumbnail"); if (thumbnail) { - thumbnailURL = this._getTextContent(thumbnail); + thumbnailURL = self._getTextContent(thumbnail); thumbnailWidth = thumbnail.getAttribute("width"); thumbnailHeight = thumbnail.getAttribute("height"); } - let caption = this._getDescendantTextContent(previewNode, "caption"); + let caption = self._getDescendantTextContent(previewNode, "caption"); let screenshot = new AddonManagerPrivate.AddonScreenshot(fullURL, fullWidth, fullHeight, thumbnailURL, thumbnailWidth, thumbnailHeight, caption); @@ -1198,7 +1216,7 @@ this.AddonRepository = { break; case "all_compatible_os": let nodes = node.getElementsByTagName("os"); - addon.isPlatformCompatible = Array.some(nodes, function(aNode) { + addon.isPlatformCompatible = Array.some(nodes, function parseAddon_platformCompatFilter(aNode) { let text = aNode.textContent.toLowerCase().trim(); return text == "all" || text == Services.appinfo.OS.toLowerCase(); }); @@ -1244,10 +1262,21 @@ this.AddonRepository = { return result; }, - _parseAddons: function(aElements, aTotalResults, aSkip) { + _parseAddons: function AddonRepo_parseAddons(aElements, aTotalResults, aSkip) { + let self = this; let results = []; - let isSameApplication = aAppNode => this._getTextContent(aAppNode) == Services.appinfo.ID; + function isSameApplication(aAppNode) { +#ifdef MOZ_PHOENIX_EXTENSIONS + if (self._getTextContent(aAppNode) == Services.appinfo.ID || + self._getTextContent(aAppNode) == FIREFOX_ID) { +#else + if (self._getTextContent(aAppNode) == Services.appinfo.ID) { +#endif + return true; + } + return false; + } for (let i = 0; i < aElements.length && results.length < this._maxResults; i++) { let element = aElements[i]; @@ -1257,13 +1286,13 @@ this.AddonRepository = { continue; let applications = tags.getElementsByTagName("appID"); - let compatible = Array.some(applications, aAppNode => { + let compatible = Array.some(applications, function parseAddons_applicationsCompatFilter(aAppNode) { if (!isSameApplication(aAppNode)) return false; let parent = aAppNode.parentNode; - let minVersion = this._getDescendantTextContent(parent, "min_version"); - let maxVersion = this._getDescendantTextContent(parent, "max_version"); + let minVersion = self._getDescendantTextContent(parent, "min_version"); + let maxVersion = self._getDescendantTextContent(parent, "max_version"); if (minVersion == null || maxVersion == null) return false; @@ -1291,7 +1320,7 @@ this.AddonRepository = { // Ignore add-on missing a required attribute let requiredAttributes = ["id", "name", "version", "type", "creator"]; - if (requiredAttributes.some(aAttribute => !result.addon[aAttribute])) + if (requiredAttributes.some(function parseAddons_attributeFilter(aAttribute) !result.addon[aAttribute])) continue; // Ignore add-on with a type AddonManager doesn't understand: @@ -1322,28 +1351,28 @@ this.AddonRepository = { } // Create an AddonInstall for each result - for (let result of results) { - let addon = result.addon; - let callback = aInstall => { + results.forEach(function(aResult) { + let addon = aResult.addon; + let callback = function addonInstallCallback(aInstall) { addon.install = aInstall; pendingResults--; if (pendingResults == 0) - this._reportSuccess(results, aTotalResults); + self._reportSuccess(results, aTotalResults); } - if (result.xpiURL) { - AddonManager.getInstallForURL(result.xpiURL, callback, - "application/x-xpinstall", result.xpiHash, + if (aResult.xpiURL) { + AddonManager.getInstallForURL(aResult.xpiURL, callback, + "application/x-xpinstall", aResult.xpiHash, addon.name, addon.icons, addon.version); } else { callback(null); } - } + }); }, // Parses addon_compatibility nodes, that describe compatibility overrides. - _parseAddonCompatElement: function(aResultObj, aElement) { + _parseAddonCompatElement: function AddonRepo_parseAddonCompatElement(aResultObj, aElement) { let guid = this._getDescendantTextContent(aElement, "guid"); if (!guid) { logger.debug("Compatibility override is missing guid."); @@ -1416,7 +1445,7 @@ this.AddonRepository = { let rangeNodes = aElement.querySelectorAll("version_ranges > version_range"); compat.compatRanges = Array.map(rangeNodes, parseRangeNode.bind(this)) - .filter(aItem => !!aItem); + .filter(function compatRangesFilter(aItem) !!aItem); if (compat.compatRanges.length == 0) return; @@ -1424,7 +1453,7 @@ this.AddonRepository = { }, // Parses addon_compatibility elements. - _parseAddonCompatData: function(aElements) { + _parseAddonCompatData: function AddonRepo_parseAddonCompatData(aElements) { let compatData = {}; Array.forEach(aElements, this._parseAddonCompatElement.bind(this, compatData)); return compatData; @@ -1445,7 +1474,7 @@ this.AddonRepository = { logger.debug("Requesting " + aURI); - this._request = new ServiceRequest(); + this._request = new XHRequest(); this._request.mozBackgroundRequest = true; this._request.open("GET", aURI, true); this._request.overrideMimeType("text/xml"); @@ -1484,21 +1513,28 @@ this.AddonRepository = { // Gets the id's of local add-ons, and the sourceURI's of local installs, // passing the results to aCallback - _getLocalAddonIds: function(aCallback) { + _getLocalAddonIds: function AddonRepo_getLocalAddonIds(aCallback) { + let self = this; let localAddonIds = {ids: null, sourceURIs: null}; - AddonManager.getAllAddons(function(aAddons) { - localAddonIds.ids = aAddons.map(a => a.id); + AddonManager.getAllAddons(function getLocalAddonIds_getAllAddons(aAddons) { + // Tycho: localAddonIds.ids = [a.id for each (a in aAddons)]; + localAddonIds.ids = []; + + for each(let a in aAddons) { + localAddonIds.ids.push(a.id); + } + if (localAddonIds.sourceURIs) aCallback(localAddonIds); }); - AddonManager.getAllInstalls(function(aInstalls) { + AddonManager.getAllInstalls(function getLocalAddonIds_getAllInstalls(aInstalls) { localAddonIds.sourceURIs = []; - for (let install of aInstalls) { - if (install.state != AddonManager.STATE_AVAILABLE) - localAddonIds.sourceURIs.push(install.sourceURI.spec); - } + aInstalls.forEach(function(aInstall) { + if (aInstall.state != AddonManager.STATE_AVAILABLE) + localAddonIds.sourceURIs.push(aInstall.sourceURI.spec); + }); if (localAddonIds.ids) aCallback(localAddonIds); @@ -1506,16 +1542,16 @@ this.AddonRepository = { }, // Create url from preference, returning null if preference does not exist - _formatURLPref: function(aPreference, aSubstitutions) { + _formatURLPref: function AddonRepo_formatURLPref(aPreference, aSubstitutions) { let url = null; try { url = Services.prefs.getCharPref(aPreference); - } catch (e) { + } catch(e) { logger.warn("_formatURLPref: Couldn't get pref: " + aPreference); return null; } - url = url.replace(/%([A-Z_]+)%/g, function(aMatch, aKey) { + url = url.replace(/%([A-Z_]+)%/g, function urlSubstitution(aMatch, aKey) { return (aKey in aSubstitutions) ? aSubstitutions[aKey] : aMatch; }); @@ -1524,7 +1560,7 @@ this.AddonRepository = { // Find a AddonCompatibilityOverride that matches a given aAddonVersion and // application/platform version. - findMatchingCompatOverride: function(aAddonVersion, + findMatchingCompatOverride: function AddonRepo_findMatchingCompatOverride(aAddonVersion, aCompatOverrides, aAppVersion, aPlatformVersion) { @@ -1552,6 +1588,9 @@ this.AddonRepository = { }; var AddonDatabase = { + // false if there was an unrecoverable error opening the database + databaseOk: true, + connectionPromise: null, // the in-memory database DB: BLANK_DB(), @@ -1594,12 +1633,8 @@ var AddonDatabase = { schema < DB_MIN_JSON_SCHEMA) { throw new Error("Invalid schema value."); } - } catch (e) { - if (e instanceof OS.File.Error && e.becauseNoSuchFile) { - logger.debug("No " + FILE_DATABASE + " found."); - } else { - logger.error(`Malformed ${FILE_DATABASE}: ${e} - resetting to empty`); - } + } catch (e if e instanceof OS.File.Error && e.becauseNoSuchFile) { + logger.debug("No " + FILE_DATABASE + " found."); // Create a blank addons.json file this._saveDBToDisk(); @@ -1618,9 +1653,14 @@ var AddonDatabase = { yield this._insertAddons(results); } + Services.prefs.setIntPref(PREF_GETADDONS_DB_SCHEMA, DB_SCHEMA); } - Services.prefs.setIntPref(PREF_GETADDONS_DB_SCHEMA, DB_SCHEMA); + return this.DB; + } catch (e) { + logger.error("Malformed " + FILE_DATABASE + ": " + e); + this.databaseOk = false; + return this.DB; } @@ -1657,7 +1697,9 @@ var AddonDatabase = { * An optional boolean to skip flushing data to disk. Useful * when the database is going to be deleted afterwards. */ - shutdown: function(aSkipFlush) { + shutdown: function AD_shutdown(aSkipFlush) { + this.databaseOk = true; + if (!this.connectionPromise) { return Promise.resolve(); } @@ -1666,8 +1708,9 @@ var AddonDatabase = { if (aSkipFlush) { return Promise.resolve(); + } else { + return this.Writer.flush(); } - return this.Writer.flush(); }, /** @@ -1678,7 +1721,7 @@ var AddonDatabase = { * An optional callback to call once complete * @return Promise{null} resolves when the database has been deleted */ - delete: function(aCallback) { + delete: function AD_delete(aCallback) { this.DB = BLANK_DB(); this._deleting = this.Writer.flush() @@ -1693,7 +1736,7 @@ var AddonDatabase = { return this._deleting; }, - toJSON: function() { + toJSON: function AD_toJSON() { let json = { schema: this.DB.schema, addons: [] @@ -1738,7 +1781,7 @@ var AddonDatabase = { * @return: Promise{Map} * Resolves when the add-ons are retrieved from the database */ - retrieveStoredData: function() { + retrieveStoredData: function (){ return this.openConnection().then(db => db.addons); }, @@ -1751,9 +1794,9 @@ var AddonDatabase = { * @param aCallback * An optional callback to call once complete */ - repopulate: function(aAddons, aCallback) { + repopulate: function AD_repopulate(aAddons, aCallback) { this.DB.addons.clear(); - this.insertAddons(aAddons, function() { + this.insertAddons(aAddons, function repopulate_insertAddons() { let now = Math.round(Date.now() / 1000); logger.debug("Cache repopulated, setting " + PREF_METADATA_LASTUPDATE + " to " + now); Services.prefs.setIntPref(PREF_METADATA_LASTUPDATE, now); @@ -1770,12 +1813,12 @@ var AddonDatabase = { * @param aCallback * An optional callback to call once complete */ - insertAddons: Task.async(function*(aAddons, aCallback) { + insertAddons: Task.async(function* (aAddons, aCallback) { yield this.openConnection(); yield this._insertAddons(aAddons, aCallback); }), - _insertAddons: Task.async(function*(aAddons, aCallback) { + _insertAddons: Task.async(function* (aAddons, aCallback) { for (let addon of aAddons) { this._insertAddon(addon); } @@ -1794,7 +1837,7 @@ var AddonDatabase = { * @param aCallback * The callback to call once complete */ - _insertAddon: function(aAddon) { + _insertAddon: function AD__insertAddon(aAddon) { let newAddon = this._parseAddon(aAddon); if (!newAddon || !newAddon.id || @@ -1812,7 +1855,7 @@ var AddonDatabase = { * The object to parse * @return Returns an AddonSearchResult object. */ - _parseAddon: function(aObj) { + _parseAddon: function (aObj) { if (aObj instanceof AddonSearchResult) return aObj; @@ -1822,7 +1865,7 @@ var AddonDatabase = { let addon = new AddonSearchResult(id); - for (let expectedProperty of Object.keys(AddonSearchResult.prototype)) { + for (let [expectedProperty,] of Iterator(AddonSearchResult.prototype)) { if (!(expectedProperty in aObj) || typeof(aObj[expectedProperty]) === "function") continue; @@ -1870,8 +1913,8 @@ var AddonDatabase = { case "icons": if (!addon.icons) addon.icons = {}; - for (let size of Object.keys(aObj.icons)) { - addon.icons[size] = aObj.icons[size]; + for (let [size, url] of Iterator(aObj.icons)) { + addon.icons[size] = url; } break; @@ -1937,7 +1980,7 @@ var AddonDatabase = { * The JS object to use * @return The created developer */ - _makeDeveloper: function(aObj) { + _makeDeveloper: function (aObj) { let name = aObj.name; let url = aObj.url; return new AddonManagerPrivate.AddonAuthor(name, url); @@ -1951,7 +1994,7 @@ var AddonDatabase = { * The JS object to use * @return The created screenshot */ - _makeScreenshot: function(aObj) { + _makeScreenshot: function (aObj) { let url = aObj.url; let width = aObj.width; let height = aObj.height; @@ -1971,7 +2014,7 @@ var AddonDatabase = { * The JS object to use * @return The created CompatibilityOverride */ - _makeCompatOverride: function(aObj) { + _makeCompatOverride: function (aObj) { let type = aObj.type; let minVersion = aObj.minVersion; let maxVersion = aObj.maxVersion; |