Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | "use strict"; |
michael@0 | 6 | |
michael@0 | 7 | const Cc = Components.classes; |
michael@0 | 8 | const Ci = Components.interfaces; |
michael@0 | 9 | const Cu = Components.utils; |
michael@0 | 10 | |
michael@0 | 11 | Cu.import("resource://gre/modules/Services.jsm"); |
michael@0 | 12 | Cu.import("resource://gre/modules/AddonManager.jsm"); |
michael@0 | 13 | Cu.import("resource://gre/modules/FileUtils.jsm"); |
michael@0 | 14 | |
michael@0 | 15 | const KEY_PROFILEDIR = "ProfD"; |
michael@0 | 16 | const FILE_DATABASE = "addons.sqlite"; |
michael@0 | 17 | const LAST_DB_SCHEMA = 4; |
michael@0 | 18 | |
michael@0 | 19 | // Add-on properties present in the columns of the database |
michael@0 | 20 | const PROP_SINGLE = ["id", "type", "name", "version", "creator", "description", |
michael@0 | 21 | "fullDescription", "developerComments", "eula", |
michael@0 | 22 | "homepageURL", "supportURL", "contributionURL", |
michael@0 | 23 | "contributionAmount", "averageRating", "reviewCount", |
michael@0 | 24 | "reviewURL", "totalDownloads", "weeklyDownloads", |
michael@0 | 25 | "dailyUsers", "sourceURI", "repositoryStatus", "size", |
michael@0 | 26 | "updateDate"]; |
michael@0 | 27 | |
michael@0 | 28 | Cu.import("resource://gre/modules/Log.jsm"); |
michael@0 | 29 | const LOGGER_ID = "addons.repository.sqlmigrator"; |
michael@0 | 30 | |
michael@0 | 31 | // Create a new logger for use by the Addons Repository SQL Migrator |
michael@0 | 32 | // (Requires AddonManager.jsm) |
michael@0 | 33 | let logger = Log.repository.getLogger(LOGGER_ID); |
michael@0 | 34 | |
michael@0 | 35 | this.EXPORTED_SYMBOLS = ["AddonRepository_SQLiteMigrator"]; |
michael@0 | 36 | |
michael@0 | 37 | |
michael@0 | 38 | this.AddonRepository_SQLiteMigrator = { |
michael@0 | 39 | |
michael@0 | 40 | /** |
michael@0 | 41 | * Migrates data from a previous SQLite version of the |
michael@0 | 42 | * database to the JSON version. |
michael@0 | 43 | * |
michael@0 | 44 | * @param structFunctions an object that contains functions |
michael@0 | 45 | * to create the various objects used |
michael@0 | 46 | * in the new JSON format |
michael@0 | 47 | * @param aCallback A callback to be called when migration |
michael@0 | 48 | * finishes, with the results in an array |
michael@0 | 49 | * @returns bool True if a migration will happen (DB was |
michael@0 | 50 | * found and succesfully opened) |
michael@0 | 51 | */ |
michael@0 | 52 | migrate: function(aCallback) { |
michael@0 | 53 | if (!this._openConnection()) { |
michael@0 | 54 | this._closeConnection(); |
michael@0 | 55 | aCallback([]); |
michael@0 | 56 | return false; |
michael@0 | 57 | } |
michael@0 | 58 | |
michael@0 | 59 | logger.debug("Importing addon repository from previous " + FILE_DATABASE + " storage."); |
michael@0 | 60 | |
michael@0 | 61 | this._retrieveStoredData((results) => { |
michael@0 | 62 | this._closeConnection(); |
michael@0 | 63 | let resultArray = [addon for ([,addon] of Iterator(results))]; |
michael@0 | 64 | logger.debug(resultArray.length + " addons imported.") |
michael@0 | 65 | aCallback(resultArray); |
michael@0 | 66 | }); |
michael@0 | 67 | |
michael@0 | 68 | return true; |
michael@0 | 69 | }, |
michael@0 | 70 | |
michael@0 | 71 | /** |
michael@0 | 72 | * Synchronously opens a new connection to the database file. |
michael@0 | 73 | * |
michael@0 | 74 | * @return bool Whether the DB was opened successfully. |
michael@0 | 75 | */ |
michael@0 | 76 | _openConnection: function AD_openConnection() { |
michael@0 | 77 | delete this.connection; |
michael@0 | 78 | |
michael@0 | 79 | let dbfile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_DATABASE], true); |
michael@0 | 80 | if (!dbfile.exists()) |
michael@0 | 81 | return false; |
michael@0 | 82 | |
michael@0 | 83 | try { |
michael@0 | 84 | this.connection = Services.storage.openUnsharedDatabase(dbfile); |
michael@0 | 85 | } catch (e) { |
michael@0 | 86 | return false; |
michael@0 | 87 | } |
michael@0 | 88 | |
michael@0 | 89 | this.connection.executeSimpleSQL("PRAGMA locking_mode = EXCLUSIVE"); |
michael@0 | 90 | |
michael@0 | 91 | // Any errors in here should rollback |
michael@0 | 92 | try { |
michael@0 | 93 | this.connection.beginTransaction(); |
michael@0 | 94 | |
michael@0 | 95 | switch (this.connection.schemaVersion) { |
michael@0 | 96 | case 0: |
michael@0 | 97 | return false; |
michael@0 | 98 | |
michael@0 | 99 | case 1: |
michael@0 | 100 | logger.debug("Upgrading database schema to version 2"); |
michael@0 | 101 | this.connection.executeSimpleSQL("ALTER TABLE screenshot ADD COLUMN width INTEGER"); |
michael@0 | 102 | this.connection.executeSimpleSQL("ALTER TABLE screenshot ADD COLUMN height INTEGER"); |
michael@0 | 103 | this.connection.executeSimpleSQL("ALTER TABLE screenshot ADD COLUMN thumbnailWidth INTEGER"); |
michael@0 | 104 | this.connection.executeSimpleSQL("ALTER TABLE screenshot ADD COLUMN thumbnailHeight INTEGER"); |
michael@0 | 105 | case 2: |
michael@0 | 106 | logger.debug("Upgrading database schema to version 3"); |
michael@0 | 107 | this.connection.createTable("compatibility_override", |
michael@0 | 108 | "addon_internal_id INTEGER, " + |
michael@0 | 109 | "num INTEGER, " + |
michael@0 | 110 | "type TEXT, " + |
michael@0 | 111 | "minVersion TEXT, " + |
michael@0 | 112 | "maxVersion TEXT, " + |
michael@0 | 113 | "appID TEXT, " + |
michael@0 | 114 | "appMinVersion TEXT, " + |
michael@0 | 115 | "appMaxVersion TEXT, " + |
michael@0 | 116 | "PRIMARY KEY (addon_internal_id, num)"); |
michael@0 | 117 | case 3: |
michael@0 | 118 | logger.debug("Upgrading database schema to version 4"); |
michael@0 | 119 | this.connection.createTable("icon", |
michael@0 | 120 | "addon_internal_id INTEGER, " + |
michael@0 | 121 | "size INTEGER, " + |
michael@0 | 122 | "url TEXT, " + |
michael@0 | 123 | "PRIMARY KEY (addon_internal_id, size)"); |
michael@0 | 124 | this._createIndices(); |
michael@0 | 125 | this._createTriggers(); |
michael@0 | 126 | this.connection.schemaVersion = LAST_DB_SCHEMA; |
michael@0 | 127 | case LAST_DB_SCHEMA: |
michael@0 | 128 | break; |
michael@0 | 129 | default: |
michael@0 | 130 | return false; |
michael@0 | 131 | } |
michael@0 | 132 | this.connection.commitTransaction(); |
michael@0 | 133 | } catch (e) { |
michael@0 | 134 | logger.error("Failed to open " + FILE_DATABASE + ". Data import will not happen.", e); |
michael@0 | 135 | this.logSQLError(this.connection.lastError, this.connection.lastErrorString); |
michael@0 | 136 | this.connection.rollbackTransaction(); |
michael@0 | 137 | return false; |
michael@0 | 138 | } |
michael@0 | 139 | |
michael@0 | 140 | return true; |
michael@0 | 141 | }, |
michael@0 | 142 | |
michael@0 | 143 | _closeConnection: function() { |
michael@0 | 144 | for each (let stmt in this.asyncStatementsCache) |
michael@0 | 145 | stmt.finalize(); |
michael@0 | 146 | this.asyncStatementsCache = {}; |
michael@0 | 147 | |
michael@0 | 148 | if (this.connection) |
michael@0 | 149 | this.connection.asyncClose(); |
michael@0 | 150 | |
michael@0 | 151 | delete this.connection; |
michael@0 | 152 | }, |
michael@0 | 153 | |
michael@0 | 154 | /** |
michael@0 | 155 | * Asynchronously retrieve all add-ons from the database, and pass it |
michael@0 | 156 | * to the specified callback |
michael@0 | 157 | * |
michael@0 | 158 | * @param aCallback |
michael@0 | 159 | * The callback to pass the add-ons back to |
michael@0 | 160 | */ |
michael@0 | 161 | _retrieveStoredData: function AD_retrieveStoredData(aCallback) { |
michael@0 | 162 | let self = this; |
michael@0 | 163 | let addons = {}; |
michael@0 | 164 | |
michael@0 | 165 | // Retrieve all data from the addon table |
michael@0 | 166 | function getAllAddons() { |
michael@0 | 167 | self.getAsyncStatement("getAllAddons").executeAsync({ |
michael@0 | 168 | handleResult: function getAllAddons_handleResult(aResults) { |
michael@0 | 169 | let row = null; |
michael@0 | 170 | while ((row = aResults.getNextRow())) { |
michael@0 | 171 | let internal_id = row.getResultByName("internal_id"); |
michael@0 | 172 | addons[internal_id] = self._makeAddonFromAsyncRow(row); |
michael@0 | 173 | } |
michael@0 | 174 | }, |
michael@0 | 175 | |
michael@0 | 176 | handleError: self.asyncErrorLogger, |
michael@0 | 177 | |
michael@0 | 178 | handleCompletion: function getAllAddons_handleCompletion(aReason) { |
michael@0 | 179 | if (aReason != Ci.mozIStorageStatementCallback.REASON_FINISHED) { |
michael@0 | 180 | logger.error("Error retrieving add-ons from database. Returning empty results"); |
michael@0 | 181 | aCallback({}); |
michael@0 | 182 | return; |
michael@0 | 183 | } |
michael@0 | 184 | |
michael@0 | 185 | getAllDevelopers(); |
michael@0 | 186 | } |
michael@0 | 187 | }); |
michael@0 | 188 | } |
michael@0 | 189 | |
michael@0 | 190 | // Retrieve all data from the developer table |
michael@0 | 191 | function getAllDevelopers() { |
michael@0 | 192 | self.getAsyncStatement("getAllDevelopers").executeAsync({ |
michael@0 | 193 | handleResult: function getAllDevelopers_handleResult(aResults) { |
michael@0 | 194 | let row = null; |
michael@0 | 195 | while ((row = aResults.getNextRow())) { |
michael@0 | 196 | let addon_internal_id = row.getResultByName("addon_internal_id"); |
michael@0 | 197 | if (!(addon_internal_id in addons)) { |
michael@0 | 198 | logger.warn("Found a developer not linked to an add-on in database"); |
michael@0 | 199 | continue; |
michael@0 | 200 | } |
michael@0 | 201 | |
michael@0 | 202 | let addon = addons[addon_internal_id]; |
michael@0 | 203 | if (!addon.developers) |
michael@0 | 204 | addon.developers = []; |
michael@0 | 205 | |
michael@0 | 206 | addon.developers.push(self._makeDeveloperFromAsyncRow(row)); |
michael@0 | 207 | } |
michael@0 | 208 | }, |
michael@0 | 209 | |
michael@0 | 210 | handleError: self.asyncErrorLogger, |
michael@0 | 211 | |
michael@0 | 212 | handleCompletion: function getAllDevelopers_handleCompletion(aReason) { |
michael@0 | 213 | if (aReason != Ci.mozIStorageStatementCallback.REASON_FINISHED) { |
michael@0 | 214 | logger.error("Error retrieving developers from database. Returning empty results"); |
michael@0 | 215 | aCallback({}); |
michael@0 | 216 | return; |
michael@0 | 217 | } |
michael@0 | 218 | |
michael@0 | 219 | getAllScreenshots(); |
michael@0 | 220 | } |
michael@0 | 221 | }); |
michael@0 | 222 | } |
michael@0 | 223 | |
michael@0 | 224 | // Retrieve all data from the screenshot table |
michael@0 | 225 | function getAllScreenshots() { |
michael@0 | 226 | self.getAsyncStatement("getAllScreenshots").executeAsync({ |
michael@0 | 227 | handleResult: function getAllScreenshots_handleResult(aResults) { |
michael@0 | 228 | let row = null; |
michael@0 | 229 | while ((row = aResults.getNextRow())) { |
michael@0 | 230 | let addon_internal_id = row.getResultByName("addon_internal_id"); |
michael@0 | 231 | if (!(addon_internal_id in addons)) { |
michael@0 | 232 | logger.warn("Found a screenshot not linked to an add-on in database"); |
michael@0 | 233 | continue; |
michael@0 | 234 | } |
michael@0 | 235 | |
michael@0 | 236 | let addon = addons[addon_internal_id]; |
michael@0 | 237 | if (!addon.screenshots) |
michael@0 | 238 | addon.screenshots = []; |
michael@0 | 239 | addon.screenshots.push(self._makeScreenshotFromAsyncRow(row)); |
michael@0 | 240 | } |
michael@0 | 241 | }, |
michael@0 | 242 | |
michael@0 | 243 | handleError: self.asyncErrorLogger, |
michael@0 | 244 | |
michael@0 | 245 | handleCompletion: function getAllScreenshots_handleCompletion(aReason) { |
michael@0 | 246 | if (aReason != Ci.mozIStorageStatementCallback.REASON_FINISHED) { |
michael@0 | 247 | logger.error("Error retrieving screenshots from database. Returning empty results"); |
michael@0 | 248 | aCallback({}); |
michael@0 | 249 | return; |
michael@0 | 250 | } |
michael@0 | 251 | |
michael@0 | 252 | getAllCompatOverrides(); |
michael@0 | 253 | } |
michael@0 | 254 | }); |
michael@0 | 255 | } |
michael@0 | 256 | |
michael@0 | 257 | function getAllCompatOverrides() { |
michael@0 | 258 | self.getAsyncStatement("getAllCompatOverrides").executeAsync({ |
michael@0 | 259 | handleResult: function getAllCompatOverrides_handleResult(aResults) { |
michael@0 | 260 | let row = null; |
michael@0 | 261 | while ((row = aResults.getNextRow())) { |
michael@0 | 262 | let addon_internal_id = row.getResultByName("addon_internal_id"); |
michael@0 | 263 | if (!(addon_internal_id in addons)) { |
michael@0 | 264 | logger.warn("Found a compatibility override not linked to an add-on in database"); |
michael@0 | 265 | continue; |
michael@0 | 266 | } |
michael@0 | 267 | |
michael@0 | 268 | let addon = addons[addon_internal_id]; |
michael@0 | 269 | if (!addon.compatibilityOverrides) |
michael@0 | 270 | addon.compatibilityOverrides = []; |
michael@0 | 271 | addon.compatibilityOverrides.push(self._makeCompatOverrideFromAsyncRow(row)); |
michael@0 | 272 | } |
michael@0 | 273 | }, |
michael@0 | 274 | |
michael@0 | 275 | handleError: self.asyncErrorLogger, |
michael@0 | 276 | |
michael@0 | 277 | handleCompletion: function getAllCompatOverrides_handleCompletion(aReason) { |
michael@0 | 278 | if (aReason != Ci.mozIStorageStatementCallback.REASON_FINISHED) { |
michael@0 | 279 | logger.error("Error retrieving compatibility overrides from database. Returning empty results"); |
michael@0 | 280 | aCallback({}); |
michael@0 | 281 | return; |
michael@0 | 282 | } |
michael@0 | 283 | |
michael@0 | 284 | getAllIcons(); |
michael@0 | 285 | } |
michael@0 | 286 | }); |
michael@0 | 287 | } |
michael@0 | 288 | |
michael@0 | 289 | function getAllIcons() { |
michael@0 | 290 | self.getAsyncStatement("getAllIcons").executeAsync({ |
michael@0 | 291 | handleResult: function getAllIcons_handleResult(aResults) { |
michael@0 | 292 | let row = null; |
michael@0 | 293 | while ((row = aResults.getNextRow())) { |
michael@0 | 294 | let addon_internal_id = row.getResultByName("addon_internal_id"); |
michael@0 | 295 | if (!(addon_internal_id in addons)) { |
michael@0 | 296 | logger.warn("Found an icon not linked to an add-on in database"); |
michael@0 | 297 | continue; |
michael@0 | 298 | } |
michael@0 | 299 | |
michael@0 | 300 | let addon = addons[addon_internal_id]; |
michael@0 | 301 | let { size, url } = self._makeIconFromAsyncRow(row); |
michael@0 | 302 | addon.icons[size] = url; |
michael@0 | 303 | if (size == 32) |
michael@0 | 304 | addon.iconURL = url; |
michael@0 | 305 | } |
michael@0 | 306 | }, |
michael@0 | 307 | |
michael@0 | 308 | handleError: self.asyncErrorLogger, |
michael@0 | 309 | |
michael@0 | 310 | handleCompletion: function getAllIcons_handleCompletion(aReason) { |
michael@0 | 311 | if (aReason != Ci.mozIStorageStatementCallback.REASON_FINISHED) { |
michael@0 | 312 | logger.error("Error retrieving icons from database. Returning empty results"); |
michael@0 | 313 | aCallback({}); |
michael@0 | 314 | return; |
michael@0 | 315 | } |
michael@0 | 316 | |
michael@0 | 317 | let returnedAddons = {}; |
michael@0 | 318 | for each (let addon in addons) |
michael@0 | 319 | returnedAddons[addon.id] = addon; |
michael@0 | 320 | aCallback(returnedAddons); |
michael@0 | 321 | } |
michael@0 | 322 | }); |
michael@0 | 323 | } |
michael@0 | 324 | |
michael@0 | 325 | // Begin asynchronous process |
michael@0 | 326 | getAllAddons(); |
michael@0 | 327 | }, |
michael@0 | 328 | |
michael@0 | 329 | // A cache of statements that are used and need to be finalized on shutdown |
michael@0 | 330 | asyncStatementsCache: {}, |
michael@0 | 331 | |
michael@0 | 332 | /** |
michael@0 | 333 | * Gets a cached async statement or creates a new statement if it doesn't |
michael@0 | 334 | * already exist. |
michael@0 | 335 | * |
michael@0 | 336 | * @param aKey |
michael@0 | 337 | * A unique key to reference the statement |
michael@0 | 338 | * @return a mozIStorageAsyncStatement for the SQL corresponding to the |
michael@0 | 339 | * unique key |
michael@0 | 340 | */ |
michael@0 | 341 | getAsyncStatement: function AD_getAsyncStatement(aKey) { |
michael@0 | 342 | if (aKey in this.asyncStatementsCache) |
michael@0 | 343 | return this.asyncStatementsCache[aKey]; |
michael@0 | 344 | |
michael@0 | 345 | let sql = this.queries[aKey]; |
michael@0 | 346 | try { |
michael@0 | 347 | return this.asyncStatementsCache[aKey] = this.connection.createAsyncStatement(sql); |
michael@0 | 348 | } catch (e) { |
michael@0 | 349 | logger.error("Error creating statement " + aKey + " (" + sql + ")"); |
michael@0 | 350 | throw Components.Exception("Error creating statement " + aKey + " (" + sql + "): " + e, |
michael@0 | 351 | e.result); |
michael@0 | 352 | } |
michael@0 | 353 | }, |
michael@0 | 354 | |
michael@0 | 355 | // The queries used by the database |
michael@0 | 356 | queries: { |
michael@0 | 357 | getAllAddons: "SELECT internal_id, id, type, name, version, " + |
michael@0 | 358 | "creator, creatorURL, description, fullDescription, " + |
michael@0 | 359 | "developerComments, eula, homepageURL, supportURL, " + |
michael@0 | 360 | "contributionURL, contributionAmount, averageRating, " + |
michael@0 | 361 | "reviewCount, reviewURL, totalDownloads, weeklyDownloads, " + |
michael@0 | 362 | "dailyUsers, sourceURI, repositoryStatus, size, updateDate " + |
michael@0 | 363 | "FROM addon", |
michael@0 | 364 | |
michael@0 | 365 | getAllDevelopers: "SELECT addon_internal_id, name, url FROM developer " + |
michael@0 | 366 | "ORDER BY addon_internal_id, num", |
michael@0 | 367 | |
michael@0 | 368 | getAllScreenshots: "SELECT addon_internal_id, url, width, height, " + |
michael@0 | 369 | "thumbnailURL, thumbnailWidth, thumbnailHeight, caption " + |
michael@0 | 370 | "FROM screenshot ORDER BY addon_internal_id, num", |
michael@0 | 371 | |
michael@0 | 372 | getAllCompatOverrides: "SELECT addon_internal_id, type, minVersion, " + |
michael@0 | 373 | "maxVersion, appID, appMinVersion, appMaxVersion " + |
michael@0 | 374 | "FROM compatibility_override " + |
michael@0 | 375 | "ORDER BY addon_internal_id, num", |
michael@0 | 376 | |
michael@0 | 377 | getAllIcons: "SELECT addon_internal_id, size, url FROM icon " + |
michael@0 | 378 | "ORDER BY addon_internal_id, size", |
michael@0 | 379 | }, |
michael@0 | 380 | |
michael@0 | 381 | /** |
michael@0 | 382 | * Make add-on structure from an asynchronous row. |
michael@0 | 383 | * |
michael@0 | 384 | * @param aRow |
michael@0 | 385 | * The asynchronous row to use |
michael@0 | 386 | * @return The created add-on |
michael@0 | 387 | */ |
michael@0 | 388 | _makeAddonFromAsyncRow: function AD__makeAddonFromAsyncRow(aRow) { |
michael@0 | 389 | // This is intentionally not an AddonSearchResult object in order |
michael@0 | 390 | // to allow AddonDatabase._parseAddon to parse it, same as if it |
michael@0 | 391 | // was read from the JSON database. |
michael@0 | 392 | |
michael@0 | 393 | let addon = { icons: {} }; |
michael@0 | 394 | |
michael@0 | 395 | for (let prop of PROP_SINGLE) { |
michael@0 | 396 | addon[prop] = aRow.getResultByName(prop) |
michael@0 | 397 | }; |
michael@0 | 398 | |
michael@0 | 399 | return addon; |
michael@0 | 400 | }, |
michael@0 | 401 | |
michael@0 | 402 | /** |
michael@0 | 403 | * Make a developer from an asynchronous row |
michael@0 | 404 | * |
michael@0 | 405 | * @param aRow |
michael@0 | 406 | * The asynchronous row to use |
michael@0 | 407 | * @return The created developer |
michael@0 | 408 | */ |
michael@0 | 409 | _makeDeveloperFromAsyncRow: function AD__makeDeveloperFromAsyncRow(aRow) { |
michael@0 | 410 | let name = aRow.getResultByName("name"); |
michael@0 | 411 | let url = aRow.getResultByName("url") |
michael@0 | 412 | return new AddonManagerPrivate.AddonAuthor(name, url); |
michael@0 | 413 | }, |
michael@0 | 414 | |
michael@0 | 415 | /** |
michael@0 | 416 | * Make a screenshot from an asynchronous row |
michael@0 | 417 | * |
michael@0 | 418 | * @param aRow |
michael@0 | 419 | * The asynchronous row to use |
michael@0 | 420 | * @return The created screenshot |
michael@0 | 421 | */ |
michael@0 | 422 | _makeScreenshotFromAsyncRow: function AD__makeScreenshotFromAsyncRow(aRow) { |
michael@0 | 423 | let url = aRow.getResultByName("url"); |
michael@0 | 424 | let width = aRow.getResultByName("width"); |
michael@0 | 425 | let height = aRow.getResultByName("height"); |
michael@0 | 426 | let thumbnailURL = aRow.getResultByName("thumbnailURL"); |
michael@0 | 427 | let thumbnailWidth = aRow.getResultByName("thumbnailWidth"); |
michael@0 | 428 | let thumbnailHeight = aRow.getResultByName("thumbnailHeight"); |
michael@0 | 429 | let caption = aRow.getResultByName("caption"); |
michael@0 | 430 | return new AddonManagerPrivate.AddonScreenshot(url, width, height, thumbnailURL, |
michael@0 | 431 | thumbnailWidth, thumbnailHeight, caption); |
michael@0 | 432 | }, |
michael@0 | 433 | |
michael@0 | 434 | /** |
michael@0 | 435 | * Make a CompatibilityOverride from an asynchronous row |
michael@0 | 436 | * |
michael@0 | 437 | * @param aRow |
michael@0 | 438 | * The asynchronous row to use |
michael@0 | 439 | * @return The created CompatibilityOverride |
michael@0 | 440 | */ |
michael@0 | 441 | _makeCompatOverrideFromAsyncRow: function AD_makeCompatOverrideFromAsyncRow(aRow) { |
michael@0 | 442 | let type = aRow.getResultByName("type"); |
michael@0 | 443 | let minVersion = aRow.getResultByName("minVersion"); |
michael@0 | 444 | let maxVersion = aRow.getResultByName("maxVersion"); |
michael@0 | 445 | let appID = aRow.getResultByName("appID"); |
michael@0 | 446 | let appMinVersion = aRow.getResultByName("appMinVersion"); |
michael@0 | 447 | let appMaxVersion = aRow.getResultByName("appMaxVersion"); |
michael@0 | 448 | return new AddonManagerPrivate.AddonCompatibilityOverride(type, |
michael@0 | 449 | minVersion, |
michael@0 | 450 | maxVersion, |
michael@0 | 451 | appID, |
michael@0 | 452 | appMinVersion, |
michael@0 | 453 | appMaxVersion); |
michael@0 | 454 | }, |
michael@0 | 455 | |
michael@0 | 456 | /** |
michael@0 | 457 | * Make an icon from an asynchronous row |
michael@0 | 458 | * |
michael@0 | 459 | * @param aRow |
michael@0 | 460 | * The asynchronous row to use |
michael@0 | 461 | * @return An object containing the size and URL of the icon |
michael@0 | 462 | */ |
michael@0 | 463 | _makeIconFromAsyncRow: function AD_makeIconFromAsyncRow(aRow) { |
michael@0 | 464 | let size = aRow.getResultByName("size"); |
michael@0 | 465 | let url = aRow.getResultByName("url"); |
michael@0 | 466 | return { size: size, url: url }; |
michael@0 | 467 | }, |
michael@0 | 468 | |
michael@0 | 469 | /** |
michael@0 | 470 | * A helper function to log an SQL error. |
michael@0 | 471 | * |
michael@0 | 472 | * @param aError |
michael@0 | 473 | * The storage error code associated with the error |
michael@0 | 474 | * @param aErrorString |
michael@0 | 475 | * An error message |
michael@0 | 476 | */ |
michael@0 | 477 | logSQLError: function AD_logSQLError(aError, aErrorString) { |
michael@0 | 478 | logger.error("SQL error " + aError + ": " + aErrorString); |
michael@0 | 479 | }, |
michael@0 | 480 | |
michael@0 | 481 | /** |
michael@0 | 482 | * A helper function to log any errors that occur during async statements. |
michael@0 | 483 | * |
michael@0 | 484 | * @param aError |
michael@0 | 485 | * A mozIStorageError to log |
michael@0 | 486 | */ |
michael@0 | 487 | asyncErrorLogger: function AD_asyncErrorLogger(aError) { |
michael@0 | 488 | logger.error("Async SQL error " + aError.result + ": " + aError.message); |
michael@0 | 489 | }, |
michael@0 | 490 | |
michael@0 | 491 | /** |
michael@0 | 492 | * Synchronously creates the triggers in the database. |
michael@0 | 493 | */ |
michael@0 | 494 | _createTriggers: function AD__createTriggers() { |
michael@0 | 495 | this.connection.executeSimpleSQL("DROP TRIGGER IF EXISTS delete_addon"); |
michael@0 | 496 | this.connection.executeSimpleSQL("CREATE TRIGGER delete_addon AFTER DELETE " + |
michael@0 | 497 | "ON addon BEGIN " + |
michael@0 | 498 | "DELETE FROM developer WHERE addon_internal_id=old.internal_id; " + |
michael@0 | 499 | "DELETE FROM screenshot WHERE addon_internal_id=old.internal_id; " + |
michael@0 | 500 | "DELETE FROM compatibility_override WHERE addon_internal_id=old.internal_id; " + |
michael@0 | 501 | "DELETE FROM icon WHERE addon_internal_id=old.internal_id; " + |
michael@0 | 502 | "END"); |
michael@0 | 503 | }, |
michael@0 | 504 | |
michael@0 | 505 | /** |
michael@0 | 506 | * Synchronously creates the indices in the database. |
michael@0 | 507 | */ |
michael@0 | 508 | _createIndices: function AD__createIndices() { |
michael@0 | 509 | this.connection.executeSimpleSQL("CREATE INDEX IF NOT EXISTS developer_idx " + |
michael@0 | 510 | "ON developer (addon_internal_id)"); |
michael@0 | 511 | this.connection.executeSimpleSQL("CREATE INDEX IF NOT EXISTS screenshot_idx " + |
michael@0 | 512 | "ON screenshot (addon_internal_id)"); |
michael@0 | 513 | this.connection.executeSimpleSQL("CREATE INDEX IF NOT EXISTS compatibility_override_idx " + |
michael@0 | 514 | "ON compatibility_override (addon_internal_id)"); |
michael@0 | 515 | this.connection.executeSimpleSQL("CREATE INDEX IF NOT EXISTS icon_idx " + |
michael@0 | 516 | "ON icon (addon_internal_id)"); |
michael@0 | 517 | } |
michael@0 | 518 | } |