toolkit/components/places/tests/queries/head_queries.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim:set ts=2 sw=2 sts=2 et: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 const Ci = Components.interfaces;
michael@0 8 const Cc = Components.classes;
michael@0 9 const Cr = Components.results;
michael@0 10 const Cu = Components.utils;
michael@0 11
michael@0 12 Cu.import("resource://gre/modules/Services.jsm");
michael@0 13
michael@0 14 // Import common head.
michael@0 15 let (commonFile = do_get_file("../head_common.js", false)) {
michael@0 16 let uri = Services.io.newFileURI(commonFile);
michael@0 17 Services.scriptloader.loadSubScript(uri.spec, this);
michael@0 18 }
michael@0 19
michael@0 20 // Put any other stuff relative to this test folder below.
michael@0 21
michael@0 22
michael@0 23 // Some Useful Date constants - PRTime uses microseconds, so convert
michael@0 24 const DAY_MICROSEC = 86400000000;
michael@0 25 const today = Date.now() * 1000;
michael@0 26 const yesterday = today - DAY_MICROSEC;
michael@0 27 const lastweek = today - (DAY_MICROSEC * 7);
michael@0 28 const daybefore = today - (DAY_MICROSEC * 2);
michael@0 29 const tomorrow = today + DAY_MICROSEC;
michael@0 30 const old = today - (DAY_MICROSEC * 3);
michael@0 31 const futureday = today + (DAY_MICROSEC * 3);
michael@0 32 const olderthansixmonths = today - (DAY_MICROSEC * 31 * 7);
michael@0 33
michael@0 34
michael@0 35 /**
michael@0 36 * Generalized function to pull in an array of objects of data and push it into
michael@0 37 * the database. It does NOT do any checking to see that the input is
michael@0 38 * appropriate. This function is an asynchronous task, it can be called using
michael@0 39 * "Task.spawn" or using the "yield" function inside another task.
michael@0 40 */
michael@0 41 function task_populateDB(aArray)
michael@0 42 {
michael@0 43 // Iterate over aArray and execute all the instructions that can be done with
michael@0 44 // asynchronous APIs, excluding those that will be done in batch mode later.
michael@0 45 for ([, data] in Iterator(aArray)) {
michael@0 46 try {
michael@0 47 // make the data object into a query data object in order to create proper
michael@0 48 // default values for anything left unspecified
michael@0 49 var qdata = new queryData(data);
michael@0 50 if (qdata.isVisit) {
michael@0 51 // Then we should add a visit for this node
michael@0 52 yield promiseAddVisits({
michael@0 53 uri: uri(qdata.uri),
michael@0 54 transition: qdata.transType,
michael@0 55 visitDate: qdata.lastVisit,
michael@0 56 referrer: qdata.referrer ? uri(qdata.referrer) : null,
michael@0 57 title: qdata.title
michael@0 58 });
michael@0 59 if (qdata.visitCount && !qdata.isDetails) {
michael@0 60 // Set a fake visit_count, this is not a real count but can be used
michael@0 61 // to test sorting by visit_count.
michael@0 62 let stmt = DBConn().createAsyncStatement(
michael@0 63 "UPDATE moz_places SET visit_count = :vc WHERE url = :url");
michael@0 64 stmt.params.vc = qdata.visitCount;
michael@0 65 stmt.params.url = qdata.uri;
michael@0 66 try {
michael@0 67 stmt.executeAsync();
michael@0 68 }
michael@0 69 catch (ex) {
michael@0 70 print("Error while setting visit_count.");
michael@0 71 }
michael@0 72 finally {
michael@0 73 stmt.finalize();
michael@0 74 }
michael@0 75 }
michael@0 76 }
michael@0 77
michael@0 78 if (qdata.isRedirect) {
michael@0 79 // This must be async to properly enqueue after the updateFrecency call
michael@0 80 // done by the visit addition.
michael@0 81 let stmt = DBConn().createAsyncStatement(
michael@0 82 "UPDATE moz_places SET hidden = 1 WHERE url = :url");
michael@0 83 stmt.params.url = qdata.uri;
michael@0 84 try {
michael@0 85 stmt.executeAsync();
michael@0 86 }
michael@0 87 catch (ex) {
michael@0 88 print("Error while setting hidden.");
michael@0 89 }
michael@0 90 finally {
michael@0 91 stmt.finalize();
michael@0 92 }
michael@0 93 }
michael@0 94
michael@0 95 if (qdata.isDetails) {
michael@0 96 // Then we add extraneous page details for testing
michael@0 97 yield promiseAddVisits({
michael@0 98 uri: uri(qdata.uri),
michael@0 99 visitDate: qdata.lastVisit,
michael@0 100 title: qdata.title
michael@0 101 });
michael@0 102 }
michael@0 103 } catch (ex) {
michael@0 104 // use the data object here in case instantiation of qdata failed
michael@0 105 LOG("Problem with this URI: " + data.uri);
michael@0 106 do_throw("Error creating database: " + ex + "\n");
michael@0 107 }
michael@0 108 }
michael@0 109
michael@0 110 // Now execute the part of the instructions made with synchronous APIs.
michael@0 111 PlacesUtils.history.runInBatchMode({
michael@0 112 runBatched: function (aUserData)
michael@0 113 {
michael@0 114 aArray.forEach(function (data)
michael@0 115 {
michael@0 116 try {
michael@0 117 // make the data object into a query data object in order to create proper
michael@0 118 // default values for anything left unspecified
michael@0 119 var qdata = new queryData(data);
michael@0 120
michael@0 121 if (qdata.markPageAsTyped) {
michael@0 122 PlacesUtils.history.markPageAsTyped(uri(qdata.uri));
michael@0 123 }
michael@0 124
michael@0 125 if (qdata.isPageAnnotation) {
michael@0 126 if (qdata.removeAnnotation)
michael@0 127 PlacesUtils.annotations.removePageAnnotation(uri(qdata.uri),
michael@0 128 qdata.annoName);
michael@0 129 else {
michael@0 130 PlacesUtils.annotations.setPageAnnotation(uri(qdata.uri),
michael@0 131 qdata.annoName,
michael@0 132 qdata.annoVal,
michael@0 133 qdata.annoFlags,
michael@0 134 qdata.annoExpiration);
michael@0 135 }
michael@0 136 }
michael@0 137
michael@0 138 if (qdata.isItemAnnotation) {
michael@0 139 if (qdata.removeAnnotation)
michael@0 140 PlacesUtils.annotations.removeItemAnnotation(qdata.itemId,
michael@0 141 qdata.annoName);
michael@0 142 else {
michael@0 143 PlacesUtils.annotations.setItemAnnotation(qdata.itemId,
michael@0 144 qdata.annoName,
michael@0 145 qdata.annoVal,
michael@0 146 qdata.annoFlags,
michael@0 147 qdata.annoExpiration);
michael@0 148 }
michael@0 149 }
michael@0 150
michael@0 151 if (qdata.isFolder) {
michael@0 152 let folderId = PlacesUtils.bookmarks.createFolder(qdata.parentFolder,
michael@0 153 qdata.title,
michael@0 154 qdata.index);
michael@0 155 if (qdata.readOnly)
michael@0 156 PlacesUtils.bookmarks.setFolderReadonly(folderId, true);
michael@0 157 }
michael@0 158
michael@0 159 if (qdata.isLivemark) {
michael@0 160 PlacesUtils.livemarks.addLivemark({ title: qdata.title
michael@0 161 , parentId: qdata.parentFolder
michael@0 162 , index: qdata.index
michael@0 163 , feedURI: uri(qdata.feedURI)
michael@0 164 , siteURI: uri(qdata.uri)
michael@0 165 }).then(null, do_throw);
michael@0 166 }
michael@0 167
michael@0 168 if (qdata.isBookmark) {
michael@0 169 let itemId = PlacesUtils.bookmarks.insertBookmark(qdata.parentFolder,
michael@0 170 uri(qdata.uri),
michael@0 171 qdata.index,
michael@0 172 qdata.title);
michael@0 173 if (qdata.keyword)
michael@0 174 PlacesUtils.bookmarks.setKeywordForBookmark(itemId, qdata.keyword);
michael@0 175 if (qdata.dateAdded)
michael@0 176 PlacesUtils.bookmarks.setItemDateAdded(itemId, qdata.dateAdded);
michael@0 177 if (qdata.lastModified)
michael@0 178 PlacesUtils.bookmarks.setItemLastModified(itemId, qdata.lastModified);
michael@0 179 }
michael@0 180
michael@0 181 if (qdata.isTag) {
michael@0 182 PlacesUtils.tagging.tagURI(uri(qdata.uri), qdata.tagArray);
michael@0 183 }
michael@0 184
michael@0 185 if (qdata.isDynContainer) {
michael@0 186 PlacesUtils.bookmarks.createDynamicContainer(qdata.parentFolder,
michael@0 187 qdata.title,
michael@0 188 qdata.contractId,
michael@0 189 qdata.index);
michael@0 190 }
michael@0 191
michael@0 192 if (qdata.isSeparator)
michael@0 193 PlacesUtils.bookmarks.insertSeparator(qdata.parentFolder, qdata.index);
michael@0 194 } catch (ex) {
michael@0 195 // use the data object here in case instantiation of qdata failed
michael@0 196 LOG("Problem with this URI: " + data.uri);
michael@0 197 do_throw("Error creating database: " + ex + "\n");
michael@0 198 }
michael@0 199 }); // End of function and array
michael@0 200 }
michael@0 201 }, null);
michael@0 202 }
michael@0 203
michael@0 204
michael@0 205 /**
michael@0 206 * The Query Data Object - this object encapsulates data for our queries and is
michael@0 207 * used to parameterize our calls to the Places APIs to put data into the
michael@0 208 * database. It also has some interesting meta functions to determine which APIs
michael@0 209 * should be called, and to determine if this object should show up in the
michael@0 210 * resulting query.
michael@0 211 * Its parameter is an object specifying which attributes you want to set.
michael@0 212 * For ex:
michael@0 213 * var myobj = new queryData({isVisit: true, uri:"http://mozilla.com", title="foo"});
michael@0 214 * Note that it doesn't do any input checking on that object.
michael@0 215 */
michael@0 216 function queryData(obj) {
michael@0 217 this.isVisit = obj.isVisit ? obj.isVisit : false;
michael@0 218 this.isBookmark = obj.isBookmark ? obj.isBookmark: false;
michael@0 219 this.uri = obj.uri ? obj.uri : "";
michael@0 220 this.lastVisit = obj.lastVisit ? obj.lastVisit : today;
michael@0 221 this.referrer = obj.referrer ? obj.referrer : null;
michael@0 222 this.transType = obj.transType ? obj.transType : Ci.nsINavHistoryService.TRANSITION_TYPED;
michael@0 223 this.isRedirect = obj.isRedirect ? obj.isRedirect : false;
michael@0 224 this.isDetails = obj.isDetails ? obj.isDetails : false;
michael@0 225 this.title = obj.title ? obj.title : "";
michael@0 226 this.markPageAsTyped = obj.markPageAsTyped ? obj.markPageAsTyped : false;
michael@0 227 this.isPageAnnotation = obj.isPageAnnotation ? obj.isPageAnnotation : false;
michael@0 228 this.removeAnnotation= obj.removeAnnotation ? true : false;
michael@0 229 this.annoName = obj.annoName ? obj.annoName : "";
michael@0 230 this.annoVal = obj.annoVal ? obj.annoVal : "";
michael@0 231 this.annoFlags = obj.annoFlags ? obj.annoFlags : 0;
michael@0 232 this.annoExpiration = obj.annoExpiration ? obj.annoExpiration : 0;
michael@0 233 this.isItemAnnotation = obj.isItemAnnotation ? obj.isItemAnnotation : false;
michael@0 234 this.itemId = obj.itemId ? obj.itemId : 0;
michael@0 235 this.annoMimeType = obj.annoMimeType ? obj.annoMimeType : "";
michael@0 236 this.isTag = obj.isTag ? obj.isTag : false;
michael@0 237 this.tagArray = obj.tagArray ? obj.tagArray : null;
michael@0 238 this.isLivemark = obj.isLivemark ? obj.isLivemark : false;
michael@0 239 this.parentFolder = obj.parentFolder ? obj.parentFolder
michael@0 240 : PlacesUtils.placesRootId;
michael@0 241 this.feedURI = obj.feedURI ? obj.feedURI : "";
michael@0 242 this.index = obj.index ? obj.index : PlacesUtils.bookmarks.DEFAULT_INDEX;
michael@0 243 this.isFolder = obj.isFolder ? obj.isFolder : false;
michael@0 244 this.contractId = obj.contractId ? obj.contractId : "";
michael@0 245 this.lastModified = obj.lastModified ? obj.lastModified : today;
michael@0 246 this.dateAdded = obj.dateAdded ? obj.dateAdded : today;
michael@0 247 this.keyword = obj.keyword ? obj.keyword : "";
michael@0 248 this.visitCount = obj.visitCount ? obj.visitCount : 0;
michael@0 249 this.readOnly = obj.readOnly ? obj.readOnly : false;
michael@0 250 this.isSeparator = obj.hasOwnProperty("isSeparator") && obj.isSeparator;
michael@0 251
michael@0 252 // And now, the attribute for whether or not this object should appear in the
michael@0 253 // resulting query
michael@0 254 this.isInQuery = obj.isInQuery ? obj.isInQuery : false;
michael@0 255 }
michael@0 256
michael@0 257 // All attributes are set in the constructor above
michael@0 258 queryData.prototype = { }
michael@0 259
michael@0 260
michael@0 261 /**
michael@0 262 * Helper function to compare an array of query objects with a result set.
michael@0 263 * It assumes the array of query objects contains the SAME SORT as the result
michael@0 264 * set. It checks the the uri, title, time, and bookmarkIndex properties of
michael@0 265 * the results, where appropriate.
michael@0 266 */
michael@0 267 function compareArrayToResult(aArray, aRoot) {
michael@0 268 LOG("Comparing Array to Results");
michael@0 269
michael@0 270 var wasOpen = aRoot.containerOpen;
michael@0 271 if (!wasOpen)
michael@0 272 aRoot.containerOpen = true;
michael@0 273
michael@0 274 // check expected number of results against actual
michael@0 275 var expectedResultCount = aArray.filter(function(aEl) { return aEl.isInQuery; }).length;
michael@0 276 if (expectedResultCount != aRoot.childCount) {
michael@0 277 // Debugging code for failures.
michael@0 278 dump_table("moz_places");
michael@0 279 dump_table("moz_historyvisits");
michael@0 280 LOG("Found children:");
michael@0 281 for (let i = 0; i < aRoot.childCount; i++) {
michael@0 282 LOG(aRoot.getChild(i).uri);
michael@0 283 }
michael@0 284 LOG("Expected:");
michael@0 285 for (let i = 0; i < aArray.length; i++) {
michael@0 286 if (aArray[i].isInQuery)
michael@0 287 LOG(aArray[i].uri);
michael@0 288 }
michael@0 289 }
michael@0 290 do_check_eq(expectedResultCount, aRoot.childCount);
michael@0 291
michael@0 292 var inQueryIndex = 0;
michael@0 293 for (var i = 0; i < aArray.length; i++) {
michael@0 294 if (aArray[i].isInQuery) {
michael@0 295 var child = aRoot.getChild(inQueryIndex);
michael@0 296 //LOG("testing testData[" + i + "] vs result[" + inQueryIndex + "]");
michael@0 297 if (!aArray[i].isFolder && !aArray[i].isSeparator) {
michael@0 298 LOG("testing testData[" + aArray[i].uri + "] vs result[" + child.uri + "]");
michael@0 299 if (aArray[i].uri != child.uri) {
michael@0 300 dump_table("moz_places");
michael@0 301 do_throw("Expected " + aArray[i].uri + " found " + child.uri);
michael@0 302 }
michael@0 303 }
michael@0 304 if (!aArray[i].isSeparator && aArray[i].title != child.title)
michael@0 305 do_throw("Expected " + aArray[i].title + " found " + child.title);
michael@0 306 if (aArray[i].hasOwnProperty("lastVisit") &&
michael@0 307 aArray[i].lastVisit != child.time)
michael@0 308 do_throw("Expected " + aArray[i].lastVisit + " found " + child.time);
michael@0 309 if (aArray[i].hasOwnProperty("index") &&
michael@0 310 aArray[i].index != PlacesUtils.bookmarks.DEFAULT_INDEX &&
michael@0 311 aArray[i].index != child.bookmarkIndex)
michael@0 312 do_throw("Expected " + aArray[i].index + " found " + child.bookmarkIndex);
michael@0 313
michael@0 314 inQueryIndex++;
michael@0 315 }
michael@0 316 }
michael@0 317
michael@0 318 if (!wasOpen)
michael@0 319 aRoot.containerOpen = false;
michael@0 320 LOG("Comparing Array to Results passes");
michael@0 321 }
michael@0 322
michael@0 323
michael@0 324 /**
michael@0 325 * Helper function to check to see if one object either is or is not in the
michael@0 326 * result set. It can accept either a queryData object or an array of queryData
michael@0 327 * objects. If it gets an array, it only compares the first object in the array
michael@0 328 * to see if it is in the result set.
michael@0 329 * Returns: True if item is in query set, and false if item is not in query set
michael@0 330 * If input is an array, returns True if FIRST object in array is in
michael@0 331 * query set. To compare entire array, use the function above.
michael@0 332 */
michael@0 333 function isInResult(aQueryData, aRoot) {
michael@0 334 var rv = false;
michael@0 335 var uri;
michael@0 336 var wasOpen = aRoot.containerOpen;
michael@0 337 if (!wasOpen)
michael@0 338 aRoot.containerOpen = true;
michael@0 339
michael@0 340 // If we have an array, pluck out the first item. If an object, pluc out the
michael@0 341 // URI, we just compare URI's here.
michael@0 342 if ("uri" in aQueryData) {
michael@0 343 uri = aQueryData.uri;
michael@0 344 } else {
michael@0 345 uri = aQueryData[0].uri;
michael@0 346 }
michael@0 347
michael@0 348 for (var i=0; i < aRoot.childCount; i++) {
michael@0 349 if (uri == aRoot.getChild(i).uri) {
michael@0 350 rv = true;
michael@0 351 break;
michael@0 352 }
michael@0 353 }
michael@0 354 if (!wasOpen)
michael@0 355 aRoot.containerOpen = false;
michael@0 356 return rv;
michael@0 357 }
michael@0 358
michael@0 359
michael@0 360 /**
michael@0 361 * A nice helper function for debugging things. It LOGs the contents of a
michael@0 362 * result set.
michael@0 363 */
michael@0 364 function displayResultSet(aRoot) {
michael@0 365
michael@0 366 var wasOpen = aRoot.containerOpen;
michael@0 367 if (!wasOpen)
michael@0 368 aRoot.containerOpen = true;
michael@0 369
michael@0 370 if (!aRoot.hasChildren) {
michael@0 371 // Something wrong? Empty result set?
michael@0 372 LOG("Result Set Empty");
michael@0 373 return;
michael@0 374 }
michael@0 375
michael@0 376 for (var i=0; i < aRoot.childCount; ++i) {
michael@0 377 LOG("Result Set URI: " + aRoot.getChild(i).uri + " Title: " +
michael@0 378 aRoot.getChild(i).title + " Visit Time: " + aRoot.getChild(i).time);
michael@0 379 }
michael@0 380 if (!wasOpen)
michael@0 381 aRoot.containerOpen = false;
michael@0 382 }

mercurial