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 { Logger } = ChromeUtils.import("resource://tps/logger.jsm"); 13 14const { FormHistory } = ChromeUtils.import( 15 "resource://gre/modules/FormHistory.jsm" 16); 17const { Log } = ChromeUtils.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( 30 "Error occurred updating form history: " + Log.exceptionStr(error) 31 ); 32 reject(error); 33 }, 34 handleCompletion(reason) { 35 resolve(); 36 }, 37 }; 38 FormHistory.update(data, handlers); 39 }); 40 }, 41 42 /** 43 * insertValue 44 * 45 * Adds the specified value for the specified fieldname into form history. 46 * 47 * @param fieldname The form fieldname to insert 48 * @param value The form value to insert 49 * @param us The time, in microseconds, to use for the lastUsed 50 * and firstUsed columns 51 * @return Promise<undefined> 52 */ 53 insertValue(fieldname, value, us) { 54 let data = { 55 op: "add", 56 fieldname, 57 value, 58 timesUsed: 1, 59 firstUsed: us, 60 lastUsed: us, 61 }; 62 return this._update(data); 63 }, 64 65 /** 66 * updateValue 67 * 68 * Updates a row in the moz_formhistory table with a new value. 69 * 70 * @param id The id of the row to update 71 * @param newvalue The new value to set 72 * @return Promise<undefined> 73 */ 74 updateValue(id, newvalue) { 75 return this._update({ op: "update", guid: id, value: newvalue }); 76 }, 77 78 /** 79 * getDataForValue 80 * 81 * Retrieves a set of values for a row in the database that 82 * corresponds to the given fieldname and value. 83 * 84 * @param fieldname The fieldname of the row to query 85 * @param value The value of the row to query 86 * @return Promise<null if no row is found with the specified fieldname and value, 87 * or an object containing the row's guid, lastUsed, and firstUsed 88 * values> 89 */ 90 getDataForValue(fieldname, value) { 91 return new Promise((resolve, reject) => { 92 let result = null; 93 let handlers = { 94 handleResult(oneResult) { 95 if (result != null) { 96 reject("more than 1 result for this query"); 97 return; 98 } 99 result = oneResult; 100 }, 101 handleError(error) { 102 Logger.logError( 103 "Error occurred updating form history: " + Log.exceptionStr(error) 104 ); 105 reject(error); 106 }, 107 handleCompletion(reason) { 108 resolve(result); 109 }, 110 }; 111 FormHistory.search( 112 ["guid", "lastUsed", "firstUsed"], 113 { fieldname, value }, 114 handlers 115 ); 116 }); 117 }, 118 119 /** 120 * remove 121 * 122 * Removes the specified GUID from the database. 123 * 124 * @param guid The guid of the item to delete 125 * @return Promise<> 126 */ 127 remove(guid) { 128 return this._update({ op: "remove", guid }); 129 }, 130}; 131 132/** 133 * FormData class constructor 134 * 135 * Initializes instance properties. 136 */ 137function FormData(props, msSinceEpoch) { 138 this.fieldname = null; 139 this.value = null; 140 this.date = 0; 141 this.newvalue = null; 142 this.usSinceEpoch = msSinceEpoch * 1000; 143 144 for (var prop in props) { 145 if (prop in this) { 146 this[prop] = props[prop]; 147 } 148 } 149} 150 151/** 152 * FormData instance methods 153 */ 154FormData.prototype = { 155 /** 156 * hours_to_us 157 * 158 * Converts hours since present to microseconds since epoch. 159 * 160 * @param hours The number of hours since the present time (e.g., 0 is 161 * 'now', and -1 is 1 hour ago) 162 * @return the corresponding number of microseconds since the epoch 163 */ 164 hours_to_us(hours) { 165 return this.usSinceEpoch + hours * 60 * 60 * 1000 * 1000; 166 }, 167 168 /** 169 * Create 170 * 171 * If this FormData object doesn't exist in the moz_formhistory database, 172 * add it. Throws on error. 173 * 174 * @return nothing 175 */ 176 Create() { 177 Logger.AssertTrue( 178 this.fieldname != null && this.value != null, 179 "Must specify both fieldname and value" 180 ); 181 182 return FormDB.getDataForValue(this.fieldname, this.value).then(formdata => { 183 if (!formdata) { 184 // this item doesn't exist yet in the db, so we need to insert it 185 return FormDB.insertValue( 186 this.fieldname, 187 this.value, 188 this.hours_to_us(this.date) 189 ); 190 } 191 /* Right now, we ignore this case. If bug 552531 is ever fixed, 192 we might need to add code here to update the firstUsed or 193 lastUsed fields, as appropriate. 194 */ 195 return null; 196 }); 197 }, 198 199 /** 200 * Find 201 * 202 * Attempts to locate an entry in the moz_formhistory database that 203 * matches the fieldname and value for this FormData object. 204 * 205 * @return true if this entry exists in the database, otherwise false 206 */ 207 Find() { 208 return FormDB.getDataForValue(this.fieldname, this.value).then(formdata => { 209 let status = formdata != null; 210 if (status) { 211 /* 212 //form history dates currently not synced! bug 552531 213 let us = this.hours_to_us(this.date); 214 status = Logger.AssertTrue( 215 us >= formdata.firstUsed && us <= formdata.lastUsed, 216 "No match for with that date value"); 217 218 if (status) 219 */ 220 this.id = formdata.guid; 221 } 222 return status; 223 }); 224 }, 225 226 /** 227 * Remove 228 * 229 * Removes the row represented by this FormData instance from the 230 * moz_formhistory database. 231 * 232 * @return nothing 233 */ 234 async Remove() { 235 const formdata = await FormDB.getDataForValue(this.fieldname, this.value); 236 if (!formdata) { 237 return; 238 } 239 await FormDB.remove(formdata.guid); 240 }, 241}; 242