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.

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

mercurial