|
1 const {Cc,Ci,Cu} = require("chrome"); |
|
2 const ObservableObject = require("devtools/shared/observable-object"); |
|
3 const promise = require("devtools/toolkit/deprecated-sync-thenables"); |
|
4 |
|
5 const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.js"); |
|
6 const {generateUUID} = Cc['@mozilla.org/uuid-generator;1'].getService(Ci.nsIUUIDGenerator); |
|
7 |
|
8 /** |
|
9 * IndexedDB wrapper that just save project objects |
|
10 * |
|
11 * The only constraint is that project objects have to have |
|
12 * a unique `location` object. |
|
13 */ |
|
14 |
|
15 const global = this; |
|
16 const IDB = { |
|
17 _db: null, |
|
18 |
|
19 open: function () { |
|
20 let deferred = promise.defer(); |
|
21 |
|
22 var idbManager = Cc["@mozilla.org/dom/indexeddb/manager;1"] |
|
23 .getService(Ci.nsIIndexedDatabaseManager); |
|
24 idbManager.initWindowless(global); |
|
25 |
|
26 let request = global.indexedDB.open("AppProjects", 5); |
|
27 request.onerror = function(event) { |
|
28 deferred.reject("Unable to open AppProjects indexedDB. " + |
|
29 "Error code: " + event.target.errorCode); |
|
30 }; |
|
31 request.onupgradeneeded = function(event) { |
|
32 let db = event.target.result; |
|
33 db.createObjectStore("projects", { keyPath: "location" }); |
|
34 }; |
|
35 |
|
36 request.onsuccess = function() { |
|
37 let db = IDB._db = request.result; |
|
38 let objectStore = db.transaction("projects").objectStore("projects"); |
|
39 let projects = [] |
|
40 objectStore.openCursor().onsuccess = function(event) { |
|
41 let cursor = event.target.result; |
|
42 if (cursor) { |
|
43 if (cursor.value.location) { |
|
44 // We need to make sure this object has a `.location` property. |
|
45 // The UI depends on this property. |
|
46 // This should not be needed as we make sure to register valid |
|
47 // projects, but in the past (before bug 924568), we might have |
|
48 // registered invalid objects. |
|
49 projects.push(cursor.value); |
|
50 } |
|
51 cursor.continue(); |
|
52 } else { |
|
53 deferred.resolve(projects); |
|
54 } |
|
55 }; |
|
56 }; |
|
57 |
|
58 return deferred.promise; |
|
59 }, |
|
60 |
|
61 add: function(project) { |
|
62 let deferred = promise.defer(); |
|
63 |
|
64 if (!project.location) { |
|
65 // We need to make sure this object has a `.location` property. |
|
66 deferred.reject("Missing location property on project object."); |
|
67 } else { |
|
68 let transaction = IDB._db.transaction(["projects"], "readwrite"); |
|
69 let objectStore = transaction.objectStore("projects"); |
|
70 let request = objectStore.add(project); |
|
71 request.onerror = function(event) { |
|
72 deferred.reject("Unable to add project to the AppProjects indexedDB: " + |
|
73 this.error.name + " - " + this.error.message ); |
|
74 }; |
|
75 request.onsuccess = function() { |
|
76 deferred.resolve(); |
|
77 }; |
|
78 } |
|
79 |
|
80 return deferred.promise; |
|
81 }, |
|
82 |
|
83 update: function(project) { |
|
84 let deferred = promise.defer(); |
|
85 |
|
86 var transaction = IDB._db.transaction(["projects"], "readwrite"); |
|
87 var objectStore = transaction.objectStore("projects"); |
|
88 var request = objectStore.put(project); |
|
89 request.onerror = function(event) { |
|
90 deferred.reject("Unable to update project to the AppProjects indexedDB: " + |
|
91 this.error.name + " - " + this.error.message ); |
|
92 }; |
|
93 request.onsuccess = function() { |
|
94 deferred.resolve(); |
|
95 }; |
|
96 |
|
97 return deferred.promise; |
|
98 }, |
|
99 |
|
100 remove: function(location) { |
|
101 let deferred = promise.defer(); |
|
102 |
|
103 let request = IDB._db.transaction(["projects"], "readwrite") |
|
104 .objectStore("projects") |
|
105 .delete(location); |
|
106 request.onsuccess = function(event) { |
|
107 deferred.resolve(); |
|
108 }; |
|
109 request.onerror = function() { |
|
110 deferred.reject("Unable to delete project to the AppProjects indexedDB: " + |
|
111 this.error.name + " - " + this.error.message ); |
|
112 }; |
|
113 |
|
114 return deferred.promise; |
|
115 } |
|
116 }; |
|
117 |
|
118 const store = new ObservableObject({ projects:[] }); |
|
119 |
|
120 let loadDeferred = promise.defer(); |
|
121 |
|
122 IDB.open().then(function (projects) { |
|
123 store.object.projects = projects; |
|
124 AppProjects.emit("ready", store.object.projects); |
|
125 loadDeferred.resolve(); |
|
126 }); |
|
127 |
|
128 const AppProjects = { |
|
129 load: function() { |
|
130 return loadDeferred.promise; |
|
131 }, |
|
132 |
|
133 addPackaged: function(folder) { |
|
134 let project = { |
|
135 type: "packaged", |
|
136 location: folder.path, |
|
137 // We need a unique id, that is the app origin, |
|
138 // in order to identify the app when being installed on the device. |
|
139 // The packaged app local path is a valid id, but only on the client. |
|
140 // This origin will be used to generate the true id of an app: |
|
141 // its manifest URL. |
|
142 // If the app ends up specifying an explicit origin in its manifest, |
|
143 // we will override this random UUID on app install. |
|
144 packagedAppOrigin: generateUUID().toString().slice(1, -1) |
|
145 }; |
|
146 return IDB.add(project).then(function () { |
|
147 store.object.projects.push(project); |
|
148 // return the added objects (proxified) |
|
149 return store.object.projects[store.object.projects.length - 1]; |
|
150 }); |
|
151 }, |
|
152 |
|
153 addHosted: function(manifestURL) { |
|
154 let project = { |
|
155 type: "hosted", |
|
156 location: manifestURL |
|
157 }; |
|
158 return IDB.add(project).then(function () { |
|
159 store.object.projects.push(project); |
|
160 // return the added objects (proxified) |
|
161 return store.object.projects[store.object.projects.length - 1]; |
|
162 }); |
|
163 }, |
|
164 |
|
165 update: function (project) { |
|
166 return IDB.update({ |
|
167 type: project.type, |
|
168 location: project.location, |
|
169 packagedAppOrigin: project.packagedAppOrigin |
|
170 }).then(() => project); |
|
171 }, |
|
172 |
|
173 remove: function(location) { |
|
174 return IDB.remove(location).then(function () { |
|
175 let projects = store.object.projects; |
|
176 for (let i = 0; i < projects.length; i++) { |
|
177 if (projects[i].location == location) { |
|
178 projects.splice(i, 1); |
|
179 return; |
|
180 } |
|
181 } |
|
182 throw new Error("Unable to find project in AppProjects store"); |
|
183 }); |
|
184 }, |
|
185 |
|
186 get: function(location) { |
|
187 let projects = store.object.projects; |
|
188 for (let i = 0; i < projects.length; i++) { |
|
189 if (projects[i].location == location) { |
|
190 return projects[i]; |
|
191 } |
|
192 } |
|
193 return null; |
|
194 }, |
|
195 |
|
196 store: store |
|
197 }; |
|
198 |
|
199 EventEmitter.decorate(AppProjects); |
|
200 |
|
201 exports.AppProjects = AppProjects; |
|
202 |