1/* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /* This is a JavaScript module (JSM) to be imported via 6 Components.utils.import() and acts as a singleton. Only the following 7 listed symbols will exposed on import, and only when and where imported. 8 */ 9 10var EXPORTED_SYMBOLS = ["FormData"]; 11 12const {classes: Cc, interfaces: Ci, utils: Cu} = Components; 13 14Cu.import("resource://tps/logger.jsm"); 15 16Cu.import("resource://gre/modules/FormHistory.jsm"); 17Cu.import("resource://gre/modules/Log.jsm"); 18 19/** 20 * FormDB 21 * 22 * Helper object containing methods to interact with the FormHistory module. 23 */ 24var FormDB = { 25 _update(data) { 26 return new Promise((resolve, reject) => { 27 let handlers = { 28 handleError(error) { 29 Logger.logError("Error occurred updating form history: " + Log.exceptionStr(error)); 30 reject(error); 31 }, 32 handleCompletion(reason) { 33 resolve(); 34 } 35 } 36 FormHistory.update(data, handlers); 37 }); 38 }, 39 40 /** 41 * insertValue 42 * 43 * Adds the specified value for the specified fieldname into form history. 44 * 45 * @param fieldname The form fieldname to insert 46 * @param value The form value to insert 47 * @param us The time, in microseconds, to use for the lastUsed 48 * and firstUsed columns 49 * @return Promise<undefined> 50 */ 51 insertValue(fieldname, value, us) { 52 let data = { op: "add", fieldname, value, timesUsed: 1, 53 firstUsed: us, lastUsed: us } 54 return this._update(data); 55 }, 56 57 /** 58 * updateValue 59 * 60 * Updates a row in the moz_formhistory table with a new value. 61 * 62 * @param id The id of the row to update 63 * @param newvalue The new value to set 64 * @return Promise<undefined> 65 */ 66 updateValue(id, newvalue) { 67 return this._update({ op: "update", guid: id, value: newvalue }); 68 }, 69 70 /** 71 * getDataForValue 72 * 73 * Retrieves a set of values for a row in the database that 74 * corresponds to the given fieldname and value. 75 * 76 * @param fieldname The fieldname of the row to query 77 * @param value The value of the row to query 78 * @return Promise<null if no row is found with the specified fieldname and value, 79 * or an object containing the row's guid, lastUsed, and firstUsed 80 * values> 81 */ 82 getDataForValue(fieldname, value) { 83 return new Promise((resolve, reject) => { 84 let result = null; 85 let handlers = { 86 handleResult(oneResult) { 87 if (result != null) { 88 reject("more than 1 result for this query"); 89 return; 90 } 91 result = oneResult; 92 }, 93 handleError(error) { 94 Logger.logError("Error occurred updating form history: " + Log.exceptionStr(error)); 95 reject(error); 96 }, 97 handleCompletion(reason) { 98 resolve(result); 99 } 100 } 101 FormHistory.search(["guid", "lastUsed", "firstUsed"], { fieldname }, handlers); 102 }); 103 }, 104 105 /** 106 * remove 107 * 108 * Removes the specified GUID from the database. 109 * 110 * @param guid The guid of the item to delete 111 * @return Promise<> 112 */ 113 remove(guid) { 114 return this._update({ op: "remove", guid }); 115 }, 116}; 117 118/** 119 * FormData class constructor 120 * 121 * Initializes instance properties. 122 */ 123function FormData(props, usSinceEpoch) { 124 this.fieldname = null; 125 this.value = null; 126 this.date = 0; 127 this.newvalue = null; 128 this.usSinceEpoch = usSinceEpoch; 129 130 for (var prop in props) { 131 if (prop in this) 132 this[prop] = props[prop]; 133 } 134} 135 136/** 137 * FormData instance methods 138 */ 139FormData.prototype = { 140 /** 141 * hours_to_us 142 * 143 * Converts hours since present to microseconds since epoch. 144 * 145 * @param hours The number of hours since the present time (e.g., 0 is 146 * 'now', and -1 is 1 hour ago) 147 * @return the corresponding number of microseconds since the epoch 148 */ 149 hours_to_us: function(hours) { 150 return this.usSinceEpoch + (hours * 60 * 60 * 1000 * 1000); 151 }, 152 153 /** 154 * Create 155 * 156 * If this FormData object doesn't exist in the moz_formhistory database, 157 * add it. Throws on error. 158 * 159 * @return nothing 160 */ 161 Create: function() { 162 Logger.AssertTrue(this.fieldname != null && this.value != null, 163 "Must specify both fieldname and value"); 164 165 return FormDB.getDataForValue(this.fieldname, this.value).then(formdata => { 166 if (!formdata) { 167 // this item doesn't exist yet in the db, so we need to insert it 168 return FormDB.insertValue(this.fieldname, this.value, 169 this.hours_to_us(this.date)); 170 } else { 171 /* Right now, we ignore this case. If bug 552531 is ever fixed, 172 we might need to add code here to update the firstUsed or 173 lastUsed fields, as appropriate. 174 */ 175 } 176 }); 177 }, 178 179 /** 180 * Find 181 * 182 * Attempts to locate an entry in the moz_formhistory database that 183 * matches the fieldname and value for this FormData object. 184 * 185 * @return true if this entry exists in the database, otherwise false 186 */ 187 Find: function() { 188 return FormDB.getDataForValue(this.fieldname, this.value).then(formdata => { 189 let status = formdata != null; 190 if (status) { 191 /* 192 //form history dates currently not synced! bug 552531 193 let us = this.hours_to_us(this.date); 194 status = Logger.AssertTrue( 195 us >= formdata.firstUsed && us <= formdata.lastUsed, 196 "No match for with that date value"); 197 198 if (status) 199 */ 200 this.id = formdata.guid; 201 } 202 return status; 203 }); 204 }, 205 206 /** 207 * Remove 208 * 209 * Removes the row represented by this FormData instance from the 210 * moz_formhistory database. 211 * 212 * @return nothing 213 */ 214 Remove: function() { 215 /* Right now Weave doesn't handle this correctly, see bug 568363. 216 */ 217 return FormDB.remove(this.id); 218 }, 219}; 220