2015-11-02 16:18:58 +00:00
|
|
|
/* 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/. */
|
|
|
|
|
2016-05-17 18:25:54 +00:00
|
|
|
const {Cc, Ci, Cu, Cr} = require("chrome");
|
2015-12-11 17:17:58 +00:00
|
|
|
const promise = require("promise");
|
2013-08-20 08:06:00 +00:00
|
|
|
|
2015-11-02 16:18:58 +00:00
|
|
|
const {EventEmitter} = Cu.import("resource://devtools/shared/event-emitter.js", {});
|
2016-05-17 18:25:54 +00:00
|
|
|
const {generateUUID} = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
|
2015-11-02 16:18:58 +00:00
|
|
|
const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm", {});
|
|
|
|
const {indexedDB} = require("sdk/indexed-db");
|
2013-08-20 08:06:00 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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,
|
2014-11-18 21:32:49 +00:00
|
|
|
databaseName: "AppProjects",
|
2013-08-20 08:06:00 +00:00
|
|
|
|
|
|
|
open: function () {
|
|
|
|
let deferred = promise.defer();
|
|
|
|
|
2014-11-18 21:32:49 +00:00
|
|
|
let request = indexedDB.open(IDB.databaseName, 5);
|
2016-05-17 18:25:54 +00:00
|
|
|
request.onerror = function (event) {
|
2014-11-18 21:32:49 +00:00
|
|
|
deferred.reject("Unable to open AppProjects indexedDB: " +
|
2016-05-17 18:25:54 +00:00
|
|
|
this.error.name + " - " + this.error.message);
|
2013-08-20 08:06:00 +00:00
|
|
|
};
|
2016-05-17 18:25:54 +00:00
|
|
|
request.onupgradeneeded = function (event) {
|
2013-08-20 08:06:00 +00:00
|
|
|
let db = event.target.result;
|
|
|
|
db.createObjectStore("projects", { keyPath: "location" });
|
|
|
|
};
|
|
|
|
|
2016-05-17 18:25:54 +00:00
|
|
|
request.onsuccess = function () {
|
2013-08-20 08:06:00 +00:00
|
|
|
let db = IDB._db = request.result;
|
|
|
|
let objectStore = db.transaction("projects").objectStore("projects");
|
2016-01-07 07:50:53 +00:00
|
|
|
let projects = [];
|
2014-05-12 06:53:00 +00:00
|
|
|
let toRemove = [];
|
2016-05-17 18:25:54 +00:00
|
|
|
objectStore.openCursor().onsuccess = function (event) {
|
2013-08-20 08:06:00 +00:00
|
|
|
let cursor = event.target.result;
|
|
|
|
if (cursor) {
|
2013-10-09 13:31:34 +00:00
|
|
|
if (cursor.value.location) {
|
2014-05-12 06:53:00 +00:00
|
|
|
|
2013-10-09 13:31:34 +00:00
|
|
|
// 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.
|
2014-05-12 06:53:00 +00:00
|
|
|
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
}
|
2013-10-09 13:31:34 +00:00
|
|
|
}
|
2013-08-20 08:06:00 +00:00
|
|
|
cursor.continue();
|
|
|
|
} else {
|
2014-05-12 06:53:00 +00:00
|
|
|
let removePromises = [];
|
|
|
|
for (let location of toRemove) {
|
|
|
|
removePromises.push(IDB.remove(location));
|
|
|
|
}
|
|
|
|
promise.all(removePromises).then(() => {
|
|
|
|
deferred.resolve(projects);
|
|
|
|
});
|
2013-08-20 08:06:00 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
return deferred.promise;
|
|
|
|
},
|
|
|
|
|
2016-05-17 18:25:54 +00:00
|
|
|
add: function (project) {
|
2013-08-20 08:06:00 +00:00
|
|
|
let deferred = promise.defer();
|
|
|
|
|
2013-10-09 13:31:34 +00:00
|
|
|
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);
|
2016-05-17 18:25:54 +00:00
|
|
|
request.onerror = function (event) {
|
2013-10-09 13:31:34 +00:00
|
|
|
deferred.reject("Unable to add project to the AppProjects indexedDB: " +
|
2016-05-17 18:25:54 +00:00
|
|
|
this.error.name + " - " + this.error.message);
|
2013-10-09 13:31:34 +00:00
|
|
|
};
|
2016-05-17 18:25:54 +00:00
|
|
|
request.onsuccess = function () {
|
2013-10-09 13:31:34 +00:00
|
|
|
deferred.resolve();
|
|
|
|
};
|
|
|
|
}
|
2013-08-20 08:06:00 +00:00
|
|
|
|
|
|
|
return deferred.promise;
|
|
|
|
},
|
|
|
|
|
2016-05-17 18:25:54 +00:00
|
|
|
update: function (project) {
|
2013-09-26 18:07:44 +00:00
|
|
|
let deferred = promise.defer();
|
|
|
|
|
|
|
|
var transaction = IDB._db.transaction(["projects"], "readwrite");
|
|
|
|
var objectStore = transaction.objectStore("projects");
|
|
|
|
var request = objectStore.put(project);
|
2016-05-17 18:25:54 +00:00
|
|
|
request.onerror = function (event) {
|
2013-09-26 18:07:44 +00:00
|
|
|
deferred.reject("Unable to update project to the AppProjects indexedDB: " +
|
2016-05-17 18:25:54 +00:00
|
|
|
this.error.name + " - " + this.error.message);
|
2013-09-26 18:07:44 +00:00
|
|
|
};
|
2016-05-17 18:25:54 +00:00
|
|
|
request.onsuccess = function () {
|
2013-09-26 18:07:44 +00:00
|
|
|
deferred.resolve();
|
|
|
|
};
|
|
|
|
|
|
|
|
return deferred.promise;
|
|
|
|
},
|
|
|
|
|
2016-05-17 18:25:54 +00:00
|
|
|
remove: function (location) {
|
2013-08-20 08:06:00 +00:00
|
|
|
let deferred = promise.defer();
|
|
|
|
|
|
|
|
let request = IDB._db.transaction(["projects"], "readwrite")
|
|
|
|
.objectStore("projects")
|
|
|
|
.delete(location);
|
2016-05-17 18:25:54 +00:00
|
|
|
request.onsuccess = function (event) {
|
2013-08-20 08:06:00 +00:00
|
|
|
deferred.resolve();
|
|
|
|
};
|
2016-05-17 18:25:54 +00:00
|
|
|
request.onerror = function () {
|
2013-08-20 08:06:00 +00:00
|
|
|
deferred.reject("Unable to delete project to the AppProjects indexedDB: " +
|
2016-05-17 18:25:54 +00:00
|
|
|
this.error.name + " - " + this.error.message);
|
2013-08-20 08:06:00 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
return deferred.promise;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-09-15 18:19:45 +00:00
|
|
|
var loadDeferred = promise.defer();
|
2013-09-25 02:22:08 +00:00
|
|
|
|
2014-11-18 21:32:49 +00:00
|
|
|
loadDeferred.resolve(IDB.open().then(function (projects) {
|
2016-01-07 07:50:53 +00:00
|
|
|
AppProjects.projects = projects;
|
|
|
|
AppProjects.emit("ready", projects);
|
2014-11-18 21:32:49 +00:00
|
|
|
}));
|
2013-08-20 08:06:00 +00:00
|
|
|
|
|
|
|
const AppProjects = {
|
2016-05-17 18:25:54 +00:00
|
|
|
load: function () {
|
2013-09-25 02:22:08 +00:00
|
|
|
return loadDeferred.promise;
|
|
|
|
},
|
|
|
|
|
2016-05-17 18:25:54 +00:00
|
|
|
addPackaged: function (folder) {
|
2014-05-12 06:53:00 +00:00
|
|
|
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");
|
|
|
|
}
|
2013-08-20 08:06:00 +00:00
|
|
|
let project = {
|
|
|
|
type: "packaged",
|
2013-09-10 11:26:00 +00:00
|
|
|
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.
|
2013-09-26 18:07:44 +00:00
|
|
|
// If the app ends up specifying an explicit origin in its manifest,
|
|
|
|
// we will override this random UUID on app install.
|
2013-09-10 11:26:00 +00:00
|
|
|
packagedAppOrigin: generateUUID().toString().slice(1, -1)
|
2013-08-20 08:06:00 +00:00
|
|
|
};
|
2016-01-07 07:50:53 +00:00
|
|
|
return IDB.add(project).then(() => {
|
|
|
|
this.projects.push(project);
|
|
|
|
return project;
|
2013-08-20 08:06:00 +00:00
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2016-05-17 18:25:54 +00:00
|
|
|
addHosted: function (manifestURL) {
|
2014-05-12 06:53:00 +00:00
|
|
|
let existingProject = this.get(manifestURL);
|
|
|
|
if (existingProject) {
|
|
|
|
return promise.reject("Already added");
|
|
|
|
}
|
2013-08-20 08:06:00 +00:00
|
|
|
let project = {
|
|
|
|
type: "hosted",
|
|
|
|
location: manifestURL
|
|
|
|
};
|
2016-01-07 07:50:53 +00:00
|
|
|
return IDB.add(project).then(() => {
|
|
|
|
this.projects.push(project);
|
|
|
|
return project;
|
2013-08-20 08:06:00 +00:00
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2013-09-26 18:07:44 +00:00
|
|
|
update: function (project) {
|
2014-05-12 06:53:00 +00:00
|
|
|
return IDB.update(project);
|
2013-09-26 18:07:44 +00:00
|
|
|
},
|
|
|
|
|
2016-05-17 18:25:54 +00:00
|
|
|
updateLocation: function (project, newLocation) {
|
2014-09-18 06:14:00 +00:00
|
|
|
return IDB.remove(project.location)
|
|
|
|
.then(() => {
|
|
|
|
project.location = newLocation;
|
|
|
|
return IDB.add(project);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2016-05-17 18:25:54 +00:00
|
|
|
remove: function (location) {
|
2016-01-07 07:50:53 +00:00
|
|
|
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);
|
2013-08-20 08:06:00 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
throw new Error("Unable to find project in AppProjects store");
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2016-05-17 18:25:54 +00:00
|
|
|
get: function (location) {
|
2016-01-07 07:50:53 +00:00
|
|
|
for (let i = 0; i < this.projects.length; i++) {
|
|
|
|
if (this.projects[i].location == location) {
|
|
|
|
return this.projects[i];
|
2013-08-20 08:06:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
},
|
|
|
|
|
2016-01-07 07:50:53 +00:00
|
|
|
projects: []
|
2013-08-20 08:06:00 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
EventEmitter.decorate(AppProjects);
|
|
|
|
|
|
|
|
exports.AppProjects = AppProjects;
|