1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // The history system runs on a background thread so that potentially slow
6 // database operations don't delay the browser. This backend processing is
7 // represented by HistoryBackend. The HistoryService's job is to dispatch to
8 // that thread.
9 //
10 // Main thread History thread
11 // ----------- --------------
12 // HistoryService <----------------> HistoryBackend
13 // -> HistoryDatabase
14 // -> SQLite connection to History
15 // -> ArchivedDatabase
16 // -> SQLite connection to Archived History
17 // -> TextDatabaseManager
18 // -> SQLite connection to one month's data
19 // -> SQLite connection to one month's data
20 // ...
21 // -> ThumbnailDatabase
22 // -> SQLite connection to Thumbnails
23 // (and favicons)
24
25 #include "chrome/browser/history/history.h"
26
27 #include "base/callback.h"
28 #include "base/memory/ref_counted.h"
29 #include "base/message_loop.h"
30 #include "base/path_service.h"
31 #include "base/string_util.h"
32 #include "base/task.h"
33 #include "chrome/browser/autocomplete/history_url_provider.h"
34 #include "chrome/browser/browser_process.h"
35 #include "chrome/browser/history/download_create_info.h"
36 #include "chrome/browser/history/history_backend.h"
37 #include "chrome/browser/history/history_notifications.h"
38 #include "chrome/browser/history/history_types.h"
39 #include "chrome/browser/history/in_memory_database.h"
40 #include "chrome/browser/history/in_memory_history_backend.h"
41 #include "chrome/browser/history/top_sites.h"
42 #include "chrome/browser/prefs/pref_service.h"
43 #include "chrome/browser/profiles/profile.h"
44 #include "chrome/browser/ui/profile_error_dialog.h"
45 #include "chrome/browser/visitedlink/visitedlink_master.h"
46 #include "chrome/common/chrome_constants.h"
47 #include "chrome/common/pref_names.h"
48 #include "chrome/common/thumbnail_score.h"
49 #include "chrome/common/url_constants.h"
50 #include "content/browser/browser_thread.h"
51 #include "content/common/notification_service.h"
52 #include "grit/chromium_strings.h"
53 #include "grit/generated_resources.h"
54 #include "third_party/skia/include/core/SkBitmap.h"
55
56 using base::Time;
57 using history::HistoryBackend;
58
59 namespace {
60
61 static const char* kHistoryThreadName = "Chrome_HistoryThread";
62
63 } // namespace
64
65 // Sends messages from the backend to us on the main thread. This must be a
66 // separate class from the history service so that it can hold a reference to
67 // the history service (otherwise we would have to manually AddRef and
68 // Release when the Backend has a reference to us).
69 class HistoryService::BackendDelegate : public HistoryBackend::Delegate {
70 public:
BackendDelegate(HistoryService * history_service)71 explicit BackendDelegate(HistoryService* history_service)
72 : history_service_(history_service),
73 message_loop_(MessageLoop::current()) {
74 }
75
NotifyProfileError(sql::InitStatus init_status)76 virtual void NotifyProfileError(sql::InitStatus init_status) OVERRIDE {
77 // Send to the history service on the main thread.
78 message_loop_->PostTask(FROM_HERE, NewRunnableMethod(history_service_.get(),
79 &HistoryService::NotifyProfileError, init_status));
80 }
81
SetInMemoryBackend(history::InMemoryHistoryBackend * backend)82 virtual void SetInMemoryBackend(
83 history::InMemoryHistoryBackend* backend) OVERRIDE {
84 // Send the backend to the history service on the main thread.
85 message_loop_->PostTask(FROM_HERE, NewRunnableMethod(history_service_.get(),
86 &HistoryService::SetInMemoryBackend, backend));
87 }
88
BroadcastNotifications(NotificationType type,history::HistoryDetails * details)89 virtual void BroadcastNotifications(
90 NotificationType type,
91 history::HistoryDetails* details) OVERRIDE {
92 // Send the notification on the history thread.
93 if (NotificationService::current()) {
94 Details<history::HistoryDetails> det(details);
95 NotificationService::current()->Notify(type,
96 NotificationService::AllSources(),
97 det);
98 }
99 // Send the notification to the history service on the main thread.
100 message_loop_->PostTask(FROM_HERE, NewRunnableMethod(history_service_.get(),
101 &HistoryService::BroadcastNotifications, type, details));
102 }
103
DBLoaded()104 virtual void DBLoaded() OVERRIDE {
105 message_loop_->PostTask(FROM_HERE, NewRunnableMethod(history_service_.get(),
106 &HistoryService::OnDBLoaded));
107 }
108
StartTopSitesMigration()109 virtual void StartTopSitesMigration() OVERRIDE {
110 message_loop_->PostTask(FROM_HERE, NewRunnableMethod(history_service_.get(),
111 &HistoryService::StartTopSitesMigration));
112 }
113
114 private:
115 scoped_refptr<HistoryService> history_service_;
116 MessageLoop* message_loop_;
117 };
118
119 // static
120 const history::StarID HistoryService::kBookmarkBarID = 1;
121
122 // The history thread is intentionally not a BrowserThread because the
123 // sync integration unit tests depend on being able to create more than one
124 // history thread.
HistoryService()125 HistoryService::HistoryService()
126 : thread_(new base::Thread(kHistoryThreadName)),
127 profile_(NULL),
128 backend_loaded_(false),
129 bookmark_service_(NULL),
130 no_db_(false),
131 needs_top_sites_migration_(false) {
132 }
133
HistoryService(Profile * profile)134 HistoryService::HistoryService(Profile* profile)
135 : thread_(new base::Thread(kHistoryThreadName)),
136 profile_(profile),
137 backend_loaded_(false),
138 bookmark_service_(NULL),
139 no_db_(false),
140 needs_top_sites_migration_(false) {
141 DCHECK(profile_);
142 registrar_.Add(this, NotificationType::HISTORY_URLS_DELETED,
143 Source<Profile>(profile_));
144 registrar_.Add(this, NotificationType::TEMPLATE_URL_REMOVED,
145 Source<Profile>(profile_));
146 }
147
~HistoryService()148 HistoryService::~HistoryService() {
149 // Shutdown the backend. This does nothing if Cleanup was already invoked.
150 Cleanup();
151 }
152
BackendLoaded()153 bool HistoryService::BackendLoaded() {
154 // NOTE: We start the backend loading even though it completes asynchronously
155 // and thus won't affect the return value of this function. This is because
156 // callers of this assume that if the backend isn't yet loaded it will be
157 // soon, so they will either listen for notifications or just retry this call
158 // later. If we've purged the backend, we haven't necessarily restarted it
159 // loading by now, so we need to trigger the load in order to maintain that
160 // expectation.
161 LoadBackendIfNecessary();
162 return backend_loaded_;
163 }
164
UnloadBackend()165 void HistoryService::UnloadBackend() {
166 if (!history_backend_)
167 return; // Already unloaded.
168
169 // Get rid of the in-memory backend.
170 in_memory_backend_.reset();
171
172 // The backend's destructor must run on the history thread since it is not
173 // threadsafe. So this thread must not be the last thread holding a reference
174 // to the backend, or a crash could happen.
175 //
176 // We have a reference to the history backend. There is also an extra
177 // reference held by our delegate installed in the backend, which
178 // HistoryBackend::Closing will release. This means if we scheduled a call
179 // to HistoryBackend::Closing and *then* released our backend reference, there
180 // will be a race between us and the backend's Closing function to see who is
181 // the last holder of a reference. If the backend thread's Closing manages to
182 // run before we release our backend refptr, the last reference will be held
183 // by this thread and the destructor will be called from here.
184 //
185 // Therefore, we create a task to run the Closing operation first. This holds
186 // a reference to the backend. Then we release our reference, then we schedule
187 // the task to run. After the task runs, it will delete its reference from
188 // the history thread, ensuring everything works properly.
189 Task* closing_task =
190 NewRunnableMethod(history_backend_.get(), &HistoryBackend::Closing);
191 history_backend_ = NULL;
192 ScheduleTask(PRIORITY_NORMAL, closing_task);
193 }
194
Cleanup()195 void HistoryService::Cleanup() {
196 if (!thread_) {
197 // We've already cleaned up.
198 return;
199 }
200
201 // Unload the backend.
202 UnloadBackend();
203
204 // Delete the thread, which joins with the background thread. We defensively
205 // NULL the pointer before deleting it in case somebody tries to use it
206 // during shutdown, but this shouldn't happen.
207 base::Thread* thread = thread_;
208 thread_ = NULL;
209 delete thread;
210 }
211
NotifyRenderProcessHostDestruction(const void * host)212 void HistoryService::NotifyRenderProcessHostDestruction(const void* host) {
213 ScheduleAndForget(PRIORITY_NORMAL,
214 &HistoryBackend::NotifyRenderProcessHostDestruction, host);
215 }
216
InMemoryDatabase()217 history::URLDatabase* HistoryService::InMemoryDatabase() {
218 // NOTE: See comments in BackendLoaded() as to why we call
219 // LoadBackendIfNecessary() here even though it won't affect the return value
220 // for this call.
221 LoadBackendIfNecessary();
222 if (in_memory_backend_.get())
223 return in_memory_backend_->db();
224 return NULL;
225 }
226
InMemoryIndex()227 history::InMemoryURLIndex* HistoryService::InMemoryIndex() {
228 // NOTE: See comments in BackendLoaded() as to why we call
229 // LoadBackendIfNecessary() here even though it won't affect the return value
230 // for this call.
231 LoadBackendIfNecessary();
232 if (in_memory_backend_.get())
233 return in_memory_backend_->InMemoryIndex();
234 return NULL;
235 }
236
SetSegmentPresentationIndex(int64 segment_id,int index)237 void HistoryService::SetSegmentPresentationIndex(int64 segment_id, int index) {
238 ScheduleAndForget(PRIORITY_UI,
239 &HistoryBackend::SetSegmentPresentationIndex,
240 segment_id, index);
241 }
242
SetKeywordSearchTermsForURL(const GURL & url,TemplateURLID keyword_id,const string16 & term)243 void HistoryService::SetKeywordSearchTermsForURL(const GURL& url,
244 TemplateURLID keyword_id,
245 const string16& term) {
246 ScheduleAndForget(PRIORITY_UI,
247 &HistoryBackend::SetKeywordSearchTermsForURL,
248 url, keyword_id, term);
249 }
250
DeleteAllSearchTermsForKeyword(TemplateURLID keyword_id)251 void HistoryService::DeleteAllSearchTermsForKeyword(
252 TemplateURLID keyword_id) {
253 ScheduleAndForget(PRIORITY_UI,
254 &HistoryBackend::DeleteAllSearchTermsForKeyword,
255 keyword_id);
256 }
257
GetMostRecentKeywordSearchTerms(TemplateURLID keyword_id,const string16 & prefix,int max_count,CancelableRequestConsumerBase * consumer,GetMostRecentKeywordSearchTermsCallback * callback)258 HistoryService::Handle HistoryService::GetMostRecentKeywordSearchTerms(
259 TemplateURLID keyword_id,
260 const string16& prefix,
261 int max_count,
262 CancelableRequestConsumerBase* consumer,
263 GetMostRecentKeywordSearchTermsCallback* callback) {
264 return Schedule(PRIORITY_UI, &HistoryBackend::GetMostRecentKeywordSearchTerms,
265 consumer,
266 new history::GetMostRecentKeywordSearchTermsRequest(callback),
267 keyword_id, prefix, max_count);
268 }
269
URLsNoLongerBookmarked(const std::set<GURL> & urls)270 void HistoryService::URLsNoLongerBookmarked(const std::set<GURL>& urls) {
271 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::URLsNoLongerBookmarked,
272 urls);
273 }
274
ScheduleDBTask(HistoryDBTask * task,CancelableRequestConsumerBase * consumer)275 HistoryService::Handle HistoryService::ScheduleDBTask(
276 HistoryDBTask* task,
277 CancelableRequestConsumerBase* consumer) {
278 history::HistoryDBTaskRequest* request = new history::HistoryDBTaskRequest(
279 NewCallback(task, &HistoryDBTask::DoneRunOnMainThread));
280 request->value = task; // The value is the task to execute.
281 return Schedule(PRIORITY_UI, &HistoryBackend::ProcessDBTask, consumer,
282 request);
283 }
284
QuerySegmentUsageSince(CancelableRequestConsumerBase * consumer,const Time from_time,int max_result_count,SegmentQueryCallback * callback)285 HistoryService::Handle HistoryService::QuerySegmentUsageSince(
286 CancelableRequestConsumerBase* consumer,
287 const Time from_time,
288 int max_result_count,
289 SegmentQueryCallback* callback) {
290 return Schedule(PRIORITY_UI, &HistoryBackend::QuerySegmentUsage,
291 consumer, new history::QuerySegmentUsageRequest(callback),
292 from_time, max_result_count);
293 }
294
SetOnBackendDestroyTask(Task * task)295 void HistoryService::SetOnBackendDestroyTask(Task* task) {
296 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::SetOnBackendDestroyTask,
297 MessageLoop::current(), task);
298 }
299
AddPage(const GURL & url,const void * id_scope,int32 page_id,const GURL & referrer,PageTransition::Type transition,const history::RedirectList & redirects,history::VisitSource visit_source,bool did_replace_entry)300 void HistoryService::AddPage(const GURL& url,
301 const void* id_scope,
302 int32 page_id,
303 const GURL& referrer,
304 PageTransition::Type transition,
305 const history::RedirectList& redirects,
306 history::VisitSource visit_source,
307 bool did_replace_entry) {
308 AddPage(url, Time::Now(), id_scope, page_id, referrer, transition, redirects,
309 visit_source, did_replace_entry);
310 }
311
AddPage(const GURL & url,Time time,const void * id_scope,int32 page_id,const GURL & referrer,PageTransition::Type transition,const history::RedirectList & redirects,history::VisitSource visit_source,bool did_replace_entry)312 void HistoryService::AddPage(const GURL& url,
313 Time time,
314 const void* id_scope,
315 int32 page_id,
316 const GURL& referrer,
317 PageTransition::Type transition,
318 const history::RedirectList& redirects,
319 history::VisitSource visit_source,
320 bool did_replace_entry) {
321 scoped_refptr<history::HistoryAddPageArgs> request(
322 new history::HistoryAddPageArgs(url, time, id_scope, page_id, referrer,
323 redirects, transition, visit_source,
324 did_replace_entry));
325 AddPage(*request);
326 }
327
AddPage(const history::HistoryAddPageArgs & add_page_args)328 void HistoryService::AddPage(const history::HistoryAddPageArgs& add_page_args) {
329 DCHECK(thread_) << "History service being called after cleanup";
330
331 // Filter out unwanted URLs. We don't add auto-subframe URLs. They are a
332 // large part of history (think iframes for ads) and we never display them in
333 // history UI. We will still add manual subframes, which are ones the user
334 // has clicked on to get.
335 if (!CanAddURL(add_page_args.url))
336 return;
337
338 // Add link & all redirects to visited link list.
339 VisitedLinkMaster* visited_links;
340 if (profile_ && (visited_links = profile_->GetVisitedLinkMaster())) {
341 visited_links->AddURL(add_page_args.url);
342
343 if (!add_page_args.redirects.empty()) {
344 // We should not be asked to add a page in the middle of a redirect chain.
345 DCHECK_EQ(add_page_args.url,
346 add_page_args.redirects[add_page_args.redirects.size() - 1]);
347
348 // We need the !redirects.empty() condition above since size_t is unsigned
349 // and will wrap around when we subtract one from a 0 size.
350 for (size_t i = 0; i < add_page_args.redirects.size() - 1; i++)
351 visited_links->AddURL(add_page_args.redirects[i]);
352 }
353 }
354
355 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::AddPage,
356 scoped_refptr<history::HistoryAddPageArgs>(
357 add_page_args.Clone()));
358 }
359
AddPageNoVisitForBookmark(const GURL & url)360 void HistoryService::AddPageNoVisitForBookmark(const GURL& url) {
361 if (!CanAddURL(url))
362 return;
363
364 ScheduleAndForget(PRIORITY_NORMAL,
365 &HistoryBackend::AddPageNoVisitForBookmark, url);
366 }
367
SetPageTitle(const GURL & url,const string16 & title)368 void HistoryService::SetPageTitle(const GURL& url,
369 const string16& title) {
370 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::SetPageTitle, url, title);
371 }
372
AddPageWithDetails(const GURL & url,const string16 & title,int visit_count,int typed_count,Time last_visit,bool hidden,history::VisitSource visit_source)373 void HistoryService::AddPageWithDetails(const GURL& url,
374 const string16& title,
375 int visit_count,
376 int typed_count,
377 Time last_visit,
378 bool hidden,
379 history::VisitSource visit_source) {
380 // Filter out unwanted URLs.
381 if (!CanAddURL(url))
382 return;
383
384 // Add to the visited links system.
385 VisitedLinkMaster* visited_links;
386 if (profile_ && (visited_links = profile_->GetVisitedLinkMaster()))
387 visited_links->AddURL(url);
388
389 history::URLRow row(url);
390 row.set_title(title);
391 row.set_visit_count(visit_count);
392 row.set_typed_count(typed_count);
393 row.set_last_visit(last_visit);
394 row.set_hidden(hidden);
395
396 std::vector<history::URLRow> rows;
397 rows.push_back(row);
398
399 ScheduleAndForget(PRIORITY_NORMAL,
400 &HistoryBackend::AddPagesWithDetails, rows, visit_source);
401 }
402
AddPagesWithDetails(const std::vector<history::URLRow> & info,history::VisitSource visit_source)403 void HistoryService::AddPagesWithDetails(
404 const std::vector<history::URLRow>& info,
405 history::VisitSource visit_source) {
406
407 // Add to the visited links system.
408 VisitedLinkMaster* visited_links;
409 if (profile_ && (visited_links = profile_->GetVisitedLinkMaster())) {
410 std::vector<GURL> urls;
411 urls.reserve(info.size());
412 for (std::vector<history::URLRow>::const_iterator i = info.begin();
413 i != info.end();
414 ++i)
415 urls.push_back(i->url());
416
417 visited_links->AddURLs(urls);
418 }
419
420 ScheduleAndForget(PRIORITY_NORMAL,
421 &HistoryBackend::AddPagesWithDetails, info, visit_source);
422 }
423
SetPageContents(const GURL & url,const string16 & contents)424 void HistoryService::SetPageContents(const GURL& url,
425 const string16& contents) {
426 if (!CanAddURL(url))
427 return;
428
429 ScheduleAndForget(PRIORITY_LOW, &HistoryBackend::SetPageContents,
430 url, contents);
431 }
432
SetPageThumbnail(const GURL & page_url,const SkBitmap & thumbnail,const ThumbnailScore & score)433 void HistoryService::SetPageThumbnail(const GURL& page_url,
434 const SkBitmap& thumbnail,
435 const ThumbnailScore& score) {
436 if (!CanAddURL(page_url))
437 return;
438
439 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::SetPageThumbnail,
440 page_url, thumbnail, score);
441 }
442
GetPageThumbnail(const GURL & page_url,CancelableRequestConsumerBase * consumer,ThumbnailDataCallback * callback)443 HistoryService::Handle HistoryService::GetPageThumbnail(
444 const GURL& page_url,
445 CancelableRequestConsumerBase* consumer,
446 ThumbnailDataCallback* callback) {
447 return Schedule(PRIORITY_NORMAL, &HistoryBackend::GetPageThumbnail, consumer,
448 new history::GetPageThumbnailRequest(callback), page_url);
449 }
450
GetFavicon(FaviconService::GetFaviconRequest * request,const GURL & icon_url,history::IconType icon_type)451 void HistoryService::GetFavicon(FaviconService::GetFaviconRequest* request,
452 const GURL& icon_url,
453 history::IconType icon_type) {
454 Schedule(PRIORITY_NORMAL, &HistoryBackend::GetFavicon, NULL, request,
455 icon_url, icon_type);
456 }
457
UpdateFaviconMappingAndFetch(FaviconService::GetFaviconRequest * request,const GURL & page_url,const GURL & icon_url,history::IconType icon_type)458 void HistoryService::UpdateFaviconMappingAndFetch(
459 FaviconService::GetFaviconRequest* request,
460 const GURL& page_url,
461 const GURL& icon_url,
462 history::IconType icon_type) {
463 Schedule(PRIORITY_NORMAL, &HistoryBackend::UpdateFaviconMappingAndFetch, NULL,
464 request, page_url, icon_url, history::FAVICON);
465 }
466
GetFaviconForURL(FaviconService::GetFaviconRequest * request,const GURL & page_url,int icon_types)467 void HistoryService::GetFaviconForURL(
468 FaviconService::GetFaviconRequest* request,
469 const GURL& page_url,
470 int icon_types) {
471 Schedule(PRIORITY_NORMAL, &HistoryBackend::GetFaviconForURL, NULL, request,
472 page_url, icon_types);
473 }
474
SetFavicon(const GURL & page_url,const GURL & icon_url,const std::vector<unsigned char> & image_data,history::IconType icon_type)475 void HistoryService::SetFavicon(const GURL& page_url,
476 const GURL& icon_url,
477 const std::vector<unsigned char>& image_data,
478 history::IconType icon_type) {
479 if (!CanAddURL(page_url))
480 return;
481
482 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::SetFavicon,
483 page_url, icon_url,
484 scoped_refptr<RefCountedMemory>(new RefCountedBytes(image_data)),
485 icon_type);
486 }
487
SetFaviconOutOfDateForPage(const GURL & page_url)488 void HistoryService::SetFaviconOutOfDateForPage(const GURL& page_url) {
489 ScheduleAndForget(PRIORITY_NORMAL,
490 &HistoryBackend::SetFaviconOutOfDateForPage, page_url);
491 }
492
SetImportedFavicons(const std::vector<history::ImportedFaviconUsage> & favicon_usage)493 void HistoryService::SetImportedFavicons(
494 const std::vector<history::ImportedFaviconUsage>& favicon_usage) {
495 ScheduleAndForget(PRIORITY_NORMAL,
496 &HistoryBackend::SetImportedFavicons, favicon_usage);
497 }
498
IterateURLs(URLEnumerator * enumerator)499 void HistoryService::IterateURLs(URLEnumerator* enumerator) {
500 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::IterateURLs, enumerator);
501 }
502
QueryURL(const GURL & url,bool want_visits,CancelableRequestConsumerBase * consumer,QueryURLCallback * callback)503 HistoryService::Handle HistoryService::QueryURL(
504 const GURL& url,
505 bool want_visits,
506 CancelableRequestConsumerBase* consumer,
507 QueryURLCallback* callback) {
508 return Schedule(PRIORITY_UI, &HistoryBackend::QueryURL, consumer,
509 new history::QueryURLRequest(callback), url, want_visits);
510 }
511
512 // Downloads -------------------------------------------------------------------
513
514 // Handle creation of a download by creating an entry in the history service's
515 // 'downloads' table.
CreateDownload(const DownloadCreateInfo & create_info,CancelableRequestConsumerBase * consumer,HistoryService::DownloadCreateCallback * callback)516 HistoryService::Handle HistoryService::CreateDownload(
517 const DownloadCreateInfo& create_info,
518 CancelableRequestConsumerBase* consumer,
519 HistoryService::DownloadCreateCallback* callback) {
520 return Schedule(PRIORITY_NORMAL, &HistoryBackend::CreateDownload, consumer,
521 new history::DownloadCreateRequest(callback), create_info);
522 }
523
524 // Handle queries for a list of all downloads in the history database's
525 // 'downloads' table.
QueryDownloads(CancelableRequestConsumerBase * consumer,DownloadQueryCallback * callback)526 HistoryService::Handle HistoryService::QueryDownloads(
527 CancelableRequestConsumerBase* consumer,
528 DownloadQueryCallback* callback) {
529 return Schedule(PRIORITY_NORMAL, &HistoryBackend::QueryDownloads, consumer,
530 new history::DownloadQueryRequest(callback));
531 }
532
533 // Changes all IN_PROGRESS in the database entries to CANCELED.
534 // IN_PROGRESS entries are the corrupted entries, not updated by next function
535 // because of the crash or some other extremal exit.
CleanUpInProgressEntries()536 void HistoryService::CleanUpInProgressEntries() {
537 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::CleanUpInProgressEntries);
538 }
539
540 // Handle updates for a particular download. This is a 'fire and forget'
541 // operation, so we don't need to be called back.
UpdateDownload(int64 received_bytes,int32 state,int64 db_handle)542 void HistoryService::UpdateDownload(int64 received_bytes,
543 int32 state,
544 int64 db_handle) {
545 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::UpdateDownload,
546 received_bytes, state, db_handle);
547 }
548
UpdateDownloadPath(const FilePath & path,int64 db_handle)549 void HistoryService::UpdateDownloadPath(const FilePath& path,
550 int64 db_handle) {
551 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::UpdateDownloadPath,
552 path, db_handle);
553 }
554
RemoveDownload(int64 db_handle)555 void HistoryService::RemoveDownload(int64 db_handle) {
556 ScheduleAndForget(PRIORITY_NORMAL,
557 &HistoryBackend::RemoveDownload, db_handle);
558 }
559
RemoveDownloadsBetween(Time remove_begin,Time remove_end)560 void HistoryService::RemoveDownloadsBetween(Time remove_begin,
561 Time remove_end) {
562 ScheduleAndForget(PRIORITY_NORMAL,
563 &HistoryBackend::RemoveDownloadsBetween,
564 remove_begin,
565 remove_end);
566 }
567
QueryHistory(const string16 & text_query,const history::QueryOptions & options,CancelableRequestConsumerBase * consumer,QueryHistoryCallback * callback)568 HistoryService::Handle HistoryService::QueryHistory(
569 const string16& text_query,
570 const history::QueryOptions& options,
571 CancelableRequestConsumerBase* consumer,
572 QueryHistoryCallback* callback) {
573 return Schedule(PRIORITY_UI, &HistoryBackend::QueryHistory, consumer,
574 new history::QueryHistoryRequest(callback),
575 text_query, options);
576 }
577
QueryRedirectsFrom(const GURL & from_url,CancelableRequestConsumerBase * consumer,QueryRedirectsCallback * callback)578 HistoryService::Handle HistoryService::QueryRedirectsFrom(
579 const GURL& from_url,
580 CancelableRequestConsumerBase* consumer,
581 QueryRedirectsCallback* callback) {
582 return Schedule(PRIORITY_UI, &HistoryBackend::QueryRedirectsFrom, consumer,
583 new history::QueryRedirectsRequest(callback), from_url);
584 }
585
QueryRedirectsTo(const GURL & to_url,CancelableRequestConsumerBase * consumer,QueryRedirectsCallback * callback)586 HistoryService::Handle HistoryService::QueryRedirectsTo(
587 const GURL& to_url,
588 CancelableRequestConsumerBase* consumer,
589 QueryRedirectsCallback* callback) {
590 return Schedule(PRIORITY_NORMAL, &HistoryBackend::QueryRedirectsTo, consumer,
591 new history::QueryRedirectsRequest(callback), to_url);
592 }
593
GetVisitCountToHost(const GURL & url,CancelableRequestConsumerBase * consumer,GetVisitCountToHostCallback * callback)594 HistoryService::Handle HistoryService::GetVisitCountToHost(
595 const GURL& url,
596 CancelableRequestConsumerBase* consumer,
597 GetVisitCountToHostCallback* callback) {
598 return Schedule(PRIORITY_UI, &HistoryBackend::GetVisitCountToHost, consumer,
599 new history::GetVisitCountToHostRequest(callback), url);
600 }
601
QueryTopURLsAndRedirects(int result_count,CancelableRequestConsumerBase * consumer,QueryTopURLsAndRedirectsCallback * callback)602 HistoryService::Handle HistoryService::QueryTopURLsAndRedirects(
603 int result_count,
604 CancelableRequestConsumerBase* consumer,
605 QueryTopURLsAndRedirectsCallback* callback) {
606 return Schedule(PRIORITY_NORMAL, &HistoryBackend::QueryTopURLsAndRedirects,
607 consumer, new history::QueryTopURLsAndRedirectsRequest(callback),
608 result_count);
609 }
610
QueryMostVisitedURLs(int result_count,int days_back,CancelableRequestConsumerBase * consumer,QueryMostVisitedURLsCallback * callback)611 HistoryService::Handle HistoryService::QueryMostVisitedURLs(
612 int result_count,
613 int days_back,
614 CancelableRequestConsumerBase* consumer,
615 QueryMostVisitedURLsCallback* callback) {
616 return Schedule(PRIORITY_NORMAL, &HistoryBackend::QueryMostVisitedURLs,
617 consumer,
618 new history::QueryMostVisitedURLsRequest(callback),
619 result_count, days_back);
620 }
621
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)622 void HistoryService::Observe(NotificationType type,
623 const NotificationSource& source,
624 const NotificationDetails& details) {
625 if (!thread_)
626 return;
627
628 switch (type.value) {
629 case NotificationType::HISTORY_URLS_DELETED: {
630 // Update the visited link system for deleted URLs. We will update the
631 // visited link system for added URLs as soon as we get the add
632 // notification (we don't have to wait for the backend, which allows us to
633 // be faster to update the state).
634 //
635 // For deleted URLs, we don't typically know what will be deleted since
636 // delete notifications are by time. We would also like to be more
637 // respectful of privacy and never tell the user something is gone when it
638 // isn't. Therefore, we update the delete URLs after the fact.
639 if (!profile_)
640 return; // No profile, probably unit testing.
641 Details<history::URLsDeletedDetails> deleted_details(details);
642 VisitedLinkMaster* visited_links = profile_->GetVisitedLinkMaster();
643 if (!visited_links)
644 return; // Nobody to update.
645 if (deleted_details->all_history)
646 visited_links->DeleteAllURLs();
647 else // Delete individual ones.
648 visited_links->DeleteURLs(deleted_details->urls);
649 break;
650 }
651
652 case NotificationType::TEMPLATE_URL_REMOVED:
653 DeleteAllSearchTermsForKeyword(*(Details<TemplateURLID>(details).ptr()));
654 break;
655
656 default:
657 NOTREACHED();
658 }
659 }
660
Init(const FilePath & history_dir,BookmarkService * bookmark_service,bool no_db)661 bool HistoryService::Init(const FilePath& history_dir,
662 BookmarkService* bookmark_service,
663 bool no_db) {
664 if (!thread_->Start()) {
665 Cleanup();
666 return false;
667 }
668
669 history_dir_ = history_dir;
670 bookmark_service_ = bookmark_service;
671 no_db_ = no_db;
672
673 // Create the history backend.
674 LoadBackendIfNecessary();
675 return true;
676 }
677
ScheduleAutocomplete(HistoryURLProvider * provider,HistoryURLProviderParams * params)678 void HistoryService::ScheduleAutocomplete(HistoryURLProvider* provider,
679 HistoryURLProviderParams* params) {
680 ScheduleAndForget(PRIORITY_UI, &HistoryBackend::ScheduleAutocomplete,
681 scoped_refptr<HistoryURLProvider>(provider), params);
682 }
683
ScheduleTask(SchedulePriority priority,Task * task)684 void HistoryService::ScheduleTask(SchedulePriority priority,
685 Task* task) {
686 // TODO(brettw): do prioritization.
687 thread_->message_loop()->PostTask(FROM_HERE, task);
688 }
689
690 // static
CanAddURL(const GURL & url)691 bool HistoryService::CanAddURL(const GURL& url) {
692 if (!url.is_valid())
693 return false;
694
695 // TODO: We should allow kChromeUIScheme URLs if they have been explicitly
696 // typed. Right now, however, these are marked as typed even when triggered
697 // by a shortcut or menu action.
698 if (url.SchemeIs(chrome::kJavaScriptScheme) ||
699 url.SchemeIs(chrome::kChromeDevToolsScheme) ||
700 url.SchemeIs(chrome::kChromeUIScheme) ||
701 url.SchemeIs(chrome::kViewSourceScheme) ||
702 url.SchemeIs(chrome::kChromeInternalScheme))
703 return false;
704
705 if (url.SchemeIs(chrome::kAboutScheme)) {
706 if (LowerCaseEqualsASCII(url.path(), "blank"))
707 return false;
708 // We allow all other about URLs since the user may like to see things
709 // like "about:memory" or "about:histograms" in their history and
710 // autocomplete.
711 }
712
713 return true;
714 }
715
SetInMemoryBackend(history::InMemoryHistoryBackend * mem_backend)716 void HistoryService::SetInMemoryBackend(
717 history::InMemoryHistoryBackend* mem_backend) {
718 DCHECK(!in_memory_backend_.get()) << "Setting mem DB twice";
719 in_memory_backend_.reset(mem_backend);
720
721 // The database requires additional initialization once we own it.
722 in_memory_backend_->AttachToHistoryService(profile_);
723 }
724
NotifyProfileError(sql::InitStatus init_status)725 void HistoryService::NotifyProfileError(sql::InitStatus init_status) {
726 ShowProfileErrorDialog(
727 (init_status == sql::INIT_FAILURE) ?
728 IDS_COULDNT_OPEN_PROFILE_ERROR : IDS_PROFILE_TOO_NEW_ERROR);
729 }
730
DeleteURL(const GURL & url)731 void HistoryService::DeleteURL(const GURL& url) {
732 // We will update the visited links when we observe the delete notifications.
733 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::DeleteURL, url);
734 }
735
ExpireHistoryBetween(const std::set<GURL> & restrict_urls,Time begin_time,Time end_time,CancelableRequestConsumerBase * consumer,ExpireHistoryCallback * callback)736 void HistoryService::ExpireHistoryBetween(
737 const std::set<GURL>& restrict_urls,
738 Time begin_time, Time end_time,
739 CancelableRequestConsumerBase* consumer,
740 ExpireHistoryCallback* callback) {
741
742 // We will update the visited links when we observe the delete notifications.
743 Schedule(PRIORITY_UI, &HistoryBackend::ExpireHistoryBetween, consumer,
744 new history::ExpireHistoryRequest(callback),
745 restrict_urls, begin_time, end_time);
746 }
747
BroadcastNotifications(NotificationType type,history::HistoryDetails * details_deleted)748 void HistoryService::BroadcastNotifications(
749 NotificationType type,
750 history::HistoryDetails* details_deleted) {
751 // We take ownership of the passed-in pointer and delete it. It was made for
752 // us on another thread, so the caller doesn't know when we will handle it.
753 scoped_ptr<history::HistoryDetails> details(details_deleted);
754 // TODO(evanm): this is currently necessitated by generate_profile, which
755 // runs without a browser process. generate_profile should really create
756 // a browser process, at which point this check can then be nuked.
757 if (!g_browser_process)
758 return;
759
760 if (!thread_)
761 return;
762
763 // The source of all of our notifications is the profile. Note that this
764 // pointer is NULL in unit tests.
765 Source<Profile> source(profile_);
766
767 // The details object just contains the pointer to the object that the
768 // backend has allocated for us. The receiver of the notification will cast
769 // this to the proper type.
770 Details<history::HistoryDetails> det(details_deleted);
771
772 NotificationService::current()->Notify(type, source, det);
773 }
774
LoadBackendIfNecessary()775 void HistoryService::LoadBackendIfNecessary() {
776 if (!thread_ || history_backend_)
777 return; // Failed to init, or already started loading.
778
779 scoped_refptr<HistoryBackend> backend(
780 new HistoryBackend(history_dir_,
781 new BackendDelegate(this),
782 bookmark_service_));
783 history_backend_.swap(backend);
784
785 // There may not be a profile when unit testing.
786 std::string languages;
787 if (profile_) {
788 PrefService* prefs = profile_->GetPrefs();
789 languages = prefs->GetString(prefs::kAcceptLanguages);
790 }
791 ScheduleAndForget(PRIORITY_UI, &HistoryBackend::Init, languages, no_db_);
792 }
793
OnDBLoaded()794 void HistoryService::OnDBLoaded() {
795 backend_loaded_ = true;
796 NotificationService::current()->Notify(NotificationType::HISTORY_LOADED,
797 Source<Profile>(profile_),
798 Details<HistoryService>(this));
799 if (thread_ && profile_) {
800 // We don't want to force creation of TopSites.
801 history::TopSites* ts = profile_->GetTopSitesWithoutCreating();
802 if (ts)
803 ts->HistoryLoaded();
804 }
805 }
806
StartTopSitesMigration()807 void HistoryService::StartTopSitesMigration() {
808 needs_top_sites_migration_ = true;
809 if (thread_ && profile_) {
810 // We don't want to force creation of TopSites.
811 history::TopSites* ts = profile_->GetTopSitesWithoutCreating();
812 if (ts)
813 ts->MigrateFromHistory();
814 }
815 }
816
OnTopSitesReady()817 void HistoryService::OnTopSitesReady() {
818 ScheduleAndForget(PRIORITY_NORMAL,
819 &HistoryBackend::MigrateThumbnailsDatabase);
820 }
821