diff options
Diffstat (limited to 'devtools/client/webide/modules/app-projects.js')
-rw-r--r-- | devtools/client/webide/modules/app-projects.js | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/devtools/client/webide/modules/app-projects.js b/devtools/client/webide/modules/app-projects.js new file mode 100644 index 000000000..691d09064 --- /dev/null +++ b/devtools/client/webide/modules/app-projects.js @@ -0,0 +1,235 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const {Cc, Ci, Cu, Cr} = require("chrome"); +const promise = require("promise"); + +const EventEmitter = require("devtools/shared/event-emitter"); +const {generateUUID} = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator); +const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm", {}); + +/** + * IndexedDB wrapper that just save project objects + * + * The only constraint is that project objects have to have + * a unique `location` object. + */ + +const IDB = { + _db: null, + databaseName: "AppProjects", + + open: function () { + let deferred = promise.defer(); + + let request = indexedDB.open(IDB.databaseName, 5); + request.onerror = function (event) { + deferred.reject("Unable to open AppProjects indexedDB: " + + this.error.name + " - " + this.error.message); + }; + request.onupgradeneeded = function (event) { + let db = event.target.result; + db.createObjectStore("projects", { keyPath: "location" }); + }; + + request.onsuccess = function () { + let db = IDB._db = request.result; + let objectStore = db.transaction("projects").objectStore("projects"); + let projects = []; + let toRemove = []; + objectStore.openCursor().onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + if (cursor.value.location) { + + // We need to make sure this object has a `.location` property. + // The UI depends on this property. + // This should not be needed as we make sure to register valid + // projects, but in the past (before bug 924568), we might have + // registered invalid objects. + + + // We also want to make sure the location is valid. + // If the location doesn't exist, we remove the project. + + try { + let file = FileUtils.File(cursor.value.location); + if (file.exists()) { + projects.push(cursor.value); + } else { + toRemove.push(cursor.value.location); + } + } catch (e) { + if (e.result == Cr.NS_ERROR_FILE_UNRECOGNIZED_PATH) { + // A URL + projects.push(cursor.value); + } + } + } + cursor.continue(); + } else { + let removePromises = []; + for (let location of toRemove) { + removePromises.push(IDB.remove(location)); + } + promise.all(removePromises).then(() => { + deferred.resolve(projects); + }); + } + }; + }; + + return deferred.promise; + }, + + add: function (project) { + let deferred = promise.defer(); + + if (!project.location) { + // We need to make sure this object has a `.location` property. + deferred.reject("Missing location property on project object."); + } else { + let transaction = IDB._db.transaction(["projects"], "readwrite"); + let objectStore = transaction.objectStore("projects"); + let request = objectStore.add(project); + request.onerror = function (event) { + deferred.reject("Unable to add project to the AppProjects indexedDB: " + + this.error.name + " - " + this.error.message); + }; + request.onsuccess = function () { + deferred.resolve(); + }; + } + + return deferred.promise; + }, + + update: function (project) { + let deferred = promise.defer(); + + var transaction = IDB._db.transaction(["projects"], "readwrite"); + var objectStore = transaction.objectStore("projects"); + var request = objectStore.put(project); + request.onerror = function (event) { + deferred.reject("Unable to update project to the AppProjects indexedDB: " + + this.error.name + " - " + this.error.message); + }; + request.onsuccess = function () { + deferred.resolve(); + }; + + return deferred.promise; + }, + + remove: function (location) { + let deferred = promise.defer(); + + let request = IDB._db.transaction(["projects"], "readwrite") + .objectStore("projects") + .delete(location); + request.onsuccess = function (event) { + deferred.resolve(); + }; + request.onerror = function () { + deferred.reject("Unable to delete project to the AppProjects indexedDB: " + + this.error.name + " - " + this.error.message); + }; + + return deferred.promise; + } +}; + +var loadDeferred = promise.defer(); + +loadDeferred.resolve(IDB.open().then(function (projects) { + AppProjects.projects = projects; + AppProjects.emit("ready", projects); +})); + +const AppProjects = { + load: function () { + return loadDeferred.promise; + }, + + addPackaged: function (folder) { + let file = FileUtils.File(folder.path); + if (!file.exists()) { + return promise.reject("path doesn't exist"); + } + let existingProject = this.get(folder.path); + if (existingProject) { + return promise.reject("Already added"); + } + let project = { + type: "packaged", + location: folder.path, + // We need a unique id, that is the app origin, + // in order to identify the app when being installed on the device. + // The packaged app local path is a valid id, but only on the client. + // This origin will be used to generate the true id of an app: + // its manifest URL. + // If the app ends up specifying an explicit origin in its manifest, + // we will override this random UUID on app install. + packagedAppOrigin: generateUUID().toString().slice(1, -1) + }; + return IDB.add(project).then(() => { + this.projects.push(project); + return project; + }); + }, + + addHosted: function (manifestURL) { + let existingProject = this.get(manifestURL); + if (existingProject) { + return promise.reject("Already added"); + } + let project = { + type: "hosted", + location: manifestURL + }; + return IDB.add(project).then(() => { + this.projects.push(project); + return project; + }); + }, + + update: function (project) { + return IDB.update(project); + }, + + updateLocation: function (project, newLocation) { + return IDB.remove(project.location) + .then(() => { + project.location = newLocation; + return IDB.add(project); + }); + }, + + remove: function (location) { + return IDB.remove(location).then(() => { + for (let i = 0; i < this.projects.length; i++) { + if (this.projects[i].location == location) { + this.projects.splice(i, 1); + return; + } + } + throw new Error("Unable to find project in AppProjects store"); + }); + }, + + get: function (location) { + for (let i = 0; i < this.projects.length; i++) { + if (this.projects[i].location == location) { + return this.projects[i]; + } + } + return null; + }, + + projects: [] +}; + +EventEmitter.decorate(AppProjects); + +exports.AppProjects = AppProjects; |