Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
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/. */
6
7
#include <stdio.h>
8
9
#include "mozilla/DebugOnly.h"
10
#include "mozilla/IntegerPrintfMacros.h"
11
12
#include "nsNavHistory.h"
13
14
#include "mozIPlacesAutoComplete.h"
15
#include "nsNavBookmarks.h"
16
#include "nsAnnotationService.h"
17
#include "nsFaviconService.h"
18
#include "nsPlacesMacros.h"
19
#include "nsPlacesTriggers.h"
20
#include "DateTimeFormat.h"
21
#include "History.h"
22
#include "Helpers.h"
23
24
#include "nsTArray.h"
25
#include "nsCollationCID.h"
26
#include "nsNetUtil.h"
27
#include "nsPrintfCString.h"
28
#include "nsPromiseFlatString.h"
29
#include "nsString.h"
30
#include "nsUnicharUtils.h"
31
#include "prsystem.h"
32
#include "prtime.h"
33
#include "nsEscape.h"
34
#include "nsIEffectiveTLDService.h"
35
#include "nsIClassInfoImpl.h"
36
#include "nsIIDNService.h"
37
#include "nsQueryObject.h"
38
#include "nsThreadUtils.h"
39
#include "nsAppDirectoryServiceDefs.h"
40
#include "nsMathUtils.h"
41
#include "mozilla/storage.h"
42
#include "mozilla/Preferences.h"
43
#include <algorithm>
44
45
#ifdef MOZ_XUL
46
# include "nsIAutoCompleteInput.h"
47
# include "nsIAutoCompletePopup.h"
48
#endif
49
50
using namespace mozilla;
51
using namespace mozilla::places;
52
53
// The maximum number of things that we will store in the recent events list
54
// before calling ExpireNonrecentEvents. This number should be big enough so it
55
// is very difficult to get that many unconsumed events (for example, typed but
56
// never visited) in the RECENT_EVENT_THRESHOLD. Otherwise, we'll start
57
// checking each one for every page visit, which will be somewhat slower.
58
#define RECENT_EVENT_QUEUE_MAX_LENGTH 128
59
60
// preference ID strings
61
#define PREF_HISTORY_ENABLED "places.history.enabled"
62
63
#define PREF_FREC_NUM_VISITS "places.frecency.numVisits"
64
#define PREF_FREC_NUM_VISITS_DEF 10
65
#define PREF_FREC_FIRST_BUCKET_CUTOFF "places.frecency.firstBucketCutoff"
66
#define PREF_FREC_FIRST_BUCKET_CUTOFF_DEF 4
67
#define PREF_FREC_SECOND_BUCKET_CUTOFF "places.frecency.secondBucketCutoff"
68
#define PREF_FREC_SECOND_BUCKET_CUTOFF_DEF 14
69
#define PREF_FREC_THIRD_BUCKET_CUTOFF "places.frecency.thirdBucketCutoff"
70
#define PREF_FREC_THIRD_BUCKET_CUTOFF_DEF 31
71
#define PREF_FREC_FOURTH_BUCKET_CUTOFF "places.frecency.fourthBucketCutoff"
72
#define PREF_FREC_FOURTH_BUCKET_CUTOFF_DEF 90
73
#define PREF_FREC_FIRST_BUCKET_WEIGHT "places.frecency.firstBucketWeight"
74
#define PREF_FREC_FIRST_BUCKET_WEIGHT_DEF 100
75
#define PREF_FREC_SECOND_BUCKET_WEIGHT "places.frecency.secondBucketWeight"
76
#define PREF_FREC_SECOND_BUCKET_WEIGHT_DEF 70
77
#define PREF_FREC_THIRD_BUCKET_WEIGHT "places.frecency.thirdBucketWeight"
78
#define PREF_FREC_THIRD_BUCKET_WEIGHT_DEF 50
79
#define PREF_FREC_FOURTH_BUCKET_WEIGHT "places.frecency.fourthBucketWeight"
80
#define PREF_FREC_FOURTH_BUCKET_WEIGHT_DEF 30
81
#define PREF_FREC_DEFAULT_BUCKET_WEIGHT "places.frecency.defaultBucketWeight"
82
#define PREF_FREC_DEFAULT_BUCKET_WEIGHT_DEF 10
83
#define PREF_FREC_EMBED_VISIT_BONUS "places.frecency.embedVisitBonus"
84
#define PREF_FREC_EMBED_VISIT_BONUS_DEF 0
85
#define PREF_FREC_FRAMED_LINK_VISIT_BONUS "places.frecency.framedLinkVisitBonus"
86
#define PREF_FREC_FRAMED_LINK_VISIT_BONUS_DEF 0
87
#define PREF_FREC_LINK_VISIT_BONUS "places.frecency.linkVisitBonus"
88
#define PREF_FREC_LINK_VISIT_BONUS_DEF 100
89
#define PREF_FREC_TYPED_VISIT_BONUS "places.frecency.typedVisitBonus"
90
#define PREF_FREC_TYPED_VISIT_BONUS_DEF 2000
91
#define PREF_FREC_BOOKMARK_VISIT_BONUS "places.frecency.bookmarkVisitBonus"
92
#define PREF_FREC_BOOKMARK_VISIT_BONUS_DEF 75
93
#define PREF_FREC_DOWNLOAD_VISIT_BONUS "places.frecency.downloadVisitBonus"
94
#define PREF_FREC_DOWNLOAD_VISIT_BONUS_DEF 0
95
#define PREF_FREC_PERM_REDIRECT_VISIT_BONUS \
96
"places.frecency.permRedirectVisitBonus"
97
#define PREF_FREC_PERM_REDIRECT_VISIT_BONUS_DEF 0
98
#define PREF_FREC_TEMP_REDIRECT_VISIT_BONUS \
99
"places.frecency.tempRedirectVisitBonus"
100
#define PREF_FREC_TEMP_REDIRECT_VISIT_BONUS_DEF 0
101
#define PREF_FREC_REDIR_SOURCE_VISIT_BONUS \
102
"places.frecency.redirectSourceVisitBonus"
103
#define PREF_FREC_REDIR_SOURCE_VISIT_BONUS_DEF 25
104
#define PREF_FREC_DEFAULT_VISIT_BONUS "places.frecency.defaultVisitBonus"
105
#define PREF_FREC_DEFAULT_VISIT_BONUS_DEF 0
106
#define PREF_FREC_UNVISITED_BOOKMARK_BONUS \
107
"places.frecency.unvisitedBookmarkBonus"
108
#define PREF_FREC_UNVISITED_BOOKMARK_BONUS_DEF 140
109
#define PREF_FREC_UNVISITED_TYPED_BONUS "places.frecency.unvisitedTypedBonus"
110
#define PREF_FREC_UNVISITED_TYPED_BONUS_DEF 200
111
#define PREF_FREC_RELOAD_VISIT_BONUS "places.frecency.reloadVisitBonus"
112
#define PREF_FREC_RELOAD_VISIT_BONUS_DEF 0
113
114
// This is a 'hidden' pref for the purposes of unit tests.
115
#define PREF_FREC_DECAY_RATE "places.frecency.decayRate"
116
#define PREF_FREC_DECAY_RATE_DEF 0.975f
117
// An adaptive history entry is removed if unused for these many days.
118
#define ADAPTIVE_HISTORY_EXPIRE_DAYS 90
119
120
// In order to avoid calling PR_now() too often we use a cached "now" value
121
// for repeating stuff. These are milliseconds between "now" cache refreshes.
122
#define RENEW_CACHED_NOW_TIMEOUT ((int32_t)3 * PR_MSEC_PER_SEC)
123
124
// character-set annotation
125
#define CHARSET_ANNO NS_LITERAL_CSTRING("URIProperties/characterSet")
126
127
// These macros are used when splitting history by date.
128
// These are the day containers and catch-all final container.
129
#define HISTORY_ADDITIONAL_DATE_CONT_NUM 3
130
// We use a guess of the number of months considering all of them 30 days
131
// long, but we split only the last 6 months.
132
#define HISTORY_DATE_CONT_NUM(_daysFromOldestVisit) \
133
(HISTORY_ADDITIONAL_DATE_CONT_NUM + \
134
std::min(6, (int32_t)ceilf((float)_daysFromOldestVisit / 30)))
135
// Max number of containers, used to initialize the params hash.
136
#define HISTORY_DATE_CONT_LENGTH 8
137
138
// Initial length of the embed visits cache.
139
#define EMBED_VISITS_INITIAL_CACHE_LENGTH 64
140
141
// Initial length of the recent events cache.
142
#define RECENT_EVENTS_INITIAL_CACHE_LENGTH 64
143
144
// Observed topics.
145
#ifdef MOZ_XUL
146
# define TOPIC_AUTOCOMPLETE_FEEDBACK_INCOMING "autocomplete-will-enter-text"
147
#endif
148
#define TOPIC_IDLE_DAILY "idle-daily"
149
#define TOPIC_PREF_CHANGED "nsPref:changed"
150
#define TOPIC_PROFILE_TEARDOWN "profile-change-teardown"
151
#define TOPIC_PROFILE_CHANGE "profile-before-change"
152
153
static const char* kObservedPrefs[] = {PREF_HISTORY_ENABLED,
154
PREF_FREC_NUM_VISITS,
155
PREF_FREC_FIRST_BUCKET_CUTOFF,
156
PREF_FREC_SECOND_BUCKET_CUTOFF,
157
PREF_FREC_THIRD_BUCKET_CUTOFF,
158
PREF_FREC_FOURTH_BUCKET_CUTOFF,
159
PREF_FREC_FIRST_BUCKET_WEIGHT,
160
PREF_FREC_SECOND_BUCKET_WEIGHT,
161
PREF_FREC_THIRD_BUCKET_WEIGHT,
162
PREF_FREC_FOURTH_BUCKET_WEIGHT,
163
PREF_FREC_DEFAULT_BUCKET_WEIGHT,
164
PREF_FREC_EMBED_VISIT_BONUS,
165
PREF_FREC_FRAMED_LINK_VISIT_BONUS,
166
PREF_FREC_LINK_VISIT_BONUS,
167
PREF_FREC_TYPED_VISIT_BONUS,
168
PREF_FREC_BOOKMARK_VISIT_BONUS,
169
PREF_FREC_DOWNLOAD_VISIT_BONUS,
170
PREF_FREC_PERM_REDIRECT_VISIT_BONUS,
171
PREF_FREC_TEMP_REDIRECT_VISIT_BONUS,
172
PREF_FREC_REDIR_SOURCE_VISIT_BONUS,
173
PREF_FREC_DEFAULT_VISIT_BONUS,
174
PREF_FREC_UNVISITED_BOOKMARK_BONUS,
175
PREF_FREC_UNVISITED_TYPED_BONUS,
176
nullptr};
177
178
NS_IMPL_ADDREF(nsNavHistory)
179
NS_IMPL_RELEASE(nsNavHistory)
180
181
NS_IMPL_CLASSINFO(nsNavHistory, nullptr, nsIClassInfo::SINGLETON,
182
NS_NAVHISTORYSERVICE_CID)
183
NS_INTERFACE_MAP_BEGIN(nsNavHistory)
184
NS_INTERFACE_MAP_ENTRY(nsINavHistoryService)
185
NS_INTERFACE_MAP_ENTRY(nsIObserver)
186
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
187
NS_INTERFACE_MAP_ENTRY(mozIStorageVacuumParticipant)
188
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsINavHistoryService)
189
NS_IMPL_QUERY_CLASSINFO(nsNavHistory)
190
NS_INTERFACE_MAP_END
191
192
// We don't care about flattening everything
193
NS_IMPL_CI_INTERFACE_GETTER(nsNavHistory, nsINavHistoryService)
194
195
namespace {
196
197
static nsCString GetSimpleBookmarksQueryParent(
198
const RefPtr<nsNavHistoryQuery>& aQuery,
199
const RefPtr<nsNavHistoryQueryOptions>& aOptions);
200
static void ParseSearchTermsFromQuery(const RefPtr<nsNavHistoryQuery>& aQuery,
201
nsTArray<nsString>* aTerms);
202
203
void GetTagsSqlFragment(int64_t aTagsFolder, const nsACString& aRelation,
204
bool aHasSearchTerms, nsACString& _sqlFragment) {
205
if (!aHasSearchTerms)
206
_sqlFragment.AssignLiteral("null");
207
else {
208
// This subquery DOES NOT order tags for performance reasons.
209
_sqlFragment.Assign(
210
NS_LITERAL_CSTRING("(SELECT GROUP_CONCAT(t_t.title, ',') "
211
"FROM moz_bookmarks b_t "
212
"JOIN moz_bookmarks t_t ON t_t.id = +b_t.parent "
213
"WHERE b_t.fk = ") +
214
aRelation +
215
NS_LITERAL_CSTRING(" "
216
"AND t_t.parent = ") +
217
nsPrintfCString("%" PRId64, aTagsFolder) +
218
NS_LITERAL_CSTRING(" "
219
")"));
220
}
221
222
_sqlFragment.AppendLiteral(" AS tags ");
223
}
224
225
/**
226
* Recalculates invalid frecencies in chunks on the storage thread, optionally
227
* decays frecencies, and notifies history observers on the main thread.
228
*/
229
class FixAndDecayFrecencyRunnable final : public Runnable {
230
public:
231
explicit FixAndDecayFrecencyRunnable(Database* aDB, float aDecayRate)
232
: Runnable("places::FixAndDecayFrecencyRunnable"),
233
mDB(aDB),
234
mDecayRate(aDecayRate),
235
mDecayReason(mozIStorageStatementCallback::REASON_FINISHED) {}
236
237
NS_IMETHOD
238
Run() override {
239
if (NS_IsMainThread()) {
240
nsNavHistory* navHistory = nsNavHistory::GetHistoryService();
241
NS_ENSURE_STATE(navHistory);
242
243
navHistory->DecayFrecencyCompleted(mDecayReason);
244
return NS_OK;
245
}
246
247
MOZ_ASSERT(!NS_IsMainThread(),
248
"Frecencies should be recalculated on async thread");
249
250
nsCOMPtr<mozIStorageStatement> updateStmt = mDB->GetStatement(
251
"UPDATE moz_places "
252
"SET frecency = CALCULATE_FRECENCY(id) "
253
"WHERE id IN ("
254
"SELECT id FROM moz_places "
255
"WHERE frecency < 0 "
256
"ORDER BY frecency ASC "
257
"LIMIT 400"
258
")");
259
NS_ENSURE_STATE(updateStmt);
260
nsresult rv = updateStmt->Execute();
261
NS_ENSURE_SUCCESS(rv, rv);
262
263
nsCOMPtr<mozIStorageStatement> selectStmt = mDB->GetStatement(
264
"SELECT id FROM moz_places WHERE frecency < 0 "
265
"LIMIT 1");
266
NS_ENSURE_STATE(selectStmt);
267
bool hasResult = false;
268
rv = selectStmt->ExecuteStep(&hasResult);
269
NS_ENSURE_SUCCESS(rv, rv);
270
if (hasResult) {
271
// There are more invalid frecencies to fix. Re-dispatch to the async
272
// storage thread for the next chunk.
273
return NS_DispatchToCurrentThread(this);
274
}
275
276
mozStorageTransaction transaction(
277
mDB->MainConn(), false, mozIStorageConnection::TRANSACTION_IMMEDIATE);
278
279
if (NS_WARN_IF(NS_FAILED(DecayFrecencies()))) {
280
mDecayReason = mozIStorageStatementCallback::REASON_ERROR;
281
}
282
283
// We've finished fixing and decaying frecencies. Trigger frecency updates
284
// for all affected origins.
285
nsCOMPtr<mozIStorageStatement> updateOriginFrecenciesStmt =
286
mDB->GetStatement("DELETE FROM moz_updateoriginsupdate_temp");
287
NS_ENSURE_STATE(updateOriginFrecenciesStmt);
288
rv = updateOriginFrecenciesStmt->Execute();
289
NS_ENSURE_SUCCESS(rv, rv);
290
291
rv = transaction.Commit();
292
NS_ENSURE_SUCCESS(rv, rv);
293
294
// Re-dispatch to the main thread to notify observers.
295
return NS_DispatchToMainThread(this);
296
}
297
298
private:
299
nsresult DecayFrecencies() {
300
TimeStamp start = TimeStamp::Now();
301
302
// Globally decay places frecency rankings to estimate reduced frecency
303
// values of pages that haven't been visited for a while, i.e., they do
304
// not get an updated frecency. A scaling factor of .975 results in .5 the
305
// original value after 28 days.
306
// When changing the scaling factor, ensure that the barrier in
307
// moz_places_afterupdate_frecency_trigger still ignores these changes.
308
nsCOMPtr<mozIStorageStatement> decayFrecency = mDB->GetStatement(
309
"UPDATE moz_places SET frecency = ROUND(frecency * :decay_rate) "
310
"WHERE frecency > 0");
311
NS_ENSURE_STATE(decayFrecency);
312
nsresult rv = decayFrecency->BindDoubleByName(
313
NS_LITERAL_CSTRING("decay_rate"), static_cast<double>(mDecayRate));
314
NS_ENSURE_SUCCESS(rv, rv);
315
rv = decayFrecency->Execute();
316
NS_ENSURE_SUCCESS(rv, rv);
317
318
// Decay potentially unused adaptive entries (e.g. those that are at 1)
319
// to allow better chances for new entries that will start at 1.
320
nsCOMPtr<mozIStorageStatement> decayAdaptive = mDB->GetStatement(
321
"UPDATE moz_inputhistory SET use_count = use_count * :decay_rate");
322
NS_ENSURE_STATE(decayAdaptive);
323
rv = decayAdaptive->BindDoubleByName(NS_LITERAL_CSTRING("decay_rate"),
324
static_cast<double>(mDecayRate));
325
NS_ENSURE_SUCCESS(rv, rv);
326
rv = decayAdaptive->Execute();
327
NS_ENSURE_SUCCESS(rv, rv);
328
329
// Delete any adaptive entries that won't help in ordering anymore.
330
nsCOMPtr<mozIStorageStatement> deleteAdaptive = mDB->GetStatement(
331
"DELETE FROM moz_inputhistory WHERE use_count < :use_count");
332
NS_ENSURE_STATE(deleteAdaptive);
333
rv = deleteAdaptive->BindDoubleByName(
334
NS_LITERAL_CSTRING("use_count"),
335
std::pow(static_cast<double>(mDecayRate),
336
ADAPTIVE_HISTORY_EXPIRE_DAYS));
337
NS_ENSURE_SUCCESS(rv, rv);
338
rv = deleteAdaptive->Execute();
339
NS_ENSURE_SUCCESS(rv, rv);
340
341
Telemetry::AccumulateTimeDelta(
342
Telemetry::PLACES_IDLE_FRECENCY_DECAY_TIME_MS, start);
343
344
return NS_OK;
345
}
346
347
RefPtr<Database> mDB;
348
float mDecayRate;
349
uint16_t mDecayReason;
350
};
351
352
} // namespace
353
354
// Queries rows indexes to bind or get values, if adding a new one, be sure to
355
// update nsNavBookmarks statements and its kGetChildrenIndex_* constants
356
const int32_t nsNavHistory::kGetInfoIndex_PageID = 0;
357
const int32_t nsNavHistory::kGetInfoIndex_URL = 1;
358
const int32_t nsNavHistory::kGetInfoIndex_Title = 2;
359
const int32_t nsNavHistory::kGetInfoIndex_RevHost = 3;
360
const int32_t nsNavHistory::kGetInfoIndex_VisitCount = 4;
361
const int32_t nsNavHistory::kGetInfoIndex_VisitDate = 5;
362
const int32_t nsNavHistory::kGetInfoIndex_FaviconURL = 6;
363
const int32_t nsNavHistory::kGetInfoIndex_ItemId = 7;
364
const int32_t nsNavHistory::kGetInfoIndex_ItemDateAdded = 8;
365
const int32_t nsNavHistory::kGetInfoIndex_ItemLastModified = 9;
366
const int32_t nsNavHistory::kGetInfoIndex_ItemParentId = 10;
367
const int32_t nsNavHistory::kGetInfoIndex_ItemTags = 11;
368
const int32_t nsNavHistory::kGetInfoIndex_Frecency = 12;
369
const int32_t nsNavHistory::kGetInfoIndex_Hidden = 13;
370
const int32_t nsNavHistory::kGetInfoIndex_Guid = 14;
371
const int32_t nsNavHistory::kGetInfoIndex_VisitId = 15;
372
const int32_t nsNavHistory::kGetInfoIndex_FromVisitId = 16;
373
const int32_t nsNavHistory::kGetInfoIndex_VisitType = 17;
374
// These columns are followed by corresponding constants in nsNavBookmarks.cpp,
375
// which must be kept in sync:
376
// nsNavBookmarks::kGetChildrenIndex_Guid = 18;
377
// nsNavBookmarks::kGetChildrenIndex_Position = 19;
378
// nsNavBookmarks::kGetChildrenIndex_Type = 20;
379
// nsNavBookmarks::kGetChildrenIndex_PlaceID = 21;
380
381
PLACES_FACTORY_SINGLETON_IMPLEMENTATION(nsNavHistory, gHistoryService)
382
383
nsNavHistory::nsNavHistory()
384
: mCachedNow(0),
385
mRecentTyped(RECENT_EVENTS_INITIAL_CACHE_LENGTH),
386
mRecentLink(RECENT_EVENTS_INITIAL_CACHE_LENGTH),
387
mRecentBookmark(RECENT_EVENTS_INITIAL_CACHE_LENGTH),
388
mEmbedVisits(EMBED_VISITS_INITIAL_CACHE_LENGTH),
389
mHistoryEnabled(true),
390
mNumVisitsForFrecency(10),
391
mDecayFrecencyPendingCount(0),
392
mTagsFolder(-1),
393
mDaysOfHistory(-1),
394
mLastCachedStartOfDay(INT64_MAX),
395
mLastCachedEndOfDay(0),
396
mCanNotify(true)
397
#ifdef XP_WIN
398
,
399
mCryptoProviderInitialized(false)
400
#endif
401
{
402
NS_ASSERTION(!gHistoryService,
403
"Attempting to create two instances of the service!");
404
#ifdef XP_WIN
405
BOOL cryptoAcquired =
406
CryptAcquireContext(&mCryptoProvider, 0, 0, PROV_RSA_FULL,
407
CRYPT_VERIFYCONTEXT | CRYPT_SILENT);
408
if (cryptoAcquired) {
409
mCryptoProviderInitialized = true;
410
}
411
#endif
412
gHistoryService = this;
413
}
414
415
nsNavHistory::~nsNavHistory() {
416
MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread");
417
418
// remove the static reference to the service. Check to make sure its us
419
// in case somebody creates an extra instance of the service.
420
NS_ASSERTION(gHistoryService == this,
421
"Deleting a non-singleton instance of the service");
422
423
if (gHistoryService == this) gHistoryService = nullptr;
424
425
#ifdef XP_WIN
426
if (mCryptoProviderInitialized) {
427
Unused << CryptReleaseContext(mCryptoProvider, 0);
428
}
429
#endif
430
}
431
432
nsresult nsNavHistory::Init() {
433
LoadPrefs();
434
435
mDB = Database::GetDatabase();
436
NS_ENSURE_STATE(mDB);
437
438
/*****************************************************************************
439
*** IMPORTANT NOTICE!
440
***
441
*** Nothing after these add observer calls should return anything but NS_OK.
442
*** If a failure code is returned, this nsNavHistory object will be held onto
443
*** by the observer service and the preference service.
444
****************************************************************************/
445
446
// Observe preferences changes.
447
Preferences::AddWeakObservers(this, kObservedPrefs);
448
449
nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
450
if (obsSvc) {
451
(void)obsSvc->AddObserver(this, TOPIC_PLACES_CONNECTION_CLOSED, true);
452
(void)obsSvc->AddObserver(this, TOPIC_IDLE_DAILY, true);
453
#ifdef MOZ_XUL
454
(void)obsSvc->AddObserver(this, TOPIC_AUTOCOMPLETE_FEEDBACK_INCOMING, true);
455
#endif
456
}
457
458
// Don't add code that can fail here! Do it up above, before we add our
459
// observers.
460
461
return NS_OK;
462
}
463
464
NS_IMETHODIMP
465
nsNavHistory::GetDatabaseStatus(uint16_t* aDatabaseStatus) {
466
NS_ENSURE_ARG_POINTER(aDatabaseStatus);
467
*aDatabaseStatus = mDB->GetDatabaseStatus();
468
return NS_OK;
469
}
470
471
uint32_t nsNavHistory::GetRecentFlags(nsIURI* aURI) {
472
uint32_t result = 0;
473
nsAutoCString spec;
474
nsresult rv = aURI->GetSpec(spec);
475
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Unable to get aURI's spec");
476
477
if (NS_SUCCEEDED(rv)) {
478
if (CheckIsRecentEvent(&mRecentTyped, spec)) result |= RECENT_TYPED;
479
if (CheckIsRecentEvent(&mRecentLink, spec)) result |= RECENT_ACTIVATED;
480
if (CheckIsRecentEvent(&mRecentBookmark, spec)) result |= RECENT_BOOKMARKED;
481
}
482
483
return result;
484
}
485
486
nsresult nsNavHistory::GetIdForPage(nsIURI* aURI, int64_t* _pageId,
487
nsCString& _GUID) {
488
*_pageId = 0;
489
490
nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
491
"SELECT id, url, title, rev_host, visit_count, guid "
492
"FROM moz_places "
493
"WHERE url_hash = hash(:page_url) AND url = :page_url ");
494
NS_ENSURE_STATE(stmt);
495
mozStorageStatementScoper scoper(stmt);
496
497
nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI);
498
NS_ENSURE_SUCCESS(rv, rv);
499
500
bool hasEntry = false;
501
rv = stmt->ExecuteStep(&hasEntry);
502
NS_ENSURE_SUCCESS(rv, rv);
503
504
if (hasEntry) {
505
rv = stmt->GetInt64(0, _pageId);
506
NS_ENSURE_SUCCESS(rv, rv);
507
rv = stmt->GetUTF8String(5, _GUID);
508
NS_ENSURE_SUCCESS(rv, rv);
509
}
510
511
return NS_OK;
512
}
513
514
nsresult nsNavHistory::GetOrCreateIdForPage(nsIURI* aURI, int64_t* _pageId,
515
nsCString& _GUID) {
516
nsresult rv = GetIdForPage(aURI, _pageId, _GUID);
517
NS_ENSURE_SUCCESS(rv, rv);
518
519
if (*_pageId != 0) {
520
return NS_OK;
521
}
522
523
{
524
// Create a new hidden, untyped and unvisited entry.
525
nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
526
"INSERT INTO moz_places (url, url_hash, rev_host, hidden, frecency, "
527
"guid) "
528
"VALUES (:page_url, hash(:page_url), :rev_host, :hidden, :frecency, "
529
":guid) ");
530
NS_ENSURE_STATE(stmt);
531
mozStorageStatementScoper scoper(stmt);
532
533
rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI);
534
NS_ENSURE_SUCCESS(rv, rv);
535
// host (reversed with trailing period)
536
nsAutoString revHost;
537
rv = GetReversedHostname(aURI, revHost);
538
// Not all URI types have hostnames, so this is optional.
539
if (NS_SUCCEEDED(rv)) {
540
rv = stmt->BindStringByName(NS_LITERAL_CSTRING("rev_host"), revHost);
541
} else {
542
rv = stmt->BindNullByName(NS_LITERAL_CSTRING("rev_host"));
543
}
544
NS_ENSURE_SUCCESS(rv, rv);
545
rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("hidden"), 1);
546
NS_ENSURE_SUCCESS(rv, rv);
547
nsAutoCString spec;
548
rv = aURI->GetSpec(spec);
549
NS_ENSURE_SUCCESS(rv, rv);
550
rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("frecency"),
551
IsQueryURI(spec) ? 0 : -1);
552
NS_ENSURE_SUCCESS(rv, rv);
553
rv = GenerateGUID(_GUID);
554
NS_ENSURE_SUCCESS(rv, rv);
555
rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), _GUID);
556
NS_ENSURE_SUCCESS(rv, rv);
557
558
rv = stmt->Execute();
559
NS_ENSURE_SUCCESS(rv, rv);
560
561
*_pageId = sLastInsertedPlaceId;
562
}
563
564
{
565
// Trigger the updates to the moz_origins tables
566
nsCOMPtr<mozIStorageStatement> stmt =
567
mDB->GetStatement("DELETE FROM moz_updateoriginsinsert_temp");
568
NS_ENSURE_STATE(stmt);
569
mozStorageStatementScoper scoper(stmt);
570
}
571
572
return NS_OK;
573
}
574
575
void nsNavHistory::LoadPrefs() {
576
// History preferences.
577
mHistoryEnabled = Preferences::GetBool(PREF_HISTORY_ENABLED, true);
578
579
// Frecency preferences.
580
#define FRECENCY_PREF(_prop, _pref) \
581
_prop = Preferences::GetInt(_pref, _pref##_DEF)
582
583
FRECENCY_PREF(mNumVisitsForFrecency, PREF_FREC_NUM_VISITS);
584
FRECENCY_PREF(mFirstBucketCutoffInDays, PREF_FREC_FIRST_BUCKET_CUTOFF);
585
FRECENCY_PREF(mSecondBucketCutoffInDays, PREF_FREC_SECOND_BUCKET_CUTOFF);
586
FRECENCY_PREF(mThirdBucketCutoffInDays, PREF_FREC_THIRD_BUCKET_CUTOFF);
587
FRECENCY_PREF(mFourthBucketCutoffInDays, PREF_FREC_FOURTH_BUCKET_CUTOFF);
588
FRECENCY_PREF(mEmbedVisitBonus, PREF_FREC_EMBED_VISIT_BONUS);
589
FRECENCY_PREF(mFramedLinkVisitBonus, PREF_FREC_FRAMED_LINK_VISIT_BONUS);
590
FRECENCY_PREF(mLinkVisitBonus, PREF_FREC_LINK_VISIT_BONUS);
591
FRECENCY_PREF(mTypedVisitBonus, PREF_FREC_TYPED_VISIT_BONUS);
592
FRECENCY_PREF(mBookmarkVisitBonus, PREF_FREC_BOOKMARK_VISIT_BONUS);
593
FRECENCY_PREF(mDownloadVisitBonus, PREF_FREC_DOWNLOAD_VISIT_BONUS);
594
FRECENCY_PREF(mPermRedirectVisitBonus, PREF_FREC_PERM_REDIRECT_VISIT_BONUS);
595
FRECENCY_PREF(mTempRedirectVisitBonus, PREF_FREC_TEMP_REDIRECT_VISIT_BONUS);
596
FRECENCY_PREF(mRedirectSourceVisitBonus, PREF_FREC_REDIR_SOURCE_VISIT_BONUS);
597
FRECENCY_PREF(mDefaultVisitBonus, PREF_FREC_DEFAULT_VISIT_BONUS);
598
FRECENCY_PREF(mUnvisitedBookmarkBonus, PREF_FREC_UNVISITED_BOOKMARK_BONUS);
599
FRECENCY_PREF(mUnvisitedTypedBonus, PREF_FREC_UNVISITED_TYPED_BONUS);
600
FRECENCY_PREF(mReloadVisitBonus, PREF_FREC_RELOAD_VISIT_BONUS);
601
FRECENCY_PREF(mFirstBucketWeight, PREF_FREC_FIRST_BUCKET_WEIGHT);
602
FRECENCY_PREF(mSecondBucketWeight, PREF_FREC_SECOND_BUCKET_WEIGHT);
603
FRECENCY_PREF(mThirdBucketWeight, PREF_FREC_THIRD_BUCKET_WEIGHT);
604
FRECENCY_PREF(mFourthBucketWeight, PREF_FREC_FOURTH_BUCKET_WEIGHT);
605
FRECENCY_PREF(mDefaultWeight, PREF_FREC_DEFAULT_BUCKET_WEIGHT);
606
607
#undef FRECENCY_PREF
608
}
609
610
void nsNavHistory::UpdateDaysOfHistory(PRTime visitTime) {
611
if (mDaysOfHistory == 0) {
612
mDaysOfHistory = 1;
613
}
614
615
if (visitTime > mLastCachedEndOfDay || visitTime < mLastCachedStartOfDay) {
616
mDaysOfHistory = -1;
617
}
618
}
619
620
void nsNavHistory::NotifyTitleChange(nsIURI* aURI, const nsString& aTitle,
621
const nsACString& aGUID) {
622
MOZ_ASSERT(!aGUID.IsEmpty());
623
NOTIFY_OBSERVERS(mCanNotify, mObservers, nsINavHistoryObserver,
624
OnTitleChanged(aURI, aTitle, aGUID));
625
}
626
627
void nsNavHistory::NotifyFrecencyChanged(const nsACString& aSpec,
628
int32_t aNewFrecency,
629
const nsACString& aGUID, bool aHidden,
630
PRTime aLastVisitDate) {
631
MOZ_ASSERT(!aGUID.IsEmpty());
632
633
nsCOMPtr<nsIURI> uri;
634
Unused << NS_NewURI(getter_AddRefs(uri), aSpec);
635
// We cannot assert since some automated tests are checking this path.
636
NS_WARNING_ASSERTION(uri,
637
"Invalid URI in nsNavHistory::NotifyFrecencyChanged");
638
// Notify a frecency change only if we have a valid uri, otherwise
639
// the observer couldn't gather any useful data from the notification.
640
if (!uri) {
641
return;
642
}
643
NOTIFY_OBSERVERS(
644
mCanNotify, mObservers, nsINavHistoryObserver,
645
OnFrecencyChanged(uri, aNewFrecency, aGUID, aHidden, aLastVisitDate));
646
}
647
648
void nsNavHistory::NotifyManyFrecenciesChanged() {
649
NOTIFY_OBSERVERS(mCanNotify, mObservers, nsINavHistoryObserver,
650
OnManyFrecenciesChanged());
651
}
652
653
void nsNavHistory::DispatchFrecencyChangedNotification(
654
const nsACString& aSpec, int32_t aNewFrecency, const nsACString& aGUID,
655
bool aHidden, PRTime aLastVisitDate) const {
656
Unused << NS_DispatchToMainThread(
657
NewRunnableMethod<nsCString, int32_t, nsCString, bool, PRTime>(
658
"nsNavHistory::NotifyFrecencyChanged",
659
const_cast<nsNavHistory*>(this), &nsNavHistory::NotifyFrecencyChanged,
660
aSpec, aNewFrecency, aGUID, aHidden, aLastVisitDate));
661
}
662
663
NS_IMETHODIMP
664
nsNavHistory::RecalculateOriginFrecencyStats(nsIObserver* aCallback) {
665
RefPtr<nsNavHistory> self(this);
666
nsMainThreadPtrHandle<nsIObserver> callback(
667
!aCallback ? nullptr
668
: new nsMainThreadPtrHolder<nsIObserver>(
669
"nsNavHistory::RecalculateOriginFrecencyStats callback",
670
aCallback));
671
672
nsCOMPtr<nsIEventTarget> target(do_GetInterface(mDB->MainConn()));
673
NS_ENSURE_STATE(target);
674
nsresult rv = target->Dispatch(NS_NewRunnableFunction(
675
"nsNavHistory::RecalculateOriginFrecencyStats", [self, callback] {
676
Unused << self->RecalculateOriginFrecencyStatsInternal();
677
Unused << NS_DispatchToMainThread(NS_NewRunnableFunction(
678
"nsNavHistory::RecalculateOriginFrecencyStats callback",
679
[callback] {
680
if (callback) {
681
Unused << callback->Observe(nullptr, "", nullptr);
682
}
683
}));
684
}));
685
NS_ENSURE_SUCCESS(rv, rv);
686
687
return NS_OK;
688
}
689
690
nsresult nsNavHistory::RecalculateOriginFrecencyStatsInternal() {
691
nsCOMPtr<mozIStorageConnection> conn(mDB->MainConn());
692
NS_ENSURE_STATE(conn);
693
694
nsresult rv = conn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
695
"INSERT OR REPLACE INTO moz_meta(key, value) VALUES "
696
"( "
697
"'" MOZ_META_KEY_ORIGIN_FRECENCY_COUNT
698
"' , "
699
"(SELECT COUNT(*) FROM moz_origins WHERE frecency > 0) "
700
"), "
701
"( "
702
"'" MOZ_META_KEY_ORIGIN_FRECENCY_SUM
703
"', "
704
"(SELECT TOTAL(frecency) FROM moz_origins WHERE frecency > 0) "
705
"), "
706
"( "
707
"'" MOZ_META_KEY_ORIGIN_FRECENCY_SUM_OF_SQUARES
708
"' , "
709
"(SELECT TOTAL(frecency * frecency) FROM moz_origins WHERE frecency > 0) "
710
") "));
711
NS_ENSURE_SUCCESS(rv, rv);
712
713
return NS_OK;
714
}
715
716
Atomic<int64_t> nsNavHistory::sLastInsertedPlaceId(0);
717
Atomic<int64_t> nsNavHistory::sLastInsertedVisitId(0);
718
719
void // static
720
nsNavHistory::StoreLastInsertedId(const nsACString& aTable,
721
const int64_t aLastInsertedId) {
722
if (aTable.EqualsLiteral("moz_places")) {
723
nsNavHistory::sLastInsertedPlaceId = aLastInsertedId;
724
} else if (aTable.EqualsLiteral("moz_historyvisits")) {
725
nsNavHistory::sLastInsertedVisitId = aLastInsertedId;
726
} else {
727
MOZ_ASSERT(false, "Trying to store the insert id for an unknown table?");
728
}
729
}
730
731
int32_t nsNavHistory::GetDaysOfHistory() {
732
MOZ_ASSERT(NS_IsMainThread(), "This can only be called on the main thread");
733
734
if (mDaysOfHistory != -1) return mDaysOfHistory;
735
736
// SQLite doesn't have a CEIL() function, so we must do that later.
737
// We should also take into account timers resolution, that may be as bad as
738
// 16ms on Windows, so in some cases the difference may be 0, if the
739
// check is done near the visit. Thus remember to check for NULL separately.
740
nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
741
"SELECT CAST(( "
742
"strftime('%s','now','localtime','utc') - "
743
"(SELECT MIN(visit_date)/1000000 FROM moz_historyvisits) "
744
") AS DOUBLE) "
745
"/86400, "
746
"strftime('%s','now','localtime','+1 day','start of day','utc') * "
747
"1000000");
748
NS_ENSURE_TRUE(stmt, 0);
749
mozStorageStatementScoper scoper(stmt);
750
751
bool hasResult;
752
if (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
753
// If we get NULL, then there are no visits, otherwise there must always be
754
// at least 1 day of history.
755
bool hasNoVisits;
756
(void)stmt->GetIsNull(0, &hasNoVisits);
757
mDaysOfHistory =
758
hasNoVisits
759
? 0
760
: std::max(1, static_cast<int32_t>(ceil(stmt->AsDouble(0))));
761
mLastCachedStartOfDay =
762
NormalizeTime(nsINavHistoryQuery::TIME_RELATIVE_TODAY, 0);
763
mLastCachedEndOfDay = stmt->AsInt64(1) - 1; // Start of tomorrow - 1.
764
}
765
766
return mDaysOfHistory;
767
}
768
769
PRTime nsNavHistory::GetNow() {
770
if (!mCachedNow) {
771
mCachedNow = PR_Now();
772
if (!mExpireNowTimer) mExpireNowTimer = NS_NewTimer();
773
if (mExpireNowTimer)
774
mExpireNowTimer->InitWithNamedFuncCallback(
775
expireNowTimerCallback, this, RENEW_CACHED_NOW_TIMEOUT,
776
nsITimer::TYPE_ONE_SHOT, "nsNavHistory::GetNow");
777
}
778
return mCachedNow;
779
}
780
781
void nsNavHistory::expireNowTimerCallback(nsITimer* aTimer, void* aClosure) {
782
nsNavHistory* history = static_cast<nsNavHistory*>(aClosure);
783
if (history) {
784
history->mCachedNow = 0;
785
history->mExpireNowTimer = nullptr;
786
}
787
}
788
789
/**
790
* Code borrowed from mozilla/xpfe/components/history/src/nsGlobalHistory.cpp
791
* Pass in a pre-normalized now and a date, and we'll find the difference since
792
* midnight on each of the days.
793
*/
794
static PRTime NormalizeTimeRelativeToday(PRTime aTime) {
795
// round to midnight this morning
796
PRExplodedTime explodedTime;
797
PR_ExplodeTime(aTime, PR_LocalTimeParameters, &explodedTime);
798
799
// set to midnight (0:00)
800
explodedTime.tm_min = explodedTime.tm_hour = explodedTime.tm_sec =
801
explodedTime.tm_usec = 0;
802
803
return PR_ImplodeTime(&explodedTime);
804
}
805
806
// nsNavHistory::NormalizeTime
807
//
808
// Converts a nsINavHistoryQuery reference+offset time into a PRTime
809
// relative to the epoch.
810
//
811
// It is important that this function NOT use the current time optimization.
812
// It is called to update queries, and we really need to know what right
813
// now is because those incoming values will also have current times that
814
// we will have to compare against.
815
816
PRTime // static
817
nsNavHistory::NormalizeTime(uint32_t aRelative, PRTime aOffset) {
818
PRTime ref;
819
switch (aRelative) {
820
case nsINavHistoryQuery::TIME_RELATIVE_EPOCH:
821
return aOffset;
822
case nsINavHistoryQuery::TIME_RELATIVE_TODAY:
823
ref = NormalizeTimeRelativeToday(PR_Now());
824
break;
825
case nsINavHistoryQuery::TIME_RELATIVE_NOW:
826
ref = PR_Now();
827
break;
828
default:
829
MOZ_ASSERT_UNREACHABLE("Invalid relative time");
830
return 0;
831
}
832
return ref + aOffset;
833
}
834
835
// nsNavHistory::DomainNameFromURI
836
//
837
// This does the www.mozilla.org -> mozilla.org and
838
// foo.theregister.co.uk -> theregister.co.uk conversion
839
void nsNavHistory::DomainNameFromURI(nsIURI* aURI, nsACString& aDomainName) {
840
// lazily get the effective tld service
841
if (!mTLDService)
842
mTLDService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
843
844
if (mTLDService) {
845
// get the base domain for a given hostname.
846
// e.g. for "images.bbc.co.uk", this would be "bbc.co.uk".
847
nsresult rv = mTLDService->GetBaseDomain(aURI, 0, aDomainName);
848
if (NS_SUCCEEDED(rv)) return;
849
}
850
851
// just return the original hostname
852
// (it's also possible the host is an IP address)
853
aURI->GetAsciiHost(aDomainName);
854
}
855
856
bool nsNavHistory::hasHistoryEntries() { return GetDaysOfHistory() > 0; }
857
858
// Call this method before visiting a URL in order to help determine the
859
// transition type of the visit.
860
//
861
// @see MarkPageAsTyped
862
863
NS_IMETHODIMP
864
nsNavHistory::MarkPageAsFollowedBookmark(nsIURI* aURI) {
865
NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
866
NS_ENSURE_ARG(aURI);
867
868
// don't add when history is disabled
869
if (IsHistoryDisabled()) return NS_OK;
870
871
nsAutoCString uriString;
872
nsresult rv = aURI->GetSpec(uriString);
873
NS_ENSURE_SUCCESS(rv, rv);
874
875
mRecentBookmark.Put(uriString, GetNow());
876
877
if (mRecentBookmark.Count() > RECENT_EVENT_QUEUE_MAX_LENGTH)
878
ExpireNonrecentEvents(&mRecentBookmark);
879
880
return NS_OK;
881
}
882
883
// nsNavHistory::CanAddURI
884
//
885
// Filter out unwanted URIs such as "chrome:", "mailbox:", etc.
886
//
887
// The model is if we don't know differently then add which basically means
888
// we are suppose to try all the things we know not to allow in and then if
889
// we don't bail go on and allow it in.
890
891
NS_IMETHODIMP
892
nsNavHistory::CanAddURI(nsIURI* aURI, bool* canAdd) {
893
NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
894
NS_ENSURE_ARG(aURI);
895
NS_ENSURE_ARG_POINTER(canAdd);
896
897
// Default to false.
898
*canAdd = false;
899
900
// If history is disabled, don't add any entry.
901
if (IsHistoryDisabled()) {
902
return NS_OK;
903
}
904
905
// If the url length is over a threshold, don't add it.
906
nsCString spec;
907
nsresult rv = aURI->GetSpec(spec);
908
NS_ENSURE_SUCCESS(rv, rv);
909
if (!mDB || spec.Length() > mDB->MaxUrlLength()) {
910
return NS_OK;
911
}
912
913
nsAutoCString scheme;
914
rv = aURI->GetScheme(scheme);
915
NS_ENSURE_SUCCESS(rv, rv);
916
917
// first check the most common cases (HTTP, HTTPS) to allow in to avoid most
918
// of the work
919
if (scheme.EqualsLiteral("http")) {
920
*canAdd = true;
921
return NS_OK;
922
}
923
if (scheme.EqualsLiteral("https")) {
924
*canAdd = true;
925
return NS_OK;
926
}
927
928
// now check for all bad things
929
if (scheme.EqualsLiteral("about") || scheme.EqualsLiteral("blob") ||
930
scheme.EqualsLiteral("chrome") || scheme.EqualsLiteral("data") ||
931
scheme.EqualsLiteral("imap") || scheme.EqualsLiteral("javascript") ||
932
scheme.EqualsLiteral("mailbox") || scheme.EqualsLiteral("moz-anno") ||
933
scheme.EqualsLiteral("news") || scheme.EqualsLiteral("page-icon") ||
934
scheme.EqualsLiteral("resource") || scheme.EqualsLiteral("view-source")) {
935
return NS_OK;
936
}
937
*canAdd = true;
938
return NS_OK;
939
}
940
941
// nsNavHistory::GetNewQuery
942
943
NS_IMETHODIMP
944
nsNavHistory::GetNewQuery(nsINavHistoryQuery** _retval) {
945
NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
946
NS_ENSURE_ARG_POINTER(_retval);
947
948
RefPtr<nsNavHistoryQuery> query = new nsNavHistoryQuery();
949
query.forget(_retval);
950
return NS_OK;
951
}
952
953
// nsNavHistory::GetNewQueryOptions
954
955
NS_IMETHODIMP
956
nsNavHistory::GetNewQueryOptions(nsINavHistoryQueryOptions** _retval) {
957
NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
958
NS_ENSURE_ARG_POINTER(_retval);
959
960
RefPtr<nsNavHistoryQueryOptions> queryOptions =
961
new nsNavHistoryQueryOptions();
962
queryOptions.forget(_retval);
963
return NS_OK;
964
}
965
966
NS_IMETHODIMP
967
nsNavHistory::ExecuteQuery(nsINavHistoryQuery* aQuery,
968
nsINavHistoryQueryOptions* aOptions,
969
nsINavHistoryResult** _retval) {
970
NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
971
NS_ENSURE_ARG(aQuery);
972
NS_ENSURE_ARG(aOptions);
973
NS_ENSURE_ARG_POINTER(_retval);
974
975
// Clone the input query and options, because the caller might change the
976
// objects, but we always want to reflect the original parameters.
977
nsCOMPtr<nsINavHistoryQuery> queryClone;
978
aQuery->Clone(getter_AddRefs(queryClone));
979
NS_ENSURE_STATE(queryClone);
980
RefPtr<nsNavHistoryQuery> query = do_QueryObject(queryClone);
981
NS_ENSURE_STATE(query);
982
nsCOMPtr<nsINavHistoryQueryOptions> optionsClone;
983
aOptions->Clone(getter_AddRefs(optionsClone));
984
NS_ENSURE_STATE(optionsClone);
985
RefPtr<nsNavHistoryQueryOptions> options = do_QueryObject(optionsClone);
986
NS_ENSURE_STATE(options);
987
988
// Create the root node.
989
RefPtr<nsNavHistoryContainerResultNode> rootNode;
990
991
nsCString folderGuid = GetSimpleBookmarksQueryParent(query, options);
992
if (!folderGuid.IsEmpty()) {
993
// In the simple case where we're just querying children of a single
994
// bookmark folder, we can more efficiently generate results.
995
nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService();
996
NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY);
997
RefPtr<nsNavHistoryResultNode> tempRootNode;
998
nsresult rv = bookmarks->ResultNodeForContainer(
999
folderGuid, options, getter_AddRefs(tempRootNode));
1000
if (NS_SUCCEEDED(rv)) {
1001
rootNode = tempRootNode->GetAsContainer();
1002
} else {
1003
NS_WARNING("Generating a generic empty node for a broken query!");
1004
// This is a perf hack to generate an empty query that skips filtering.
1005
options->SetExcludeItems(true);
1006
}
1007
}
1008
1009
if (!rootNode) {
1010
// Either this is not a folder shortcut, or is a broken one. In both cases
1011
// just generate a query node.
1012
nsAutoCString queryUri;
1013
nsresult rv = QueryToQueryString(query, options, queryUri);
1014
NS_ENSURE_SUCCESS(rv, rv);
1015
rootNode = new nsNavHistoryQueryResultNode(EmptyCString(), 0, queryUri,
1016
query, options);
1017
}
1018
1019
// Create the result that will hold nodes. Inject batching status into it.
1020
RefPtr<nsNavHistoryResult> result =
1021
new nsNavHistoryResult(rootNode, query, options);
1022
result.forget(_retval);
1023
return NS_OK;
1024
}
1025
1026
// determine from our nsNavHistoryQuery array and nsNavHistoryQueryOptions
1027
// if this is the place query from the history menu.
1028
// from browser-menubar.inc, our history menu query is:
1029
// place:sort=4&maxResults=10
1030
// note, any maxResult > 0 will still be considered a history menu query
1031
// or if this is the place query from the old "Most Visited" item in some
1032
// profiles: folder: place:sort=8&maxResults=10 note, any maxResult > 0 will
1033
// still be considered a Most Visited menu query
1034
static bool IsOptimizableHistoryQuery(
1035
const RefPtr<nsNavHistoryQuery>& aQuery,
1036
const RefPtr<nsNavHistoryQueryOptions>& aOptions, uint16_t aSortMode) {
1037
if (aOptions->QueryType() != nsINavHistoryQueryOptions::QUERY_TYPE_HISTORY)
1038
return false;
1039
1040
if (aOptions->ResultType() != nsINavHistoryQueryOptions::RESULTS_AS_URI)
1041
return false;
1042
1043
if (aOptions->SortingMode() != aSortMode) return false;
1044
1045
if (aOptions->MaxResults() <= 0) return false;
1046
1047
if (aOptions->ExcludeItems()) return false;
1048
1049
if (aOptions->IncludeHidden()) return false;
1050
1051
if (aQuery->MinVisits() != -1 || aQuery->MaxVisits() != -1) return false;
1052
1053
if (aQuery->BeginTime() || aQuery->BeginTimeReference()) return false;
1054
1055
if (aQuery->EndTime() || aQuery->EndTimeReference()) return false;
1056
1057
if (!aQuery->SearchTerms().IsEmpty()) return false;
1058
1059
if (aQuery->OnlyBookmarked()) return false;
1060
1061
if (aQuery->DomainIsHost() || !aQuery->Domain().IsEmpty()) return false;
1062
1063
if (aQuery->AnnotationIsNot() || !aQuery->Annotation().IsEmpty())
1064
return false;
1065
1066
if (aQuery->Parents().Length() > 0) return false;
1067
1068
if (aQuery->Tags().Length() > 0) return false;
1069
1070
if (aQuery->Transitions().Length() > 0) return false;
1071
1072
return true;
1073
}
1074
1075
static bool NeedToFilterResultSet(const RefPtr<nsNavHistoryQuery>& aQuery,
1076
nsNavHistoryQueryOptions* aOptions) {
1077
return aOptions->ExcludeQueries();
1078
}
1079
1080
// ** Helper class for ConstructQueryString **/
1081
1082
class PlacesSQLQueryBuilder {
1083
public:
1084
PlacesSQLQueryBuilder(const nsCString& aConditions,
1085
const RefPtr<nsNavHistoryQuery>& aQuery,
1086
const RefPtr<nsNavHistoryQueryOptions>& aOptions,
1087
bool aUseLimit, nsNavHistory::StringHash& aAddParams,
1088
bool aHasSearchTerms);
1089
1090
nsresult GetQueryString(nsCString& aQueryString);
1091
1092
private:
1093
nsresult Select();
1094
1095
nsresult SelectAsURI();
1096
nsresult SelectAsVisit();
1097
nsresult SelectAsDay();
1098
nsresult SelectAsSite();
1099
nsresult SelectAsTag();
1100
nsresult SelectAsRoots();
1101
nsresult SelectAsLeftPane();
1102
1103
nsresult Where();
1104
nsresult GroupBy();
1105
nsresult OrderBy();
1106
nsresult Limit();
1107
1108
void OrderByColumnIndexAsc(int32_t aIndex);
1109
void OrderByColumnIndexDesc(int32_t aIndex);
1110
// Use these if you want a case insensitive sorting.
1111
void OrderByTextColumnIndexAsc(int32_t aIndex);
1112
void OrderByTextColumnIndexDesc(int32_t aIndex);
1113
1114
const nsCString& mConditions;
1115
bool mUseLimit;
1116
bool mHasSearchTerms;
1117
1118
uint16_t mResultType;
1119
uint16_t mQueryType;
1120
bool mIncludeHidden;
1121
uint16_t mSortingMode;
1122
uint32_t mMaxResults;
1123
1124
nsCString mQueryString;
1125
nsCString mGroupBy;
1126
bool mHasDateColumns;
1127
bool mSkipOrderBy;
1128
1129
nsNavHistory::StringHash& mAddParams;
1130
};
1131
1132
PlacesSQLQueryBuilder::PlacesSQLQueryBuilder(
1133
const nsCString& aConditions, const RefPtr<nsNavHistoryQuery>& aQuery,
1134
const RefPtr<nsNavHistoryQueryOptions>& aOptions, bool aUseLimit,
1135
nsNavHistory::StringHash& aAddParams, bool aHasSearchTerms)
1136
: mConditions(aConditions),
1137
mUseLimit(aUseLimit),
1138
mHasSearchTerms(aHasSearchTerms),
1139
mResultType(aOptions->ResultType()),
1140
mQueryType(aOptions->QueryType()),
1141
mIncludeHidden(aOptions->IncludeHidden()),
1142
mSortingMode(aOptions->SortingMode()),
1143
mMaxResults(aOptions->MaxResults()),
1144
mSkipOrderBy(false),
1145
mAddParams(aAddParams) {
1146
mHasDateColumns =
1147
(mQueryType == nsINavHistoryQueryOptions::QUERY_TYPE_BOOKMARKS);
1148
// Force the default sorting mode for tag queries.
1149
if (mSortingMode == nsINavHistoryQueryOptions::SORT_BY_NONE &&
1150
aQuery->Tags().Length() > 0) {
1151
mSortingMode = nsINavHistoryQueryOptions::SORT_BY_TITLE_ASCENDING;
1152
}
1153
}
1154
1155
nsresult PlacesSQLQueryBuilder::GetQueryString(nsCString& aQueryString) {
1156
nsresult rv = Select();
1157
NS_ENSURE_SUCCESS(rv, rv);
1158
rv = Where();
1159
NS_ENSURE_SUCCESS(rv, rv);
1160
rv = GroupBy();
1161
NS_ENSURE_SUCCESS(rv, rv);
1162
rv = OrderBy();
1163
NS_ENSURE_SUCCESS(rv, rv);
1164
rv = Limit();
1165
NS_ENSURE_SUCCESS(rv, rv);
1166
1167
aQueryString = mQueryString;
1168
return NS_OK;
1169
}
1170
1171
nsresult PlacesSQLQueryBuilder::Select() {
1172
nsresult rv;
1173
1174
switch (mResultType) {
1175
case nsINavHistoryQueryOptions::RESULTS_AS_URI:
1176
rv = SelectAsURI();
1177
NS_ENSURE_SUCCESS(rv, rv);
1178
break;
1179
1180
case nsINavHistoryQueryOptions::RESULTS_AS_VISIT:
1181
rv = SelectAsVisit();
1182
NS_ENSURE_SUCCESS(rv, rv);
1183
break;
1184
1185
case nsINavHistoryQueryOptions::RESULTS_AS_DATE_QUERY:
1186
case nsINavHistoryQueryOptions::RESULTS_AS_DATE_SITE_QUERY:
1187
rv = SelectAsDay();
1188
NS_ENSURE_SUCCESS(rv, rv);
1189
break;
1190
1191
case nsINavHistoryQueryOptions::RESULTS_AS_SITE_QUERY:
1192
rv = SelectAsSite();
1193
NS_ENSURE_SUCCESS(rv, rv);
1194
break;
1195
1196
case nsINavHistoryQueryOptions::RESULTS_AS_TAGS_ROOT:
1197
rv = SelectAsTag();
1198
NS_ENSURE_SUCCESS(rv, rv);
1199
break;
1200
1201
case nsINavHistoryQueryOptions::RESULTS_AS_ROOTS_QUERY:
1202
rv = SelectAsRoots();
1203
NS_ENSURE_SUCCESS(rv, rv);
1204
break;
1205
1206
case nsINavHistoryQueryOptions::RESULTS_AS_LEFT_PANE_QUERY:
1207
rv = SelectAsLeftPane();
1208
NS_ENSURE_SUCCESS(rv, rv);
1209
break;
1210
1211
default:
1212
MOZ_ASSERT_UNREACHABLE("Invalid result type");
1213
}
1214
return NS_OK;
1215
}
1216
1217
nsresult PlacesSQLQueryBuilder::SelectAsURI() {
1218
nsNavHistory* history = nsNavHistory::GetHistoryService();
1219
NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
1220
nsAutoCString tagsSqlFragment;
1221
1222
switch (mQueryType) {
1223
case nsINavHistoryQueryOptions::QUERY_TYPE_HISTORY:
1224
GetTagsSqlFragment(history->GetTagsFolder(), NS_LITERAL_CSTRING("h.id"),
1225
mHasSearchTerms, tagsSqlFragment);
1226
1227
mQueryString = NS_LITERAL_CSTRING(
1228
"SELECT h.id, h.url, h.title AS page_title, "
1229
"h.rev_host, h.visit_count, "
1230
"h.last_visit_date, null, null, null, null, null, ") +
1231
tagsSqlFragment +
1232
NS_LITERAL_CSTRING(
1233
", h.frecency, h.hidden, h.guid, "
1234
"null, null, null "
1235
"FROM moz_places h "
1236
// WHERE 1 is a no-op since additonal conditions will
1237
// start with AND.
1238
"WHERE 1 "
1239
"{QUERY_OPTIONS_VISITS} {QUERY_OPTIONS_PLACES} "
1240
"{ADDITIONAL_CONDITIONS} ");
1241
break;
1242
1243
case nsINavHistoryQueryOptions::QUERY_TYPE_BOOKMARKS:
1244
1245
GetTagsSqlFragment(history->GetTagsFolder(), NS_LITERAL_CSTRING("b.fk"),
1246
mHasSearchTerms, tagsSqlFragment);
1247
mQueryString =
1248
NS_LITERAL_CSTRING(
1249
"SELECT b.fk, h.url, b.title AS page_title, "
1250
"h.rev_host, h.visit_count, h.last_visit_date, null, b.id, "
1251
"b.dateAdded, b.lastModified, b.parent, ") +
1252
tagsSqlFragment +
1253
NS_LITERAL_CSTRING(
1254
", h.frecency, h.hidden, h.guid,"
1255
"null, null, null, b.guid, b.position, b.type, b.fk "
1256
"FROM moz_bookmarks b "
1257
"JOIN moz_places h ON b.fk = h.id "
1258
"WHERE NOT EXISTS "
1259
"(SELECT id FROM moz_bookmarks "
1260
"WHERE id = b.parent AND parent = ") +
1261
nsPrintfCString("%" PRId64, history->GetTagsFolder()) +
1262
NS_LITERAL_CSTRING(
1263
") "
1264
"AND NOT h.url_hash BETWEEN hash('place', 'prefix_lo') AND "
1265
"hash('place', 'prefix_hi') "
1266
"{ADDITIONAL_CONDITIONS}");
1267
break;
1268
1269
default:
1270
return NS_ERROR_NOT_IMPLEMENTED;
1271
}
1272
return NS_OK;
1273
}
1274
1275
nsresult PlacesSQLQueryBuilder::SelectAsVisit() {
1276
nsNavHistory* history = nsNavHistory::GetHistoryService();
1277
NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
1278
nsAutoCString tagsSqlFragment;
1279
GetTagsSqlFragment(history->GetTagsFolder(), NS_LITERAL_CSTRING("h.id"),
1280
mHasSearchTerms, tagsSqlFragment);
1281
mQueryString =
1282
NS_LITERAL_CSTRING(
1283
"SELECT h.id, h.url, h.title AS page_title, h.rev_host, "
1284
"h.visit_count, "
1285
"v.visit_date, null, null, null, null, null, ") +
1286
tagsSqlFragment +
1287
NS_LITERAL_CSTRING(
1288
", h.frecency, h.hidden, h.guid, "
1289
"v.id, v.from_visit, v.visit_type "
1290
"FROM moz_places h "
1291
"JOIN moz_historyvisits v ON h.id = v.place_id "
1292
// WHERE 1 is a no-op since additonal conditions will start with AND.
1293
"WHERE 1 "
1294
"{QUERY_OPTIONS_VISITS} {QUERY_OPTIONS_PLACES} "
1295
"{ADDITIONAL_CONDITIONS} ");
1296
1297
return NS_OK;
1298
}
1299
1300
nsresult PlacesSQLQueryBuilder::SelectAsDay() {
1301
mSkipOrderBy = true;
1302
1303
// Sort child queries based on sorting mode if it's provided, otherwise
1304
// fallback to default sort by title ascending.
1305
uint16_t sortingMode = nsINavHistoryQueryOptions::SORT_BY_TITLE_ASCENDING;
1306
if (mSortingMode != nsINavHistoryQueryOptions::SORT_BY_NONE &&
1307
mResultType == nsINavHistoryQueryOptions::RESULTS_AS_DATE_QUERY)
1308
sortingMode = mSortingMode;
1309
1310
uint16_t resultType =
1311
mResultType == nsINavHistoryQueryOptions::RESULTS_AS_DATE_QUERY
1312
? (uint16_t)nsINavHistoryQueryOptions::RESULTS_AS_URI
1313
: (uint16_t)nsINavHistoryQueryOptions::RESULTS_AS_SITE_QUERY;
1314
1315
// beginTime will become the node's time property, we don't use endTime
1316
// because it could overlap, and we use time to sort containers and find
1317
// insert position in a result.
1318
mQueryString = nsPrintfCString(
1319
"SELECT null, "
1320
"'place:type=%d&sort=%d&beginTime='||beginTime||'&endTime='||endTime, "
1321
"dayTitle, null, null, beginTime, null, null, null, null, null, null, "
1322
"null, null, null "
1323
"FROM (", // TOUTER BEGIN
1324
resultType, sortingMode);
1325
1326
nsNavHistory* history = nsNavHistory::GetHistoryService();
1327
NS_ENSURE_STATE(history);
1328
1329
int32_t daysOfHistory = history->GetDaysOfHistory();
1330
for (int32_t i = 0; i <= HISTORY_DATE_CONT_NUM(daysOfHistory); i++) {
1331
nsAutoCString dateName;
1332
// Timeframes are calculated as BeginTime <= container < EndTime.
1333
// Notice times can't be relative to now, since to recognize a query we
1334
// must ensure it won't change based on the time it is built.
1335
// So, to select till now, we really select till start of tomorrow, that is
1336
// a fixed timestamp.
1337
// These are used as limits for the inside containers.
1338
nsAutoCString sqlFragmentContainerBeginTime, sqlFragmentContainerEndTime;
1339
// These are used to query if the container should be visible.
1340
nsAutoCString sqlFragmentSearchBeginTime, sqlFragmentSearchEndTime;
1341
switch (i) {
1342
case 0:
1343
// Today
1344
history->GetStringFromName("finduri-AgeInDays-is-0", dateName);
1345
// From start of today
1346
sqlFragmentContainerBeginTime = NS_LITERAL_CSTRING(
1347
"(strftime('%s','now','localtime','start of day','utc')*1000000)");
1348
// To now (tomorrow)
1349
sqlFragmentContainerEndTime = NS_LITERAL_CSTRING(
1350
"(strftime('%s','now','localtime','start of day','+1 "
1351
"day','utc')*1000000)");
1352
// Search for the same timeframe.
1353
sqlFragmentSearchBeginTime = sqlFragmentContainerBeginTime;
1354
sqlFragmentSearchEndTime = sqlFragmentContainerEndTime;
1355
break;
1356
case 1:
1357
// Yesterday
1358
history->GetStringFromName("finduri-AgeInDays-is-1", dateName);
1359
// From start of yesterday
1360
sqlFragmentContainerBeginTime = NS_LITERAL_CSTRING(
1361
"(strftime('%s','now','localtime','start of day','-1 "
1362
"day','utc')*1000000)");
1363
// To start of today
1364
sqlFragmentContainerEndTime = NS_LITERAL_CSTRING(
1365
"(strftime('%s','now','localtime','start of day','utc')*1000000)");
1366
// Search for the same timeframe.
1367
sqlFragmentSearchBeginTime = sqlFragmentContainerBeginTime;
1368
sqlFragmentSearchEndTime = sqlFragmentContainerEndTime;
1369
break;
1370
case 2:
1371
// Last 7 days
1372
history->GetAgeInDaysString(7, "finduri-AgeInDays-last-is", dateName);
1373
// From start of 7 days ago
1374
sqlFragmentContainerBeginTime = NS_LITERAL_CSTRING(
1375
"(strftime('%s','now','localtime','start of day','-7 "
1376
"days','utc')*1000000)");
1377
// To now (tomorrow)
1378
sqlFragmentContainerEndTime = NS_LITERAL_CSTRING(
1379
"(strftime('%s','now','localtime','start of day','+1 "
1380
"day','utc')*1000000)");
1381
// This is an overlapped container, but we show it only if there are
1382
// visits older than yesterday.
1383
sqlFragmentSearchBeginTime = sqlFragmentContainerBeginTime;
1384
sqlFragmentSearchEndTime = NS_LITERAL_CSTRING(
1385
"(strftime('%s','now','localtime','start of day','-1 "
1386
"day','utc')*1000000)");
1387
break;
1388
case 3:
1389
// This month
1390
history->GetStringFromName("finduri-AgeInMonths-is-0", dateName);
1391
// From start of this month
1392
sqlFragmentContainerBeginTime = NS_LITERAL_CSTRING(
1393
"(strftime('%s','now','localtime','start of "
1394
"month','utc')*1000000)");
1395
// To now (tomorrow)
1396
sqlFragmentContainerEndTime = NS_LITERAL_CSTRING(
1397
"(strftime('%s','now','localtime','start of day','+1 "
1398
"day','utc')*1000000)");
1399
// This is an overlapped container, but we show it only if there are
1400
// visits older than 7 days ago.
1401
sqlFragmentSearchBeginTime = sqlFragmentContainerBeginTime;
1402
sqlFragmentSearchEndTime = NS_LITERAL_CSTRING(
1403
"(strftime('%s','now','localtime','start of day','-7 "
1404
"days','utc')*1000000)");
1405
break;
1406
default:
1407
if (i == HISTORY_ADDITIONAL_DATE_CONT_NUM + 6) {
1408
// Older than 6 months
1409
history->GetAgeInDaysString(6, "finduri-AgeInMonths-isgreater",
1410
dateName);
1411
// From start of epoch
1412
sqlFragmentContainerBeginTime =
1413
NS_LITERAL_CSTRING("(datetime(0, 'unixepoch')*1000000)");
1414
// To start of 6 months ago ( 5 months + this month).
1415
sqlFragmentContainerEndTime = NS_LITERAL_CSTRING(
1416
"(strftime('%s','now','localtime','start of month','-5 "
1417
"months','utc')*1000000)");
1418
// Search for the same timeframe.
1419
sqlFragmentSearchBeginTime = sqlFragmentContainerBeginTime;
1420
sqlFragmentSearchEndTime = sqlFragmentContainerEndTime;
1421
break;
1422
}
1423
int32_t MonthIndex = i - HISTORY_ADDITIONAL_DATE_CONT_NUM;
1424
// Previous months' titles are month's name if inside this year,
1425
// month's name and year for previous years.
1426
PRExplodedTime tm;
1427
PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &tm);
1428
uint16_t currentYear = tm.tm_year;
1429
// Set day before month, setting month without day could cause issues.
1430
// For example setting month to February when today is 30, since
1431
// February has not 30 days, will return March instead.
1432
// Also, we use day 2 instead of day 1, so that the GMT month is always
1433
// the same as the local month. (Bug 603002)
1434
tm.tm_mday = 2;
1435
tm.tm_month -= MonthIndex;
1436
// Notice we use GMTParameters because we just want to get the first
1437
// day of each month. Using LocalTimeParameters would instead force us
1438
// to apply a DST correction that we don't really need here.
1439
PR_NormalizeTime(&tm, PR_GMTParameters);
1440
// If the container is for a past year, add the year to its title,
1441
// otherwise just show the month name.
1442
if (tm.tm_year < currentYear) {
1443
nsNavHistory::GetMonthYear(tm, dateName);
1444
} else {
1445
nsNavHistory::GetMonthName(tm, dateName);
1446
}
1447
1448
// From start of MonthIndex + 1 months ago
1449
sqlFragmentContainerBeginTime = NS_LITERAL_CSTRING(
1450
"(strftime('%s','now','localtime','start of month','-");
1451
sqlFragmentContainerBeginTime.AppendInt(MonthIndex);
1452
sqlFragmentContainerBeginTime.AppendLiteral(" months','utc')*1000000)");
1453
// To start of MonthIndex months ago
1454
sqlFragmentContainerEndTime = NS_LITERAL_CSTRING(
1455
"(strftime('%s','now','localtime','start of month','-");
1456
sqlFragmentContainerEndTime.AppendInt(MonthIndex - 1);
1457
sqlFragmentContainerEndTime.AppendLiteral(" months','utc')*1000000)");
1458
// Search for the same timeframe.
1459
sqlFragmentSearchBeginTime = sqlFragmentContainerBeginTime;
1460
sqlFragmentSearchEndTime = sqlFragmentContainerEndTime;
1461
break;
1462
}
1463
1464
nsPrintfCString dateParam("dayTitle%d", i);
1465
mAddParams.Put(dateParam, dateName);
1466
1467
nsPrintfCString dayRange(
1468
"SELECT :%s AS dayTitle, "
1469
"%s AS beginTime, "
1470
"%s AS endTime "
1471
"WHERE EXISTS ( "
1472
"SELECT id FROM moz_historyvisits "
1473
"WHERE visit_date >= %s "
1474
"AND visit_date < %s "
1475
"AND visit_type NOT IN (0,%d,%d) "
1476
"{QUERY_OPTIONS_VISITS} "
1477
"LIMIT 1 "
1478
") ",
1479
dateParam.get(), sqlFragmentContainerBeginTime.get(),
1480
sqlFragmentContainerEndTime.get(), sqlFragmentSearchBeginTime.get(),
1481
sqlFragmentSearchEndTime.get(), nsINavHistoryService::TRANSITION_EMBED,
1482
nsINavHistoryService::TRANSITION_FRAMED_LINK);
1483
1484
mQueryString.Append(dayRange);
1485
1486
if (i < HISTORY_DATE_CONT_NUM(daysOfHistory))
1487
mQueryString.AppendLiteral(" UNION ALL ");
1488
}
1489
1490
mQueryString.AppendLiteral(") "); // TOUTER END
1491
1492
return NS_OK;
1493
}
1494
1495
nsresult PlacesSQLQueryBuilder::SelectAsSite() {
1496
nsAutoCString localFiles;
1497
1498
nsNavHistory* history = nsNavHistory::GetHistoryService();
1499
NS_ENSURE_STATE(history);
1500
1501
history->GetStringFromName("localhost", localFiles);
1502
mAddParams.Put(NS_LITERAL_CSTRING("localhost"), localFiles);
1503
1504
// If there are additional conditions the query has to join on visits too.
1505
nsAutoCString visitsJoin;
1506
nsAutoCString additionalConditions;
1507
nsAutoCString timeConstraints;
1508
if (!mConditions.IsEmpty()) {
1509
visitsJoin.AssignLiteral("JOIN moz_historyvisits v ON v.place_id = h.id ");
1510
additionalConditions.AssignLiteral(
1511
"{QUERY_OPTIONS_VISITS} "
1512
"{QUERY_OPTIONS_PLACES} "
1513
"{ADDITIONAL_CONDITIONS} ");
1514
timeConstraints.AssignLiteral(
1515
"||'&beginTime='||:begin_time||"
1516
"'&endTime='||:end_time");
1517
}
1518
1519
mQueryString = nsPrintfCString(
1520
"SELECT null, 'place:type=%d&sort=%d&domain=&domainIsHost=true'%s, "
1521
":localhost, :localhost, null, null, null, null, null, null, null, "
1522
"null, null, null "
1523
"WHERE EXISTS ( "
1524
"SELECT h.id FROM moz_places h "
1525
"%s "
1526
"WHERE h.hidden = 0 "
1527
"AND h.visit_count > 0 "
1528
"AND h.url_hash BETWEEN hash('file', 'prefix_lo') AND "
1529
"hash('file', 'prefix_hi') "
1530
"%s "
1531
"LIMIT 1 "
1532
") "
1533
"UNION ALL "
1534
"SELECT null, "
1535
"'place:type=%d&sort=%d&domain='||host||'&domainIsHost=true'%s, "
1536
"host, host, null, null, null, null, null, null, null, "
1537
"null, null, null "
1538
"FROM ( "
1539
"SELECT get_unreversed_host(h.rev_host) AS host "
1540
"FROM moz_places h "
1541
"%s "
1542
"WHERE h.hidden = 0 "
1543
"AND h.rev_host <> '.' "
1544
"AND h.visit_count > 0 "
1545
"%s "
1546
"GROUP BY h.rev_host "
1547
"ORDER BY host ASC "
1548
") ",
1549
nsINavHistoryQueryOptions::RESULTS_AS_URI, mSortingMode,
1550
timeConstraints.get(), visitsJoin.get(), additionalConditions.get(),
1551
nsINavHistoryQueryOptions::RESULTS_AS_URI, mSortingMode,
1552
timeConstraints.get(), visitsJoin.get(), additionalConditions.get());
1553
1554
return NS_OK;
1555
}
1556
1557
nsresult PlacesSQLQueryBuilder::SelectAsTag() {
1558
nsNavHistory* history = nsNavHistory::GetHistoryService();
1559
NS_ENSURE_STATE(history);
1560
1561
// This allows sorting by date fields what is not possible with
1562
// other history queries.
1563
mHasDateColumns = true;
1564
1565
// TODO (Bug 1449939): This is likely wrong, since the tag name should
1566
// probably be urlencoded, and we have no util for that in SQL, yet.
1567
// We could encode the tag when the user sets it though.
1568
mQueryString = nsPrintfCString(
1569
"SELECT null, 'place:tag=' || title, "
1570
"title, null, null, null, null, null, dateAdded, "
1571
"lastModified, null, null, null, null, null, null "
1572
"FROM moz_bookmarks "
1573
"WHERE parent = %" PRId64,
1574
history->GetTagsFolder());
1575
1576
return NS_OK;
1577
}
1578
1579
nsresult PlacesSQLQueryBuilder::SelectAsRoots() {
1580
nsNavHistory* history = nsNavHistory::GetHistoryService();
1581
NS_ENSURE_STATE(history);
1582
1583
nsAutoCString toolbarTitle;
1584
nsAutoCString menuTitle;
1585
nsAutoCString unfiledTitle;
1586
1587
history->GetStringFromName("BookmarksToolbarFolderTitle", toolbarTitle);
1588
mAddParams.Put(NS_LITERAL_CSTRING("BookmarksToolbarFolderTitle"),
1589
toolbarTitle);
1590
history->GetStringFromName("BookmarksMenuFolderTitle", menuTitle);
1591
mAddParams.Put(NS_LITERAL_CSTRING("BookmarksMenuFolderTitle"), menuTitle);
1592
history->GetStringFromName("OtherBookmarksFolderTitle", unfiledTitle);
1593
mAddParams.Put(NS_LITERAL_CSTRING("OtherBookmarksFolderTitle"), unfiledTitle);
1594
1595
nsAutoCString mobileString;
1596
1597
if (Preferences::GetBool(MOBILE_BOOKMARKS_PREF, false)) {
1598
nsAutoCString mobileTitle;
1599
history->GetStringFromName("MobileBookmarksFolderTitle", mobileTitle);
1600
mAddParams.Put(NS_LITERAL_CSTRING("MobileBookmarksFolderTitle"),
1601
mobileTitle);
1602
1603
mobileString = NS_LITERAL_CSTRING(
1604
","
1605
"(null, 'place:parent=" MOBILE_ROOT_GUID
1606
"', :MobileBookmarksFolderTitle, null, null, null, "
1607
"null, null, 0, 0, null, null, null, null, "
1608
"'" MOBILE_BOOKMARKS_VIRTUAL_GUID "', null) ");
1609
}
1610
1611
mQueryString =
1612
NS_LITERAL_CSTRING(
1613
"SELECT * FROM ("
1614
"VALUES(null, 'place:parent=" TOOLBAR_ROOT_GUID
1615
"', :BookmarksToolbarFolderTitle, null, null, null, "
1616
"null, null, 0, 0, null, null, null, null, 'toolbar____v', null), "
1617
"(null, 'place:parent=" MENU_ROOT_GUID
1618
"', :BookmarksMenuFolderTitle, null, null, null, "
1619
"null, null, 0, 0, null, null, null, null, 'menu_______v', null), "
1620
"(null, 'place:parent=" UNFILED_ROOT_GUID
1621
"', :OtherBookmarksFolderTitle, null, null, null, "
1622
"null, null, 0, 0, null, null, null, null, 'unfiled____v', null) ") +
1623
mobileString + NS_LITERAL_CSTRING(")");
1624
1625
return NS_OK;
1626
}
1627
1628
nsresult PlacesSQLQueryBuilder::SelectAsLeftPane() {
1629
nsNavHistory* history = nsNavHistory::GetHistoryService();
1630
NS_ENSURE_STATE(history);
1631
1632
nsAutoCString historyTitle;
1633
nsAutoCString downloadsTitle;
1634
nsAutoCString tagsTitle;
1635
nsAutoCString allBookmarksTitle;
1636
1637
history->GetStringFromName("OrganizerQueryHistory", historyTitle);
1638
mAddParams.Put(NS_LITERAL_CSTRING("OrganizerQueryHistory"), historyTitle);
1639
history->GetStringFromName("OrganizerQueryDownloads", downloadsTitle);
1640
mAddParams.Put(NS_LITERAL_CSTRING("OrganizerQueryDownloads"), downloadsTitle);
1641
history->GetStringFromName("TagsFolderTitle", tagsTitle);
1642
mAddParams.Put(NS_LITERAL_CSTRING("TagsFolderTitle"), tagsTitle);
1643
history->GetStringFromName("OrganizerQueryAllBookmarks", allBookmarksTitle);
1644
mAddParams.Put(NS_LITERAL_CSTRING("OrganizerQueryAllBookmarks"),
1645
allBookmarksTitle);
1646
1647
mQueryString = nsPrintfCString(
1648
"SELECT * FROM ("
1649
"VALUES"
1650
"(null, 'place:type=%d&sort=%d', :OrganizerQueryHistory, null, null, "
1651
"null, "
1652
"null, null, 0, 0, null, null, null, null, 'history____v', null), "
1653
"(null, 'place:transition=%d&sort=%d', :OrganizerQueryDownloads, null, "
1654
"null, null, "
1655
"null, null, 0, 0, null, null, null, null, 'downloads__v', null), "
1656
"(null, 'place:type=%d&sort=%d', :TagsFolderTitle, null, null, null, "
1657
"null, null, 0, 0, null, null, null, null, 'tags_______v', null), "
1658
"(null, 'place:type=%d', :OrganizerQueryAllBookmarks, null, null, null, "
1659
"null, null, 0, 0, null, null, null, null, 'allbms_____v', null) "
1660
")",
1661
nsINavHistoryQueryOptions::RESULTS_AS_DATE_QUERY,
1662
nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING,
1663
nsINavHistoryService::TRANSITION_DOWNLOAD,
1664
nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING,
1665
nsINavHistoryQueryOptions::RESULTS_AS_TAGS_ROOT,
1666
nsINavHistoryQueryOptions::SORT_BY_TITLE_ASCENDING,
1667
nsINavHistoryQueryOptions::RESULTS_AS_ROOTS_QUERY);
1668
return NS_OK;
1669
}
1670
1671
nsresult PlacesSQLQueryBuilder::Where() {
1672
// Set query options
1673
nsAutoCString additionalVisitsConditions;
1674
nsAutoCString additionalPlacesConditions;
1675
1676
if (!mIncludeHidden) {
1677
additionalPlacesConditions += NS_LITERAL_CSTRING("AND hidden = 0 ");
1678
}
1679
1680
if (mQueryType == nsINavHistoryQueryOptions::QUERY_TYPE_HISTORY) {
1681
// last_visit_date is updated for any kind of visit, so it's a good
1682
// indicator whether the page has visits.
1683
additionalPlacesConditions +=
1684
NS_LITERAL_CSTRING("AND last_visit_date NOTNULL ");
1685
}
1686
1687
if (mResultType == nsINavHistoryQueryOptions::RESULTS_AS_URI &&
1688
!additionalVisitsConditions.IsEmpty()) {
1689
// URI results don't join on visits.
1690
nsAutoCString tmp = additionalVisitsConditions;
1691
additionalVisitsConditions =
1692
"AND EXISTS (SELECT 1 FROM moz_historyvisits WHERE place_id = h.id ";
1693
additionalVisitsConditions.Append(tmp);
1694
additionalVisitsConditions.AppendLiteral("LIMIT 1)");
1695
}
1696
1697
mQueryString.ReplaceSubstring("{QUERY_OPTIONS_VISITS}",
1698
additionalVisitsConditions.get());
1699
mQueryString.ReplaceSubstring("{QUERY_OPTIONS_PLACES}",
1700
additionalPlacesConditions.get());
1701
1702
// If we used WHERE already, we inject the conditions
1703
// in place of {ADDITIONAL_CONDITIONS}
1704
if (mQueryString.Find("{ADDITIONAL_CONDITIONS}") != kNotFound) {
1705
nsAutoCString innerCondition;
1706
// If we have condition AND it
1707
if (!mConditions.IsEmpty()) {
1708
innerCondition = " AND (";
1709
innerCondition += mConditions;
1710
innerCondition += ")";
1711
}
1712
mQueryString.ReplaceSubstring("{ADDITIONAL_CONDITIONS}",
1713
innerCondition.get());
1714
1715
} else if (!mConditions.IsEmpty()) {
1716
mQueryString += "WHERE ";
1717
mQueryString += mConditions;
1718
}
1719
return NS_OK;
1720
}
1721
1722
nsresult PlacesSQLQueryBuilder::GroupBy() {
1723
mQueryString += mGroupBy;
1724
return NS_OK;
1725
}
1726
1727
nsresult PlacesSQLQueryBuilder::OrderBy() {
1728
if (mSkipOrderBy) return NS_OK;
1729
1730
// Sort clause: we will sort later, but if it comes out of the DB sorted,
1731
// our later sort will be basically free. The DB can sort these for free
1732
// most of the time anyway, because it has indices over these items.
1733
switch (mSortingMode) {
1734
case nsINavHistoryQueryOptions::SORT_BY_NONE:
1735
// Ensure sorting does not change based on tables status.
1736
if (mResultType == nsINavHistoryQueryOptions::RESULTS_AS_URI) {
1737
if (mQueryType == nsINavHistoryQueryOptions::QUERY_TYPE_BOOKMARKS)
1738
mQueryString += NS_LITERAL_CSTRING(" ORDER BY b.id ASC ");
1739
else if (mQueryType == nsINavHistoryQueryOptions::QUERY_TYPE_HISTORY)
1740
mQueryString += NS_LITERAL_CSTRING(" ORDER BY h.id ASC ");
1741
}
1742
break;
1743
case nsINavHistoryQueryOptions::SORT_BY_TITLE_ASCENDING:
1744
case nsINavHistoryQueryOptions::SORT_BY_TITLE_DESCENDING:
1745
// If the user wants few results, we limit them by date, necessitating
1746
// a sort by date here (see the IDL definition for maxResults).
1747
// Otherwise we will do actual sorting by title, but since we could need
1748
// to special sort for some locale we will repeat a second sorting at the
1749
// end in nsNavHistoryResult, that should be faster since the list will be
1750
// almost ordered.
1751
if (mMaxResults > 0)
1752
OrderByColumnIndexDesc(nsNavHistory::kGetInfoIndex_VisitDate);
1753
else if (mSortingMode ==
1754
nsINavHistoryQueryOptions::SORT_BY_TITLE_ASCENDING)
1755
OrderByTextColumnIndexAsc(nsNavHistory::kGetInfoIndex_Title);
1756
else
1757
OrderByTextColumnIndexDesc(nsNavHistory::kGetInfoIndex_Title);
1758
break;
1759
case nsINavHistoryQueryOptions::SORT_BY_DATE_ASCENDING:
1760
OrderByColumnIndexAsc(nsNavHistory::kGetInfoIndex_VisitDate);
1761
break;
1762
case nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING:
1763
OrderByColumnIndexDesc(nsNavHistory::kGetInfoIndex_VisitDate);
1764
break;
1765
case nsINavHistoryQueryOptions::SORT_BY_URI_ASCENDING:
1766
OrderByColumnIndexAsc(nsNavHistory::kGetInfoIndex_URL);
1767
break;
1768
case nsINavHistoryQueryOptions::SORT_BY_URI_DESCENDING:
1769
OrderByColumnIndexDesc(nsNavHistory::kGetInfoIndex_URL);
1770
break;
1771
case nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_ASCENDING:
1772
OrderByColumnIndexAsc(nsNavHistory::kGetInfoIndex_VisitCount);
1773
break;
1774
case nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_DESCENDING:
1775
OrderByColumnIndexDesc(nsNavHistory::kGetInfoIndex_VisitCount);
1776
break;
1777
case nsINavHistoryQueryOptions::SORT_BY_DATEADDED_ASCENDING:
1778
if (mHasDateColumns)
1779
OrderByColumnIndexAsc(nsNavHistory::kGetInfoIndex_ItemDateAdded);
1780
break;
1781
case nsINavHistoryQueryOptions::SORT_BY_DATEADDED_DESCENDING:
1782
if (mHasDateColumns)
1783
OrderByColumnIndexDesc(nsNavHistory::kGetInfoIndex_ItemDateAdded);
1784
break;
1785
case nsINavHistoryQueryOptions::SORT_BY_LASTMODIFIED_ASCENDING:
1786
if (mHasDateColumns)
1787
OrderByColumnIndexAsc(nsNavHistory::kGetInfoIndex_ItemLastModified);
1788