Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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 | this.EXPORTED_SYMBOLS = ['FormEngine', 'FormRec']; |
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/XPCOMUtils.jsm"); |
michael@0 | 12 | Cu.import("resource://services-sync/engines.js"); |
michael@0 | 13 | Cu.import("resource://services-sync/record.js"); |
michael@0 | 14 | Cu.import("resource://services-common/async.js"); |
michael@0 | 15 | Cu.import("resource://services-sync/util.js"); |
michael@0 | 16 | Cu.import("resource://services-sync/constants.js"); |
michael@0 | 17 | Cu.import("resource://gre/modules/Log.jsm"); |
michael@0 | 18 | |
michael@0 | 19 | const FORMS_TTL = 5184000; // 60 days |
michael@0 | 20 | |
michael@0 | 21 | this.FormRec = function FormRec(collection, id) { |
michael@0 | 22 | CryptoWrapper.call(this, collection, id); |
michael@0 | 23 | } |
michael@0 | 24 | FormRec.prototype = { |
michael@0 | 25 | __proto__: CryptoWrapper.prototype, |
michael@0 | 26 | _logName: "Sync.Record.Form", |
michael@0 | 27 | ttl: FORMS_TTL |
michael@0 | 28 | }; |
michael@0 | 29 | |
michael@0 | 30 | Utils.deferGetSet(FormRec, "cleartext", ["name", "value"]); |
michael@0 | 31 | |
michael@0 | 32 | |
michael@0 | 33 | let FormWrapper = { |
michael@0 | 34 | _log: Log.repository.getLogger("Sync.Engine.Forms"), |
michael@0 | 35 | |
michael@0 | 36 | _getEntryCols: ["fieldname", "value"], |
michael@0 | 37 | _guidCols: ["guid"], |
michael@0 | 38 | |
michael@0 | 39 | // Do a "sync" search by spinning the event loop until it completes. |
michael@0 | 40 | _searchSpinningly: function(terms, searchData) { |
michael@0 | 41 | let results = []; |
michael@0 | 42 | let cb = Async.makeSpinningCallback(); |
michael@0 | 43 | let callbacks = { |
michael@0 | 44 | handleResult: function(result) { |
michael@0 | 45 | results.push(result); |
michael@0 | 46 | }, |
michael@0 | 47 | handleCompletion: function(reason) { |
michael@0 | 48 | cb(null, results); |
michael@0 | 49 | } |
michael@0 | 50 | }; |
michael@0 | 51 | Svc.FormHistory.search(terms, searchData, callbacks); |
michael@0 | 52 | return cb.wait(); |
michael@0 | 53 | }, |
michael@0 | 54 | |
michael@0 | 55 | _updateSpinningly: function(changes) { |
michael@0 | 56 | if (!Svc.FormHistory.enabled) { |
michael@0 | 57 | return; // update isn't going to do anything. |
michael@0 | 58 | } |
michael@0 | 59 | let cb = Async.makeSpinningCallback(); |
michael@0 | 60 | let callbacks = { |
michael@0 | 61 | handleCompletion: function(reason) { |
michael@0 | 62 | cb(); |
michael@0 | 63 | } |
michael@0 | 64 | }; |
michael@0 | 65 | Svc.FormHistory.update(changes, callbacks); |
michael@0 | 66 | return cb.wait(); |
michael@0 | 67 | }, |
michael@0 | 68 | |
michael@0 | 69 | getEntry: function (guid) { |
michael@0 | 70 | let results = this._searchSpinningly(this._getEntryCols, {guid: guid}); |
michael@0 | 71 | if (!results.length) { |
michael@0 | 72 | return null; |
michael@0 | 73 | } |
michael@0 | 74 | return {name: results[0].fieldname, value: results[0].value}; |
michael@0 | 75 | }, |
michael@0 | 76 | |
michael@0 | 77 | getGUID: function (name, value) { |
michael@0 | 78 | // Query for the provided entry. |
michael@0 | 79 | let query = { fieldname: name, value: value }; |
michael@0 | 80 | let results = this._searchSpinningly(this._guidCols, query); |
michael@0 | 81 | return results.length ? results[0].guid : null; |
michael@0 | 82 | }, |
michael@0 | 83 | |
michael@0 | 84 | hasGUID: function (guid) { |
michael@0 | 85 | // We could probably use a count function here, but searchSpinningly exists... |
michael@0 | 86 | return this._searchSpinningly(this._guidCols, {guid: guid}).length != 0; |
michael@0 | 87 | }, |
michael@0 | 88 | |
michael@0 | 89 | replaceGUID: function (oldGUID, newGUID) { |
michael@0 | 90 | let changes = { |
michael@0 | 91 | op: "update", |
michael@0 | 92 | guid: oldGUID, |
michael@0 | 93 | newGuid: newGUID, |
michael@0 | 94 | } |
michael@0 | 95 | this._updateSpinningly(changes); |
michael@0 | 96 | } |
michael@0 | 97 | |
michael@0 | 98 | }; |
michael@0 | 99 | |
michael@0 | 100 | this.FormEngine = function FormEngine(service) { |
michael@0 | 101 | SyncEngine.call(this, "Forms", service); |
michael@0 | 102 | } |
michael@0 | 103 | FormEngine.prototype = { |
michael@0 | 104 | __proto__: SyncEngine.prototype, |
michael@0 | 105 | _storeObj: FormStore, |
michael@0 | 106 | _trackerObj: FormTracker, |
michael@0 | 107 | _recordObj: FormRec, |
michael@0 | 108 | applyIncomingBatchSize: FORMS_STORE_BATCH_SIZE, |
michael@0 | 109 | |
michael@0 | 110 | get prefName() "history", |
michael@0 | 111 | |
michael@0 | 112 | _findDupe: function _findDupe(item) { |
michael@0 | 113 | return FormWrapper.getGUID(item.name, item.value); |
michael@0 | 114 | } |
michael@0 | 115 | }; |
michael@0 | 116 | |
michael@0 | 117 | function FormStore(name, engine) { |
michael@0 | 118 | Store.call(this, name, engine); |
michael@0 | 119 | } |
michael@0 | 120 | FormStore.prototype = { |
michael@0 | 121 | __proto__: Store.prototype, |
michael@0 | 122 | |
michael@0 | 123 | _processChange: function (change) { |
michael@0 | 124 | // If this._changes is defined, then we are applying a batch, so we |
michael@0 | 125 | // can defer it. |
michael@0 | 126 | if (this._changes) { |
michael@0 | 127 | this._changes.push(change); |
michael@0 | 128 | return; |
michael@0 | 129 | } |
michael@0 | 130 | |
michael@0 | 131 | // Otherwise we must handle the change synchronously, right now. |
michael@0 | 132 | FormWrapper._updateSpinningly(change); |
michael@0 | 133 | }, |
michael@0 | 134 | |
michael@0 | 135 | applyIncomingBatch: function (records) { |
michael@0 | 136 | // We collect all the changes to be made then apply them all at once. |
michael@0 | 137 | this._changes = []; |
michael@0 | 138 | let failures = Store.prototype.applyIncomingBatch.call(this, records); |
michael@0 | 139 | if (this._changes.length) { |
michael@0 | 140 | FormWrapper._updateSpinningly(this._changes); |
michael@0 | 141 | } |
michael@0 | 142 | delete this._changes; |
michael@0 | 143 | return failures; |
michael@0 | 144 | }, |
michael@0 | 145 | |
michael@0 | 146 | getAllIDs: function () { |
michael@0 | 147 | let results = FormWrapper._searchSpinningly(["guid"], []) |
michael@0 | 148 | let guids = {}; |
michael@0 | 149 | for (let result of results) { |
michael@0 | 150 | guids[result.guid] = true; |
michael@0 | 151 | } |
michael@0 | 152 | return guids; |
michael@0 | 153 | }, |
michael@0 | 154 | |
michael@0 | 155 | changeItemID: function (oldID, newID) { |
michael@0 | 156 | FormWrapper.replaceGUID(oldID, newID); |
michael@0 | 157 | }, |
michael@0 | 158 | |
michael@0 | 159 | itemExists: function (id) { |
michael@0 | 160 | return FormWrapper.hasGUID(id); |
michael@0 | 161 | }, |
michael@0 | 162 | |
michael@0 | 163 | createRecord: function (id, collection) { |
michael@0 | 164 | let record = new FormRec(collection, id); |
michael@0 | 165 | let entry = FormWrapper.getEntry(id); |
michael@0 | 166 | if (entry != null) { |
michael@0 | 167 | record.name = entry.name; |
michael@0 | 168 | record.value = entry.value; |
michael@0 | 169 | } else { |
michael@0 | 170 | record.deleted = true; |
michael@0 | 171 | } |
michael@0 | 172 | return record; |
michael@0 | 173 | }, |
michael@0 | 174 | |
michael@0 | 175 | create: function (record) { |
michael@0 | 176 | this._log.trace("Adding form record for " + record.name); |
michael@0 | 177 | let change = { |
michael@0 | 178 | op: "add", |
michael@0 | 179 | fieldname: record.name, |
michael@0 | 180 | value: record.value |
michael@0 | 181 | }; |
michael@0 | 182 | this._processChange(change); |
michael@0 | 183 | }, |
michael@0 | 184 | |
michael@0 | 185 | remove: function (record) { |
michael@0 | 186 | this._log.trace("Removing form record: " + record.id); |
michael@0 | 187 | let change = { |
michael@0 | 188 | op: "remove", |
michael@0 | 189 | guid: record.id |
michael@0 | 190 | }; |
michael@0 | 191 | this._processChange(change); |
michael@0 | 192 | }, |
michael@0 | 193 | |
michael@0 | 194 | update: function (record) { |
michael@0 | 195 | this._log.trace("Ignoring form record update request!"); |
michael@0 | 196 | }, |
michael@0 | 197 | |
michael@0 | 198 | wipe: function () { |
michael@0 | 199 | let change = { |
michael@0 | 200 | op: "remove" |
michael@0 | 201 | }; |
michael@0 | 202 | FormWrapper._updateSpinningly(change); |
michael@0 | 203 | } |
michael@0 | 204 | }; |
michael@0 | 205 | |
michael@0 | 206 | function FormTracker(name, engine) { |
michael@0 | 207 | Tracker.call(this, name, engine); |
michael@0 | 208 | } |
michael@0 | 209 | FormTracker.prototype = { |
michael@0 | 210 | __proto__: Tracker.prototype, |
michael@0 | 211 | |
michael@0 | 212 | QueryInterface: XPCOMUtils.generateQI([ |
michael@0 | 213 | Ci.nsIObserver, |
michael@0 | 214 | Ci.nsISupportsWeakReference]), |
michael@0 | 215 | |
michael@0 | 216 | startTracking: function() { |
michael@0 | 217 | Svc.Obs.add("satchel-storage-changed", this); |
michael@0 | 218 | }, |
michael@0 | 219 | |
michael@0 | 220 | stopTracking: function() { |
michael@0 | 221 | Svc.Obs.remove("satchel-storage-changed", this); |
michael@0 | 222 | }, |
michael@0 | 223 | |
michael@0 | 224 | observe: function (subject, topic, data) { |
michael@0 | 225 | Tracker.prototype.observe.call(this, subject, topic, data); |
michael@0 | 226 | |
michael@0 | 227 | switch (topic) { |
michael@0 | 228 | case "satchel-storage-changed": |
michael@0 | 229 | if (data == "formhistory-add" || data == "formhistory-remove") { |
michael@0 | 230 | let guid = subject.QueryInterface(Ci.nsISupportsString).toString(); |
michael@0 | 231 | this.trackEntry(guid); |
michael@0 | 232 | } |
michael@0 | 233 | break; |
michael@0 | 234 | } |
michael@0 | 235 | }, |
michael@0 | 236 | |
michael@0 | 237 | trackEntry: function (guid) { |
michael@0 | 238 | this.addChangedID(guid); |
michael@0 | 239 | this.score += SCORE_INCREMENT_MEDIUM; |
michael@0 | 240 | }, |
michael@0 | 241 | }; |