mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-26 18:31:29 +00:00
Bug 564270 - Can not search Places by transition type
This commit is contained in:
parent
96299b3d2f
commit
4256887d4f
@ -912,7 +912,7 @@ interface nsINavHistoryObserver : nsISupports
|
||||
* can be non-negligible.
|
||||
*/
|
||||
|
||||
[scriptable, uuid(6f5668f0-da8e-4069-a0de-6680e5cd8570)]
|
||||
[scriptable, uuid(dc87ae79-22f1-4dcf-975b-852b01d210cb)]
|
||||
interface nsINavHistoryQuery : nsISupports
|
||||
{
|
||||
/**
|
||||
@ -967,6 +967,27 @@ interface nsINavHistoryQuery : nsISupports
|
||||
attribute long minVisits;
|
||||
attribute long maxVisits;
|
||||
|
||||
/**
|
||||
* When the set of transitions is nonempty, results are limited to pages which
|
||||
* have at least one visit for each of the transition types.
|
||||
* @note: For searching on more than one transition this can be very slow.
|
||||
*
|
||||
* Limit results to the specified list of transition types.
|
||||
*/
|
||||
void setTransitions([const,array, size_is(count)] in unsigned long transitions,
|
||||
in unsigned long count);
|
||||
|
||||
/**
|
||||
* Get the transitions set for this query.
|
||||
*/
|
||||
void getTransitions([optional] out unsigned long count,
|
||||
[retval,array,size_is(count)] out unsigned long transitions);
|
||||
|
||||
/**
|
||||
* Get the count of the set query transitions.
|
||||
*/
|
||||
readonly attribute unsigned long transitionCount;
|
||||
|
||||
/**
|
||||
* When set, returns only bookmarked items, when unset, returns anything. Setting this
|
||||
* is equivalent to listing all bookmark folders in the 'folders' parameter.
|
||||
|
@ -2305,6 +2305,7 @@ nsNavHistory::GetUpdateRequirements(const nsCOMArray<nsNavHistoryQuery>& aQuerie
|
||||
|
||||
PRBool nonTimeBasedItems = PR_FALSE;
|
||||
PRBool domainBasedItems = PR_FALSE;
|
||||
PRBool queryContainsTransitions = PR_FALSE;
|
||||
|
||||
for (i = 0; i < aQueries.Count(); i ++) {
|
||||
nsNavHistoryQuery* query = aQueries[i];
|
||||
@ -2314,6 +2315,10 @@ nsNavHistory::GetUpdateRequirements(const nsCOMArray<nsNavHistoryQuery>& aQuerie
|
||||
query->Tags().Length() > 0) {
|
||||
return QUERYUPDATE_COMPLEX_WITH_BOOKMARKS;
|
||||
}
|
||||
|
||||
if (query->Transitions().Length() > 0)
|
||||
queryContainsTransitions = PR_TRUE;
|
||||
|
||||
// Note: we don't currently have any complex non-bookmarked items, but these
|
||||
// are expected to be added. Put detection of these items here.
|
||||
if (! query->SearchTerms().IsEmpty() ||
|
||||
@ -2329,6 +2334,9 @@ nsNavHistory::GetUpdateRequirements(const nsCOMArray<nsNavHistoryQuery>& aQuerie
|
||||
nsINavHistoryQueryOptions::RESULTS_AS_TAG_QUERY)
|
||||
return QUERYUPDATE_COMPLEX_WITH_BOOKMARKS;
|
||||
|
||||
if (queryContainsTransitions)
|
||||
return QUERYUPDATE_COMPLEX;
|
||||
|
||||
// Whenever there is a maximum number of results,
|
||||
// and we are not a bookmark query we must requery. This
|
||||
// is because we can't generally know if any given addition/change causes
|
||||
@ -2859,9 +2867,7 @@ nsNavHistory::AddVisit(nsIURI* aURI, PRTime aTime, nsIURI* aReferringURI,
|
||||
// GetQueryResults to maintain consistency.
|
||||
// FIXME bug 325241: make a way to observe hidden URLs
|
||||
PRUint32 added = 0;
|
||||
if (!hidden && aTransitionType != TRANSITION_EMBED &&
|
||||
aTransitionType != TRANSITION_FRAMED_LINK &&
|
||||
aTransitionType != TRANSITION_DOWNLOAD) {
|
||||
if (!hidden) {
|
||||
NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
|
||||
nsINavHistoryObserver,
|
||||
OnVisit(aURI, *aVisitID, aTime, aSessionID,
|
||||
@ -3064,6 +3070,9 @@ PRBool IsOptimizableHistoryQuery(const nsCOMArray<nsNavHistoryQuery>& aQueries,
|
||||
if (aQuery->Tags().Length() > 0)
|
||||
return PR_FALSE;
|
||||
|
||||
if (aQuery->Transitions().Length() > 0)
|
||||
return PR_FALSE;
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
@ -6340,6 +6349,26 @@ nsNavHistory::QueryToSelectClause(nsNavHistoryQuery* aQuery, // const
|
||||
clause.Str(")");
|
||||
}
|
||||
|
||||
// transitions
|
||||
const nsTArray<PRUint32>& transitions = aQuery->Transitions();
|
||||
// Optimize single transition query, since this is the most common use case.
|
||||
if (transitions.Length() == 1) {
|
||||
clause.Condition("v.visit_type =").Param(":transition0_");
|
||||
}
|
||||
else if (transitions.Length() > 1) {
|
||||
for (PRUint32 i = 0; i < transitions.Length(); ++i) {
|
||||
nsPrintfCString param(":transition%d_", i);
|
||||
clause.Str("EXISTS (SELECT 1 FROM moz_historyvisits "
|
||||
"WHERE place_id = h.id AND visit_type = "
|
||||
).Param(param.get()).Str(" UNION ALL "
|
||||
"SELECT 1 FROM moz_historyvisits_temp "
|
||||
"WHERE place_id = h.id AND visit_type = "
|
||||
).Param(param.get()).Str(" LIMIT 1)");
|
||||
if (i < transitions.Length() - 1)
|
||||
clause.Str("AND");
|
||||
}
|
||||
}
|
||||
|
||||
// parent parameter is used in tag contents queries.
|
||||
// Only one folder should be defined for them.
|
||||
if (aOptions->ResultType() == nsINavHistoryQueryOptions::RESULTS_AS_TAG_CONTENTS &&
|
||||
@ -6485,6 +6514,16 @@ nsNavHistory::BindQueryClauseParameters(mozIStorageStatement* statement,
|
||||
}
|
||||
}
|
||||
|
||||
// transitions
|
||||
const nsTArray<PRUint32>& transitions = aQuery->Transitions();
|
||||
if (transitions.Length() > 0) {
|
||||
for (PRUint32 i = 0; i < transitions.Length(); ++i) {
|
||||
nsPrintfCString paramName("transition%d_", i);
|
||||
rv = statement->BindInt64ByName(paramName + qIndex, transitions[i]);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
}
|
||||
|
||||
// parent parameter
|
||||
if (aOptions->ResultType() == nsINavHistoryQueryOptions::RESULTS_AS_TAG_CONTENTS &&
|
||||
aQuery->Folders().Length() == 1) {
|
||||
|
@ -175,6 +175,7 @@ static void SetOptionsKeyUint32(const nsCString& aValue,
|
||||
#define QUERYKEY_TAG "tag"
|
||||
#define QUERYKEY_NOTTAGS "!tags"
|
||||
#define QUERYKEY_ASYNC_ENABLED "asyncEnabled"
|
||||
#define QUERYKEY_TRANSITION "transition"
|
||||
|
||||
inline void AppendAmpersandIfNonempty(nsACString& aString)
|
||||
{
|
||||
@ -524,6 +525,14 @@ nsNavHistory::QueriesToQueryString(nsINavHistoryQuery **aQueries,
|
||||
NS_LITERAL_CSTRING(QUERYKEY_NOTTAGS),
|
||||
query,
|
||||
&nsINavHistoryQuery::GetTagsAreNot);
|
||||
|
||||
// transitions
|
||||
const nsTArray<PRUint32>& transitions = query->Transitions();
|
||||
for (PRUint32 i = 0; i < transitions.Length(); ++i) {
|
||||
AppendAmpersandIfNonempty(queryString);
|
||||
queryString += NS_LITERAL_CSTRING(QUERYKEY_TRANSITION "=");
|
||||
AppendInt64(queryString, transitions[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// sorting
|
||||
@ -687,6 +696,7 @@ nsNavHistory::TokensToQueries(const nsTArray<QueryKeyValuePair>& aTokens,
|
||||
|
||||
nsTArray<PRInt64> folders;
|
||||
nsTArray<nsString> tags;
|
||||
nsTArray<PRUint32> transitions;
|
||||
for (PRUint32 i = 0; i < aTokens.Length(); i ++) {
|
||||
const QueryKeyValuePair& kvp = aTokens[i];
|
||||
|
||||
@ -800,6 +810,18 @@ nsNavHistory::TokensToQueries(const nsTArray<QueryKeyValuePair>& aTokens,
|
||||
} else if (kvp.key.EqualsLiteral(QUERYKEY_NOTTAGS)) {
|
||||
SetQueryKeyBool(kvp.value, query, &nsINavHistoryQuery::SetTagsAreNot);
|
||||
|
||||
// transition
|
||||
} else if (kvp.key.EqualsLiteral(QUERYKEY_TRANSITION)) {
|
||||
PRUint32 transition = kvp.value.ToInteger(&rv);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
if (!transitions.Contains(transition))
|
||||
NS_ENSURE_TRUE(transitions.AppendElement(transition),
|
||||
NS_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
else {
|
||||
NS_WARNING("Invalid Int32 transition value.");
|
||||
}
|
||||
|
||||
// new query component
|
||||
} else if (kvp.key.EqualsLiteral(QUERYKEY_SEPARATOR)) {
|
||||
|
||||
@ -814,6 +836,12 @@ nsNavHistory::TokensToQueries(const nsTArray<QueryKeyValuePair>& aTokens,
|
||||
tags.Clear();
|
||||
}
|
||||
|
||||
if (transitions.Length() > 0) {
|
||||
rv = query->SetTransitions(transitions);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
transitions.Clear();
|
||||
}
|
||||
|
||||
query = new nsNavHistoryQuery();
|
||||
if (! query)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
@ -896,6 +924,11 @@ nsNavHistory::TokensToQueries(const nsTArray<QueryKeyValuePair>& aTokens,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
if (transitions.Length() > 0) {
|
||||
rv = query->SetTransitions(transitions);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -1336,6 +1369,40 @@ NS_IMETHODIMP nsNavHistoryQuery::SetFolders(const PRInt64 *aFolders,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsNavHistoryQuery::GetTransitions(PRUint32* aCount,
|
||||
PRUint32** aTransitions)
|
||||
{
|
||||
PRUint32 count = mTransitions.Length();
|
||||
PRUint32* transitions = nsnull;
|
||||
if (count > 0) {
|
||||
transitions = reinterpret_cast<PRUint32*>
|
||||
(NS_Alloc(count * sizeof(PRUint32)));
|
||||
NS_ENSURE_TRUE(transitions, NS_ERROR_OUT_OF_MEMORY);
|
||||
for (PRUint32 i = 0; i < count; ++i) {
|
||||
transitions[i] = mTransitions[i];
|
||||
}
|
||||
}
|
||||
*aCount = count;
|
||||
*aTransitions = transitions;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsNavHistoryQuery::GetTransitionCount(PRUint32* aCount)
|
||||
{
|
||||
*aCount = mTransitions.Length();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsNavHistoryQuery::SetTransitions(const PRUint32* aTransitions,
|
||||
PRUint32 aCount)
|
||||
{
|
||||
if (!mTransitions.ReplaceElementsAt(0, mTransitions.Length(), aTransitions,
|
||||
aCount))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsNavHistoryQuery::Clone(nsINavHistoryQuery** _retval)
|
||||
{
|
||||
*_retval = nsnull;
|
||||
|
@ -88,6 +88,16 @@ public:
|
||||
}
|
||||
PRBool TagsAreNot() { return mTagsAreNot; }
|
||||
|
||||
const nsTArray<PRUint32>& Transitions() const { return mTransitions; }
|
||||
nsresult SetTransitions(const nsTArray<PRUint32>& aTransitions)
|
||||
{
|
||||
if (!mTransitions.ReplaceElementsAt(0, mTransitions.Length(),
|
||||
aTransitions))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
~nsNavHistoryQuery() {}
|
||||
|
||||
@ -110,6 +120,7 @@ protected:
|
||||
nsTArray<PRInt64> mFolders;
|
||||
nsTArray<nsString> mTags;
|
||||
PRBool mTagsAreNot;
|
||||
nsTArray<PRUint32> mTransitions;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsNavHistoryQuery, NS_NAVHISTORYQUERY_IID)
|
||||
|
@ -349,6 +349,38 @@ const querySwitches = [
|
||||
}
|
||||
]
|
||||
},
|
||||
// transitions
|
||||
{
|
||||
desc: "tests nsINavHistoryQuery.getTransitions",
|
||||
matches: function (aQuery1, aQuery2) {
|
||||
var q1Trans = aQuery1.getTransitions();
|
||||
var q2Trans = aQuery2.getTransitions();
|
||||
if (q1Trans.length !== q2Trans.length)
|
||||
return false;
|
||||
for (let i = 0; i < q1Trans.length; i++) {
|
||||
if (q2Trans.indexOf(q1Trans[i]) < 0)
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < q2Trans.length; i++) {
|
||||
if (q1Trans.indexOf(q2Trans[i]) < 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
runs: [
|
||||
function (aQuery, aQueryOptions) {
|
||||
aQuery.setTransitions([], 0);
|
||||
},
|
||||
function (aQuery, aQueryOptions) {
|
||||
aQuery.setTransitions([Ci.nsINavHistoryService.TRANSITION_DOWNLOAD],
|
||||
1);
|
||||
},
|
||||
function (aQuery, aQueryOptions) {
|
||||
aQuery.setTransitions([Ci.nsINavHistoryService.TRANSITION_TYPED,
|
||||
Ci.nsINavHistoryService.TRANSITION_BOOKMARK], 2);
|
||||
}
|
||||
]
|
||||
},
|
||||
];
|
||||
|
||||
// nsINavHistoryQueryOptions switches
|
||||
|
177
toolkit/components/places/tests/queries/test_transitions.js
Normal file
177
toolkit/components/places/tests/queries/test_transitions.js
Normal file
@ -0,0 +1,177 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
var beginTime = Date.now();
|
||||
var testData = [
|
||||
{
|
||||
isVisit: true,
|
||||
title: "page 0",
|
||||
uri: "http://mozilla.com/",
|
||||
transType: Ci.nsINavHistoryService.TRANSITION_TYPED
|
||||
},
|
||||
{
|
||||
isVisit: true,
|
||||
title: "page 1",
|
||||
uri: "http://google.com/",
|
||||
transType: Ci.nsINavHistoryService.TRANSITION_DOWNLOAD
|
||||
},
|
||||
{
|
||||
isVisit: true,
|
||||
title: "page 2",
|
||||
uri: "http://microsoft.com/",
|
||||
transType: Ci.nsINavHistoryService.TRANSITION_DOWNLOAD
|
||||
},
|
||||
{
|
||||
isVisit: true,
|
||||
title: "page 3",
|
||||
uri: "http://en.wikipedia.org/",
|
||||
transType: Ci.nsINavHistoryService.TRANSITION_BOOKMARK
|
||||
},
|
||||
{
|
||||
isVisit: true,
|
||||
title: "page 4",
|
||||
uri: "http://fr.wikipedia.org/",
|
||||
transType: Ci.nsINavHistoryService.TRANSITION_DOWNLOAD
|
||||
},
|
||||
{
|
||||
isVisit: true,
|
||||
title: "page 5",
|
||||
uri: "http://apple.com/",
|
||||
transType: Ci.nsINavHistoryService.TRANSITION_TYPED
|
||||
},
|
||||
{
|
||||
isVisit: true,
|
||||
title: "page 6",
|
||||
uri: "http://campus-bike-store.com/",
|
||||
transType: Ci.nsINavHistoryService.TRANSITION_DOWNLOAD
|
||||
},
|
||||
{
|
||||
isVisit: true,
|
||||
title: "page 7",
|
||||
uri: "http://uwaterloo.ca/",
|
||||
transType: Ci.nsINavHistoryService.TRANSITION_TYPED
|
||||
},
|
||||
{
|
||||
isVisit: true,
|
||||
title: "page 8",
|
||||
uri: "http://pugcleaner.com/",
|
||||
transType: Ci.nsINavHistoryService.TRANSITION_BOOKMARK
|
||||
},
|
||||
{
|
||||
isVisit: true,
|
||||
title: "page 9",
|
||||
uri: "http://de.wikipedia.org/",
|
||||
transType: Ci.nsINavHistoryService.TRANSITION_TYPED
|
||||
},
|
||||
{
|
||||
isVisit: true,
|
||||
title: "arewefastyet",
|
||||
uri: "http://arewefastyet.com/",
|
||||
transType: Ci.nsINavHistoryService.TRANSITION_DOWNLOAD
|
||||
},
|
||||
{
|
||||
isVisit: true,
|
||||
title: "arewefastyet",
|
||||
uri: "http://arewefastyet.com/",
|
||||
transType: Ci.nsINavHistoryService.TRANSITION_BOOKMARK
|
||||
}];
|
||||
// sets of indices of testData array by transition type
|
||||
var testDataTyped = [0, 5, 7, 9];
|
||||
var testDataDownload = [1, 2, 4, 6, 10];
|
||||
var testDataBookmark = [3, 8, 11];
|
||||
|
||||
/**
|
||||
* run_test is where the magic happens. This is automatically run by the test
|
||||
* harness. It is where you do the work of creating the query, running it, and
|
||||
* playing with the result set.
|
||||
*/
|
||||
function run_test() {
|
||||
|
||||
let timeNow = Date.now();
|
||||
for each (let item in testData) {
|
||||
PlacesUtils.history.addVisit(uri(item.uri), timeNow++ * 1000, null,
|
||||
item.transType, false, 0);
|
||||
}
|
||||
|
||||
for (let i in testData) {
|
||||
testData[i].title = null;
|
||||
}
|
||||
|
||||
dump_table("moz_places");
|
||||
dump_table("moz_places_temp");
|
||||
dump_table("moz_historyvisits");
|
||||
dump_table("moz_historyvisits_temp");
|
||||
|
||||
var numSortFunc = function (a,b) { return (a - b); };
|
||||
var arrs = testDataTyped.concat(testDataDownload).concat(testDataBookmark)
|
||||
.sort(numSortFunc);
|
||||
|
||||
// Four tests which compare the result of a query to an expected set.
|
||||
var data = arrs.filter(function (index) {
|
||||
return (testData[index].uri.match(/arewefastyet\.com/) &&
|
||||
testData[index].transType ==
|
||||
Ci.nsINavHistoryService.TRANSITION_DOWNLOAD);
|
||||
});
|
||||
|
||||
compareQueryToTestData("place:domain=arewefastyet.com&transition=" +
|
||||
Ci.nsINavHistoryService.TRANSITION_DOWNLOAD,
|
||||
data.slice());
|
||||
|
||||
compareQueryToTestData("place:transition=" +
|
||||
Ci.nsINavHistoryService.TRANSITION_DOWNLOAD,
|
||||
testDataDownload.slice());
|
||||
|
||||
compareQueryToTestData("place:transition=" +
|
||||
Ci.nsINavHistoryService.TRANSITION_TYPED,
|
||||
testDataTyped.slice());
|
||||
|
||||
compareQueryToTestData("place:transition=" +
|
||||
Ci.nsINavHistoryService.TRANSITION_DOWNLOAD +
|
||||
"&transition=" +
|
||||
Ci.nsINavHistoryService.TRANSITION_BOOKMARK,
|
||||
data);
|
||||
|
||||
// Tests the live update property of transitions.
|
||||
var query = {};
|
||||
var options = {};
|
||||
PlacesUtils.history.
|
||||
queryStringToQueries("place:transition=" +
|
||||
Ci.nsINavHistoryService.TRANSITION_DOWNLOAD,
|
||||
query, {}, options);
|
||||
query = (query.value)[0];
|
||||
options = PlacesUtils.history.getNewQueryOptions();
|
||||
var result = PlacesUtils.history.executeQuery(query, options);
|
||||
var root = result.root;
|
||||
root.containerOpen = true;
|
||||
do_check_eq(testDataDownload.length, root.childCount);
|
||||
PlacesUtils.history
|
||||
.addVisit(PlacesUtils._uri("http://getfirefox.com"),
|
||||
Date.now() * 1000, null,
|
||||
Ci.nsINavHistoryService.TRANSITION_DOWNLOAD, false, 0);
|
||||
do_check_eq(testDataDownload.length + 1, root.childCount);
|
||||
root.containerOpen = false;
|
||||
|
||||
PlacesUtils.bhistory.removeAllPages();
|
||||
}
|
||||
|
||||
/*
|
||||
* Takes a query and a set of indices. The indices correspond to elements
|
||||
* of testData that are the result of the query.
|
||||
*/
|
||||
function compareQueryToTestData(queryStr, data) {
|
||||
var query = {};
|
||||
var options = {};
|
||||
PlacesUtils.history.queryStringToQueries(queryStr, query, {}, options);
|
||||
query = query.value[0];
|
||||
options = options.value;
|
||||
var result = PlacesUtils.history.executeQuery(query, options);
|
||||
var root = result.root;
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
data[i] = testData[data[i]];
|
||||
data[i].isInQuery = true;
|
||||
}
|
||||
compareArrayToResult(data, root);
|
||||
}
|
Loading…
Reference in New Issue
Block a user