history_backend.cc revision 58537e28ecd584eab876aee8be7156509866d23a
15f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// Copyright (c) 2012 The Chromium Authors. All rights reserved. 25f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// Use of this source code is governed by a BSD-style license that can be 35f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// found in the LICENSE file. 45f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 50bc735ffcfb223c0186419547abaa5c84482663eChris Lattner#include "chrome/browser/history/history_backend.h" 60bc735ffcfb223c0186419547abaa5c84482663eChris Lattner 75f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include <algorithm> 85f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include <functional> 95f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include <list> 105f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include <map> 115f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include <set> 125f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include <vector> 135f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 145f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include "base/basictypes.h" 155f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include "base/bind.h" 165f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include "base/compiler_specific.h" 175f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include "base/files/file_enumerator.h" 189c1b750c59d510e6c9eccb1f37bccc46ccfe6844Ted Kremenek#include "base/memory/scoped_ptr.h" 19eb50ed88c2aa040fac08bf2a50bde4dd3da6eb19Chris Lattner#include "base/memory/scoped_vector.h" 205d75de0f821023f4ed4815825bf3aea8a0b5e40dChris Lattner#include "base/message_loop/message_loop.h" 21c7229c338c21ef26b01ef3ecf9eec4fd373fa9ecChris Lattner#include "base/metrics/histogram.h" 225f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include "base/rand_util.h" 23cc1a875f94630e58d24a55577ffbf0e89b7da8c7Chris Lattner#include "base/strings/string_util.h" 24caaa7df2c78bbd40197823034c0275f3dcbd63e7Ted Kremenek#include "base/strings/utf_string_conversions.h" 255f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include "base/time/time.h" 265f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include "chrome/browser/autocomplete/history_url_provider.h" 275f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include "chrome/browser/bookmarks/bookmark_service.h" 285f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include "chrome/browser/chrome_notification_types.h" 295f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include "chrome/browser/favicon/favicon_changed_details.h" 305f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include "chrome/browser/history/download_row.h" 315f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include "chrome/browser/history/history_db_task.h" 325f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include "chrome/browser/history/history_notifications.h" 335f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include "chrome/browser/history/history_publisher.h" 345f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include "chrome/browser/history/in_memory_history_backend.h" 355f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include "chrome/browser/history/page_usage_data.h" 365f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include "chrome/browser/history/select_favicon_frames.h" 375f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include "chrome/browser/history/top_sites.h" 385f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include "chrome/browser/history/typed_url_syncable_service.h" 39e5956bd2730c051835f9acd9e957c5d79f99e7c3Chris Lattner#include "chrome/browser/history/visit_filter.h" 405f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include "chrome/common/chrome_constants.h" 415f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include "chrome/common/importer/imported_favicon_usage.h" 425f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include "chrome/common/url_constants.h" 435f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include "grit/chromium_strings.h" 445f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include "grit/generated_resources.h" 455f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include "net/base/registry_controlled_domains/registry_controlled_domain.h" 465f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include "sql/error_delegate_util.h" 475f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include "url/gurl.h" 485f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 495f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#if defined(OS_ANDROID) 505f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include "chrome/browser/history/android/android_provider_backend.h" 515f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#endif 525f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 535f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerusing base::Time; 545f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerusing base::TimeDelta; 555f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerusing base::TimeTicks; 565f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 575f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer/* The HistoryBackend consists of a number of components: 585f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 595f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer HistoryDatabase (stores past 3 months of history) 605f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer URLDatabase (stores a list of URLs) 615f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer DownloadDatabase (stores a list of downloads) 625f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer VisitDatabase (stores a list of visits for the URLs) 635f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer VisitSegmentDatabase (stores groups of URLs for the most visited view). 645f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 655f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer ArchivedDatabase (stores history older than 3 months) 665f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer URLDatabase (stores a list of URLs) 675f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer DownloadDatabase (stores a list of downloads) 685f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer VisitDatabase (stores a list of visits for the URLs) 695f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 705f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer (this does not store visit segments as they expire after 3 mos.) 715f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 725f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer ExpireHistoryBackend (manages moving things from HistoryDatabase to 735f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer the ArchivedDatabase and deleting) 745f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer*/ 755f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 7603db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidisnamespace history { 7703db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis 7803db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis// How long we keep segment data for in days. Currently 3 months. 795f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// This value needs to be greater or equal to 805f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// MostVisitedModel::kMostVisitedScope but we don't want to introduce a direct 815f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// dependency between MostVisitedModel and the history backend. 825f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerstatic const int kSegmentDataRetention = 90; 8368d331a78e655d97294e94fcfa63f92cc1f40578Steve Naroff 8468d331a78e655d97294e94fcfa63f92cc1f40578Steve Naroff// How long we'll wait to do a commit, so that things are batched together. 8568d331a78e655d97294e94fcfa63f92cc1f40578Steve Naroffstatic const int kCommitIntervalSeconds = 10; 8668d331a78e655d97294e94fcfa63f92cc1f40578Steve Naroff 8768d331a78e655d97294e94fcfa63f92cc1f40578Steve Naroff// The amount of time before we re-fetch the favicon. 8868d331a78e655d97294e94fcfa63f92cc1f40578Steve Naroffstatic const int kFaviconRefetchDays = 7; 8968d331a78e655d97294e94fcfa63f92cc1f40578Steve Naroff 9029238a0bf7cbf5b396efb451a0adb5fe4aa037caSteve Naroff// GetSessionTabs returns all open tabs, or tabs closed kSessionCloseTimeWindow 912e1cd4264d363ca869bf37ef160902f211d21b8cDouglas Gregor// seconds ago. 925f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerstatic const int kSessionCloseTimeWindowSecs = 10; 935f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 945f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// The maximum number of items we'll allow in the redirect list before 955f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// deleting some. 965f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerstatic const int kMaxRedirectCount = 32; 979c1b750c59d510e6c9eccb1f37bccc46ccfe6844Ted Kremenek 989c1b750c59d510e6c9eccb1f37bccc46ccfe6844Ted Kremenek// The number of days old a history entry can be before it is considered "old" 99caaa7df2c78bbd40197823034c0275f3dcbd63e7Ted Kremenek// and is archived. 1005f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerstatic const int kArchiveDaysThreshold = 90; 1019c1b750c59d510e6c9eccb1f37bccc46ccfe6844Ted Kremenek 1029c1b750c59d510e6c9eccb1f37bccc46ccfe6844Ted Kremenek#if defined(OS_ANDROID) 1039c1b750c59d510e6c9eccb1f37bccc46ccfe6844Ted Kremenek// The maximum number of top sites to track when recording top page visit stats. 1049c1b750c59d510e6c9eccb1f37bccc46ccfe6844Ted Kremenekstatic const size_t kPageVisitStatsMaxTopSites = 50; 1059c1b750c59d510e6c9eccb1f37bccc46ccfe6844Ted Kremenek#endif 1069c1b750c59d510e6c9eccb1f37bccc46ccfe6844Ted Kremenek 1079c1b750c59d510e6c9eccb1f37bccc46ccfe6844Ted Kremenek// Converts from PageUsageData to MostVisitedURL. |redirects| is a 1089c1b750c59d510e6c9eccb1f37bccc46ccfe6844Ted Kremenek// list of redirects for this URL. Empty list means no redirects. 1099c1b750c59d510e6c9eccb1f37bccc46ccfe6844Ted KremenekMostVisitedURL MakeMostVisitedURL(const PageUsageData& page_data, 1109c1b750c59d510e6c9eccb1f37bccc46ccfe6844Ted Kremenek const RedirectList& redirects) { 1115f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer MostVisitedURL mv; 1125f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mv.url = page_data.GetURL(); 1135f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mv.title = page_data.GetTitle(); 1145f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (redirects.empty()) { 1155f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // Redirects must contain at least the target url. 1166cfe7594a46b5d270142cfcb688a9c1a3a487a48Chris Lattner mv.redirects.push_back(mv.url); 1176cfe7594a46b5d270142cfcb688a9c1a3a487a48Chris Lattner } else { 118caaa7df2c78bbd40197823034c0275f3dcbd63e7Ted Kremenek mv.redirects = redirects; 1195f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (mv.redirects[mv.redirects.size() - 1] != mv.url) { 1205f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // The last url must be the target url. 1215f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mv.redirects.push_back(mv.url); 1226cfe7594a46b5d270142cfcb688a9c1a3a487a48Chris Lattner } 1235f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 1249c1b750c59d510e6c9eccb1f37bccc46ccfe6844Ted Kremenek return mv; 1259c1b750c59d510e6c9eccb1f37bccc46ccfe6844Ted Kremenek} 1269c1b750c59d510e6c9eccb1f37bccc46ccfe6844Ted Kremenek 1279c1b750c59d510e6c9eccb1f37bccc46ccfe6844Ted Kremenek// This task is run on a timer so that commits happen at regular intervals 1285f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// so they are batched together. The important thing about this class is that 1299c1b750c59d510e6c9eccb1f37bccc46ccfe6844Ted Kremenek// it supports canceling of the task so the reference to the backend will be 1309c1b750c59d510e6c9eccb1f37bccc46ccfe6844Ted Kremenek// freed. The problem is that when history is shutting down, there is likely 1319c1b750c59d510e6c9eccb1f37bccc46ccfe6844Ted Kremenek// to be one of these commits still pending and holding a reference. 1329c1b750c59d510e6c9eccb1f37bccc46ccfe6844Ted Kremenek// 1339c1b750c59d510e6c9eccb1f37bccc46ccfe6844Ted Kremenek// The backend can call Cancel to have this task release the reference. The 1345f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// task will still run (if we ever get to processing the event before 1355f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// shutdown), but it will not do anything. 1365f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// 1375f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// Note that this is a refcounted object and is not a task in itself. It should 1385f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// be assigned to a RunnableMethod. 1395f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// 1405f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// TODO(brettw): bug 1165182: This should be replaced with a 141cc1a875f94630e58d24a55577ffbf0e89b7da8c7Chris Lattner// base::WeakPtrFactory which will handle everything automatically (like we do 142cc1a875f94630e58d24a55577ffbf0e89b7da8c7Chris Lattner// in ExpireHistoryBackend). 143cc1a875f94630e58d24a55577ffbf0e89b7da8c7Chris Lattnerclass CommitLaterTask : public base::RefCounted<CommitLaterTask> { 144cc1a875f94630e58d24a55577ffbf0e89b7da8c7Chris Lattner public: 1455f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer explicit CommitLaterTask(HistoryBackend* history_backend) 1465f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer : history_backend_(history_backend) { 1475f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 1485f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 1495f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // The backend will call this function if it is being destroyed so that we 1505f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // release our reference. 1515f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer void Cancel() { 1529594acf32de2939b15eafa8fe818607bfc56bf66Chris Lattner history_backend_ = NULL; 153aa39197431a0a0b1326ecf6b3be6a11f6e2f8503Chris Lattner } 154aa39197431a0a0b1326ecf6b3be6a11f6e2f8503Chris Lattner 155aa39197431a0a0b1326ecf6b3be6a11f6e2f8503Chris Lattner void RunCommit() { 15653b0dabbe52219a8057659b90539837394ef0fa1Chris Lattner if (history_backend_.get()) 1576cfe7594a46b5d270142cfcb688a9c1a3a487a48Chris Lattner history_backend_->Commit(); 1586cfe7594a46b5d270142cfcb688a9c1a3a487a48Chris Lattner } 1596cfe7594a46b5d270142cfcb688a9c1a3a487a48Chris Lattner 1606cfe7594a46b5d270142cfcb688a9c1a3a487a48Chris Lattner private: 1619e0ed0bd5a3a7bac73973980ff32132a7724e674Argyrios Kyrtzidis friend class base::RefCounted<CommitLaterTask>; 162e671e1bc73615eda155059a772266ed2882d758cChris Lattner 16303db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis ~CommitLaterTask() {} 16403db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis 16503db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis scoped_refptr<HistoryBackend> history_backend_; 16603db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis}; 16703db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis 16803db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis// HistoryBackend -------------------------------------------------------------- 16903db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis 17003db1b31dd926409b7defc1c90b66549464652c0Argyrios KyrtzidisHistoryBackend::HistoryBackend(const base::FilePath& history_dir, 17103db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis int id, 17203db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis Delegate* delegate, 17303db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis BookmarkService* bookmark_service) 174a9e274c01ebae45629d93aaa07be450fb77dd3cbArgyrios Kyrtzidis : delegate_(delegate), 175a9e274c01ebae45629d93aaa07be450fb77dd3cbArgyrios Kyrtzidis id_(id), 176a9e274c01ebae45629d93aaa07be450fb77dd3cbArgyrios Kyrtzidis history_dir_(history_dir), 177a9e274c01ebae45629d93aaa07be450fb77dd3cbArgyrios Kyrtzidis scheduled_kill_db_(false), 178a9e274c01ebae45629d93aaa07be450fb77dd3cbArgyrios Kyrtzidis expirer_(this, bookmark_service), 17903db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis recent_redirects_(kMaxRedirectCount), 1805f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer backend_destroy_message_loop_(NULL), 1815f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer segment_queried_(false), 1825f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer bookmark_service_(bookmark_service) { 1835f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer} 1845f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 1855f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid SpencerHistoryBackend::~HistoryBackend() { 1865f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer DCHECK(!scheduled_commit_.get()) << "Deleting without cleanup"; 1875f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer ReleaseDBTasks(); 1885f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 1895f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#if defined(OS_ANDROID) 1905f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // Release AndroidProviderBackend before other objects. 1915f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer android_provider_backend_.reset(); 1925f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#endif 19329238a0bf7cbf5b396efb451a0adb5fe4aa037caSteve Naroff 1942e1cd4264d363ca869bf37ef160902f211d21b8cDouglas Gregor // First close the databases before optionally running the "destroy" task. 195e0579f08d4a4ae57971b74ca1863106df4c6399fTed Kremenek CloseAllDatabases(); 19694b3cdb57fd5d245963e597626e0dfd88d479795Chris Lattner 197ea8646993741739d8a04d67396fe466dcc3a104fTed Kremenek if (!backend_destroy_task_.is_null()) { 198ea8646993741739d8a04d67396fe466dcc3a104fTed Kremenek // Notify an interested party (typically a unit test) that we're done. 1995f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer DCHECK(backend_destroy_message_loop_); 2005f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer backend_destroy_message_loop_->PostTask(FROM_HERE, backend_destroy_task_); 2015f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 2025f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 2035f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#if defined(OS_ANDROID) 2045f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer sql::Connection::Delete(GetAndroidCacheFileName()); 2055f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#endif 2065f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer} 2075f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 2085f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencervoid HistoryBackend::Init(const std::string& languages, bool force_fail) { 2095f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (!force_fail) 2105f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer InitImpl(languages); 211caaa7df2c78bbd40197823034c0275f3dcbd63e7Ted Kremenek delegate_->DBLoaded(id_); 2125f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer typed_url_syncable_service_.reset(new TypedUrlSyncableService(this)); 2135f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer memory_pressure_listener_.reset(new base::MemoryPressureListener( 2145f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer base::Bind(&HistoryBackend::OnMemoryPressure, base::Unretained(this)))); 2155f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#if defined(OS_ANDROID) 2165f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer PopulateMostVisitedURLMap(); 2175f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#endif 2185f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer} 219eb50ed88c2aa040fac08bf2a50bde4dd3da6eb19Chris Lattner 220eb50ed88c2aa040fac08bf2a50bde4dd3da6eb19Chris Lattnervoid HistoryBackend::SetOnBackendDestroyTask(base::MessageLoop* message_loop, 221eb50ed88c2aa040fac08bf2a50bde4dd3da6eb19Chris Lattner const base::Closure& task) { 2225f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (!backend_destroy_task_.is_null()) 2235f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer DLOG(WARNING) << "Setting more than one destroy task, overriding"; 224eb50ed88c2aa040fac08bf2a50bde4dd3da6eb19Chris Lattner backend_destroy_message_loop_ = message_loop; 2255f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer backend_destroy_task_ = task; 2265f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer} 2275f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 228cc1a875f94630e58d24a55577ffbf0e89b7da8c7Chris Lattnervoid HistoryBackend::Closing() { 229cc1a875f94630e58d24a55577ffbf0e89b7da8c7Chris Lattner // Any scheduled commit will have a reference to us, we must make it 230cc1a875f94630e58d24a55577ffbf0e89b7da8c7Chris Lattner // release that reference before we can be destroyed. 231cc1a875f94630e58d24a55577ffbf0e89b7da8c7Chris Lattner CancelScheduledCommit(); 232cc1a875f94630e58d24a55577ffbf0e89b7da8c7Chris Lattner 233cc1a875f94630e58d24a55577ffbf0e89b7da8c7Chris Lattner // Release our reference to the delegate, this reference will be keeping the 234cc1a875f94630e58d24a55577ffbf0e89b7da8c7Chris Lattner // history service alive. 235cc1a875f94630e58d24a55577ffbf0e89b7da8c7Chris Lattner delegate_.reset(); 236cc1a875f94630e58d24a55577ffbf0e89b7da8c7Chris Lattner} 237cc1a875f94630e58d24a55577ffbf0e89b7da8c7Chris Lattner 238e3d5e3ae5bd8028774f07d7c3751d4db82118942Chris Lattnervoid HistoryBackend::NotifyRenderProcessHostDestruction(const void* host) { 239aa39197431a0a0b1326ecf6b3be6a11f6e2f8503Chris Lattner tracker_.NotifyRenderProcessHostDestruction(host); 240aa39197431a0a0b1326ecf6b3be6a11f6e2f8503Chris Lattner} 241aa39197431a0a0b1326ecf6b3be6a11f6e2f8503Chris Lattner 242aa39197431a0a0b1326ecf6b3be6a11f6e2f8503Chris Lattnerbase::FilePath HistoryBackend::GetThumbnailFileName() const { 24353b0dabbe52219a8057659b90539837394ef0fa1Chris Lattner return history_dir_.Append(chrome::kThumbnailsFilename); 2445f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer} 2455f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 2465f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerbase::FilePath HistoryBackend::GetFaviconsFileName() const { 2475f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer return history_dir_.Append(chrome::kFaviconsFilename); 2485f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer} 2495f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 2505f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerbase::FilePath HistoryBackend::GetArchivedFileName() const { 2515f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer return history_dir_.Append(chrome::kArchivedHistoryFilename); 2525f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer} 2535f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 2545f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#if defined(OS_ANDROID) 2555f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerbase::FilePath HistoryBackend::GetAndroidCacheFileName() const { 2565f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer return history_dir_.Append(chrome::kAndroidCacheFilename); 2575f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer} 2585f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#endif 2595f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 2605f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid SpencerSegmentID HistoryBackend::GetLastSegmentID(VisitID from_visit) { 2615f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // Set is used to detect referrer loops. Should not happen, but can 2624095080aff204008eefb26b100906c6ca2bc4bb6Daniel Dunbar // if the database is corrupt. 2634095080aff204008eefb26b100906c6ca2bc4bb6Daniel Dunbar std::set<VisitID> visit_set; 2644095080aff204008eefb26b100906c6ca2bc4bb6Daniel Dunbar VisitID visit_id = from_visit; 2654095080aff204008eefb26b100906c6ca2bc4bb6Daniel Dunbar while (visit_id) { 2664095080aff204008eefb26b100906c6ca2bc4bb6Daniel Dunbar VisitRow row; 2674095080aff204008eefb26b100906c6ca2bc4bb6Daniel Dunbar if (!db_->GetRowForVisit(visit_id, &row)) 26853b0dabbe52219a8057659b90539837394ef0fa1Chris Lattner return 0; 2697dcc968f17a6ff9088c9651dddccc8d4025a1271Ted Kremenek if (row.segment_id) 27095041a2029a069386ee67439f6d0fb524a9d184fTed Kremenek return row.segment_id; // Found a visit in this change with a segment. 27153b0dabbe52219a8057659b90539837394ef0fa1Chris Lattner 2725f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // Check the referrer of this visit, if any. 2735f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer visit_id = row.referring_visit; 2745f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 27553b0dabbe52219a8057659b90539837394ef0fa1Chris Lattner if (visit_set.find(visit_id) != visit_set.end()) { 2765f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer NOTREACHED() << "Loop in referer chain, giving up"; 2775f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer break; 2785f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 2795f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer visit_set.insert(visit_id); 280d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner } 2815f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer return 0; 2825f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer} 2836b884508c3bc97cc9df9516adb92fbf88dd0a2e4Chris Lattner 2846b884508c3bc97cc9df9516adb92fbf88dd0a2e4Chris LattnerSegmentID HistoryBackend::UpdateSegments( 2856b884508c3bc97cc9df9516adb92fbf88dd0a2e4Chris Lattner const GURL& url, 2866b884508c3bc97cc9df9516adb92fbf88dd0a2e4Chris Lattner VisitID from_visit, 2876b884508c3bc97cc9df9516adb92fbf88dd0a2e4Chris Lattner VisitID visit_id, 2886b884508c3bc97cc9df9516adb92fbf88dd0a2e4Chris Lattner content::PageTransition transition_type, 2896b884508c3bc97cc9df9516adb92fbf88dd0a2e4Chris Lattner const Time ts) { 2906b884508c3bc97cc9df9516adb92fbf88dd0a2e4Chris Lattner if (!db_) 2916b884508c3bc97cc9df9516adb92fbf88dd0a2e4Chris Lattner return 0; 2926b884508c3bc97cc9df9516adb92fbf88dd0a2e4Chris Lattner 2936b884508c3bc97cc9df9516adb92fbf88dd0a2e4Chris Lattner // We only consider main frames. 2946b884508c3bc97cc9df9516adb92fbf88dd0a2e4Chris Lattner if (!content::PageTransitionIsMainFrame(transition_type)) 2956b884508c3bc97cc9df9516adb92fbf88dd0a2e4Chris Lattner return 0; 2965f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 2975f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer SegmentID segment_id = 0; 2985f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer content::PageTransition t = 2995f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer content::PageTransitionStripQualifier(transition_type); 3005f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 30103db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis // Are we at the beginning of a new segment? 30203db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis // Note that navigating to an existing entry (with back/forward) reuses the 303ed5c38682c056c147c8a4abb748b4f285de206ddArgyrios Kyrtzidis // same transition type. We are not adding it as a new segment in that case 304ed5c38682c056c147c8a4abb748b4f285de206ddArgyrios Kyrtzidis // because if this was the target of a redirect, we might end up with 305ed5c38682c056c147c8a4abb748b4f285de206ddArgyrios Kyrtzidis // 2 entries for the same final URL. Ex: User types google.net, gets 30603db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis // redirected to google.com. A segment is created for google.net. On 307a9e274c01ebae45629d93aaa07be450fb77dd3cbArgyrios Kyrtzidis // google.com users navigates through a link, then press back. That last 308ed5c38682c056c147c8a4abb748b4f285de206ddArgyrios Kyrtzidis // navigation is for the entry google.com transition typed. We end up adding 309ed5c38682c056c147c8a4abb748b4f285de206ddArgyrios Kyrtzidis // a segment for that one as well. So we end up with google.net and google.com 31003db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis // in the segment table, showing as 2 entries in the NTP. 311ed5c38682c056c147c8a4abb748b4f285de206ddArgyrios Kyrtzidis // Note also that we should still be updating the visit count for that segment 312ed5c38682c056c147c8a4abb748b4f285de206ddArgyrios Kyrtzidis // which we are not doing now. It should be addressed when 313ed5c38682c056c147c8a4abb748b4f285de206ddArgyrios Kyrtzidis // http://crbug.com/96860 is fixed. 31403db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis if ((t == content::PAGE_TRANSITION_TYPED || 315a9e274c01ebae45629d93aaa07be450fb77dd3cbArgyrios Kyrtzidis t == content::PAGE_TRANSITION_AUTO_BOOKMARK) && 31603db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis (transition_type & content::PAGE_TRANSITION_FORWARD_BACK) == 0) { 317ed5c38682c056c147c8a4abb748b4f285de206ddArgyrios Kyrtzidis // If so, create or get the segment. 318ed5c38682c056c147c8a4abb748b4f285de206ddArgyrios Kyrtzidis std::string segment_name = db_->ComputeSegmentName(url); 31903db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis URLID url_id = db_->GetRowForURL(url, NULL); 32003db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis if (!url_id) 32103db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis return 0; 322a9e274c01ebae45629d93aaa07be450fb77dd3cbArgyrios Kyrtzidis 32303db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis if (!(segment_id = db_->GetSegmentNamed(segment_name))) { 32403db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis if (!(segment_id = db_->CreateSegment(url_id, segment_name))) { 32503db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis NOTREACHED(); 32603db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis return 0; 32703db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis } 3285f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } else { 3295f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // Note: if we update an existing segment, we update the url used to 330d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner // represent that segment in order to minimize stale most visited 3315f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // images. 3325f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer db_->UpdateSegmentRepresentationURL(segment_id, url_id); 3339e0ed0bd5a3a7bac73973980ff32132a7724e674Argyrios Kyrtzidis } 3346cfe7594a46b5d270142cfcb688a9c1a3a487a48Chris Lattner } else { 33503db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis // Note: it is possible there is no segment ID set for this visit chain. 33603db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis // This can happen if the initial navigation wasn't AUTO_BOOKMARK or 3375f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // TYPED. (For example GENERATED). In this case this visit doesn't count 3385f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // toward any segment. 3395f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (!(segment_id = GetLastSegmentID(from_visit))) 3405f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer return 0; 3415f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 342d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner 3435f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // Set the segment in the visit. 3445f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (!db_->SetSegmentID(visit_id, segment_id)) { 3455f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer NOTREACHED(); 3465f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer return 0; 347d038def6e3f33bebf8c12bb3a95b2492c154a5ddTed Kremenek } 3485f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 3495f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // Finally, increase the counter for that segment / day. 350d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner if (!db_->IncreaseSegmentVisitCount(segment_id, ts, 1)) { 3515f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer NOTREACHED(); 3525f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer return 0; 3535f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 3545f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer return segment_id; 3555f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer} 3565f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 3575f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencervoid HistoryBackend::UpdateWithPageEndTime(const void* host, 3585f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer int32 page_id, 3595f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer const GURL& url, 3605f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer Time end_ts) { 3616b884508c3bc97cc9df9516adb92fbf88dd0a2e4Chris Lattner // Will be filled with the URL ID and the visit ID of the last addition. 3626b884508c3bc97cc9df9516adb92fbf88dd0a2e4Chris Lattner VisitID visit_id = tracker_.GetLastVisit(host, page_id, url); 3636b884508c3bc97cc9df9516adb92fbf88dd0a2e4Chris Lattner UpdateVisitDuration(visit_id, end_ts); 3646b884508c3bc97cc9df9516adb92fbf88dd0a2e4Chris Lattner} 3656b884508c3bc97cc9df9516adb92fbf88dd0a2e4Chris Lattner 36603db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidisvoid HistoryBackend::UpdateVisitDuration(VisitID visit_id, const Time end_ts) { 36703db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis if (!db_) 36803db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis return; 36903db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis 37003db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis // Get the starting visit_time for visit_id. 3719e0ed0bd5a3a7bac73973980ff32132a7724e674Argyrios Kyrtzidis VisitRow visit_row; 3723604e3895ecd850291b518e5a82246c888ce9d0fArgyrios Kyrtzidis if (db_->GetRowForVisit(visit_id, &visit_row)) { 3733604e3895ecd850291b518e5a82246c888ce9d0fArgyrios Kyrtzidis // We should never have a negative duration time even when time is skewed. 3743604e3895ecd850291b518e5a82246c888ce9d0fArgyrios Kyrtzidis visit_row.visit_duration = end_ts > visit_row.visit_time ? 3753604e3895ecd850291b518e5a82246c888ce9d0fArgyrios Kyrtzidis end_ts - visit_row.visit_time : TimeDelta::FromMicroseconds(0); 3763604e3895ecd850291b518e5a82246c888ce9d0fArgyrios Kyrtzidis db_->UpdateVisitRow(visit_row); 3773604e3895ecd850291b518e5a82246c888ce9d0fArgyrios Kyrtzidis } 3783604e3895ecd850291b518e5a82246c888ce9d0fArgyrios Kyrtzidis} 3793604e3895ecd850291b518e5a82246c888ce9d0fArgyrios Kyrtzidis 3803604e3895ecd850291b518e5a82246c888ce9d0fArgyrios Kyrtzidisvoid HistoryBackend::AddPage(const HistoryAddPageArgs& request) { 3813604e3895ecd850291b518e5a82246c888ce9d0fArgyrios Kyrtzidis if (!db_) 3823604e3895ecd850291b518e5a82246c888ce9d0fArgyrios Kyrtzidis return; 3833604e3895ecd850291b518e5a82246c888ce9d0fArgyrios Kyrtzidis 3843604e3895ecd850291b518e5a82246c888ce9d0fArgyrios Kyrtzidis // Will be filled with the URL ID and the visit ID of the last addition. 3853604e3895ecd850291b518e5a82246c888ce9d0fArgyrios Kyrtzidis std::pair<URLID, VisitID> last_ids(0, tracker_.GetLastVisit( 3863604e3895ecd850291b518e5a82246c888ce9d0fArgyrios Kyrtzidis request.id_scope, request.page_id, request.referrer)); 3873604e3895ecd850291b518e5a82246c888ce9d0fArgyrios Kyrtzidis 3883604e3895ecd850291b518e5a82246c888ce9d0fArgyrios Kyrtzidis VisitID from_visit_id = last_ids.second; 3893604e3895ecd850291b518e5a82246c888ce9d0fArgyrios Kyrtzidis 3903604e3895ecd850291b518e5a82246c888ce9d0fArgyrios Kyrtzidis // If a redirect chain is given, we expect the last item in that chain to be 3913604e3895ecd850291b518e5a82246c888ce9d0fArgyrios Kyrtzidis // the final URL. 3923604e3895ecd850291b518e5a82246c888ce9d0fArgyrios Kyrtzidis DCHECK(request.redirects.empty() || 3933604e3895ecd850291b518e5a82246c888ce9d0fArgyrios Kyrtzidis request.redirects.back() == request.url); 3946b884508c3bc97cc9df9516adb92fbf88dd0a2e4Chris Lattner 3955f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // If the user is adding older history, we need to make sure our times 396d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner // are correct. 3975f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (request.time < first_recorded_time_) 3985f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer first_recorded_time_ = request.time; 3995f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 4008ed3044a33679cbfa0617d465a50ec557d671ed7Chris Lattner content::PageTransition request_transition = request.transition; 4018ed3044a33679cbfa0617d465a50ec557d671ed7Chris Lattner content::PageTransition stripped_transition = 4028ed3044a33679cbfa0617d465a50ec557d671ed7Chris Lattner content::PageTransitionStripQualifier(request_transition); 4038ed3044a33679cbfa0617d465a50ec557d671ed7Chris Lattner bool is_keyword_generated = 4048ed3044a33679cbfa0617d465a50ec557d671ed7Chris Lattner (stripped_transition == content::PAGE_TRANSITION_KEYWORD_GENERATED); 405d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner 4065f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // If the user is navigating to a not-previously-typed intranet hostname, 4075f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // change the transition to TYPED so that the omnibox will learn that this is 408d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner // a known host. 4095f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer bool has_redirects = request.redirects.size() > 1; 4105f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (content::PageTransitionIsMainFrame(request_transition) && 4115f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer (stripped_transition != content::PAGE_TRANSITION_TYPED) && 4125f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer !is_keyword_generated) { 4135f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer const GURL& origin_url(has_redirects ? 4145f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer request.redirects[0] : request.url); 4155f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (origin_url.SchemeIs(chrome::kHttpScheme) || 4165f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer origin_url.SchemeIs(content::kHttpsScheme) || 417d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner origin_url.SchemeIs(chrome::kFtpScheme)) { 4185f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer std::string host(origin_url.host()); 4195f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer size_t registry_length = 4205f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer net::registry_controlled_domains::GetRegistryLength( 4215f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer host, 4225f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES, 4235f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES); 4245f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (registry_length == 0 && !db_->IsTypedHost(host)) { 4255f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer stripped_transition = content::PAGE_TRANSITION_TYPED; 4265f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer request_transition = 4275f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer content::PageTransitionFromInt( 4285f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer stripped_transition | 429d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner content::PageTransitionGetQualifier(request_transition)); 4305f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 4315f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 4325f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 4335f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 4345f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (!has_redirects) { 4355f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // The single entry is both a chain start and end. 4365f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer content::PageTransition t = content::PageTransitionFromInt( 4375f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer request_transition | 4385f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer content::PAGE_TRANSITION_CHAIN_START | 4395f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer content::PAGE_TRANSITION_CHAIN_END); 440d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner 441c3d8d57b010e2ed15a2a7685d5761db14f5d2252Chris Lattner // No redirect case (one element means just the page itself). 4425f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer last_ids = AddPageVisit(request.url, request.time, 4435f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer last_ids.second, t, request.visit_source); 44497ba77cf09bf7b83b679165ce67ad7d49ffd568cChris Lattner 44597ba77cf09bf7b83b679165ce67ad7d49ffd568cChris Lattner // Update the segment for this visit. KEYWORD_GENERATED visits should not 44697ba77cf09bf7b83b679165ce67ad7d49ffd568cChris Lattner // result in changing most visited, so we don't update segments (most 44797ba77cf09bf7b83b679165ce67ad7d49ffd568cChris Lattner // visited db). 4485f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (!is_keyword_generated) { 4495f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer UpdateSegments(request.url, from_visit_id, last_ids.second, t, 4505f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer request.time); 4515f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 4525f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // Update the referrer's duration. 4535f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer UpdateVisitDuration(from_visit_id, request.time); 4545f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 4555f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } else { 4565f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // Redirect case. Add the redirect chain. 4575f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 4585f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer content::PageTransition redirect_info = 4595f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer content::PAGE_TRANSITION_CHAIN_START; 4605f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 4613f1cc838f9caf469990f23fccd0940263c0c61ffChris Lattner RedirectList redirects = request.redirects; 4623f1cc838f9caf469990f23fccd0940263c0c61ffChris Lattner if (redirects[0].SchemeIs(chrome::kAboutScheme)) { 4633f1cc838f9caf469990f23fccd0940263c0c61ffChris Lattner // When the redirect source + referrer is "about" we skip it. This 4643f1cc838f9caf469990f23fccd0940263c0c61ffChris Lattner // happens when a page opens a new frame/window to about:blank and then 4653f1cc838f9caf469990f23fccd0940263c0c61ffChris Lattner // script sets the URL to somewhere else (used to hide the referrer). It 4665f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // would be nice to keep all these redirects properly but we don't ever 4675f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // see the initial about:blank load, so we don't know where the 4685f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // subsequent client redirect came from. 4695f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // 4705f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // In this case, we just don't bother hooking up the source of the 4715f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // redirects, so we remove it. 472d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner redirects.erase(redirects.begin()); 4735f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } else if (request_transition & content::PAGE_TRANSITION_CLIENT_REDIRECT) { 4745f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer redirect_info = content::PAGE_TRANSITION_CLIENT_REDIRECT; 4755f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // The first entry in the redirect chain initiated a client redirect. 4765f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // We don't add this to the database since the referrer is already 4775f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // there, so we skip over it but change the transition type of the first 4785f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // transition to client redirect. 479d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner // 4805f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // The referrer is invalid when restoring a session that features an 4815f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // https tab that redirects to a different host or to http. In this 4825f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // case we don't need to reconnect the new redirect with the existing 4835f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // chain. 4845f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (request.referrer.is_valid()) { 4855f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer DCHECK(request.referrer == redirects[0]); 486d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner redirects.erase(redirects.begin()); 4875f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 488fde2bf9befede63e3f01f84519784c17b4c81480Chris Lattner // If the navigation entry for this visit has replaced that for the 489fde2bf9befede63e3f01f84519784c17b4c81480Chris Lattner // first visit, remove the CHAIN_END marker from the first visit. This 490fde2bf9befede63e3f01f84519784c17b4c81480Chris Lattner // can be called a lot, for example, the page cycler, and most of the 4915f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // time we won't have changed anything. 4925f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer VisitRow visit_row; 4935f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (request.did_replace_entry && 4945f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer db_->GetRowForVisit(last_ids.second, &visit_row) && 4955f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer visit_row.transition & content::PAGE_TRANSITION_CHAIN_END) { 496d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner visit_row.transition = content::PageTransitionFromInt( 4975f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer visit_row.transition & ~content::PAGE_TRANSITION_CHAIN_END); 4985f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer db_->UpdateVisitRow(visit_row); 4995f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 5005f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 5015f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 502d038def6e3f33bebf8c12bb3a95b2492c154a5ddTed Kremenek 503d038def6e3f33bebf8c12bb3a95b2492c154a5ddTed Kremenek for (size_t redirect_index = 0; redirect_index < redirects.size(); 5049c1b750c59d510e6c9eccb1f37bccc46ccfe6844Ted Kremenek redirect_index++) { 5059c1b750c59d510e6c9eccb1f37bccc46ccfe6844Ted Kremenek content::PageTransition t = 5069c1b750c59d510e6c9eccb1f37bccc46ccfe6844Ted Kremenek content::PageTransitionFromInt(stripped_transition | redirect_info); 5079c1b750c59d510e6c9eccb1f37bccc46ccfe6844Ted Kremenek 5089c1b750c59d510e6c9eccb1f37bccc46ccfe6844Ted Kremenek // If this is the last transition, add a CHAIN_END marker 509d038def6e3f33bebf8c12bb3a95b2492c154a5ddTed Kremenek if (redirect_index == (redirects.size() - 1)) { 510d038def6e3f33bebf8c12bb3a95b2492c154a5ddTed Kremenek t = content::PageTransitionFromInt( 511d038def6e3f33bebf8c12bb3a95b2492c154a5ddTed Kremenek t | content::PAGE_TRANSITION_CHAIN_END); 512caaa7df2c78bbd40197823034c0275f3dcbd63e7Ted Kremenek } 5139c1b750c59d510e6c9eccb1f37bccc46ccfe6844Ted Kremenek 5149c1b750c59d510e6c9eccb1f37bccc46ccfe6844Ted Kremenek // Record all redirect visits with the same timestamp. We don't display 515caaa7df2c78bbd40197823034c0275f3dcbd63e7Ted Kremenek // them anyway, and if we ever decide to, we can reconstruct their order 5169c1b750c59d510e6c9eccb1f37bccc46ccfe6844Ted Kremenek // from the redirect chain. 517d038def6e3f33bebf8c12bb3a95b2492c154a5ddTed Kremenek last_ids = AddPageVisit(redirects[redirect_index], 518d038def6e3f33bebf8c12bb3a95b2492c154a5ddTed Kremenek request.time, last_ids.second, 519d038def6e3f33bebf8c12bb3a95b2492c154a5ddTed Kremenek t, request.visit_source); 52053b0dabbe52219a8057659b90539837394ef0fa1Chris Lattner if (t & content::PAGE_TRANSITION_CHAIN_START) { 52153b0dabbe52219a8057659b90539837394ef0fa1Chris Lattner // Update the segment for this visit. 52253b0dabbe52219a8057659b90539837394ef0fa1Chris Lattner UpdateSegments(redirects[redirect_index], 5237bfaaaecb3113f955db31e8d8a51acffd1bc0c27Nico Weber from_visit_id, last_ids.second, t, request.time); 5245f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 5255f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // Update the visit_details for this visit. 5265f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer UpdateVisitDuration(from_visit_id, request.time); 5275f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 5285f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 5295f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // Subsequent transitions in the redirect list must all be server 5305f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // redirects. 531d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner redirect_info = content::PAGE_TRANSITION_SERVER_REDIRECT; 5325f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 5335f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 5345f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // Last, save this redirect chain for later so we can set titles & favicons 5355f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // on the redirected pages properly. 5365f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer recent_redirects_.Put(request.url, redirects); 5375f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 5385f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 5395f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // TODO(brettw) bug 1140015: Add an "add page" notification so the history 5405f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // views can keep in sync. 5415f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 5425f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // Add the last visit to the tracker so we can get outgoing transitions. 5435f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // TODO(evanm): Due to http://b/1194536 we lose the referrers of a subframe 5445f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // navigation anyway, so last_visit_id is always zero for them. But adding 5455f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // them here confuses main frame history, so we skip them for now. 5465f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (stripped_transition != content::PAGE_TRANSITION_AUTO_SUBFRAME && 5475f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer stripped_transition != content::PAGE_TRANSITION_MANUAL_SUBFRAME && 5485f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer !is_keyword_generated) { 5495f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer tracker_.AddVisit(request.id_scope, request.page_id, request.url, 5505f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer last_ids.second); 5515f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 5525f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 5535f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer ScheduleCommit(); 5545f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer} 5555f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 5565f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencervoid HistoryBackend::InitImpl(const std::string& languages) { 5575f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer DCHECK(!db_) << "Initializing HistoryBackend twice"; 5585f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // In the rare case where the db fails to initialize a dialog may get shown 5595f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // the blocks the caller, yet allows other messages through. For this reason 5605f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // we only set db_ to the created database if creation is successful. That 5615f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // way other methods won't do anything as db_ is still NULL. 5625f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 5635f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer TimeTicks beginning_time = TimeTicks::Now(); 5645f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 5655f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // Compute the file names. 5665f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer base::FilePath history_name = history_dir_.Append(chrome::kHistoryFilename); 567d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner base::FilePath thumbnail_name = GetFaviconsFileName(); 5685f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer base::FilePath archived_name = GetArchivedFileName(); 5695f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 5705f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // Delete the old index database files which are no longer used. 5715f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer DeleteFTSIndexDatabases(); 5725f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 5735f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // History database. 5745f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer db_.reset(new HistoryDatabase()); 5755f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 5765f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // Unretained to avoid a ref loop with db_. 577d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner db_->set_error_callback( 5785f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer base::Bind(&HistoryBackend::DatabaseErrorCallback, 5795f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer base::Unretained(this))); 5805f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 581d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner sql::InitStatus status = db_->Init(history_name); 5825f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer switch (status) { 5835f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer case sql::INIT_OK: 5845f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer break; 5855f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer case sql::INIT_FAILURE: { 586d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner // A NULL db_ will cause all calls on this object to notice this error 5875f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // and to not continue. If the error callback scheduled killing the 5885f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // database, the task it posted has not executed yet. Try killing the 5895f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // database now before we close it. 5905f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer bool kill_db = scheduled_kill_db_; 5915f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (kill_db) 5925f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer KillHistoryDatabase(); 5935f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer UMA_HISTOGRAM_BOOLEAN("History.AttemptedToFixProfileError", kill_db); 5945f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer delegate_->NotifyProfileError(id_, status); 5955f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer db_.reset(); 5965f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer return; 5975f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 5985f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer default: 599f1c99acc544a4e70f308db4e7200ca04cd5a06d2Chris Lattner NOTREACHED(); 6005f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 6015f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 6025f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // Fill the in-memory database and send it back to the history service on the 6035f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // main thread. 6045f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer InMemoryHistoryBackend* mem_backend = new InMemoryHistoryBackend; 6055f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (mem_backend->Init(history_name, db_.get())) 6065f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer delegate_->SetInMemoryBackend(id_, mem_backend); // Takes ownership of 6075f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // pointer. 6085f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer else 6095f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer delete mem_backend; // Error case, run without the in-memory DB. 61003db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis db_->BeginExclusiveMode(); // Must be after the mem backend read the data. 61103db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis 61203db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis // Create the history publisher which needs to be passed on to the thumbnail 61303db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis // database for publishing history. 61403db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis history_publisher_.reset(new HistoryPublisher()); 61503db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis if (!history_publisher_->Init()) { 61603db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis // The init may fail when there are no indexers wanting our history. 61703db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis // Hence no need to log the failure. 61803db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis history_publisher_.reset(); 6193604e3895ecd850291b518e5a82246c888ce9d0fArgyrios Kyrtzidis } 62003db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis 62103db1b31dd926409b7defc1c90b66549464652c0Argyrios Kyrtzidis // Thumbnail database. 6225f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // TODO(shess): "thumbnail database" these days only stores 6235f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // favicons. Thumbnails are stored in "top sites". Consider 6245f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // renaming "thumbnail" references to "favicons" or something of the 6255f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // sort. 626d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner thumbnail_db_.reset(new ThumbnailDatabase()); 627d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner if (thumbnail_db_->Init(thumbnail_name, 6285f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer history_publisher_.get(), 6295f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer db_.get()) != sql::INIT_OK) { 630d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner // Unlike the main database, we don't error out when the database is too 6315f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // new because this error is much less severe. Generally, this shouldn't 6325f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // happen since the thumbnail and main database versions should be in sync. 633d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner // We'll just continue without thumbnails & favicons in this case or any 634d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner // other error. 6355f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer LOG(WARNING) << "Could not initialize the thumbnail database."; 6365f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer thumbnail_db_.reset(); 63742e6737f2efb113563140ad794c21c7709250402Chris Lattner } 638d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner 639d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner // Archived database. 640d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner if (db_->needs_version_17_migration()) { 6415f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // See needs_version_17_migration() decl for more. In this case, we want 6425f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // to delete the archived database and need to do so before we try to 643d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner // open the file. We can ignore any error (maybe the file doesn't exist). 6445f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer sql::Connection::Delete(archived_name); 645d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner } 646d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner archived_db_.reset(new ArchivedDatabase()); 647d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner if (!archived_db_->Init(archived_name)) { 648d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner LOG(WARNING) << "Could not initialize the archived database."; 6495f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer archived_db_.reset(); 6505f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 6515f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 6525f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // Generate the history and thumbnail database metrics only after performing 653d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner // any migration work. 6542243449253475574fc6f14986ff8f7fce5d46799Chris Lattner if (base::RandInt(1, 100) == 50) { 655d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner // Only do this computation sometimes since it can be expensive. 656d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner db_->ComputeDatabaseMetrics(history_name); 657d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner if (thumbnail_db_) 6585f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer thumbnail_db_->ComputeDatabaseMetrics(); 6595f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 660ec6c574478a22008847d7ebc2498ef3336752096Ted Kremenek 661ec6c574478a22008847d7ebc2498ef3336752096Ted Kremenek // Tell the expiration module about all the nice databases we made. This must 662ec6c574478a22008847d7ebc2498ef3336752096Ted Kremenek // happen before db_->Init() is called since the callback ForceArchiveHistory 663ec6c574478a22008847d7ebc2498ef3336752096Ted Kremenek // may need to expire stuff. 664ec6c574478a22008847d7ebc2498ef3336752096Ted Kremenek // 665ec6c574478a22008847d7ebc2498ef3336752096Ted Kremenek // *sigh*, this can all be cleaned up when that migration code is removed. 666ec6c574478a22008847d7ebc2498ef3336752096Ted Kremenek // The main DB initialization should intuitively be first (not that it 667ec6c574478a22008847d7ebc2498ef3336752096Ted Kremenek // actually matters) and the expirer should be set last. 6685f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer expirer_.SetDatabases(db_.get(), archived_db_.get(), 6695f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer thumbnail_db_.get()); 6705f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 671 // Open the long-running transaction. 672 db_->BeginTransaction(); 673 if (thumbnail_db_) 674 thumbnail_db_->BeginTransaction(); 675 if (archived_db_) 676 archived_db_->BeginTransaction(); 677 678 // Get the first item in our database. 679 db_->GetStartDate(&first_recorded_time_); 680 681 // Start expiring old stuff. 682 expirer_.StartArchivingOldStuff(TimeDelta::FromDays(kArchiveDaysThreshold)); 683 684#if defined(OS_ANDROID) 685 if (thumbnail_db_) { 686 android_provider_backend_.reset(new AndroidProviderBackend( 687 GetAndroidCacheFileName(), db_.get(), thumbnail_db_.get(), 688 bookmark_service_, delegate_.get())); 689 } 690#endif 691 692 HISTOGRAM_TIMES("History.InitTime", 693 TimeTicks::Now() - beginning_time); 694} 695 696void HistoryBackend::OnMemoryPressure( 697 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) { 698 bool trim_aggressively = memory_pressure_level == 699 base::MemoryPressureListener::MEMORY_PRESSURE_CRITICAL; 700 if (db_) 701 db_->TrimMemory(trim_aggressively); 702 if (thumbnail_db_) 703 thumbnail_db_->TrimMemory(trim_aggressively); 704 if (archived_db_) 705 archived_db_->TrimMemory(trim_aggressively); 706} 707 708void HistoryBackend::CloseAllDatabases() { 709 if (db_) { 710 // Commit the long-running transaction. 711 db_->CommitTransaction(); 712 db_.reset(); 713 // Forget the first recorded time since the database is closed. 714 first_recorded_time_ = base::Time(); 715 } 716 if (thumbnail_db_) { 717 thumbnail_db_->CommitTransaction(); 718 thumbnail_db_.reset(); 719 } 720 if (archived_db_) { 721 archived_db_->CommitTransaction(); 722 archived_db_.reset(); 723 } 724} 725 726std::pair<URLID, VisitID> HistoryBackend::AddPageVisit( 727 const GURL& url, 728 Time time, 729 VisitID referring_visit, 730 content::PageTransition transition, 731 VisitSource visit_source) { 732 // Top-level frame navigations are visible, everything else is hidden 733 bool new_hidden = !content::PageTransitionIsMainFrame(transition); 734 735 // NOTE: This code must stay in sync with 736 // ExpireHistoryBackend::ExpireURLsForVisits(). 737 // TODO(pkasting): http://b/1148304 We shouldn't be marking so many URLs as 738 // typed, which would eliminate the need for this code. 739 int typed_increment = 0; 740 content::PageTransition transition_type = 741 content::PageTransitionStripQualifier(transition); 742 if ((transition_type == content::PAGE_TRANSITION_TYPED && 743 !content::PageTransitionIsRedirect(transition)) || 744 transition_type == content::PAGE_TRANSITION_KEYWORD_GENERATED) 745 typed_increment = 1; 746 747#if defined(OS_ANDROID) 748 // Only count the page visit if it came from user browsing and only count it 749 // once when cycling through a redirect chain. 750 if (visit_source == SOURCE_BROWSED && 751 (transition & content::PAGE_TRANSITION_CHAIN_END) != 0) { 752 RecordTopPageVisitStats(url); 753 } 754#endif 755 756 // See if this URL is already in the DB. 757 URLRow url_info(url); 758 URLID url_id = db_->GetRowForURL(url, &url_info); 759 if (url_id) { 760 // Update of an existing row. 761 if (content::PageTransitionStripQualifier(transition) != 762 content::PAGE_TRANSITION_RELOAD) 763 url_info.set_visit_count(url_info.visit_count() + 1); 764 if (typed_increment) 765 url_info.set_typed_count(url_info.typed_count() + typed_increment); 766 if (url_info.last_visit() < time) 767 url_info.set_last_visit(time); 768 769 // Only allow un-hiding of pages, never hiding. 770 if (!new_hidden) 771 url_info.set_hidden(false); 772 773 db_->UpdateURLRow(url_id, url_info); 774 } else { 775 // Addition of a new row. 776 url_info.set_visit_count(1); 777 url_info.set_typed_count(typed_increment); 778 url_info.set_last_visit(time); 779 url_info.set_hidden(new_hidden); 780 781 url_id = db_->AddURL(url_info); 782 if (!url_id) { 783 NOTREACHED() << "Adding URL failed."; 784 return std::make_pair(0, 0); 785 } 786 url_info.id_ = url_id; 787 } 788 789 // Add the visit with the time to the database. 790 VisitRow visit_info(url_id, time, referring_visit, transition, 0); 791 VisitID visit_id = db_->AddVisit(&visit_info, visit_source); 792 NotifyVisitObservers(visit_info); 793 794 if (visit_info.visit_time < first_recorded_time_) 795 first_recorded_time_ = visit_info.visit_time; 796 797 // Broadcast a notification of the visit. 798 if (visit_id) { 799 if (typed_url_syncable_service_.get()) 800 typed_url_syncable_service_->OnUrlVisited(transition, &url_info); 801 802 URLVisitedDetails* details = new URLVisitedDetails; 803 details->transition = transition; 804 details->row = url_info; 805 // TODO(meelapshah) Disabled due to potential PageCycler regression. 806 // Re-enable this. 807 // GetMostRecentRedirectsTo(url, &details->redirects); 808 BroadcastNotifications(chrome::NOTIFICATION_HISTORY_URL_VISITED, details); 809 } else { 810 VLOG(0) << "Failed to build visit insert statement: " 811 << "url_id = " << url_id; 812 } 813 814 return std::make_pair(url_id, visit_id); 815} 816 817void HistoryBackend::AddPagesWithDetails(const URLRows& urls, 818 VisitSource visit_source) { 819 if (!db_) 820 return; 821 822 scoped_ptr<URLsModifiedDetails> modified(new URLsModifiedDetails); 823 for (URLRows::const_iterator i = urls.begin(); i != urls.end(); ++i) { 824 DCHECK(!i->last_visit().is_null()); 825 826 // We will add to either the archived database or the main one depending on 827 // the date of the added visit. 828 URLDatabase* url_database; 829 VisitDatabase* visit_database; 830 if (IsExpiredVisitTime(i->last_visit())) { 831 if (!archived_db_) 832 return; // No archived database to save it to, just forget this. 833 url_database = archived_db_.get(); 834 visit_database = archived_db_.get(); 835 } else { 836 url_database = db_.get(); 837 visit_database = db_.get(); 838 } 839 840 URLRow existing_url; 841 URLID url_id = url_database->GetRowForURL(i->url(), &existing_url); 842 if (!url_id) { 843 // Add the page if it doesn't exist. 844 url_id = url_database->AddURL(*i); 845 if (!url_id) { 846 NOTREACHED() << "Could not add row to DB"; 847 return; 848 } 849 850 if (i->typed_count() > 0) { 851 modified->changed_urls.push_back(*i); 852 modified->changed_urls.back().set_id(url_id); // *i likely has |id_| 0. 853 } 854 } 855 856 // Sync code manages the visits itself. 857 if (visit_source != SOURCE_SYNCED) { 858 // Make up a visit to correspond to the last visit to the page. 859 VisitRow visit_info(url_id, i->last_visit(), 0, 860 content::PageTransitionFromInt( 861 content::PAGE_TRANSITION_LINK | 862 content::PAGE_TRANSITION_CHAIN_START | 863 content::PAGE_TRANSITION_CHAIN_END), 0); 864 if (!visit_database->AddVisit(&visit_info, visit_source)) { 865 NOTREACHED() << "Adding visit failed."; 866 return; 867 } 868 NotifyVisitObservers(visit_info); 869 870 if (visit_info.visit_time < first_recorded_time_) 871 first_recorded_time_ = visit_info.visit_time; 872 } 873 } 874 875 if (typed_url_syncable_service_.get()) 876 typed_url_syncable_service_->OnUrlsModified(&modified->changed_urls); 877 878 // Broadcast a notification for typed URLs that have been modified. This 879 // will be picked up by the in-memory URL database on the main thread. 880 // 881 // TODO(brettw) bug 1140015: Add an "add page" notification so the history 882 // views can keep in sync. 883 BroadcastNotifications(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED, 884 modified.release()); 885 886 ScheduleCommit(); 887} 888 889bool HistoryBackend::IsExpiredVisitTime(const base::Time& time) { 890 return time < expirer_.GetCurrentArchiveTime(); 891} 892 893void HistoryBackend::SetPageTitle(const GURL& url, 894 const string16& title) { 895 if (!db_) 896 return; 897 898 // Search for recent redirects which should get the same title. We make a 899 // dummy list containing the exact URL visited if there are no redirects so 900 // the processing below can be the same. 901 history::RedirectList dummy_list; 902 history::RedirectList* redirects; 903 RedirectCache::iterator iter = recent_redirects_.Get(url); 904 if (iter != recent_redirects_.end()) { 905 redirects = &iter->second; 906 907 // This redirect chain should have the destination URL as the last item. 908 DCHECK(!redirects->empty()); 909 DCHECK(redirects->back() == url); 910 } else { 911 // No redirect chain stored, make up one containing the URL we want so we 912 // can use the same logic below. 913 dummy_list.push_back(url); 914 redirects = &dummy_list; 915 } 916 917 scoped_ptr<URLsModifiedDetails> details(new URLsModifiedDetails); 918 for (size_t i = 0; i < redirects->size(); i++) { 919 URLRow row; 920 URLID row_id = db_->GetRowForURL(redirects->at(i), &row); 921 if (row_id && row.title() != title) { 922 row.set_title(title); 923 db_->UpdateURLRow(row_id, row); 924 details->changed_urls.push_back(row); 925 } 926 } 927 928 // Broadcast notifications for any URLs that have changed. This will 929 // update the in-memory database and the InMemoryURLIndex. 930 if (!details->changed_urls.empty()) { 931 if (typed_url_syncable_service_.get()) 932 typed_url_syncable_service_->OnUrlsModified(&details->changed_urls); 933 BroadcastNotifications(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED, 934 details.release()); 935 ScheduleCommit(); 936 } 937} 938 939void HistoryBackend::AddPageNoVisitForBookmark(const GURL& url, 940 const string16& title) { 941 if (!db_) 942 return; 943 944 URLRow url_info(url); 945 URLID url_id = db_->GetRowForURL(url, &url_info); 946 if (url_id) { 947 // URL is already known, nothing to do. 948 return; 949 } 950 951 if (!title.empty()) { 952 url_info.set_title(title); 953 } else { 954 url_info.set_title(UTF8ToUTF16(url.spec())); 955 } 956 957 url_info.set_last_visit(Time::Now()); 958 // Mark the page hidden. If the user types it in, it'll unhide. 959 url_info.set_hidden(true); 960 961 db_->AddURL(url_info); 962} 963 964void HistoryBackend::IterateURLs( 965 const scoped_refptr<visitedlink::VisitedLinkDelegate::URLEnumerator>& 966 iterator) { 967 if (db_) { 968 HistoryDatabase::URLEnumerator e; 969 if (db_->InitURLEnumeratorForEverything(&e)) { 970 URLRow info; 971 while (e.GetNextURL(&info)) { 972 iterator->OnURL(info.url()); 973 } 974 iterator->OnComplete(true); // Success. 975 return; 976 } 977 } 978 iterator->OnComplete(false); // Failure. 979} 980 981bool HistoryBackend::GetAllTypedURLs(URLRows* urls) { 982 if (db_) 983 return db_->GetAllTypedUrls(urls); 984 return false; 985} 986 987bool HistoryBackend::GetVisitsForURL(URLID id, VisitVector* visits) { 988 if (db_) 989 return db_->GetVisitsForURL(id, visits); 990 return false; 991} 992 993bool HistoryBackend::GetMostRecentVisitsForURL(URLID id, 994 int max_visits, 995 VisitVector* visits) { 996 if (db_) 997 return db_->GetMostRecentVisitsForURL(id, max_visits, visits); 998 return false; 999} 1000 1001bool HistoryBackend::UpdateURL(URLID id, const history::URLRow& url) { 1002 if (db_) 1003 return db_->UpdateURLRow(id, url); 1004 return false; 1005} 1006 1007bool HistoryBackend::AddVisits(const GURL& url, 1008 const std::vector<VisitInfo>& visits, 1009 VisitSource visit_source) { 1010 if (db_) { 1011 for (std::vector<VisitInfo>::const_iterator visit = visits.begin(); 1012 visit != visits.end(); ++visit) { 1013 if (!AddPageVisit( 1014 url, visit->first, 0, visit->second, visit_source).first) { 1015 return false; 1016 } 1017 } 1018 ScheduleCommit(); 1019 return true; 1020 } 1021 return false; 1022} 1023 1024bool HistoryBackend::RemoveVisits(const VisitVector& visits) { 1025 if (!db_) 1026 return false; 1027 1028 expirer_.ExpireVisits(visits); 1029 ScheduleCommit(); 1030 return true; 1031} 1032 1033bool HistoryBackend::GetVisitsSource(const VisitVector& visits, 1034 VisitSourceMap* sources) { 1035 if (!db_) 1036 return false; 1037 1038 db_->GetVisitsSource(visits, sources); 1039 return true; 1040} 1041 1042bool HistoryBackend::GetURL(const GURL& url, history::URLRow* url_row) { 1043 if (db_) 1044 return db_->GetRowForURL(url, url_row) != 0; 1045 return false; 1046} 1047 1048void HistoryBackend::QueryURL(scoped_refptr<QueryURLRequest> request, 1049 const GURL& url, 1050 bool want_visits) { 1051 if (request->canceled()) 1052 return; 1053 1054 bool success = false; 1055 URLRow* row = &request->value.a; 1056 VisitVector* visits = &request->value.b; 1057 if (db_) { 1058 if (db_->GetRowForURL(url, row)) { 1059 // Have a row. 1060 success = true; 1061 1062 // Optionally query the visits. 1063 if (want_visits) 1064 db_->GetVisitsForURL(row->id(), visits); 1065 } 1066 } 1067 request->ForwardResult(request->handle(), success, row, visits); 1068} 1069 1070TypedUrlSyncableService* HistoryBackend::GetTypedUrlSyncableService() const { 1071 return typed_url_syncable_service_.get(); 1072} 1073 1074// Segment usage --------------------------------------------------------------- 1075 1076void HistoryBackend::DeleteOldSegmentData() { 1077 if (db_) 1078 db_->DeleteSegmentData(Time::Now() - 1079 TimeDelta::FromDays(kSegmentDataRetention)); 1080} 1081 1082void HistoryBackend::QuerySegmentUsage( 1083 scoped_refptr<QuerySegmentUsageRequest> request, 1084 const Time from_time, 1085 int max_result_count) { 1086 if (request->canceled()) 1087 return; 1088 1089 if (db_) { 1090 db_->QuerySegmentUsage(from_time, max_result_count, &request->value.get()); 1091 1092 // If this is the first time we query segments, invoke 1093 // DeleteOldSegmentData asynchronously. We do this to cleanup old 1094 // entries. 1095 if (!segment_queried_) { 1096 segment_queried_ = true; 1097 base::MessageLoop::current()->PostTask( 1098 FROM_HERE, 1099 base::Bind(&HistoryBackend::DeleteOldSegmentData, this)); 1100 } 1101 } 1102 request->ForwardResult(request->handle(), &request->value.get()); 1103} 1104 1105void HistoryBackend::IncreaseSegmentDuration(const GURL& url, 1106 base::Time time, 1107 base::TimeDelta delta) { 1108 if (!db_) 1109 return; 1110 1111 const std::string segment_name(VisitSegmentDatabase::ComputeSegmentName(url)); 1112 SegmentID segment_id = db_->GetSegmentNamed(segment_name); 1113 if (!segment_id) { 1114 URLID url_id = db_->GetRowForURL(url, NULL); 1115 if (!url_id) 1116 return; 1117 segment_id = db_->CreateSegment(url_id, segment_name); 1118 if (!segment_id) 1119 return; 1120 } 1121 SegmentDurationID duration_id; 1122 base::TimeDelta total_delta; 1123 if (!db_->GetSegmentDuration(segment_id, time, &duration_id, 1124 &total_delta)) { 1125 db_->CreateSegmentDuration(segment_id, time, delta); 1126 return; 1127 } 1128 total_delta += delta; 1129 db_->SetSegmentDuration(duration_id, total_delta); 1130} 1131 1132void HistoryBackend::QuerySegmentDuration( 1133 scoped_refptr<QuerySegmentUsageRequest> request, 1134 const base::Time from_time, 1135 int max_result_count) { 1136 if (request->canceled()) 1137 return; 1138 1139 if (db_) { 1140 db_->QuerySegmentDuration(from_time, max_result_count, 1141 &request->value.get()); 1142 } 1143 request->ForwardResult(request->handle(), &request->value.get()); 1144} 1145 1146// Keyword visits -------------------------------------------------------------- 1147 1148void HistoryBackend::SetKeywordSearchTermsForURL(const GURL& url, 1149 TemplateURLID keyword_id, 1150 const string16& term) { 1151 if (!db_) 1152 return; 1153 1154 // Get the ID for this URL. 1155 URLRow url_row; 1156 if (!db_->GetRowForURL(url, &url_row)) { 1157 // There is a small possibility the url was deleted before the keyword 1158 // was added. Ignore the request. 1159 return; 1160 } 1161 1162 db_->SetKeywordSearchTermsForURL(url_row.id(), keyword_id, term); 1163 1164 // details is deleted by BroadcastNotifications. 1165 KeywordSearchTermDetails* details = new KeywordSearchTermDetails; 1166 details->url = url; 1167 details->keyword_id = keyword_id; 1168 details->term = term; 1169 BroadcastNotifications( 1170 chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_UPDATED, details); 1171 ScheduleCommit(); 1172} 1173 1174void HistoryBackend::DeleteAllSearchTermsForKeyword( 1175 TemplateURLID keyword_id) { 1176 if (!db_) 1177 return; 1178 1179 db_->DeleteAllSearchTermsForKeyword(keyword_id); 1180 // TODO(sky): bug 1168470. Need to move from archive dbs too. 1181 ScheduleCommit(); 1182} 1183 1184void HistoryBackend::GetMostRecentKeywordSearchTerms( 1185 scoped_refptr<GetMostRecentKeywordSearchTermsRequest> request, 1186 TemplateURLID keyword_id, 1187 const string16& prefix, 1188 int max_count) { 1189 if (request->canceled()) 1190 return; 1191 1192 if (db_) { 1193 db_->GetMostRecentKeywordSearchTerms(keyword_id, prefix, max_count, 1194 &(request->value)); 1195 } 1196 request->ForwardResult(request->handle(), &request->value); 1197} 1198 1199// Downloads ------------------------------------------------------------------- 1200 1201void HistoryBackend::GetNextDownloadId(uint32* next_id) { 1202 if (db_) 1203 db_->GetNextDownloadId(next_id); 1204} 1205 1206// Get all the download entries from the database. 1207void HistoryBackend::QueryDownloads(std::vector<DownloadRow>* rows) { 1208 if (db_) 1209 db_->QueryDownloads(rows); 1210} 1211 1212// Update a particular download entry. 1213void HistoryBackend::UpdateDownload(const history::DownloadRow& data) { 1214 if (!db_) 1215 return; 1216 db_->UpdateDownload(data); 1217 ScheduleCommit(); 1218} 1219 1220void HistoryBackend::CreateDownload(const history::DownloadRow& history_info, 1221 bool* success) { 1222 if (!db_) 1223 return; 1224 *success = db_->CreateDownload(history_info); 1225 ScheduleCommit(); 1226} 1227 1228void HistoryBackend::RemoveDownloads(const std::set<uint32>& ids) { 1229 if (!db_) 1230 return; 1231 size_t downloads_count_before = db_->CountDownloads(); 1232 base::TimeTicks started_removing = base::TimeTicks::Now(); 1233 // HistoryBackend uses a long-running Transaction that is committed 1234 // periodically, so this loop doesn't actually hit the disk too hard. 1235 for (std::set<uint32>::const_iterator it = ids.begin(); 1236 it != ids.end(); ++it) { 1237 db_->RemoveDownload(*it); 1238 } 1239 ScheduleCommit(); 1240 base::TimeTicks finished_removing = base::TimeTicks::Now(); 1241 size_t downloads_count_after = db_->CountDownloads(); 1242 1243 DCHECK_LE(downloads_count_after, downloads_count_before); 1244 if (downloads_count_after > downloads_count_before) 1245 return; 1246 size_t num_downloads_deleted = downloads_count_before - downloads_count_after; 1247 UMA_HISTOGRAM_COUNTS("Download.DatabaseRemoveDownloadsCount", 1248 num_downloads_deleted); 1249 base::TimeDelta micros = (1000 * (finished_removing - started_removing)); 1250 UMA_HISTOGRAM_TIMES("Download.DatabaseRemoveDownloadsTime", micros); 1251 if (num_downloads_deleted > 0) { 1252 UMA_HISTOGRAM_TIMES("Download.DatabaseRemoveDownloadsTimePerRecord", 1253 (1000 * micros) / num_downloads_deleted); 1254 } 1255 DCHECK_GE(ids.size(), num_downloads_deleted); 1256 if (ids.size() < num_downloads_deleted) 1257 return; 1258 UMA_HISTOGRAM_COUNTS("Download.DatabaseRemoveDownloadsCountNotRemoved", 1259 ids.size() - num_downloads_deleted); 1260} 1261 1262void HistoryBackend::QueryHistory(scoped_refptr<QueryHistoryRequest> request, 1263 const string16& text_query, 1264 const QueryOptions& options) { 1265 if (request->canceled()) 1266 return; 1267 1268 TimeTicks beginning_time = TimeTicks::Now(); 1269 1270 if (db_) { 1271 if (text_query.empty()) { 1272 // Basic history query for the main database. 1273 QueryHistoryBasic(db_.get(), db_.get(), options, &request->value); 1274 1275 // Now query the archived database. This is a bit tricky because we don't 1276 // want to query it if the queried time range isn't going to find anything 1277 // in it. 1278 // TODO(brettw) bug 1171036: do blimpie querying for the archived database 1279 // as well. 1280 // if (archived_db_.get() && 1281 // expirer_.GetCurrentArchiveTime() - TimeDelta::FromDays(7)) { 1282 } else { 1283 // Text history query. 1284 QueryHistoryText(db_.get(), db_.get(), text_query, options, 1285 &request->value); 1286 if (archived_db_.get() && 1287 expirer_.GetCurrentArchiveTime() >= options.begin_time) { 1288 QueryHistoryText(archived_db_.get(), archived_db_.get(), text_query, 1289 options, &request->value); 1290 } 1291 } 1292 } 1293 1294 request->ForwardResult(request->handle(), &request->value); 1295 1296 UMA_HISTOGRAM_TIMES("History.QueryHistory", 1297 TimeTicks::Now() - beginning_time); 1298} 1299 1300// Basic time-based querying of history. 1301void HistoryBackend::QueryHistoryBasic(URLDatabase* url_db, 1302 VisitDatabase* visit_db, 1303 const QueryOptions& options, 1304 QueryResults* result) { 1305 // First get all visits. 1306 VisitVector visits; 1307 bool has_more_results = visit_db->GetVisibleVisitsInRange(options, &visits); 1308 DCHECK(static_cast<int>(visits.size()) <= options.EffectiveMaxCount()); 1309 1310 // Now add them and the URL rows to the results. 1311 URLResult url_result; 1312 for (size_t i = 0; i < visits.size(); i++) { 1313 const VisitRow visit = visits[i]; 1314 1315 // Add a result row for this visit, get the URL info from the DB. 1316 if (!url_db->GetURLRow(visit.url_id, &url_result)) { 1317 VLOG(0) << "Failed to get id " << visit.url_id 1318 << " from history.urls."; 1319 continue; // DB out of sync and URL doesn't exist, try to recover. 1320 } 1321 1322 if (!url_result.url().is_valid()) { 1323 VLOG(0) << "Got invalid URL from history.urls with id " 1324 << visit.url_id << ": " 1325 << url_result.url().possibly_invalid_spec(); 1326 continue; // Don't report invalid URLs in case of corruption. 1327 } 1328 1329 // The archived database may be out of sync with respect to starring, 1330 // titles, last visit date, etc. Therefore, we query the main DB if the 1331 // current URL database is not the main one. 1332 if (url_db == db_.get()) { 1333 // Currently querying the archived DB, update with the main database to 1334 // catch any interesting stuff. This will update it if it exists in the 1335 // main DB, and do nothing otherwise. 1336 db_->GetRowForURL(url_result.url(), &url_result); 1337 } 1338 1339 url_result.set_visit_time(visit.visit_time); 1340 1341 // Set whether the visit was blocked for a managed user by looking at the 1342 // transition type. 1343 url_result.set_blocked_visit( 1344 (visit.transition & content::PAGE_TRANSITION_BLOCKED) != 0); 1345 1346 // We don't set any of the query-specific parts of the URLResult, since 1347 // snippets and stuff don't apply to basic querying. 1348 result->AppendURLBySwapping(&url_result); 1349 } 1350 1351 if (!has_more_results && options.begin_time <= first_recorded_time_) 1352 result->set_reached_beginning(true); 1353} 1354 1355// Text-based querying of history. 1356void HistoryBackend::QueryHistoryText(URLDatabase* url_db, 1357 VisitDatabase* visit_db, 1358 const string16& text_query, 1359 const QueryOptions& options, 1360 QueryResults* result) { 1361 URLRows text_matches; 1362 url_db->GetTextMatches(text_query, &text_matches); 1363 1364 std::vector<URLResult> matching_visits; 1365 VisitVector visits; // Declare outside loop to prevent re-construction. 1366 for (size_t i = 0; i < text_matches.size(); i++) { 1367 const URLRow& text_match = text_matches[i]; 1368 // Get all visits for given URL match. 1369 visit_db->GetVisitsForURLWithOptions(text_match.id(), options, &visits); 1370 for (size_t j = 0; j < visits.size(); j++) { 1371 URLResult url_result(text_match); 1372 url_result.set_visit_time(visits[j].visit_time); 1373 matching_visits.push_back(url_result); 1374 } 1375 } 1376 1377 std::sort(matching_visits.begin(), matching_visits.end(), 1378 URLResult::CompareVisitTime); 1379 1380 size_t max_results = options.max_count == 0 ? 1381 std::numeric_limits<size_t>::max() : static_cast<int>(options.max_count); 1382 for (std::vector<URLResult>::iterator it = matching_visits.begin(); 1383 it != matching_visits.end() && result->size() < max_results; ++it) { 1384 result->AppendURLBySwapping(&(*it)); 1385 } 1386 1387 if (matching_visits.size() == result->size() && 1388 options.begin_time <= first_recorded_time_) 1389 result->set_reached_beginning(true); 1390} 1391 1392// Frontend to GetMostRecentRedirectsFrom from the history thread. 1393void HistoryBackend::QueryRedirectsFrom( 1394 scoped_refptr<QueryRedirectsRequest> request, 1395 const GURL& url) { 1396 if (request->canceled()) 1397 return; 1398 bool success = GetMostRecentRedirectsFrom(url, &request->value); 1399 request->ForwardResult(request->handle(), url, success, &request->value); 1400} 1401 1402void HistoryBackend::QueryRedirectsTo( 1403 scoped_refptr<QueryRedirectsRequest> request, 1404 const GURL& url) { 1405 if (request->canceled()) 1406 return; 1407 bool success = GetMostRecentRedirectsTo(url, &request->value); 1408 request->ForwardResult(request->handle(), url, success, &request->value); 1409} 1410 1411void HistoryBackend::GetVisibleVisitCountToHost( 1412 scoped_refptr<GetVisibleVisitCountToHostRequest> request, 1413 const GURL& url) { 1414 if (request->canceled()) 1415 return; 1416 int count = 0; 1417 Time first_visit; 1418 const bool success = db_.get() && 1419 db_->GetVisibleVisitCountToHost(url, &count, &first_visit); 1420 request->ForwardResult(request->handle(), success, count, first_visit); 1421} 1422 1423void HistoryBackend::QueryTopURLsAndRedirects( 1424 scoped_refptr<QueryTopURLsAndRedirectsRequest> request, 1425 int result_count) { 1426 if (request->canceled()) 1427 return; 1428 1429 if (!db_) { 1430 request->ForwardResult(request->handle(), false, NULL, NULL); 1431 return; 1432 } 1433 1434 std::vector<GURL>* top_urls = &request->value.a; 1435 history::RedirectMap* redirects = &request->value.b; 1436 1437 ScopedVector<PageUsageData> data; 1438 db_->QuerySegmentUsage(base::Time::Now() - base::TimeDelta::FromDays(90), 1439 result_count, &data.get()); 1440 1441 for (size_t i = 0; i < data.size(); ++i) { 1442 top_urls->push_back(data[i]->GetURL()); 1443 RefCountedVector<GURL>* list = new RefCountedVector<GURL>; 1444 GetMostRecentRedirectsFrom(top_urls->back(), &list->data); 1445 (*redirects)[top_urls->back()] = list; 1446 } 1447 1448 request->ForwardResult(request->handle(), true, top_urls, redirects); 1449} 1450 1451// Will replace QueryTopURLsAndRedirectsRequest. 1452void HistoryBackend::QueryMostVisitedURLs( 1453 scoped_refptr<QueryMostVisitedURLsRequest> request, 1454 int result_count, 1455 int days_back) { 1456 if (request->canceled()) 1457 return; 1458 1459 if (!db_) { 1460 // No History Database - return an empty list. 1461 request->ForwardResult(request->handle(), MostVisitedURLList()); 1462 return; 1463 } 1464 1465 MostVisitedURLList* result = &request->value; 1466 QueryMostVisitedURLsImpl(result_count, days_back, result); 1467 request->ForwardResult(request->handle(), *result); 1468} 1469 1470void HistoryBackend::QueryFilteredURLs( 1471 scoped_refptr<QueryFilteredURLsRequest> request, 1472 int result_count, 1473 const history::VisitFilter& filter, 1474 bool extended_info) { 1475 if (request->canceled()) 1476 return; 1477 1478 base::Time request_start = base::Time::Now(); 1479 1480 if (!db_) { 1481 // No History Database - return an empty list. 1482 request->ForwardResult(request->handle(), FilteredURLList()); 1483 return; 1484 } 1485 1486 VisitVector visits; 1487 db_->GetDirectVisitsDuringTimes(filter, 0, &visits); 1488 1489 std::map<URLID, double> score_map; 1490 for (size_t i = 0; i < visits.size(); ++i) { 1491 score_map[visits[i].url_id] += filter.GetVisitScore(visits[i]); 1492 } 1493 1494 // TODO(georgey): experiment with visit_segment database granularity (it is 1495 // currently 24 hours) to use it directly instead of using visits database, 1496 // which is considerably slower. 1497 ScopedVector<PageUsageData> data; 1498 data.reserve(score_map.size()); 1499 for (std::map<URLID, double>::iterator it = score_map.begin(); 1500 it != score_map.end(); ++it) { 1501 PageUsageData* pud = new PageUsageData(it->first); 1502 pud->SetScore(it->second); 1503 data.push_back(pud); 1504 } 1505 1506 // Limit to the top |result_count| results. 1507 std::sort(data.begin(), data.end(), PageUsageData::Predicate); 1508 if (result_count && implicit_cast<int>(data.size()) > result_count) 1509 data.resize(result_count); 1510 1511 for (size_t i = 0; i < data.size(); ++i) { 1512 URLRow info; 1513 if (db_->GetURLRow(data[i]->GetID(), &info)) { 1514 data[i]->SetURL(info.url()); 1515 data[i]->SetTitle(info.title()); 1516 } 1517 } 1518 1519 FilteredURLList& result = request->value; 1520 for (size_t i = 0; i < data.size(); ++i) { 1521 PageUsageData* current_data = data[i]; 1522 FilteredURL url(*current_data); 1523 1524 if (extended_info) { 1525 VisitVector visits; 1526 db_->GetVisitsForURL(current_data->GetID(), &visits); 1527 if (visits.size() > 0) { 1528 url.extended_info.total_visits = visits.size(); 1529 for (size_t i = 0; i < visits.size(); ++i) { 1530 url.extended_info.duration_opened += 1531 visits[i].visit_duration.InSeconds(); 1532 if (visits[i].visit_time > url.extended_info.last_visit_time) { 1533 url.extended_info.last_visit_time = visits[i].visit_time; 1534 } 1535 } 1536 // TODO(macourteau): implement the url.extended_info.visits stat. 1537 } 1538 } 1539 result.push_back(url); 1540 } 1541 1542 int delta_time = std::max(1, std::min(999, 1543 static_cast<int>((base::Time::Now() - request_start).InMilliseconds()))); 1544 STATIC_HISTOGRAM_POINTER_BLOCK( 1545 "NewTabPage.SuggestedSitesLoadTime", 1546 Add(delta_time), 1547 base::LinearHistogram::FactoryGet("NewTabPage.SuggestedSitesLoadTime", 1548 1, 1000, 100, base::Histogram::kUmaTargetedHistogramFlag)); 1549 1550 request->ForwardResult(request->handle(), result); 1551} 1552 1553void HistoryBackend::QueryMostVisitedURLsImpl(int result_count, 1554 int days_back, 1555 MostVisitedURLList* result) { 1556 if (!db_) 1557 return; 1558 1559 ScopedVector<PageUsageData> data; 1560 db_->QuerySegmentUsage(base::Time::Now() - 1561 base::TimeDelta::FromDays(days_back), 1562 result_count, &data.get()); 1563 1564 for (size_t i = 0; i < data.size(); ++i) { 1565 PageUsageData* current_data = data[i]; 1566 RedirectList redirects; 1567 GetMostRecentRedirectsFrom(current_data->GetURL(), &redirects); 1568 MostVisitedURL url = MakeMostVisitedURL(*current_data, redirects); 1569 result->push_back(url); 1570 } 1571} 1572 1573void HistoryBackend::GetRedirectsFromSpecificVisit( 1574 VisitID cur_visit, history::RedirectList* redirects) { 1575 // Follow any redirects from the given visit and add them to the list. 1576 // It *should* be impossible to get a circular chain here, but we check 1577 // just in case to avoid infinite loops. 1578 GURL cur_url; 1579 std::set<VisitID> visit_set; 1580 visit_set.insert(cur_visit); 1581 while (db_->GetRedirectFromVisit(cur_visit, &cur_visit, &cur_url)) { 1582 if (visit_set.find(cur_visit) != visit_set.end()) { 1583 NOTREACHED() << "Loop in visit chain, giving up"; 1584 return; 1585 } 1586 visit_set.insert(cur_visit); 1587 redirects->push_back(cur_url); 1588 } 1589} 1590 1591void HistoryBackend::GetRedirectsToSpecificVisit( 1592 VisitID cur_visit, 1593 history::RedirectList* redirects) { 1594 // Follow redirects going to cur_visit. These are added to |redirects| in 1595 // the order they are found. If a redirect chain looks like A -> B -> C and 1596 // |cur_visit| = C, redirects will be {B, A} in that order. 1597 if (!db_) 1598 return; 1599 1600 GURL cur_url; 1601 std::set<VisitID> visit_set; 1602 visit_set.insert(cur_visit); 1603 while (db_->GetRedirectToVisit(cur_visit, &cur_visit, &cur_url)) { 1604 if (visit_set.find(cur_visit) != visit_set.end()) { 1605 NOTREACHED() << "Loop in visit chain, giving up"; 1606 return; 1607 } 1608 visit_set.insert(cur_visit); 1609 redirects->push_back(cur_url); 1610 } 1611} 1612 1613bool HistoryBackend::GetMostRecentRedirectsFrom( 1614 const GURL& from_url, 1615 history::RedirectList* redirects) { 1616 redirects->clear(); 1617 if (!db_) 1618 return false; 1619 1620 URLID from_url_id = db_->GetRowForURL(from_url, NULL); 1621 VisitID cur_visit = db_->GetMostRecentVisitForURL(from_url_id, NULL); 1622 if (!cur_visit) 1623 return false; // No visits for URL. 1624 1625 GetRedirectsFromSpecificVisit(cur_visit, redirects); 1626 return true; 1627} 1628 1629bool HistoryBackend::GetMostRecentRedirectsTo( 1630 const GURL& to_url, 1631 history::RedirectList* redirects) { 1632 redirects->clear(); 1633 if (!db_) 1634 return false; 1635 1636 URLID to_url_id = db_->GetRowForURL(to_url, NULL); 1637 VisitID cur_visit = db_->GetMostRecentVisitForURL(to_url_id, NULL); 1638 if (!cur_visit) 1639 return false; // No visits for URL. 1640 1641 GetRedirectsToSpecificVisit(cur_visit, redirects); 1642 return true; 1643} 1644 1645void HistoryBackend::ScheduleAutocomplete(HistoryURLProvider* provider, 1646 HistoryURLProviderParams* params) { 1647 // ExecuteWithDB should handle the NULL database case. 1648 provider->ExecuteWithDB(this, db_.get(), params); 1649} 1650 1651void HistoryBackend::DeleteFTSIndexDatabases() { 1652 // Find files on disk matching the text databases file pattern so we can 1653 // quickly test for and delete them. 1654 base::FilePath::StringType filepattern = 1655 FILE_PATH_LITERAL("History Index *"); 1656 base::FileEnumerator enumerator( 1657 history_dir_, false, base::FileEnumerator::FILES, filepattern); 1658 int num_databases_deleted = 0; 1659 base::FilePath current_file; 1660 while (!(current_file = enumerator.Next()).empty()) { 1661 if (sql::Connection::Delete(current_file)) 1662 num_databases_deleted++; 1663 } 1664 UMA_HISTOGRAM_COUNTS("History.DeleteFTSIndexDatabases", 1665 num_databases_deleted); 1666} 1667 1668void HistoryBackend::GetFavicons( 1669 const std::vector<GURL>& icon_urls, 1670 int icon_types, 1671 int desired_size_in_dip, 1672 const std::vector<ui::ScaleFactor>& desired_scale_factors, 1673 std::vector<chrome::FaviconBitmapResult>* bitmap_results) { 1674 UpdateFaviconMappingsAndFetchImpl(NULL, icon_urls, icon_types, 1675 desired_size_in_dip, desired_scale_factors, 1676 bitmap_results); 1677} 1678 1679void HistoryBackend::GetFaviconsForURL( 1680 const GURL& page_url, 1681 int icon_types, 1682 int desired_size_in_dip, 1683 const std::vector<ui::ScaleFactor>& desired_scale_factors, 1684 std::vector<chrome::FaviconBitmapResult>* bitmap_results) { 1685 DCHECK(bitmap_results); 1686 GetFaviconsFromDB(page_url, icon_types, desired_size_in_dip, 1687 desired_scale_factors, bitmap_results); 1688} 1689 1690void HistoryBackend::GetFaviconForID( 1691 chrome::FaviconID favicon_id, 1692 int desired_size_in_dip, 1693 ui::ScaleFactor desired_scale_factor, 1694 std::vector<chrome::FaviconBitmapResult>* bitmap_results) { 1695 std::vector<chrome::FaviconID> favicon_ids; 1696 favicon_ids.push_back(favicon_id); 1697 std::vector<ui::ScaleFactor> desired_scale_factors; 1698 desired_scale_factors.push_back(desired_scale_factor); 1699 1700 // Get results from DB. 1701 GetFaviconBitmapResultsForBestMatch(favicon_ids, 1702 desired_size_in_dip, 1703 desired_scale_factors, 1704 bitmap_results); 1705} 1706 1707void HistoryBackend::UpdateFaviconMappingsAndFetch( 1708 const GURL& page_url, 1709 const std::vector<GURL>& icon_urls, 1710 int icon_types, 1711 int desired_size_in_dip, 1712 const std::vector<ui::ScaleFactor>& desired_scale_factors, 1713 std::vector<chrome::FaviconBitmapResult>* bitmap_results) { 1714 UpdateFaviconMappingsAndFetchImpl(&page_url, icon_urls, icon_types, 1715 desired_size_in_dip, desired_scale_factors, 1716 bitmap_results); 1717} 1718 1719void HistoryBackend::MergeFavicon( 1720 const GURL& page_url, 1721 const GURL& icon_url, 1722 chrome::IconType icon_type, 1723 scoped_refptr<base::RefCountedMemory> bitmap_data, 1724 const gfx::Size& pixel_size) { 1725 if (!thumbnail_db_ || !db_) 1726 return; 1727 1728 chrome::FaviconID favicon_id = 1729 thumbnail_db_->GetFaviconIDForFaviconURL(icon_url, icon_type, NULL); 1730 1731 if (!favicon_id) { 1732 // There is no favicon at |icon_url|, create it. 1733 favicon_id = thumbnail_db_->AddFavicon(icon_url, icon_type); 1734 } 1735 1736 std::vector<FaviconBitmapIDSize> bitmap_id_sizes; 1737 thumbnail_db_->GetFaviconBitmapIDSizes(favicon_id, &bitmap_id_sizes); 1738 1739 // If there is already a favicon bitmap of |pixel_size| at |icon_url|, 1740 // replace it. 1741 bool bitmap_identical = false; 1742 bool replaced_bitmap = false; 1743 for (size_t i = 0; i < bitmap_id_sizes.size(); ++i) { 1744 if (bitmap_id_sizes[i].pixel_size == pixel_size) { 1745 if (IsFaviconBitmapDataEqual(bitmap_id_sizes[i].bitmap_id, bitmap_data)) { 1746 thumbnail_db_->SetFaviconBitmapLastUpdateTime( 1747 bitmap_id_sizes[i].bitmap_id, base::Time::Now()); 1748 bitmap_identical = true; 1749 } else { 1750 thumbnail_db_->SetFaviconBitmap(bitmap_id_sizes[i].bitmap_id, 1751 bitmap_data, base::Time::Now()); 1752 replaced_bitmap = true; 1753 } 1754 break; 1755 } 1756 } 1757 1758 // Create a vector of the pixel sizes of the favicon bitmaps currently at 1759 // |icon_url|. 1760 std::vector<gfx::Size> favicon_sizes; 1761 for (size_t i = 0; i < bitmap_id_sizes.size(); ++i) 1762 favicon_sizes.push_back(bitmap_id_sizes[i].pixel_size); 1763 1764 if (!replaced_bitmap && !bitmap_identical) { 1765 // Set the preexisting favicon bitmaps as expired as the preexisting favicon 1766 // bitmaps are not consistent with the merged in data. 1767 thumbnail_db_->SetFaviconOutOfDate(favicon_id); 1768 1769 // Delete an arbitrary favicon bitmap to avoid going over the limit of 1770 // |kMaxFaviconBitmapsPerIconURL|. 1771 if (bitmap_id_sizes.size() >= kMaxFaviconBitmapsPerIconURL) { 1772 thumbnail_db_->DeleteFaviconBitmap(bitmap_id_sizes[0].bitmap_id); 1773 favicon_sizes.erase(favicon_sizes.begin()); 1774 } 1775 thumbnail_db_->AddFaviconBitmap(favicon_id, bitmap_data, base::Time::Now(), 1776 pixel_size); 1777 favicon_sizes.push_back(pixel_size); 1778 } 1779 1780 // A site may have changed the favicons that it uses for |page_url|. 1781 // Example Scenario: 1782 // page_url = news.google.com 1783 // Initial State: www.google.com/favicon.ico 16x16, 32x32 1784 // MergeFavicon(news.google.com, news.google.com/news_specific.ico, ..., 1785 // ..., 16x16) 1786 // 1787 // Difficulties: 1788 // 1. Sync requires that a call to GetFaviconsForURL() returns the 1789 // |bitmap_data| passed into MergeFavicon(). 1790 // - It is invalid for the 16x16 bitmap for www.google.com/favicon.ico to 1791 // stay mapped to news.google.com because it would be unclear which 16x16 1792 // bitmap should be returned via GetFaviconsForURL(). 1793 // 1794 // 2. www.google.com/favicon.ico may be mapped to more than just 1795 // news.google.com (eg www.google.com). 1796 // - The 16x16 bitmap cannot be deleted from www.google.com/favicon.ico 1797 // 1798 // To resolve these problems, we copy all of the favicon bitmaps previously 1799 // mapped to news.google.com (|page_url|) and add them to the favicon at 1800 // news.google.com/news_specific.ico (|icon_url|). The favicon sizes for 1801 // |icon_url| are set to default to indicate that |icon_url| has incomplete 1802 // / incorrect data. 1803 // Difficulty 1: All but news.google.com/news_specific.ico are unmapped from 1804 // news.google.com 1805 // Difficulty 2: The favicon bitmaps for www.google.com/favicon.ico are not 1806 // modified. 1807 1808 std::vector<IconMapping> icon_mappings; 1809 thumbnail_db_->GetIconMappingsForPageURL(page_url, icon_type, &icon_mappings); 1810 1811 // Copy the favicon bitmaps mapped to |page_url| to the favicon at |icon_url| 1812 // till the limit of |kMaxFaviconBitmapsPerIconURL| is reached. 1813 for (size_t i = 0; i < icon_mappings.size(); ++i) { 1814 if (favicon_sizes.size() >= kMaxFaviconBitmapsPerIconURL) 1815 break; 1816 1817 if (icon_mappings[i].icon_url == icon_url) 1818 continue; 1819 1820 std::vector<FaviconBitmap> bitmaps_to_copy; 1821 thumbnail_db_->GetFaviconBitmaps(icon_mappings[i].icon_id, 1822 &bitmaps_to_copy); 1823 for (size_t j = 0; j < bitmaps_to_copy.size(); ++j) { 1824 // Do not add a favicon bitmap at a pixel size for which there is already 1825 // a favicon bitmap mapped to |icon_url|. The one there is more correct 1826 // and having multiple equally sized favicon bitmaps for |page_url| is 1827 // ambiguous in terms of GetFaviconsForURL(). 1828 std::vector<gfx::Size>::iterator it = std::find(favicon_sizes.begin(), 1829 favicon_sizes.end(), bitmaps_to_copy[j].pixel_size); 1830 if (it != favicon_sizes.end()) 1831 continue; 1832 1833 // Add the favicon bitmap as expired as it is not consistent with the 1834 // merged in data. 1835 thumbnail_db_->AddFaviconBitmap(favicon_id, 1836 bitmaps_to_copy[j].bitmap_data, base::Time(), 1837 bitmaps_to_copy[j].pixel_size); 1838 favicon_sizes.push_back(bitmaps_to_copy[j].pixel_size); 1839 1840 if (favicon_sizes.size() >= kMaxFaviconBitmapsPerIconURL) 1841 break; 1842 } 1843 } 1844 1845 // Update the favicon mappings such that only |icon_url| is mapped to 1846 // |page_url|. 1847 bool mapping_changed = false; 1848 if (icon_mappings.size() != 1 || icon_mappings[0].icon_url != icon_url) { 1849 std::vector<chrome::FaviconID> favicon_ids; 1850 favicon_ids.push_back(favicon_id); 1851 SetFaviconMappingsForPageAndRedirects(page_url, icon_type, favicon_ids); 1852 mapping_changed = true; 1853 } 1854 1855 if (mapping_changed || !bitmap_identical) 1856 SendFaviconChangedNotificationForPageAndRedirects(page_url); 1857 ScheduleCommit(); 1858} 1859 1860void HistoryBackend::SetFavicons( 1861 const GURL& page_url, 1862 chrome::IconType icon_type, 1863 const std::vector<chrome::FaviconBitmapData>& favicon_bitmap_data) { 1864 if (!thumbnail_db_ || !db_) 1865 return; 1866 1867 DCHECK(ValidateSetFaviconsParams(favicon_bitmap_data)); 1868 1869 // Build map of FaviconBitmapData for each icon url. 1870 typedef std::map<GURL, std::vector<chrome::FaviconBitmapData> > 1871 BitmapDataByIconURL; 1872 BitmapDataByIconURL grouped_by_icon_url; 1873 for (size_t i = 0; i < favicon_bitmap_data.size(); ++i) { 1874 const GURL& icon_url = favicon_bitmap_data[i].icon_url; 1875 grouped_by_icon_url[icon_url].push_back(favicon_bitmap_data[i]); 1876 } 1877 1878 // Track whether the method modifies or creates any favicon bitmaps, favicons 1879 // or icon mappings. 1880 bool data_modified = false; 1881 1882 std::vector<chrome::FaviconID> icon_ids; 1883 for (BitmapDataByIconURL::const_iterator it = grouped_by_icon_url.begin(); 1884 it != grouped_by_icon_url.end(); ++it) { 1885 const GURL& icon_url = it->first; 1886 chrome::FaviconID icon_id = 1887 thumbnail_db_->GetFaviconIDForFaviconURL(icon_url, icon_type, NULL); 1888 1889 if (!icon_id) { 1890 // TODO(pkotwicz): Remove the favicon sizes attribute from 1891 // ThumbnailDatabase::AddFavicon(). 1892 icon_id = thumbnail_db_->AddFavicon(icon_url, icon_type); 1893 data_modified = true; 1894 } 1895 icon_ids.push_back(icon_id); 1896 1897 if (!data_modified) 1898 SetFaviconBitmaps(icon_id, it->second, &data_modified); 1899 else 1900 SetFaviconBitmaps(icon_id, it->second, NULL); 1901 } 1902 1903 data_modified |= 1904 SetFaviconMappingsForPageAndRedirects(page_url, icon_type, icon_ids); 1905 1906 if (data_modified) { 1907 // Send notification to the UI as an icon mapping, favicon, or favicon 1908 // bitmap was changed by this function. 1909 SendFaviconChangedNotificationForPageAndRedirects(page_url); 1910 } 1911 ScheduleCommit(); 1912} 1913 1914void HistoryBackend::SetFaviconsOutOfDateForPage(const GURL& page_url) { 1915 std::vector<IconMapping> icon_mappings; 1916 1917 if (!thumbnail_db_ || 1918 !thumbnail_db_->GetIconMappingsForPageURL(page_url, 1919 &icon_mappings)) 1920 return; 1921 1922 for (std::vector<IconMapping>::iterator m = icon_mappings.begin(); 1923 m != icon_mappings.end(); ++m) { 1924 thumbnail_db_->SetFaviconOutOfDate(m->icon_id); 1925 } 1926 ScheduleCommit(); 1927} 1928 1929void HistoryBackend::CloneFavicons(const GURL& old_page_url, 1930 const GURL& new_page_url) { 1931 if (!thumbnail_db_) 1932 return; 1933 1934 // Prevent cross-domain cloning. 1935 if (old_page_url.GetOrigin() != new_page_url.GetOrigin()) 1936 return; 1937 1938 thumbnail_db_->CloneIconMappings(old_page_url, new_page_url); 1939 ScheduleCommit(); 1940} 1941 1942void HistoryBackend::SetImportedFavicons( 1943 const std::vector<ImportedFaviconUsage>& favicon_usage) { 1944 if (!db_ || !thumbnail_db_) 1945 return; 1946 1947 Time now = Time::Now(); 1948 1949 // Track all URLs that had their favicons set or updated. 1950 std::set<GURL> favicons_changed; 1951 1952 for (size_t i = 0; i < favicon_usage.size(); i++) { 1953 chrome::FaviconID favicon_id = thumbnail_db_->GetFaviconIDForFaviconURL( 1954 favicon_usage[i].favicon_url, chrome::FAVICON, NULL); 1955 if (!favicon_id) { 1956 // This favicon doesn't exist yet, so we create it using the given data. 1957 // TODO(pkotwicz): Pass in real pixel size. 1958 favicon_id = thumbnail_db_->AddFavicon( 1959 favicon_usage[i].favicon_url, 1960 chrome::FAVICON, 1961 new base::RefCountedBytes(favicon_usage[i].png_data), 1962 now, 1963 gfx::Size()); 1964 } 1965 1966 // Save the mapping from all the URLs to the favicon. 1967 BookmarkService* bookmark_service = GetBookmarkService(); 1968 for (std::set<GURL>::const_iterator url = favicon_usage[i].urls.begin(); 1969 url != favicon_usage[i].urls.end(); ++url) { 1970 URLRow url_row; 1971 if (!db_->GetRowForURL(*url, &url_row)) { 1972 // If the URL is present as a bookmark, add the url in history to 1973 // save the favicon mapping. This will match with what history db does 1974 // for regular bookmarked URLs with favicons - when history db is 1975 // cleaned, we keep an entry in the db with 0 visits as long as that 1976 // url is bookmarked. 1977 if (bookmark_service && bookmark_service_->IsBookmarked(*url)) { 1978 URLRow url_info(*url); 1979 url_info.set_visit_count(0); 1980 url_info.set_typed_count(0); 1981 url_info.set_last_visit(base::Time()); 1982 url_info.set_hidden(false); 1983 db_->AddURL(url_info); 1984 thumbnail_db_->AddIconMapping(*url, favicon_id); 1985 favicons_changed.insert(*url); 1986 } 1987 } else { 1988 if (!thumbnail_db_->GetIconMappingsForPageURL( 1989 *url, chrome::FAVICON, NULL)) { 1990 // URL is present in history, update the favicon *only* if it is not 1991 // set already. 1992 thumbnail_db_->AddIconMapping(*url, favicon_id); 1993 favicons_changed.insert(*url); 1994 } 1995 } 1996 } 1997 } 1998 1999 if (!favicons_changed.empty()) { 2000 // Send the notification about the changed favicon URLs. 2001 FaviconChangedDetails* changed_details = new FaviconChangedDetails; 2002 changed_details->urls.swap(favicons_changed); 2003 BroadcastNotifications(chrome::NOTIFICATION_FAVICON_CHANGED, 2004 changed_details); 2005 } 2006} 2007 2008void HistoryBackend::UpdateFaviconMappingsAndFetchImpl( 2009 const GURL* page_url, 2010 const std::vector<GURL>& icon_urls, 2011 int icon_types, 2012 int desired_size_in_dip, 2013 const std::vector<ui::ScaleFactor>& desired_scale_factors, 2014 std::vector<chrome::FaviconBitmapResult>* bitmap_results) { 2015 // If |page_url| is specified, |icon_types| must be either a single icon 2016 // type or icon types which are equivalent. 2017 DCHECK(!page_url || 2018 icon_types == chrome::FAVICON || 2019 icon_types == chrome::TOUCH_ICON || 2020 icon_types == chrome::TOUCH_PRECOMPOSED_ICON || 2021 icon_types == (chrome::TOUCH_ICON | chrome::TOUCH_PRECOMPOSED_ICON)); 2022 bitmap_results->clear(); 2023 2024 if (!thumbnail_db_) { 2025 return; 2026 } 2027 2028 std::vector<chrome::FaviconID> favicon_ids; 2029 2030 // The icon type for which the mappings will the updated and data will be 2031 // returned. 2032 chrome::IconType selected_icon_type = chrome::INVALID_ICON; 2033 2034 for (size_t i = 0; i < icon_urls.size(); ++i) { 2035 const GURL& icon_url = icon_urls[i]; 2036 chrome::IconType icon_type_out; 2037 const chrome::FaviconID favicon_id = 2038 thumbnail_db_->GetFaviconIDForFaviconURL( 2039 icon_url, icon_types, &icon_type_out); 2040 2041 if (favicon_id) { 2042 // Return and update icon mappings only for the largest icon type. As 2043 // |icon_urls| is not sorted in terms of icon type, clear |favicon_ids| 2044 // if an |icon_url| with a larger icon type is found. 2045 if (icon_type_out > selected_icon_type) { 2046 selected_icon_type = icon_type_out; 2047 favicon_ids.clear(); 2048 } 2049 if (icon_type_out == selected_icon_type) 2050 favicon_ids.push_back(favicon_id); 2051 } 2052 } 2053 2054 if (page_url && !favicon_ids.empty()) { 2055 bool mappings_updated = 2056 SetFaviconMappingsForPageAndRedirects(*page_url, selected_icon_type, 2057 favicon_ids); 2058 if (mappings_updated) { 2059 SendFaviconChangedNotificationForPageAndRedirects(*page_url); 2060 ScheduleCommit(); 2061 } 2062 } 2063 2064 GetFaviconBitmapResultsForBestMatch(favicon_ids, desired_size_in_dip, 2065 desired_scale_factors, bitmap_results); 2066} 2067 2068void HistoryBackend::SetFaviconBitmaps( 2069 chrome::FaviconID icon_id, 2070 const std::vector<chrome::FaviconBitmapData>& favicon_bitmap_data, 2071 bool* favicon_bitmaps_changed) { 2072 if (favicon_bitmaps_changed) 2073 *favicon_bitmaps_changed = false; 2074 2075 std::vector<FaviconBitmapIDSize> bitmap_id_sizes; 2076 thumbnail_db_->GetFaviconBitmapIDSizes(icon_id, &bitmap_id_sizes); 2077 2078 std::vector<chrome::FaviconBitmapData> to_add = favicon_bitmap_data; 2079 2080 for (size_t i = 0; i < bitmap_id_sizes.size(); ++i) { 2081 const gfx::Size& pixel_size = bitmap_id_sizes[i].pixel_size; 2082 std::vector<chrome::FaviconBitmapData>::iterator match_it = to_add.end(); 2083 for (std::vector<chrome::FaviconBitmapData>::iterator it = to_add.begin(); 2084 it != to_add.end(); ++it) { 2085 if (it->pixel_size == pixel_size) { 2086 match_it = it; 2087 break; 2088 } 2089 } 2090 2091 FaviconBitmapID bitmap_id = bitmap_id_sizes[i].bitmap_id; 2092 if (match_it == to_add.end()) { 2093 thumbnail_db_->DeleteFaviconBitmap(bitmap_id); 2094 2095 if (favicon_bitmaps_changed) 2096 *favicon_bitmaps_changed = true; 2097 } else { 2098 if (favicon_bitmaps_changed && 2099 !*favicon_bitmaps_changed && 2100 IsFaviconBitmapDataEqual(bitmap_id, match_it->bitmap_data)) { 2101 thumbnail_db_->SetFaviconBitmapLastUpdateTime( 2102 bitmap_id, base::Time::Now()); 2103 } else { 2104 thumbnail_db_->SetFaviconBitmap(bitmap_id, match_it->bitmap_data, 2105 base::Time::Now()); 2106 2107 if (favicon_bitmaps_changed) 2108 *favicon_bitmaps_changed = true; 2109 } 2110 to_add.erase(match_it); 2111 } 2112 } 2113 2114 for (size_t i = 0; i < to_add.size(); ++i) { 2115 thumbnail_db_->AddFaviconBitmap(icon_id, to_add[i].bitmap_data, 2116 base::Time::Now(), to_add[i].pixel_size); 2117 2118 if (favicon_bitmaps_changed) 2119 *favicon_bitmaps_changed = true; 2120 } 2121} 2122 2123bool HistoryBackend::ValidateSetFaviconsParams( 2124 const std::vector<chrome::FaviconBitmapData>& favicon_bitmap_data) const { 2125 typedef std::map<GURL, size_t> BitmapsPerIconURL; 2126 BitmapsPerIconURL num_bitmaps_per_icon_url; 2127 for (size_t i = 0; i < favicon_bitmap_data.size(); ++i) { 2128 if (!favicon_bitmap_data[i].bitmap_data.get()) 2129 return false; 2130 2131 const GURL& icon_url = favicon_bitmap_data[i].icon_url; 2132 if (!num_bitmaps_per_icon_url.count(icon_url)) 2133 num_bitmaps_per_icon_url[icon_url] = 1u; 2134 else 2135 ++num_bitmaps_per_icon_url[icon_url]; 2136 } 2137 2138 if (num_bitmaps_per_icon_url.size() > kMaxFaviconsPerPage) 2139 return false; 2140 2141 for (BitmapsPerIconURL::const_iterator it = num_bitmaps_per_icon_url.begin(); 2142 it != num_bitmaps_per_icon_url.end(); ++it) { 2143 if (it->second > kMaxFaviconBitmapsPerIconURL) 2144 return false; 2145 } 2146 return true; 2147} 2148 2149bool HistoryBackend::IsFaviconBitmapDataEqual( 2150 FaviconBitmapID bitmap_id, 2151 const scoped_refptr<base::RefCountedMemory>& new_bitmap_data) { 2152 if (!new_bitmap_data.get()) 2153 return false; 2154 2155 scoped_refptr<base::RefCountedMemory> original_bitmap_data; 2156 thumbnail_db_->GetFaviconBitmap(bitmap_id, 2157 NULL, 2158 &original_bitmap_data, 2159 NULL); 2160 return new_bitmap_data->Equals(original_bitmap_data); 2161} 2162 2163bool HistoryBackend::GetFaviconsFromDB( 2164 const GURL& page_url, 2165 int icon_types, 2166 int desired_size_in_dip, 2167 const std::vector<ui::ScaleFactor>& desired_scale_factors, 2168 std::vector<chrome::FaviconBitmapResult>* favicon_bitmap_results) { 2169 DCHECK(favicon_bitmap_results); 2170 favicon_bitmap_results->clear(); 2171 2172 if (!db_ || !thumbnail_db_) 2173 return false; 2174 2175 // Time the query. 2176 TimeTicks beginning_time = TimeTicks::Now(); 2177 2178 // Get FaviconIDs for |page_url| and one of |icon_types|. 2179 std::vector<IconMapping> icon_mappings; 2180 thumbnail_db_->GetIconMappingsForPageURL(page_url, icon_types, 2181 &icon_mappings); 2182 std::vector<chrome::FaviconID> favicon_ids; 2183 for (size_t i = 0; i < icon_mappings.size(); ++i) 2184 favicon_ids.push_back(icon_mappings[i].icon_id); 2185 2186 // Populate |favicon_bitmap_results| and |icon_url_sizes|. 2187 bool success = GetFaviconBitmapResultsForBestMatch(favicon_ids, 2188 desired_size_in_dip, desired_scale_factors, favicon_bitmap_results); 2189 UMA_HISTOGRAM_TIMES("History.GetFavIconFromDB", // historical name 2190 TimeTicks::Now() - beginning_time); 2191 return success && !favicon_bitmap_results->empty(); 2192} 2193 2194bool HistoryBackend::GetFaviconBitmapResultsForBestMatch( 2195 const std::vector<chrome::FaviconID>& candidate_favicon_ids, 2196 int desired_size_in_dip, 2197 const std::vector<ui::ScaleFactor>& desired_scale_factors, 2198 std::vector<chrome::FaviconBitmapResult>* favicon_bitmap_results) { 2199 favicon_bitmap_results->clear(); 2200 2201 if (candidate_favicon_ids.empty()) 2202 return true; 2203 2204 // Find the FaviconID and the FaviconBitmapIDs which best match 2205 // |desired_size_in_dip| and |desired_scale_factors|. 2206 // TODO(pkotwicz): Select bitmap results from multiple favicons once 2207 // content::FaviconStatus supports multiple icon URLs. 2208 chrome::FaviconID best_favicon_id = 0; 2209 std::vector<FaviconBitmapID> best_bitmap_ids; 2210 float highest_score = kSelectFaviconFramesInvalidScore; 2211 for (size_t i = 0; i < candidate_favicon_ids.size(); ++i) { 2212 std::vector<FaviconBitmapIDSize> bitmap_id_sizes; 2213 thumbnail_db_->GetFaviconBitmapIDSizes(candidate_favicon_ids[i], 2214 &bitmap_id_sizes); 2215 2216 // Build vector of gfx::Size from |bitmap_id_sizes|. 2217 std::vector<gfx::Size> sizes; 2218 for (size_t j = 0; j < bitmap_id_sizes.size(); ++j) 2219 sizes.push_back(bitmap_id_sizes[j].pixel_size); 2220 2221 std::vector<size_t> candidate_bitmap_indices; 2222 float score = 0; 2223 SelectFaviconFrameIndices(sizes, 2224 desired_scale_factors, 2225 desired_size_in_dip, 2226 &candidate_bitmap_indices, 2227 &score); 2228 if (score > highest_score) { 2229 highest_score = score; 2230 best_favicon_id = candidate_favicon_ids[i], 2231 best_bitmap_ids.clear(); 2232 for (size_t j = 0; j < candidate_bitmap_indices.size(); ++j) { 2233 size_t candidate_index = candidate_bitmap_indices[j]; 2234 best_bitmap_ids.push_back( 2235 bitmap_id_sizes[candidate_index].bitmap_id); 2236 } 2237 } 2238 } 2239 2240 // Construct FaviconBitmapResults from |best_favicon_id| and 2241 // |best_bitmap_ids|. 2242 GURL icon_url; 2243 chrome::IconType icon_type; 2244 if (!thumbnail_db_->GetFaviconHeader(best_favicon_id, &icon_url, 2245 &icon_type)) { 2246 return false; 2247 } 2248 2249 for (size_t i = 0; i < best_bitmap_ids.size(); ++i) { 2250 base::Time last_updated; 2251 chrome::FaviconBitmapResult bitmap_result; 2252 bitmap_result.icon_url = icon_url; 2253 bitmap_result.icon_type = icon_type; 2254 if (!thumbnail_db_->GetFaviconBitmap(best_bitmap_ids[i], 2255 &last_updated, 2256 &bitmap_result.bitmap_data, 2257 &bitmap_result.pixel_size)) { 2258 return false; 2259 } 2260 2261 bitmap_result.expired = (Time::Now() - last_updated) > 2262 TimeDelta::FromDays(kFaviconRefetchDays); 2263 if (bitmap_result.is_valid()) 2264 favicon_bitmap_results->push_back(bitmap_result); 2265 } 2266 return true; 2267} 2268 2269bool HistoryBackend::SetFaviconMappingsForPageAndRedirects( 2270 const GURL& page_url, 2271 chrome::IconType icon_type, 2272 const std::vector<chrome::FaviconID>& icon_ids) { 2273 if (!thumbnail_db_) 2274 return false; 2275 2276 // Find all the pages whose favicons we should set, we want to set it for 2277 // all the pages in the redirect chain if it redirected. 2278 history::RedirectList redirects; 2279 GetCachedRecentRedirects(page_url, &redirects); 2280 2281 bool mappings_changed = false; 2282 2283 // Save page <-> favicon associations. 2284 for (history::RedirectList::const_iterator i(redirects.begin()); 2285 i != redirects.end(); ++i) { 2286 mappings_changed |= SetFaviconMappingsForPage(*i, icon_type, icon_ids); 2287 } 2288 return mappings_changed; 2289} 2290 2291bool HistoryBackend::SetFaviconMappingsForPage( 2292 const GURL& page_url, 2293 chrome::IconType icon_type, 2294 const std::vector<chrome::FaviconID>& icon_ids) { 2295 DCHECK_LE(icon_ids.size(), kMaxFaviconsPerPage); 2296 bool mappings_changed = false; 2297 2298 // Two icon types are considered 'equivalent' if one of the icon types is 2299 // TOUCH_ICON and the other is TOUCH_PRECOMPOSED_ICON. 2300 // 2301 // Sets the icon mappings from |page_url| for |icon_type| to the favicons 2302 // with |icon_ids|. Mappings for |page_url| to favicons of type |icon_type| 2303 // whose FaviconID is not in |icon_ids| are removed. All icon mappings for 2304 // |page_url| to favicons of a type equivalent to |icon_type| are removed. 2305 // Remove any favicons which are orphaned as a result of the removal of the 2306 // icon mappings. 2307 2308 std::vector<chrome::FaviconID> unmapped_icon_ids = icon_ids; 2309 2310 std::vector<IconMapping> icon_mappings; 2311 thumbnail_db_->GetIconMappingsForPageURL(page_url, &icon_mappings); 2312 2313 for (std::vector<IconMapping>::iterator m = icon_mappings.begin(); 2314 m != icon_mappings.end(); ++m) { 2315 std::vector<chrome::FaviconID>::iterator icon_id_it = std::find( 2316 unmapped_icon_ids.begin(), unmapped_icon_ids.end(), m->icon_id); 2317 2318 // If the icon mapping already exists, avoid removing it and adding it back. 2319 if (icon_id_it != unmapped_icon_ids.end()) { 2320 unmapped_icon_ids.erase(icon_id_it); 2321 continue; 2322 } 2323 2324 if ((icon_type == chrome::TOUCH_ICON && 2325 m->icon_type == chrome::TOUCH_PRECOMPOSED_ICON) || 2326 (icon_type == chrome::TOUCH_PRECOMPOSED_ICON && 2327 m->icon_type == chrome::TOUCH_ICON) || (icon_type == m->icon_type)) { 2328 thumbnail_db_->DeleteIconMapping(m->mapping_id); 2329 2330 // Removing the icon mapping may have orphaned the associated favicon so 2331 // we must recheck it. This is not super fast, but this case will get 2332 // triggered rarely, since normally a page will always map to the same 2333 // favicon IDs. It will mostly happen for favicons we import. 2334 if (!thumbnail_db_->HasMappingFor(m->icon_id)) 2335 thumbnail_db_->DeleteFavicon(m->icon_id); 2336 mappings_changed = true; 2337 } 2338 } 2339 2340 for (size_t i = 0; i < unmapped_icon_ids.size(); ++i) { 2341 thumbnail_db_->AddIconMapping(page_url, unmapped_icon_ids[i]); 2342 mappings_changed = true; 2343 } 2344 return mappings_changed; 2345} 2346 2347void HistoryBackend::GetCachedRecentRedirects( 2348 const GURL& page_url, 2349 history::RedirectList* redirect_list) { 2350 RedirectCache::iterator iter = recent_redirects_.Get(page_url); 2351 if (iter != recent_redirects_.end()) { 2352 *redirect_list = iter->second; 2353 2354 // The redirect chain should have the destination URL as the last item. 2355 DCHECK(!redirect_list->empty()); 2356 DCHECK(redirect_list->back() == page_url); 2357 } else { 2358 // No known redirects, construct mock redirect chain containing |page_url|. 2359 redirect_list->push_back(page_url); 2360 } 2361} 2362 2363void HistoryBackend::SendFaviconChangedNotificationForPageAndRedirects( 2364 const GURL& page_url) { 2365 history::RedirectList redirect_list; 2366 GetCachedRecentRedirects(page_url, &redirect_list); 2367 2368 FaviconChangedDetails* changed_details = new FaviconChangedDetails; 2369 for (size_t i = 0; i < redirect_list.size(); ++i) 2370 changed_details->urls.insert(redirect_list[i]); 2371 2372 BroadcastNotifications(chrome::NOTIFICATION_FAVICON_CHANGED, 2373 changed_details); 2374} 2375 2376void HistoryBackend::Commit() { 2377 if (!db_) 2378 return; 2379 2380 // Note that a commit may not actually have been scheduled if a caller 2381 // explicitly calls this instead of using ScheduleCommit. Likewise, we 2382 // may reset the flag written by a pending commit. But this is OK! It 2383 // will merely cause extra commits (which is kind of the idea). We 2384 // could optimize more for this case (we may get two extra commits in 2385 // some cases) but it hasn't been important yet. 2386 CancelScheduledCommit(); 2387 2388 db_->CommitTransaction(); 2389 DCHECK(db_->transaction_nesting() == 0) << "Somebody left a transaction open"; 2390 db_->BeginTransaction(); 2391 2392 if (thumbnail_db_) { 2393 thumbnail_db_->CommitTransaction(); 2394 DCHECK(thumbnail_db_->transaction_nesting() == 0) << 2395 "Somebody left a transaction open"; 2396 thumbnail_db_->BeginTransaction(); 2397 } 2398 2399 if (archived_db_) { 2400 archived_db_->CommitTransaction(); 2401 archived_db_->BeginTransaction(); 2402 } 2403} 2404 2405void HistoryBackend::ScheduleCommit() { 2406 if (scheduled_commit_.get()) 2407 return; 2408 scheduled_commit_ = new CommitLaterTask(this); 2409 base::MessageLoop::current()->PostDelayedTask( 2410 FROM_HERE, 2411 base::Bind(&CommitLaterTask::RunCommit, scheduled_commit_.get()), 2412 base::TimeDelta::FromSeconds(kCommitIntervalSeconds)); 2413} 2414 2415void HistoryBackend::CancelScheduledCommit() { 2416 if (scheduled_commit_.get()) { 2417 scheduled_commit_->Cancel(); 2418 scheduled_commit_ = NULL; 2419 } 2420} 2421 2422void HistoryBackend::ProcessDBTaskImpl() { 2423 if (!db_) { 2424 // db went away, release all the refs. 2425 ReleaseDBTasks(); 2426 return; 2427 } 2428 2429 // Remove any canceled tasks. 2430 while (!db_task_requests_.empty() && db_task_requests_.front()->canceled()) { 2431 db_task_requests_.front()->Release(); 2432 db_task_requests_.pop_front(); 2433 } 2434 if (db_task_requests_.empty()) 2435 return; 2436 2437 // Run the first task. 2438 HistoryDBTaskRequest* request = db_task_requests_.front(); 2439 db_task_requests_.pop_front(); 2440 if (request->value->RunOnDBThread(this, db_.get())) { 2441 // The task is done. Notify the callback. 2442 request->ForwardResult(); 2443 // We AddRef'd the request before adding, need to release it now. 2444 request->Release(); 2445 } else { 2446 // Tasks wants to run some more. Schedule it at the end of current tasks. 2447 db_task_requests_.push_back(request); 2448 // And process it after an invoke later. 2449 base::MessageLoop::current()->PostTask( 2450 FROM_HERE, base::Bind(&HistoryBackend::ProcessDBTaskImpl, this)); 2451 } 2452} 2453 2454void HistoryBackend::ReleaseDBTasks() { 2455 for (std::list<HistoryDBTaskRequest*>::iterator i = 2456 db_task_requests_.begin(); i != db_task_requests_.end(); ++i) { 2457 (*i)->Release(); 2458 } 2459 db_task_requests_.clear(); 2460} 2461 2462//////////////////////////////////////////////////////////////////////////////// 2463// 2464// Generic operations 2465// 2466//////////////////////////////////////////////////////////////////////////////// 2467 2468void HistoryBackend::DeleteURLs(const std::vector<GURL>& urls) { 2469 expirer_.DeleteURLs(urls); 2470 2471 db_->GetStartDate(&first_recorded_time_); 2472 // Force a commit, if the user is deleting something for privacy reasons, we 2473 // want to get it on disk ASAP. 2474 Commit(); 2475} 2476 2477void HistoryBackend::DeleteURL(const GURL& url) { 2478 expirer_.DeleteURL(url); 2479 2480 db_->GetStartDate(&first_recorded_time_); 2481 // Force a commit, if the user is deleting something for privacy reasons, we 2482 // want to get it on disk ASAP. 2483 Commit(); 2484} 2485 2486void HistoryBackend::ExpireHistoryBetween( 2487 const std::set<GURL>& restrict_urls, 2488 Time begin_time, 2489 Time end_time) { 2490 if (!db_) 2491 return; 2492 2493 if (begin_time.is_null() && (end_time.is_null() || end_time.is_max()) && 2494 restrict_urls.empty()) { 2495 // Special case deleting all history so it can be faster and to reduce the 2496 // possibility of an information leak. 2497 DeleteAllHistory(); 2498 } else { 2499 // Clearing parts of history, have the expirer do the depend 2500 expirer_.ExpireHistoryBetween(restrict_urls, begin_time, end_time); 2501 2502 // Force a commit, if the user is deleting something for privacy reasons, 2503 // we want to get it on disk ASAP. 2504 Commit(); 2505 } 2506 2507 if (begin_time <= first_recorded_time_) 2508 db_->GetStartDate(&first_recorded_time_); 2509} 2510 2511void HistoryBackend::ExpireHistoryForTimes( 2512 const std::set<base::Time>& times, 2513 base::Time begin_time, base::Time end_time) { 2514 if (times.empty() || !db_) 2515 return; 2516 2517 DCHECK(*times.begin() >= begin_time) 2518 << "Min time is before begin time: " 2519 << times.begin()->ToJsTime() << " v.s. " << begin_time.ToJsTime(); 2520 DCHECK(*times.rbegin() < end_time) 2521 << "Max time is after end time: " 2522 << times.rbegin()->ToJsTime() << " v.s. " << end_time.ToJsTime(); 2523 2524 history::QueryOptions options; 2525 options.begin_time = begin_time; 2526 options.end_time = end_time; 2527 options.duplicate_policy = QueryOptions::KEEP_ALL_DUPLICATES; 2528 QueryResults results; 2529 QueryHistoryBasic(db_.get(), db_.get(), options, &results); 2530 2531 // 1st pass: find URLs that are visited at one of |times|. 2532 std::set<GURL> urls; 2533 for (size_t i = 0; i < results.size(); ++i) { 2534 if (times.count(results[i].visit_time()) > 0) 2535 urls.insert(results[i].url()); 2536 } 2537 if (urls.empty()) 2538 return; 2539 2540 // 2nd pass: collect all visit times of those URLs. 2541 std::vector<base::Time> times_to_expire; 2542 for (size_t i = 0; i < results.size(); ++i) { 2543 if (urls.count(results[i].url())) 2544 times_to_expire.push_back(results[i].visit_time()); 2545 } 2546 2547 // Put the times in reverse chronological order and remove 2548 // duplicates (for expirer_.ExpireHistoryForTimes()). 2549 std::sort(times_to_expire.begin(), times_to_expire.end(), 2550 std::greater<base::Time>()); 2551 times_to_expire.erase( 2552 std::unique(times_to_expire.begin(), times_to_expire.end()), 2553 times_to_expire.end()); 2554 2555 // Expires by times and commit. 2556 DCHECK(!times_to_expire.empty()); 2557 expirer_.ExpireHistoryForTimes(times_to_expire); 2558 Commit(); 2559 2560 DCHECK(times_to_expire.back() >= first_recorded_time_); 2561 // Update |first_recorded_time_| if we expired it. 2562 if (times_to_expire.back() == first_recorded_time_) 2563 db_->GetStartDate(&first_recorded_time_); 2564} 2565 2566void HistoryBackend::ExpireHistory( 2567 const std::vector<history::ExpireHistoryArgs>& expire_list) { 2568 if (db_) { 2569 bool update_first_recorded_time = false; 2570 2571 for (std::vector<history::ExpireHistoryArgs>::const_iterator it = 2572 expire_list.begin(); it != expire_list.end(); ++it) { 2573 expirer_.ExpireHistoryBetween(it->urls, it->begin_time, it->end_time); 2574 2575 if (it->begin_time < first_recorded_time_) 2576 update_first_recorded_time = true; 2577 } 2578 Commit(); 2579 2580 // Update |first_recorded_time_| if any deletion might have affected it. 2581 if (update_first_recorded_time) 2582 db_->GetStartDate(&first_recorded_time_); 2583 } 2584} 2585 2586void HistoryBackend::URLsNoLongerBookmarked(const std::set<GURL>& urls) { 2587 if (!db_) 2588 return; 2589 2590 for (std::set<GURL>::const_iterator i = urls.begin(); i != urls.end(); ++i) { 2591 URLRow url_row; 2592 if (!db_->GetRowForURL(*i, &url_row)) 2593 continue; // The URL isn't in the db; nothing to do. 2594 2595 VisitVector visits; 2596 db_->GetVisitsForURL(url_row.id(), &visits); 2597 2598 if (visits.empty()) 2599 expirer_.DeleteURL(*i); // There are no more visits; nuke the URL. 2600 } 2601} 2602 2603void HistoryBackend::DatabaseErrorCallback(int error, sql::Statement* stmt) { 2604 if (!scheduled_kill_db_ && sql::IsErrorCatastrophic(error)) { 2605 scheduled_kill_db_ = true; 2606 // Don't just do the close/delete here, as we are being called by |db| and 2607 // that seems dangerous. 2608 // TODO(shess): Consider changing KillHistoryDatabase() to use 2609 // RazeAndClose(). Then it can be cleared immediately. 2610 base::MessageLoop::current()->PostTask( 2611 FROM_HERE, 2612 base::Bind(&HistoryBackend::KillHistoryDatabase, this)); 2613 } 2614} 2615 2616void HistoryBackend::KillHistoryDatabase() { 2617 scheduled_kill_db_ = false; 2618 if (!db_) 2619 return; 2620 2621 // Rollback transaction because Raze() cannot be called from within a 2622 // transaction. 2623 db_->RollbackTransaction(); 2624 bool success = db_->Raze(); 2625 UMA_HISTOGRAM_BOOLEAN("History.KillHistoryDatabaseResult", success); 2626 2627#if defined(OS_ANDROID) 2628 // Release AndroidProviderBackend before other objects. 2629 android_provider_backend_.reset(); 2630#endif 2631 2632 // The expirer keeps tabs on the active databases. Tell it about the 2633 // databases which will be closed. 2634 expirer_.SetDatabases(NULL, NULL, NULL); 2635 2636 // Reopen a new transaction for |db_| for the sake of CloseAllDatabases(). 2637 db_->BeginTransaction(); 2638 CloseAllDatabases(); 2639} 2640 2641void HistoryBackend::ProcessDBTask( 2642 scoped_refptr<HistoryDBTaskRequest> request) { 2643 DCHECK(request.get()); 2644 if (request->canceled()) 2645 return; 2646 2647 bool task_scheduled = !db_task_requests_.empty(); 2648 // Make sure we up the refcount of the request. ProcessDBTaskImpl will 2649 // release when done with the task. 2650 request->AddRef(); 2651 db_task_requests_.push_back(request.get()); 2652 if (!task_scheduled) { 2653 // No other tasks are scheduled. Process request now. 2654 ProcessDBTaskImpl(); 2655 } 2656} 2657 2658void HistoryBackend::BroadcastNotifications( 2659 int type, 2660 HistoryDetails* details_deleted) { 2661 // |delegate_| may be NULL if |this| is in the process of closing (closed by 2662 // HistoryService -> HistoryBackend::Closing(). 2663 if (delegate_) 2664 delegate_->BroadcastNotifications(type, details_deleted); 2665 else 2666 delete details_deleted; 2667} 2668 2669void HistoryBackend::NotifySyncURLsDeleted(bool all_history, 2670 bool archived, 2671 URLRows* rows) { 2672 if (typed_url_syncable_service_.get()) 2673 typed_url_syncable_service_->OnUrlsDeleted(all_history, archived, rows); 2674} 2675 2676// Deleting -------------------------------------------------------------------- 2677 2678void HistoryBackend::DeleteAllHistory() { 2679 // Our approach to deleting all history is: 2680 // 1. Copy the bookmarks and their dependencies to new tables with temporary 2681 // names. 2682 // 2. Delete the original tables. Since tables can not share pages, we know 2683 // that any data we don't want to keep is now in an unused page. 2684 // 3. Renaming the temporary tables to match the original. 2685 // 4. Vacuuming the database to delete the unused pages. 2686 // 2687 // Since we are likely to have very few bookmarks and their dependencies 2688 // compared to all history, this is also much faster than just deleting from 2689 // the original tables directly. 2690 2691 // Get the bookmarked URLs. 2692 std::vector<BookmarkService::URLAndTitle> starred_urls; 2693 BookmarkService* bookmark_service = GetBookmarkService(); 2694 if (bookmark_service) 2695 bookmark_service_->GetBookmarks(&starred_urls); 2696 2697 URLRows kept_urls; 2698 for (size_t i = 0; i < starred_urls.size(); i++) { 2699 URLRow row; 2700 if (!db_->GetRowForURL(starred_urls[i].url, &row)) 2701 continue; 2702 2703 // Clear the last visit time so when we write these rows they are "clean." 2704 row.set_last_visit(Time()); 2705 row.set_visit_count(0); 2706 row.set_typed_count(0); 2707 kept_urls.push_back(row); 2708 } 2709 2710 // Clear thumbnail and favicon history. The favicons for the given URLs will 2711 // be kept. 2712 if (!ClearAllThumbnailHistory(kept_urls)) { 2713 LOG(ERROR) << "Thumbnail history could not be cleared"; 2714 // We continue in this error case. If the user wants to delete their 2715 // history, we should delete as much as we can. 2716 } 2717 2718 // ClearAllMainHistory will change the IDs of the URLs in kept_urls. 2719 // Therefore, we clear the list afterwards to make sure nobody uses this 2720 // invalid data. 2721 if (!ClearAllMainHistory(kept_urls)) 2722 LOG(ERROR) << "Main history could not be cleared"; 2723 kept_urls.clear(); 2724 2725 // Delete archived history. 2726 if (archived_db_) { 2727 // Close the database and delete the file. 2728 archived_db_.reset(); 2729 base::FilePath archived_file_name = GetArchivedFileName(); 2730 sql::Connection::Delete(archived_file_name); 2731 2732 // Now re-initialize the database (which may fail). 2733 archived_db_.reset(new ArchivedDatabase()); 2734 if (!archived_db_->Init(archived_file_name)) { 2735 LOG(WARNING) << "Could not initialize the archived database."; 2736 archived_db_.reset(); 2737 } else { 2738 // Open our long-running transaction on this database. 2739 archived_db_->BeginTransaction(); 2740 } 2741 } 2742 2743 db_->GetStartDate(&first_recorded_time_); 2744 2745 // Send out the notification that history is cleared. The in-memory database 2746 // will pick this up and clear itself. 2747 URLsDeletedDetails* details = new URLsDeletedDetails; 2748 details->all_history = true; 2749 NotifySyncURLsDeleted(true, false, NULL); 2750 BroadcastNotifications(chrome::NOTIFICATION_HISTORY_URLS_DELETED, details); 2751} 2752 2753bool HistoryBackend::ClearAllThumbnailHistory(const URLRows& kept_urls) { 2754 if (!thumbnail_db_) { 2755 // When we have no reference to the thumbnail database, maybe there was an 2756 // error opening it. In this case, we just try to blow it away to try to 2757 // fix the error if it exists. This may fail, in which case either the 2758 // file doesn't exist or there's no more we can do. 2759 sql::Connection::Delete(GetFaviconsFileName()); 2760 2761 // Older version of the database. 2762 sql::Connection::Delete(GetThumbnailFileName()); 2763 return true; 2764 } 2765 2766 // Urls to retain mappings for. 2767 std::vector<GURL> urls_to_keep; 2768 for (URLRows::const_iterator i = kept_urls.begin(); 2769 i != kept_urls.end(); ++i) { 2770 urls_to_keep.push_back(i->url()); 2771 } 2772 2773 // Isolate from any long-running transaction. 2774 thumbnail_db_->CommitTransaction(); 2775 thumbnail_db_->BeginTransaction(); 2776 2777 // TODO(shess): If this fails, perhaps the database should be razed 2778 // or deleted. 2779 if (!thumbnail_db_->RetainDataForPageUrls(urls_to_keep)) { 2780 thumbnail_db_->RollbackTransaction(); 2781 thumbnail_db_->BeginTransaction(); 2782 return false; 2783 } 2784 2785#if defined(OS_ANDROID) 2786 // TODO (michaelbai): Add the unit test once AndroidProviderBackend is 2787 // avaliable in HistoryBackend. 2788 db_->ClearAndroidURLRows(); 2789#endif 2790 2791 // Vacuum to remove all the pages associated with the dropped tables. There 2792 // must be no transaction open on the table when we do this. We assume that 2793 // our long-running transaction is open, so we complete it and start it again. 2794 DCHECK(thumbnail_db_->transaction_nesting() == 1); 2795 thumbnail_db_->CommitTransaction(); 2796 thumbnail_db_->Vacuum(); 2797 thumbnail_db_->BeginTransaction(); 2798 return true; 2799} 2800 2801bool HistoryBackend::ClearAllMainHistory(const URLRows& kept_urls) { 2802 // Create the duplicate URL table. We will copy the kept URLs into this. 2803 if (!db_->CreateTemporaryURLTable()) 2804 return false; 2805 2806 // Insert the URLs into the temporary table. 2807 for (URLRows::const_iterator i = kept_urls.begin(); i != kept_urls.end(); 2808 ++i) { 2809 db_->AddTemporaryURL(*i); 2810 } 2811 2812 // Replace the original URL table with the temporary one. 2813 if (!db_->CommitTemporaryURLTable()) 2814 return false; 2815 2816 // Delete the old tables and recreate them empty. 2817 db_->RecreateAllTablesButURL(); 2818 2819 // Vacuum to reclaim the space from the dropped tables. This must be done 2820 // when there is no transaction open, and we assume that our long-running 2821 // transaction is currently open. 2822 db_->CommitTransaction(); 2823 db_->Vacuum(); 2824 db_->BeginTransaction(); 2825 db_->GetStartDate(&first_recorded_time_); 2826 2827 return true; 2828} 2829 2830BookmarkService* HistoryBackend::GetBookmarkService() { 2831 if (bookmark_service_) 2832 bookmark_service_->BlockTillLoaded(); 2833 return bookmark_service_; 2834} 2835 2836void HistoryBackend::NotifyVisitObservers(const VisitRow& visit) { 2837 BriefVisitInfo info; 2838 info.url_id = visit.url_id; 2839 info.time = visit.visit_time; 2840 info.transition = visit.transition; 2841 // If we don't have a delegate yet during setup or shutdown, we will drop 2842 // these notifications. 2843 if (delegate_) 2844 delegate_->NotifyVisitDBObserversOnAddVisit(info); 2845} 2846 2847#if defined(OS_ANDROID) 2848void HistoryBackend::PopulateMostVisitedURLMap() { 2849 MostVisitedURLList most_visited_urls; 2850 QueryMostVisitedURLsImpl(kPageVisitStatsMaxTopSites, kSegmentDataRetention, 2851 &most_visited_urls); 2852 2853 DCHECK_LE(most_visited_urls.size(), kPageVisitStatsMaxTopSites); 2854 for (size_t i = 0; i < most_visited_urls.size(); ++i) { 2855 most_visited_urls_map_[most_visited_urls[i].url] = i; 2856 for (size_t j = 0; j < most_visited_urls[i].redirects.size(); ++j) 2857 most_visited_urls_map_[most_visited_urls[i].redirects[j]] = i; 2858 } 2859} 2860 2861void HistoryBackend::RecordTopPageVisitStats(const GURL& url) { 2862 int rank = kPageVisitStatsMaxTopSites; 2863 std::map<GURL, int>::const_iterator it = most_visited_urls_map_.find(url); 2864 if (it != most_visited_urls_map_.end()) 2865 rank = (*it).second; 2866 UMA_HISTOGRAM_ENUMERATION("History.TopSitesVisitsByRank", 2867 rank, kPageVisitStatsMaxTopSites + 1); 2868} 2869#endif 2870 2871} // namespace history 2872