| OLD | NEW |
| 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // The history system runs on a background thread so that potentially slow | 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 | 6 // database operations don't delay the browser. This backend processing is |
| 7 // represented by HistoryBackend. The HistoryService's job is to dispatch to | 7 // represented by HistoryBackend. The HistoryService's job is to dispatch to |
| 8 // that thread. | 8 // that thread. |
| 9 // | 9 // |
| 10 // Main thread History thread | 10 // Main thread History thread |
| 11 // ----------- -------------- | 11 // ----------- -------------- |
| 12 // HistoryService <----------------> HistoryBackend | 12 // HistoryService <----------------> HistoryBackend |
| 13 // -> HistoryDatabase | 13 // -> HistoryDatabase |
| 14 // -> SQLite connection to History | 14 // -> SQLite connection to History |
| 15 // -> ArchivedDatabase | 15 // -> ArchivedDatabase |
| 16 // -> SQLite connection to Archived History | 16 // -> SQLite connection to Archived History |
| 17 // -> TextDatabaseManager | 17 // -> TextDatabaseManager |
| 18 // -> SQLite connection to one month's data | 18 // -> SQLite connection to one month's data |
| 19 // -> SQLite connection to one month's data | 19 // -> SQLite connection to one month's data |
| 20 // ... | 20 // ... |
| 21 // -> ThumbnailDatabase | 21 // -> ThumbnailDatabase |
| 22 // -> SQLite connection to Thumbnails | 22 // -> SQLite connection to Thumbnails |
| 23 // (and favicons) | 23 // (and favicons) |
| 24 | 24 |
| 25 #include "chrome/browser/history/history.h" | 25 #include "chrome/browser/history/history.h" |
| 26 | 26 |
| 27 #include "app/l10n_util.h" | 27 #include "app/l10n_util.h" |
| 28 #include "base/file_util.h" | |
| 29 #include "base/message_loop.h" | 28 #include "base/message_loop.h" |
| 30 #include "base/path_service.h" | 29 #include "base/path_service.h" |
| 31 #include "base/ref_counted.h" | 30 #include "base/ref_counted.h" |
| 32 #include "base/task.h" | 31 #include "base/task.h" |
| 33 #include "chrome/browser/autocomplete/history_url_provider.h" | 32 #include "chrome/browser/autocomplete/history_url_provider.h" |
| 34 #include "chrome/browser/browser_list.h" | 33 #include "chrome/browser/browser_list.h" |
| 35 #include "chrome/browser/browser_process.h" | 34 #include "chrome/browser/browser_process.h" |
| 36 #include "chrome/browser/browser_window.h" | 35 #include "chrome/browser/browser_window.h" |
| 37 #include "chrome/browser/chrome_thread.h" | 36 #include "chrome/browser/chrome_thread.h" |
| 38 #include "chrome/browser/history/download_types.h" | 37 #include "chrome/browser/history/download_types.h" |
| (...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 147 Source<Profile>(profile_)); | 146 Source<Profile>(profile_)); |
| 148 } | 147 } |
| 149 | 148 |
| 150 HistoryService::~HistoryService() { | 149 HistoryService::~HistoryService() { |
| 151 // Shutdown the backend. This does nothing if Cleanup was already invoked. | 150 // Shutdown the backend. This does nothing if Cleanup was already invoked. |
| 152 Cleanup(); | 151 Cleanup(); |
| 153 } | 152 } |
| 154 | 153 |
| 155 bool HistoryService::Init(const FilePath& history_dir, | 154 bool HistoryService::Init(const FilePath& history_dir, |
| 156 BookmarkService* bookmark_service) { | 155 BookmarkService* bookmark_service) { |
| 157 if (!thread_->Start()) | 156 if (!thread_->Start()) { |
| 157 Cleanup(); |
| 158 return false; | 158 return false; |
| 159 } |
| 160 |
| 161 history_dir_ = history_dir; |
| 162 bookmark_service_ = bookmark_service; |
| 159 | 163 |
| 160 // Create the history backend. | 164 // Create the history backend. |
| 161 scoped_refptr<HistoryBackend> backend( | 165 LoadBackendIfNecessary(); |
| 162 new HistoryBackend(history_dir, | |
| 163 new BackendDelegate(this), | |
| 164 bookmark_service)); | |
| 165 history_backend_.swap(backend); | |
| 166 | |
| 167 ScheduleAndForget(PRIORITY_UI, &HistoryBackend::Init); | |
| 168 return true; | 166 return true; |
| 169 } | 167 } |
| 170 | 168 |
| 171 void HistoryService::Cleanup() { | 169 bool HistoryService::BackendLoaded() { |
| 172 if (!thread_) { | 170 LoadBackendIfNecessary(); |
| 173 // We've already cleaned up. | 171 return backend_loaded_; |
| 174 return; | 172 } |
| 175 } | |
| 176 | 173 |
| 177 // Shutdown is a little subtle. The backend's destructor must run on the | 174 void HistoryService::UnloadBackend() { |
| 178 // history thread since it is not threadsafe. So this thread must not be the | 175 if (!history_backend_) |
| 179 // last thread holding a reference to the backend, or a crash could happen. | 176 return; // Already unloaded. |
| 177 |
| 178 // Get rid of the in-memory backend. |
| 179 in_memory_backend_.reset(); |
| 180 |
| 181 // The backend's destructor must run on the history thread since it is not |
| 182 // threadsafe. So this thread must not be the last thread holding a reference |
| 183 // to the backend, or a crash could happen. |
| 180 // | 184 // |
| 181 // We have a reference to the history backend. There is also an extra | 185 // We have a reference to the history backend. There is also an extra |
| 182 // reference held by our delegate installed in the backend, which | 186 // reference held by our delegate installed in the backend, which |
| 183 // HistoryBackend::Closing will release. This means if we scheduled a call | 187 // HistoryBackend::Closing will release. This means if we scheduled a call |
| 184 // to HistoryBackend::Closing and *then* released our backend reference, there | 188 // to HistoryBackend::Closing and *then* released our backend reference, there |
| 185 // will be a race between us and the backend's Closing function to see who is | 189 // will be a race between us and the backend's Closing function to see who is |
| 186 // the last holder of a reference. If the backend thread's Closing manages to | 190 // the last holder of a reference. If the backend thread's Closing manages to |
| 187 // run before we release our backend refptr, the last reference will be held | 191 // run before we release our backend refptr, the last reference will be held |
| 188 // by this thread and the destructor will be called from here. | 192 // by this thread and the destructor will be called from here. |
| 189 // | 193 // |
| 190 // Therefore, we create a task to run the Closing operation first. This holds | 194 // Therefore, we create a task to run the Closing operation first. This holds |
| 191 // a reference to the backend. Then we release our reference, then we schedule | 195 // a reference to the backend. Then we release our reference, then we schedule |
| 192 // the task to run. After the task runs, it will delete its reference from | 196 // the task to run. After the task runs, it will delete its reference from |
| 193 // the history thread, ensuring everything works properly. | 197 // the history thread, ensuring everything works properly. |
| 194 Task* closing_task = | 198 Task* closing_task = |
| 195 NewRunnableMethod(history_backend_.get(), &HistoryBackend::Closing); | 199 NewRunnableMethod(history_backend_.get(), &HistoryBackend::Closing); |
| 196 history_backend_ = NULL; | 200 history_backend_ = NULL; |
| 197 ScheduleTask(PRIORITY_NORMAL, closing_task); | 201 ScheduleTask(PRIORITY_NORMAL, closing_task); |
| 202 } |
| 203 |
| 204 void HistoryService::Cleanup() { |
| 205 if (!thread_) { |
| 206 // We've already cleaned up. |
| 207 return; |
| 208 } |
| 209 |
| 210 // Unload the backend. |
| 211 UnloadBackend(); |
| 198 | 212 |
| 199 // Delete the thread, which joins with the background thread. We defensively | 213 // Delete the thread, which joins with the background thread. We defensively |
| 200 // NULL the pointer before deleting it in case somebody tries to use it | 214 // NULL the pointer before deleting it in case somebody tries to use it |
| 201 // during shutdown, but this shouldn't happen. | 215 // during shutdown, but this shouldn't happen. |
| 202 base::Thread* thread = thread_; | 216 base::Thread* thread = thread_; |
| 203 thread_ = NULL; | 217 thread_ = NULL; |
| 204 delete thread; | 218 delete thread; |
| 205 } | 219 } |
| 206 | 220 |
| 207 void HistoryService::NotifyRenderProcessHostDestruction(const void* host) { | 221 void HistoryService::NotifyRenderProcessHostDestruction(const void* host) { |
| 208 ScheduleAndForget(PRIORITY_NORMAL, | 222 ScheduleAndForget(PRIORITY_NORMAL, |
| 209 &HistoryBackend::NotifyRenderProcessHostDestruction, host); | 223 &HistoryBackend::NotifyRenderProcessHostDestruction, host); |
| 210 } | 224 } |
| 211 | 225 |
| 212 history::URLDatabase* HistoryService::in_memory_database() const { | 226 history::URLDatabase* HistoryService::InMemoryDatabase() { |
| 227 LoadBackendIfNecessary(); |
| 213 if (in_memory_backend_.get()) | 228 if (in_memory_backend_.get()) |
| 214 return in_memory_backend_->db(); | 229 return in_memory_backend_->db(); |
| 215 return NULL; | 230 return NULL; |
| 216 } | 231 } |
| 217 | 232 |
| 218 void HistoryService::SetSegmentPresentationIndex(int64 segment_id, int index) { | 233 void HistoryService::SetSegmentPresentationIndex(int64 segment_id, int index) { |
| 219 ScheduleAndForget(PRIORITY_UI, | 234 ScheduleAndForget(PRIORITY_UI, |
| 220 &HistoryBackend::SetSegmentPresentationIndex, | 235 &HistoryBackend::SetSegmentPresentationIndex, |
| 221 segment_id, index); | 236 segment_id, index); |
| 222 } | 237 } |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 290 } | 305 } |
| 291 | 306 |
| 292 void HistoryService::AddPage(const GURL& url, | 307 void HistoryService::AddPage(const GURL& url, |
| 293 Time time, | 308 Time time, |
| 294 const void* id_scope, | 309 const void* id_scope, |
| 295 int32 page_id, | 310 int32 page_id, |
| 296 const GURL& referrer, | 311 const GURL& referrer, |
| 297 PageTransition::Type transition, | 312 PageTransition::Type transition, |
| 298 const history::RedirectList& redirects, | 313 const history::RedirectList& redirects, |
| 299 bool did_replace_entry) { | 314 bool did_replace_entry) { |
| 300 DCHECK(history_backend_) << "History service being called after cleanup"; | 315 DCHECK(thread_) << "History service being called after cleanup"; |
| 301 | 316 |
| 302 // Filter out unwanted URLs. We don't add auto-subframe URLs. They are a | 317 // Filter out unwanted URLs. We don't add auto-subframe URLs. They are a |
| 303 // large part of history (think iframes for ads) and we never display them in | 318 // large part of history (think iframes for ads) and we never display them in |
| 304 // history UI. We will still add manual subframes, which are ones the user | 319 // history UI. We will still add manual subframes, which are ones the user |
| 305 // has clicked on to get. | 320 // has clicked on to get. |
| 306 if (!CanAddURL(url)) | 321 if (!CanAddURL(url)) |
| 307 return; | 322 return; |
| 308 | 323 |
| 309 // Add link & all redirects to visited link list. | 324 // Add link & all redirects to visited link list. |
| 310 VisitedLinkMaster* visited_links; | 325 VisitedLinkMaster* visited_links; |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 380 } | 395 } |
| 381 | 396 |
| 382 ScheduleAndForget(PRIORITY_NORMAL, | 397 ScheduleAndForget(PRIORITY_NORMAL, |
| 383 &HistoryBackend::AddPagesWithDetails, info); | 398 &HistoryBackend::AddPagesWithDetails, info); |
| 384 } | 399 } |
| 385 | 400 |
| 386 void HistoryService::SetPageContents(const GURL& url, | 401 void HistoryService::SetPageContents(const GURL& url, |
| 387 const std::wstring& contents) { | 402 const std::wstring& contents) { |
| 388 if (!CanAddURL(url)) | 403 if (!CanAddURL(url)) |
| 389 return; | 404 return; |
| 405 |
| 390 ScheduleAndForget(PRIORITY_LOW, &HistoryBackend::SetPageContents, | 406 ScheduleAndForget(PRIORITY_LOW, &HistoryBackend::SetPageContents, |
| 391 url, contents); | 407 url, contents); |
| 392 } | 408 } |
| 393 | 409 |
| 394 void HistoryService::SetPageThumbnail(const GURL& page_url, | 410 void HistoryService::SetPageThumbnail(const GURL& page_url, |
| 395 const SkBitmap& thumbnail, | 411 const SkBitmap& thumbnail, |
| 396 const ThumbnailScore& score) { | 412 const ThumbnailScore& score) { |
| 397 if (!CanAddURL(page_url)) | 413 if (!CanAddURL(page_url)) |
| 398 return; | 414 return; |
| 399 | 415 |
| 400 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::SetPageThumbnail, | 416 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::SetPageThumbnail, |
| 401 page_url, thumbnail, score); | 417 page_url, thumbnail, score); |
| 402 } | 418 } |
| 403 | 419 |
| 404 HistoryService::Handle HistoryService::GetPageThumbnail( | 420 HistoryService::Handle HistoryService::GetPageThumbnail( |
| 405 const GURL& page_url, | 421 const GURL& page_url, |
| 406 CancelableRequestConsumerBase* consumer, | 422 CancelableRequestConsumerBase* consumer, |
| 407 ThumbnailDataCallback* callback) { | 423 ThumbnailDataCallback* callback) { |
| 408 return Schedule(PRIORITY_NORMAL, &HistoryBackend::GetPageThumbnail, consumer, | 424 return Schedule(PRIORITY_NORMAL, &HistoryBackend::GetPageThumbnail, consumer, |
| 409 new history::GetPageThumbnailRequest(callback), page_url); | 425 new history::GetPageThumbnailRequest(callback), page_url); |
| 410 } | 426 } |
| 411 | 427 |
| 412 void HistoryService::GetFavicon(FaviconService::GetFaviconRequest* request, | 428 void HistoryService::GetFavicon(FaviconService::GetFaviconRequest* request, |
| 413 const GURL& icon_url) { | 429 const GURL& icon_url) { |
| 414 ScheduleTask(PRIORITY_NORMAL, | 430 Schedule(PRIORITY_NORMAL, &HistoryBackend::GetFavIcon, NULL, request, |
| 415 NewRunnableMethod(history_backend_.get(), | 431 icon_url); |
| 416 &HistoryBackend::GetFavIcon, | |
| 417 scoped_refptr<FaviconService::GetFaviconRequest>(request), | |
| 418 icon_url)); | |
| 419 } | 432 } |
| 420 | 433 |
| 421 void HistoryService::UpdateFaviconMappingAndFetch( | 434 void HistoryService::UpdateFaviconMappingAndFetch( |
| 422 FaviconService::GetFaviconRequest* request, | 435 FaviconService::GetFaviconRequest* request, |
| 423 const GURL& page_url, | 436 const GURL& page_url, |
| 424 const GURL& icon_url) { | 437 const GURL& icon_url) { |
| 425 ScheduleTask(PRIORITY_NORMAL, | 438 Schedule(PRIORITY_NORMAL, &HistoryBackend::UpdateFavIconMappingAndFetch, NULL, |
| 426 NewRunnableMethod(history_backend_.get(), | 439 request, page_url, icon_url); |
| 427 &HistoryBackend::UpdateFavIconMappingAndFetch, | |
| 428 scoped_refptr<FaviconService::GetFaviconRequest>(request), | |
| 429 page_url, | |
| 430 icon_url)); | |
| 431 } | 440 } |
| 432 | 441 |
| 433 void HistoryService::GetFaviconForURL( | 442 void HistoryService::GetFaviconForURL( |
| 434 FaviconService::GetFaviconRequest* request, | 443 FaviconService::GetFaviconRequest* request, |
| 435 const GURL& page_url) { | 444 const GURL& page_url) { |
| 436 ScheduleTask(PRIORITY_UI, | 445 Schedule(PRIORITY_NORMAL, &HistoryBackend::GetFavIconForURL, NULL, request, |
| 437 NewRunnableMethod(history_backend_.get(), | 446 page_url); |
| 438 &HistoryBackend::GetFavIconForURL, | |
| 439 scoped_refptr<FaviconService::GetFaviconRequest>(request), | |
| 440 page_url)); | |
| 441 } | 447 } |
| 442 | 448 |
| 443 void HistoryService::SetFavicon(const GURL& page_url, | 449 void HistoryService::SetFavicon(const GURL& page_url, |
| 444 const GURL& icon_url, | 450 const GURL& icon_url, |
| 445 const std::vector<unsigned char>& image_data) { | 451 const std::vector<unsigned char>& image_data) { |
| 446 if (!CanAddURL(page_url)) | 452 if (!CanAddURL(page_url)) |
| 447 return; | 453 return; |
| 448 | 454 |
| 449 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::SetFavIcon, | 455 ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::SetFavIcon, |
| 450 page_url, icon_url, | 456 page_url, icon_url, |
| (...skipping 238 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 689 Source<Profile> source(profile_); | 695 Source<Profile> source(profile_); |
| 690 | 696 |
| 691 // The details object just contains the pointer to the object that the | 697 // The details object just contains the pointer to the object that the |
| 692 // backend has allocated for us. The receiver of the notification will cast | 698 // backend has allocated for us. The receiver of the notification will cast |
| 693 // this to the proper type. | 699 // this to the proper type. |
| 694 Details<history::HistoryDetails> det(details_deleted); | 700 Details<history::HistoryDetails> det(details_deleted); |
| 695 | 701 |
| 696 NotificationService::current()->Notify(type, source, det); | 702 NotificationService::current()->Notify(type, source, det); |
| 697 } | 703 } |
| 698 | 704 |
| 705 void HistoryService::LoadBackendIfNecessary() { |
| 706 if (!thread_ || history_backend_) |
| 707 return; // Failed to init, or already started loading. |
| 708 |
| 709 scoped_refptr<HistoryBackend> backend( |
| 710 new HistoryBackend(history_dir_, |
| 711 new BackendDelegate(this), |
| 712 bookmark_service_)); |
| 713 history_backend_.swap(backend); |
| 714 |
| 715 ScheduleAndForget(PRIORITY_UI, &HistoryBackend::Init); |
| 716 } |
| 717 |
| 699 void HistoryService::OnDBLoaded() { | 718 void HistoryService::OnDBLoaded() { |
| 700 LOG(INFO) << "History backend finished loading"; | 719 LOG(INFO) << "History backend finished loading"; |
| 701 backend_loaded_ = true; | 720 backend_loaded_ = true; |
| 702 NotificationService::current()->Notify(NotificationType::HISTORY_LOADED, | 721 NotificationService::current()->Notify(NotificationType::HISTORY_LOADED, |
| 703 Source<Profile>(profile_), | 722 Source<Profile>(profile_), |
| 704 Details<HistoryService>(this)); | 723 Details<HistoryService>(this)); |
| 705 } | 724 } |
| OLD | NEW |