Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/history/history_backend.h" | 5 #include "chrome/browser/history/history_backend.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <functional> | 8 #include <functional> |
| 9 #include <list> | 9 #include <list> |
| 10 #include <map> | 10 #include <map> |
| 11 #include <set> | 11 #include <set> |
| 12 #include <vector> | 12 #include <vector> |
| 13 | 13 |
| 14 #include "base/basictypes.h" | 14 #include "base/basictypes.h" |
| 15 #include "base/bind.h" | 15 #include "base/bind.h" |
| 16 #include "base/compiler_specific.h" | 16 #include "base/compiler_specific.h" |
| 17 #include "base/file_util.h" | 17 #include "base/file_util.h" |
| 18 #include "base/files/file_enumerator.h" | |
| 18 #include "base/memory/scoped_ptr.h" | 19 #include "base/memory/scoped_ptr.h" |
| 19 #include "base/memory/scoped_vector.h" | 20 #include "base/memory/scoped_vector.h" |
| 20 #include "base/message_loop.h" | 21 #include "base/message_loop.h" |
| 21 #include "base/metrics/histogram.h" | 22 #include "base/metrics/histogram.h" |
| 22 #include "base/rand_util.h" | 23 #include "base/rand_util.h" |
| 23 #include "base/strings/string_util.h" | 24 #include "base/strings/string_util.h" |
| 24 #include "base/strings/utf_string_conversions.h" | 25 #include "base/strings/utf_string_conversions.h" |
| 25 #include "base/time.h" | 26 #include "base/time.h" |
| 26 #include "chrome/browser/autocomplete/history_url_provider.h" | 27 #include "chrome/browser/autocomplete/history_url_provider.h" |
| 27 #include "chrome/browser/bookmarks/bookmark_service.h" | 28 #include "chrome/browser/bookmarks/bookmark_service.h" |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 62 VisitDatabase (stores a list of visits for the URLs) | 63 VisitDatabase (stores a list of visits for the URLs) |
| 63 VisitSegmentDatabase (stores groups of URLs for the most visited view). | 64 VisitSegmentDatabase (stores groups of URLs for the most visited view). |
| 64 | 65 |
| 65 ArchivedDatabase (stores history older than 3 months) | 66 ArchivedDatabase (stores history older than 3 months) |
| 66 URLDatabase (stores a list of URLs) | 67 URLDatabase (stores a list of URLs) |
| 67 DownloadDatabase (stores a list of downloads) | 68 DownloadDatabase (stores a list of downloads) |
| 68 VisitDatabase (stores a list of visits for the URLs) | 69 VisitDatabase (stores a list of visits for the URLs) |
| 69 | 70 |
| 70 (this does not store visit segments as they expire after 3 mos.) | 71 (this does not store visit segments as they expire after 3 mos.) |
| 71 | 72 |
| 72 TextDatabaseManager (manages multiple text database for different times) | |
| 73 TextDatabase (represents a single month of full-text index). | |
| 74 ...more TextDatabase objects... | |
| 75 | |
| 76 ExpireHistoryBackend (manages moving things from HistoryDatabase to | 73 ExpireHistoryBackend (manages moving things from HistoryDatabase to |
| 77 the ArchivedDatabase and deleting) | 74 the ArchivedDatabase and deleting) |
| 78 */ | 75 */ |
| 79 | 76 |
| 80 namespace history { | 77 namespace history { |
| 81 | 78 |
| 82 // How long we keep segment data for in days. Currently 3 months. | 79 // How long we keep segment data for in days. Currently 3 months. |
| 83 // This value needs to be greater or equal to | 80 // This value needs to be greater or equal to |
| 84 // MostVisitedModel::kMostVisitedScope but we don't want to introduce a direct | 81 // MostVisitedModel::kMostVisitedScope but we don't want to introduce a direct |
| 85 // dependency between MostVisitedModel and the history backend. | 82 // dependency between MostVisitedModel and the history backend. |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 157 } | 154 } |
| 158 | 155 |
| 159 private: | 156 private: |
| 160 friend class base::RefCounted<CommitLaterTask>; | 157 friend class base::RefCounted<CommitLaterTask>; |
| 161 | 158 |
| 162 ~CommitLaterTask() {} | 159 ~CommitLaterTask() {} |
| 163 | 160 |
| 164 scoped_refptr<HistoryBackend> history_backend_; | 161 scoped_refptr<HistoryBackend> history_backend_; |
| 165 }; | 162 }; |
| 166 | 163 |
| 167 // Handles querying first the main database, then the full text database if that | |
| 168 // fails. It will optionally keep track of all URLs seen so duplicates can be | |
| 169 // eliminated. This is used by the querying sub-functions. | |
| 170 // | |
| 171 // TODO(brettw): This class may be able to be simplified or eliminated. After | |
| 172 // this was written, QueryResults can efficiently look up by URL, so the need | |
| 173 // for this extra set of previously queried URLs is less important. | |
| 174 class HistoryBackend::URLQuerier { | |
| 175 public: | |
| 176 URLQuerier(URLDatabase* main_db, URLDatabase* archived_db, bool track_unique) | |
| 177 : main_db_(main_db), | |
| 178 archived_db_(archived_db), | |
| 179 track_unique_(track_unique) { | |
| 180 } | |
| 181 | |
| 182 // When we're tracking unique URLs, returns true if this URL has been | |
| 183 // previously queried. Only call when tracking unique URLs. | |
| 184 bool HasURL(const GURL& url) { | |
| 185 DCHECK(track_unique_); | |
| 186 return unique_urls_.find(url) != unique_urls_.end(); | |
| 187 } | |
| 188 | |
| 189 bool GetRowForURL(const GURL& url, URLRow* row) { | |
| 190 if (!main_db_->GetRowForURL(url, row)) { | |
| 191 if (!archived_db_ || !archived_db_->GetRowForURL(url, row)) { | |
| 192 // This row is neither in the main nor the archived DB. | |
| 193 return false; | |
| 194 } | |
| 195 } | |
| 196 | |
| 197 if (track_unique_) | |
| 198 unique_urls_.insert(url); | |
| 199 return true; | |
| 200 } | |
| 201 | |
| 202 private: | |
| 203 URLDatabase* main_db_; // Guaranteed non-NULL. | |
| 204 URLDatabase* archived_db_; // Possibly NULL. | |
| 205 | |
| 206 bool track_unique_; | |
| 207 | |
| 208 // When track_unique_ is set, this is updated with every URL seen so far. | |
| 209 std::set<GURL> unique_urls_; | |
| 210 | |
| 211 DISALLOW_COPY_AND_ASSIGN(URLQuerier); | |
| 212 }; | |
| 213 | |
| 214 // KillHistoryDatabaseErrorDelegate ------------------------------------------- | 164 // KillHistoryDatabaseErrorDelegate ------------------------------------------- |
| 215 | 165 |
| 216 class KillHistoryDatabaseErrorDelegate : public sql::ErrorDelegate { | 166 class KillHistoryDatabaseErrorDelegate : public sql::ErrorDelegate { |
| 217 public: | 167 public: |
| 218 explicit KillHistoryDatabaseErrorDelegate(HistoryBackend* backend) | 168 explicit KillHistoryDatabaseErrorDelegate(HistoryBackend* backend) |
| 219 : backend_(backend), | 169 : backend_(backend), |
| 220 scheduled_killing_database_(false) { | 170 scheduled_killing_database_(false) { |
| 221 } | 171 } |
| 222 | 172 |
| 223 // sql::ErrorDelegate implementation. | 173 // sql::ErrorDelegate implementation. |
| (...skipping 386 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 610 // Update the visit_details for this visit. | 560 // Update the visit_details for this visit. |
| 611 UpdateVisitDuration(from_visit_id, request.time); | 561 UpdateVisitDuration(from_visit_id, request.time); |
| 612 } | 562 } |
| 613 | 563 |
| 614 // Subsequent transitions in the redirect list must all be server | 564 // Subsequent transitions in the redirect list must all be server |
| 615 // redirects. | 565 // redirects. |
| 616 redirect_info = content::PAGE_TRANSITION_SERVER_REDIRECT; | 566 redirect_info = content::PAGE_TRANSITION_SERVER_REDIRECT; |
| 617 } | 567 } |
| 618 | 568 |
| 619 // Last, save this redirect chain for later so we can set titles & favicons | 569 // Last, save this redirect chain for later so we can set titles & favicons |
| 620 // on the redirected pages properly. It is indexed by the destination page. | 570 // on the redirected pages properly. |
| 621 recent_redirects_.Put(request.url, redirects); | 571 recent_redirects_.Put(request.url, redirects); |
| 622 } | 572 } |
| 623 | 573 |
| 624 // TODO(brettw) bug 1140015: Add an "add page" notification so the history | 574 // TODO(brettw) bug 1140015: Add an "add page" notification so the history |
| 625 // views can keep in sync. | 575 // views can keep in sync. |
| 626 | 576 |
| 627 // Add the last visit to the tracker so we can get outgoing transitions. | 577 // Add the last visit to the tracker so we can get outgoing transitions. |
| 628 // TODO(evanm): Due to http://b/1194536 we lose the referrers of a subframe | 578 // TODO(evanm): Due to http://b/1194536 we lose the referrers of a subframe |
| 629 // navigation anyway, so last_visit_id is always zero for them. But adding | 579 // navigation anyway, so last_visit_id is always zero for them. But adding |
| 630 // them here confuses main frame history, so we skip them for now. | 580 // them here confuses main frame history, so we skip them for now. |
| 631 if (stripped_transition != content::PAGE_TRANSITION_AUTO_SUBFRAME && | 581 if (stripped_transition != content::PAGE_TRANSITION_AUTO_SUBFRAME && |
| 632 stripped_transition != content::PAGE_TRANSITION_MANUAL_SUBFRAME && | 582 stripped_transition != content::PAGE_TRANSITION_MANUAL_SUBFRAME && |
| 633 !is_keyword_generated) { | 583 !is_keyword_generated) { |
| 634 tracker_.AddVisit(request.id_scope, request.page_id, request.url, | 584 tracker_.AddVisit(request.id_scope, request.page_id, request.url, |
| 635 last_ids.second); | 585 last_ids.second); |
| 636 } | 586 } |
| 637 | 587 |
| 638 if (text_database_) { | |
| 639 text_database_->AddPageURL(request.url, last_ids.first, last_ids.second, | |
| 640 request.time); | |
| 641 } | |
| 642 | |
| 643 ScheduleCommit(); | 588 ScheduleCommit(); |
| 644 } | 589 } |
| 645 | 590 |
| 646 void HistoryBackend::InitImpl(const std::string& languages) { | 591 void HistoryBackend::InitImpl(const std::string& languages) { |
| 647 DCHECK(!db_) << "Initializing HistoryBackend twice"; | 592 DCHECK(!db_) << "Initializing HistoryBackend twice"; |
| 648 // In the rare case where the db fails to initialize a dialog may get shown | 593 // In the rare case where the db fails to initialize a dialog may get shown |
| 649 // the blocks the caller, yet allows other messages through. For this reason | 594 // the blocks the caller, yet allows other messages through. For this reason |
| 650 // we only set db_ to the created database if creation is successful. That | 595 // we only set db_ to the created database if creation is successful. That |
| 651 // way other methods won't do anything as db_ is still NULL. | 596 // way other methods won't do anything as db_ is still NULL. |
| 652 | 597 |
| 653 TimeTicks beginning_time = TimeTicks::Now(); | 598 TimeTicks beginning_time = TimeTicks::Now(); |
| 654 | 599 |
| 655 // Compute the file names. Note that the index file can be removed when the | 600 // Compute the file names. |
| 656 // text db manager is finished being hooked up. | |
| 657 base::FilePath history_name = history_dir_.Append(chrome::kHistoryFilename); | 601 base::FilePath history_name = history_dir_.Append(chrome::kHistoryFilename); |
| 658 base::FilePath thumbnail_name = GetThumbnailFileName(); | 602 base::FilePath thumbnail_name = GetThumbnailFileName(); |
| 659 base::FilePath archived_name = GetArchivedFileName(); | 603 base::FilePath archived_name = GetArchivedFileName(); |
| 660 | 604 |
| 605 // Delete the old index database files which are no longer used. | |
| 606 DeleteFTSIndexDatabases(); | |
| 607 | |
| 661 // History database. | 608 // History database. |
| 662 db_.reset(new HistoryDatabase()); | 609 db_.reset(new HistoryDatabase()); |
| 663 | 610 |
| 664 // |HistoryDatabase::Init| takes ownership of |error_delegate|. | 611 // |HistoryDatabase::Init| takes ownership of |error_delegate|. |
| 665 KillHistoryDatabaseErrorDelegate* error_delegate = | 612 KillHistoryDatabaseErrorDelegate* error_delegate = |
| 666 new KillHistoryDatabaseErrorDelegate(this); | 613 new KillHistoryDatabaseErrorDelegate(this); |
| 667 | 614 |
| 668 sql::InitStatus status = db_->Init(history_name, error_delegate); | 615 sql::InitStatus status = db_->Init(history_name, error_delegate); |
| 669 switch (status) { | 616 switch (status) { |
| 670 case sql::INIT_OK: | 617 case sql::INIT_OK: |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 690 // Fill the in-memory database and send it back to the history service on the | 637 // Fill the in-memory database and send it back to the history service on the |
| 691 // main thread. | 638 // main thread. |
| 692 InMemoryHistoryBackend* mem_backend = new InMemoryHistoryBackend; | 639 InMemoryHistoryBackend* mem_backend = new InMemoryHistoryBackend; |
| 693 if (mem_backend->Init(history_name, db_.get())) | 640 if (mem_backend->Init(history_name, db_.get())) |
| 694 delegate_->SetInMemoryBackend(id_, mem_backend); // Takes ownership of | 641 delegate_->SetInMemoryBackend(id_, mem_backend); // Takes ownership of |
| 695 // pointer. | 642 // pointer. |
| 696 else | 643 else |
| 697 delete mem_backend; // Error case, run without the in-memory DB. | 644 delete mem_backend; // Error case, run without the in-memory DB. |
| 698 db_->BeginExclusiveMode(); // Must be after the mem backend read the data. | 645 db_->BeginExclusiveMode(); // Must be after the mem backend read the data. |
| 699 | 646 |
| 700 // Create the history publisher which needs to be passed on to the text and | 647 // Create the history publisher which needs to be passed on to the thumbnail |
| 701 // thumbnail databases for publishing history. | 648 // database for publishing history. |
| 702 history_publisher_.reset(new HistoryPublisher()); | 649 history_publisher_.reset(new HistoryPublisher()); |
| 703 if (!history_publisher_->Init()) { | 650 if (!history_publisher_->Init()) { |
| 704 // The init may fail when there are no indexers wanting our history. | 651 // The init may fail when there are no indexers wanting our history. |
| 705 // Hence no need to log the failure. | 652 // Hence no need to log the failure. |
| 706 history_publisher_.reset(); | 653 history_publisher_.reset(); |
| 707 } | 654 } |
| 708 | 655 |
| 709 // Full-text database. This has to be first so we can pass it to the | |
| 710 // HistoryDatabase for migration. | |
| 711 text_database_.reset(new TextDatabaseManager(history_dir_, | |
| 712 db_.get(), db_.get())); | |
| 713 if (!text_database_->Init(history_publisher_.get())) { | |
| 714 LOG(WARNING) << "Text database initialization failed, running without it."; | |
| 715 text_database_.reset(); | |
| 716 } | |
| 717 if (db_->needs_version_17_migration()) { | |
| 718 // See needs_version_17_migration() decl for more. In this case, we want | |
| 719 // to erase all the text database files. This must be done after the text | |
| 720 // database manager has been initialized, since it knows about all the | |
| 721 // files it manages. | |
| 722 text_database_->DeleteAll(); | |
| 723 } | |
| 724 | |
| 725 // Thumbnail database. | 656 // Thumbnail database. |
| 726 thumbnail_db_.reset(new ThumbnailDatabase()); | 657 thumbnail_db_.reset(new ThumbnailDatabase()); |
| 727 if (!db_->GetNeedsThumbnailMigration()) { | 658 if (!db_->GetNeedsThumbnailMigration()) { |
| 728 // No convertion needed - use new filename right away. | 659 // No convertion needed - use new filename right away. |
| 729 thumbnail_name = GetFaviconsFileName(); | 660 thumbnail_name = GetFaviconsFileName(); |
| 730 } | 661 } |
| 731 if (thumbnail_db_->Init(thumbnail_name, | 662 if (thumbnail_db_->Init(thumbnail_name, |
| 732 history_publisher_.get(), | 663 history_publisher_.get(), |
| 733 db_.get()) != sql::INIT_OK) { | 664 db_.get()) != sql::INIT_OK) { |
| 734 // Unlike the main database, we don't error out when the database is too | 665 // Unlike the main database, we don't error out when the database is too |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 767 } | 698 } |
| 768 | 699 |
| 769 // Tell the expiration module about all the nice databases we made. This must | 700 // Tell the expiration module about all the nice databases we made. This must |
| 770 // happen before db_->Init() is called since the callback ForceArchiveHistory | 701 // happen before db_->Init() is called since the callback ForceArchiveHistory |
| 771 // may need to expire stuff. | 702 // may need to expire stuff. |
| 772 // | 703 // |
| 773 // *sigh*, this can all be cleaned up when that migration code is removed. | 704 // *sigh*, this can all be cleaned up when that migration code is removed. |
| 774 // The main DB initialization should intuitively be first (not that it | 705 // The main DB initialization should intuitively be first (not that it |
| 775 // actually matters) and the expirer should be set last. | 706 // actually matters) and the expirer should be set last. |
| 776 expirer_.SetDatabases(db_.get(), archived_db_.get(), | 707 expirer_.SetDatabases(db_.get(), archived_db_.get(), |
| 777 thumbnail_db_.get(), text_database_.get()); | 708 thumbnail_db_.get()); |
| 778 | 709 |
| 779 // Open the long-running transaction. | 710 // Open the long-running transaction. |
| 780 db_->BeginTransaction(); | 711 db_->BeginTransaction(); |
| 781 if (thumbnail_db_) | 712 if (thumbnail_db_) |
| 782 thumbnail_db_->BeginTransaction(); | 713 thumbnail_db_->BeginTransaction(); |
| 783 if (archived_db_) | 714 if (archived_db_) |
| 784 archived_db_->BeginTransaction(); | 715 archived_db_->BeginTransaction(); |
| 785 if (text_database_) | |
| 786 text_database_->BeginTransaction(); | |
| 787 | 716 |
| 788 // Get the first item in our database. | 717 // Get the first item in our database. |
| 789 db_->GetStartDate(&first_recorded_time_); | 718 db_->GetStartDate(&first_recorded_time_); |
| 790 | 719 |
| 791 // Start expiring old stuff. | 720 // Start expiring old stuff. |
| 792 expirer_.StartArchivingOldStuff(TimeDelta::FromDays(kArchiveDaysThreshold)); | 721 expirer_.StartArchivingOldStuff(TimeDelta::FromDays(kArchiveDaysThreshold)); |
| 793 | 722 |
| 794 #if defined(OS_ANDROID) | 723 #if defined(OS_ANDROID) |
| 795 if (thumbnail_db_) { | 724 if (thumbnail_db_) { |
| 796 android_provider_backend_.reset(new AndroidProviderBackend( | 725 android_provider_backend_.reset(new AndroidProviderBackend( |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 810 db_.reset(); | 739 db_.reset(); |
| 811 } | 740 } |
| 812 if (thumbnail_db_) { | 741 if (thumbnail_db_) { |
| 813 thumbnail_db_->CommitTransaction(); | 742 thumbnail_db_->CommitTransaction(); |
| 814 thumbnail_db_.reset(); | 743 thumbnail_db_.reset(); |
| 815 } | 744 } |
| 816 if (archived_db_) { | 745 if (archived_db_) { |
| 817 archived_db_->CommitTransaction(); | 746 archived_db_->CommitTransaction(); |
| 818 archived_db_.reset(); | 747 archived_db_.reset(); |
| 819 } | 748 } |
| 820 if (text_database_) { | |
| 821 text_database_->CommitTransaction(); | |
| 822 text_database_.reset(); | |
| 823 } | |
| 824 } | 749 } |
| 825 | 750 |
| 826 std::pair<URLID, VisitID> HistoryBackend::AddPageVisit( | 751 std::pair<URLID, VisitID> HistoryBackend::AddPageVisit( |
| 827 const GURL& url, | 752 const GURL& url, |
| 828 Time time, | 753 Time time, |
| 829 VisitID referring_visit, | 754 VisitID referring_visit, |
| 830 content::PageTransition transition, | 755 content::PageTransition transition, |
| 831 VisitSource visit_source) { | 756 VisitSource visit_source) { |
| 832 // Top-level frame navigations are visible, everything else is hidden | 757 // Top-level frame navigations are visible, everything else is hidden |
| 833 bool new_hidden = !content::PageTransitionIsMainFrame(transition); | 758 bool new_hidden = !content::PageTransitionIsMainFrame(transition); |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 868 url_info.set_typed_count(typed_increment); | 793 url_info.set_typed_count(typed_increment); |
| 869 url_info.set_last_visit(time); | 794 url_info.set_last_visit(time); |
| 870 url_info.set_hidden(new_hidden); | 795 url_info.set_hidden(new_hidden); |
| 871 | 796 |
| 872 url_id = db_->AddURL(url_info); | 797 url_id = db_->AddURL(url_info); |
| 873 if (!url_id) { | 798 if (!url_id) { |
| 874 NOTREACHED() << "Adding URL failed."; | 799 NOTREACHED() << "Adding URL failed."; |
| 875 return std::make_pair(0, 0); | 800 return std::make_pair(0, 0); |
| 876 } | 801 } |
| 877 url_info.id_ = url_id; | 802 url_info.id_ = url_id; |
| 878 | |
| 879 // We don't actually add the URL to the full text index at this point. It | |
| 880 // might be nice to do this so that even if we get no title or body, the | |
| 881 // user can search for URL components and get the page. | |
| 882 // | |
| 883 // However, in most cases, we'll get at least a title and usually contents, | |
| 884 // and this add will be redundant, slowing everything down. As a result, | |
| 885 // we ignore this edge case. | |
| 886 } | 803 } |
| 887 | 804 |
| 888 // Add the visit with the time to the database. | 805 // Add the visit with the time to the database. |
| 889 VisitRow visit_info(url_id, time, referring_visit, transition, 0); | 806 VisitRow visit_info(url_id, time, referring_visit, transition, 0); |
| 890 VisitID visit_id = db_->AddVisit(&visit_info, visit_source); | 807 VisitID visit_id = db_->AddVisit(&visit_info, visit_source); |
| 891 NotifyVisitObservers(visit_info); | 808 NotifyVisitObservers(visit_info); |
| 892 | 809 |
| 893 if (visit_info.visit_time < first_recorded_time_) | 810 if (visit_info.visit_time < first_recorded_time_) |
| 894 first_recorded_time_ = visit_info.visit_time; | 811 first_recorded_time_ = visit_info.visit_time; |
| 895 | 812 |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 945 NOTREACHED() << "Could not add row to DB"; | 862 NOTREACHED() << "Could not add row to DB"; |
| 946 return; | 863 return; |
| 947 } | 864 } |
| 948 | 865 |
| 949 if (i->typed_count() > 0) { | 866 if (i->typed_count() > 0) { |
| 950 modified->changed_urls.push_back(*i); | 867 modified->changed_urls.push_back(*i); |
| 951 modified->changed_urls.back().set_id(url_id); // *i likely has |id_| 0. | 868 modified->changed_urls.back().set_id(url_id); // *i likely has |id_| 0. |
| 952 } | 869 } |
| 953 } | 870 } |
| 954 | 871 |
| 955 // Add the page to the full text index. This function is also used for | |
| 956 // importing. Even though we don't have page contents, we can at least | |
| 957 // add the title and URL to the index so they can be searched. We don't | |
| 958 // bother to delete any already-existing FTS entries for the URL, since | |
| 959 // this is normally called on import. | |
| 960 // | |
| 961 // If you ever import *after* first run (selecting import from the menu), | |
| 962 // then these additional entries will "shadow" the originals when querying | |
| 963 // for the most recent match only, and the user won't get snippets. This is | |
| 964 // a very minor issue, and fixing it will make import slower, so we don't | |
| 965 // bother. | |
| 966 bool has_indexed = false; | |
| 967 if (text_database_) { | |
| 968 // We do not have to make it update the visit database, below, we will | |
| 969 // create the visit entry with the indexed flag set. | |
| 970 has_indexed = text_database_->AddPageData(i->url(), url_id, 0, | |
| 971 i->last_visit(), | |
| 972 i->title(), string16()); | |
| 973 } | |
| 974 | |
| 975 // Sync code manages the visits itself. | 872 // Sync code manages the visits itself. |
| 976 if (visit_source != SOURCE_SYNCED) { | 873 if (visit_source != SOURCE_SYNCED) { |
| 977 // Make up a visit to correspond to the last visit to the page. | 874 // Make up a visit to correspond to the last visit to the page. |
| 978 VisitRow visit_info(url_id, i->last_visit(), 0, | 875 VisitRow visit_info(url_id, i->last_visit(), 0, |
| 979 content::PageTransitionFromInt( | 876 content::PageTransitionFromInt( |
| 980 content::PAGE_TRANSITION_LINK | | 877 content::PAGE_TRANSITION_LINK | |
| 981 content::PAGE_TRANSITION_CHAIN_START | | 878 content::PAGE_TRANSITION_CHAIN_START | |
| 982 content::PAGE_TRANSITION_CHAIN_END), 0); | 879 content::PAGE_TRANSITION_CHAIN_END), 0); |
| 983 visit_info.is_indexed = has_indexed; | |
| 984 if (!visit_database->AddVisit(&visit_info, visit_source)) { | 880 if (!visit_database->AddVisit(&visit_info, visit_source)) { |
| 985 NOTREACHED() << "Adding visit failed."; | 881 NOTREACHED() << "Adding visit failed."; |
| 986 return; | 882 return; |
| 987 } | 883 } |
| 988 NotifyVisitObservers(visit_info); | 884 NotifyVisitObservers(visit_info); |
| 989 | 885 |
| 990 if (visit_info.visit_time < first_recorded_time_) | 886 if (visit_info.visit_time < first_recorded_time_) |
| 991 first_recorded_time_ = visit_info.visit_time; | 887 first_recorded_time_ = visit_info.visit_time; |
| 992 } | 888 } |
| 993 } | 889 } |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 1008 | 904 |
| 1009 bool HistoryBackend::IsExpiredVisitTime(const base::Time& time) { | 905 bool HistoryBackend::IsExpiredVisitTime(const base::Time& time) { |
| 1010 return time < expirer_.GetCurrentArchiveTime(); | 906 return time < expirer_.GetCurrentArchiveTime(); |
| 1011 } | 907 } |
| 1012 | 908 |
| 1013 void HistoryBackend::SetPageTitle(const GURL& url, | 909 void HistoryBackend::SetPageTitle(const GURL& url, |
| 1014 const string16& title) { | 910 const string16& title) { |
| 1015 if (!db_) | 911 if (!db_) |
| 1016 return; | 912 return; |
| 1017 | 913 |
| 1018 // Update the full text index. | |
| 1019 if (text_database_) | |
| 1020 text_database_->AddPageTitle(url, title); | |
| 1021 | |
| 1022 // Search for recent redirects which should get the same title. We make a | 914 // Search for recent redirects which should get the same title. We make a |
| 1023 // dummy list containing the exact URL visited if there are no redirects so | 915 // dummy list containing the exact URL visited if there are no redirects so |
| 1024 // the processing below can be the same. | 916 // the processing below can be the same. |
| 1025 history::RedirectList dummy_list; | 917 history::RedirectList dummy_list; |
| 1026 history::RedirectList* redirects; | 918 history::RedirectList* redirects; |
| 1027 RedirectCache::iterator iter = recent_redirects_.Get(url); | 919 RedirectCache::iterator iter = recent_redirects_.Get(url); |
| 1028 if (iter != recent_redirects_.end()) { | 920 if (iter != recent_redirects_.end()) { |
| 1029 redirects = &iter->second; | 921 redirects = &iter->second; |
| 1030 | 922 |
| 1031 // This redirect chain should have the destination URL as the last item. | 923 // This redirect chain should have the destination URL as the last item. |
| (...skipping 480 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1512 for (std::vector<URLResult>::iterator it = matching_visits.begin(); | 1404 for (std::vector<URLResult>::iterator it = matching_visits.begin(); |
| 1513 it != matching_visits.end() && result->size() < max_results; ++it) { | 1405 it != matching_visits.end() && result->size() < max_results; ++it) { |
| 1514 result->AppendURLBySwapping(&(*it)); | 1406 result->AppendURLBySwapping(&(*it)); |
| 1515 } | 1407 } |
| 1516 | 1408 |
| 1517 if (matching_visits.size() == result->size() && | 1409 if (matching_visits.size() == result->size() && |
| 1518 options.begin_time <= first_recorded_time_) | 1410 options.begin_time <= first_recorded_time_) |
| 1519 result->set_reached_beginning(true); | 1411 result->set_reached_beginning(true); |
| 1520 } | 1412 } |
| 1521 | 1413 |
| 1522 void HistoryBackend::QueryHistoryFTS(const string16& text_query, | |
| 1523 const QueryOptions& options, | |
| 1524 QueryResults* result) { | |
| 1525 if (!text_database_) | |
| 1526 return; | |
| 1527 | |
| 1528 // Full text query, first get all the FTS results in the time range. | |
| 1529 std::vector<TextDatabase::Match> fts_matches; | |
| 1530 Time first_time_searched; | |
| 1531 text_database_->GetTextMatches(text_query, options, | |
| 1532 &fts_matches, &first_time_searched); | |
| 1533 | |
| 1534 URLQuerier querier(db_.get(), archived_db_.get(), true); | |
| 1535 | |
| 1536 // Now get the row and visit information for each one. | |
| 1537 URLResult url_result; // Declare outside loop to prevent re-construction. | |
| 1538 for (size_t i = 0; i < fts_matches.size(); i++) { | |
| 1539 if (options.max_count != 0 && | |
| 1540 static_cast<int>(result->size()) >= options.max_count) | |
| 1541 break; // Got too many items. | |
| 1542 | |
| 1543 // Get the URL, querying the main and archived databases as necessary. If | |
| 1544 // this is not found, the history and full text search databases are out | |
| 1545 // of sync and we give up with this result. | |
| 1546 if (!querier.GetRowForURL(fts_matches[i].url, &url_result)) | |
| 1547 continue; | |
| 1548 | |
| 1549 if (!url_result.url().is_valid()) | |
| 1550 continue; // Don't report invalid URLs in case of corruption. | |
| 1551 | |
| 1552 // Copy over the FTS stuff that the URLDatabase doesn't know about. | |
| 1553 // We do this with swap() to avoid copying, since we know we don't | |
| 1554 // need the original any more. Note that we override the title with the | |
| 1555 // one from FTS, since that will match the title_match_positions (the | |
| 1556 // FTS title and the history DB title may differ). | |
| 1557 url_result.set_title(fts_matches[i].title); | |
| 1558 url_result.title_match_positions_.swap( | |
| 1559 fts_matches[i].title_match_positions); | |
| 1560 url_result.snippet_.Swap(&fts_matches[i].snippet); | |
| 1561 | |
| 1562 // The visit time also comes from the full text search database. Since it | |
| 1563 // has the time, we can avoid an extra query of the visits table. | |
| 1564 url_result.set_visit_time(fts_matches[i].time); | |
| 1565 | |
| 1566 // Add it to the vector, this will clear our |url_row| object as a | |
| 1567 // result of the swap. | |
| 1568 result->AppendURLBySwapping(&url_result); | |
| 1569 } | |
| 1570 | |
| 1571 if (first_time_searched <= first_recorded_time_) | |
| 1572 result->set_reached_beginning(true); | |
| 1573 } | |
| 1574 | |
| 1575 // Frontend to GetMostRecentRedirectsFrom from the history thread. | 1414 // Frontend to GetMostRecentRedirectsFrom from the history thread. |
| 1576 void HistoryBackend::QueryRedirectsFrom( | 1415 void HistoryBackend::QueryRedirectsFrom( |
| 1577 scoped_refptr<QueryRedirectsRequest> request, | 1416 scoped_refptr<QueryRedirectsRequest> request, |
| 1578 const GURL& url) { | 1417 const GURL& url) { |
| 1579 if (request->canceled()) | 1418 if (request->canceled()) |
| 1580 return; | 1419 return; |
| 1581 bool success = GetMostRecentRedirectsFrom(url, &request->value); | 1420 bool success = GetMostRecentRedirectsFrom(url, &request->value); |
| 1582 request->ForwardResult(request->handle(), url, success, &request->value); | 1421 request->ForwardResult(request->handle(), url, success, &request->value); |
| 1583 } | 1422 } |
| 1584 | 1423 |
| (...skipping 239 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1824 GetRedirectsToSpecificVisit(cur_visit, redirects); | 1663 GetRedirectsToSpecificVisit(cur_visit, redirects); |
| 1825 return true; | 1664 return true; |
| 1826 } | 1665 } |
| 1827 | 1666 |
| 1828 void HistoryBackend::ScheduleAutocomplete(HistoryURLProvider* provider, | 1667 void HistoryBackend::ScheduleAutocomplete(HistoryURLProvider* provider, |
| 1829 HistoryURLProviderParams* params) { | 1668 HistoryURLProviderParams* params) { |
| 1830 // ExecuteWithDB should handle the NULL database case. | 1669 // ExecuteWithDB should handle the NULL database case. |
| 1831 provider->ExecuteWithDB(this, db_.get(), params); | 1670 provider->ExecuteWithDB(this, db_.get(), params); |
| 1832 } | 1671 } |
| 1833 | 1672 |
| 1834 void HistoryBackend::SetPageContents(const GURL& url, | |
| 1835 const string16& contents) { | |
| 1836 // This is histogrammed in the text database manager. | |
| 1837 if (!text_database_) | |
| 1838 return; | |
| 1839 text_database_->AddPageContents(url, contents); | |
| 1840 } | |
| 1841 | |
| 1842 void HistoryBackend::SetPageThumbnail( | 1673 void HistoryBackend::SetPageThumbnail( |
| 1843 const GURL& url, | 1674 const GURL& url, |
| 1844 const gfx::Image* thumbnail, | 1675 const gfx::Image* thumbnail, |
| 1845 const ThumbnailScore& score) { | 1676 const ThumbnailScore& score) { |
| 1846 if (!db_ || !thumbnail_db_) | 1677 if (!db_ || !thumbnail_db_) |
| 1847 return; | 1678 return; |
| 1848 | 1679 |
| 1849 URLRow url_row; | 1680 URLRow url_row; |
| 1850 URLID url_id = db_->GetRowForURL(url, &url_row); | 1681 URLID url_id = db_->GetRowForURL(url, &url_row); |
| 1851 if (url_id) { | 1682 if (url_id) { |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1915 if (db_) { | 1746 if (db_) { |
| 1916 // If there is no thumbnail DB, we can still record a successful migration. | 1747 // If there is no thumbnail DB, we can still record a successful migration. |
| 1917 if (thumbnail_db_) { | 1748 if (thumbnail_db_) { |
| 1918 thumbnail_db_->RenameAndDropThumbnails(GetThumbnailFileName(), | 1749 thumbnail_db_->RenameAndDropThumbnails(GetThumbnailFileName(), |
| 1919 GetFaviconsFileName()); | 1750 GetFaviconsFileName()); |
| 1920 } | 1751 } |
| 1921 db_->ThumbnailMigrationDone(); | 1752 db_->ThumbnailMigrationDone(); |
| 1922 } | 1753 } |
| 1923 } | 1754 } |
| 1924 | 1755 |
| 1756 void HistoryBackend::DeleteFTSIndexDatabases() { | |
| 1757 // Find files on disk matching the text databases file pattern so we can | |
| 1758 // quickly test for and delete them. | |
| 1759 base::FilePath::StringType filepattern = | |
| 1760 FILE_PATH_LITERAL("History Index *"); | |
| 1761 base::FileEnumerator enumerator( | |
| 1762 history_dir_, false, base::FileEnumerator::FILES, filepattern); | |
| 1763 base::FilePath current_file; | |
| 1764 while (!(current_file = enumerator.Next()).empty()) { | |
| 1765 file_util::Delete(current_file, false); | |
|
Scott Hess - ex-Googler
2013/06/14 19:39:56
This will potentially leave junk files. Please us
rmcilroy
2013/06/17 14:11:49
Done.
| |
| 1766 } | |
|
Scott Hess - ex-Googler
2013/06/14 19:39:56
I think it would be reasonable to have this histog
rmcilroy
2013/06/17 14:11:49
Done.
| |
| 1767 } | |
| 1768 | |
| 1925 bool HistoryBackend::GetThumbnailFromOlderRedirect( | 1769 bool HistoryBackend::GetThumbnailFromOlderRedirect( |
| 1926 const GURL& page_url, | 1770 const GURL& page_url, |
| 1927 std::vector<unsigned char>* data) { | 1771 std::vector<unsigned char>* data) { |
| 1928 // Look at a few previous visit sessions. | 1772 // Look at a few previous visit sessions. |
| 1929 VisitVector older_sessions; | 1773 VisitVector older_sessions; |
| 1930 URLID page_url_id = db_->GetRowForURL(page_url, NULL); | 1774 URLID page_url_id = db_->GetRowForURL(page_url, NULL); |
| 1931 static const int kVisitsToSearchForThumbnail = 4; | 1775 static const int kVisitsToSearchForThumbnail = 4; |
| 1932 db_->GetMostRecentVisitsForURL( | 1776 db_->GetMostRecentVisitsForURL( |
| 1933 page_url_id, kVisitsToSearchForThumbnail, &older_sessions); | 1777 page_url_id, kVisitsToSearchForThumbnail, &older_sessions); |
| 1934 | 1778 |
| (...skipping 749 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2684 thumbnail_db_->CommitTransaction(); | 2528 thumbnail_db_->CommitTransaction(); |
| 2685 DCHECK(thumbnail_db_->transaction_nesting() == 0) << | 2529 DCHECK(thumbnail_db_->transaction_nesting() == 0) << |
| 2686 "Somebody left a transaction open"; | 2530 "Somebody left a transaction open"; |
| 2687 thumbnail_db_->BeginTransaction(); | 2531 thumbnail_db_->BeginTransaction(); |
| 2688 } | 2532 } |
| 2689 | 2533 |
| 2690 if (archived_db_) { | 2534 if (archived_db_) { |
| 2691 archived_db_->CommitTransaction(); | 2535 archived_db_->CommitTransaction(); |
| 2692 archived_db_->BeginTransaction(); | 2536 archived_db_->BeginTransaction(); |
| 2693 } | 2537 } |
| 2694 | |
| 2695 if (text_database_) { | |
| 2696 text_database_->CommitTransaction(); | |
| 2697 text_database_->BeginTransaction(); | |
| 2698 } | |
| 2699 } | 2538 } |
| 2700 | 2539 |
| 2701 void HistoryBackend::ScheduleCommit() { | 2540 void HistoryBackend::ScheduleCommit() { |
| 2702 if (scheduled_commit_.get()) | 2541 if (scheduled_commit_.get()) |
| 2703 return; | 2542 return; |
| 2704 scheduled_commit_ = new CommitLaterTask(this); | 2543 scheduled_commit_ = new CommitLaterTask(this); |
| 2705 base::MessageLoop::current()->PostDelayedTask( | 2544 base::MessageLoop::current()->PostDelayedTask( |
| 2706 FROM_HERE, | 2545 FROM_HERE, |
| 2707 base::Bind(&CommitLaterTask::RunCommit, scheduled_commit_.get()), | 2546 base::Bind(&CommitLaterTask::RunCommit, scheduled_commit_.get()), |
| 2708 base::TimeDelta::FromSeconds(kCommitIntervalSeconds)); | 2547 base::TimeDelta::FromSeconds(kCommitIntervalSeconds)); |
| (...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2905 bool success = db_->Raze(); | 2744 bool success = db_->Raze(); |
| 2906 UMA_HISTOGRAM_BOOLEAN("History.KillHistoryDatabaseResult", success); | 2745 UMA_HISTOGRAM_BOOLEAN("History.KillHistoryDatabaseResult", success); |
| 2907 | 2746 |
| 2908 #if defined(OS_ANDROID) | 2747 #if defined(OS_ANDROID) |
| 2909 // Release AndroidProviderBackend before other objects. | 2748 // Release AndroidProviderBackend before other objects. |
| 2910 android_provider_backend_.reset(); | 2749 android_provider_backend_.reset(); |
| 2911 #endif | 2750 #endif |
| 2912 | 2751 |
| 2913 // The expirer keeps tabs on the active databases. Tell it about the | 2752 // The expirer keeps tabs on the active databases. Tell it about the |
| 2914 // databases which will be closed. | 2753 // databases which will be closed. |
| 2915 expirer_.SetDatabases(NULL, NULL, NULL, NULL); | 2754 expirer_.SetDatabases(NULL, NULL, NULL); |
| 2916 | 2755 |
| 2917 // Reopen a new transaction for |db_| for the sake of CloseAllDatabases(). | 2756 // Reopen a new transaction for |db_| for the sake of CloseAllDatabases(). |
| 2918 db_->BeginTransaction(); | 2757 db_->BeginTransaction(); |
| 2919 CloseAllDatabases(); | 2758 CloseAllDatabases(); |
| 2920 } | 2759 } |
| 2921 | 2760 |
| 2922 void HistoryBackend::ProcessDBTask( | 2761 void HistoryBackend::ProcessDBTask( |
| 2923 scoped_refptr<HistoryDBTaskRequest> request) { | 2762 scoped_refptr<HistoryDBTaskRequest> request) { |
| 2924 DCHECK(request.get()); | 2763 DCHECK(request.get()); |
| 2925 if (request->canceled()) | 2764 if (request->canceled()) |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2995 // We continue in this error case. If the user wants to delete their | 2834 // We continue in this error case. If the user wants to delete their |
| 2996 // history, we should delete as much as we can. | 2835 // history, we should delete as much as we can. |
| 2997 } | 2836 } |
| 2998 | 2837 |
| 2999 // ClearAllMainHistory will change the IDs of the URLs in kept_urls. Therfore, | 2838 // ClearAllMainHistory will change the IDs of the URLs in kept_urls. Therfore, |
| 3000 // we clear the list afterwards to make sure nobody uses this invalid data. | 2839 // we clear the list afterwards to make sure nobody uses this invalid data. |
| 3001 if (!ClearAllMainHistory(kept_urls)) | 2840 if (!ClearAllMainHistory(kept_urls)) |
| 3002 LOG(ERROR) << "Main history could not be cleared"; | 2841 LOG(ERROR) << "Main history could not be cleared"; |
| 3003 kept_urls.clear(); | 2842 kept_urls.clear(); |
| 3004 | 2843 |
| 3005 // Delete FTS files & archived history. | 2844 // Delete archived history. |
| 3006 if (text_database_) { | |
| 3007 // We assume that the text database has one transaction on them that we need | |
| 3008 // to close & restart (the long-running history transaction). | |
| 3009 text_database_->CommitTransaction(); | |
| 3010 text_database_->DeleteAll(); | |
| 3011 text_database_->BeginTransaction(); | |
| 3012 } | |
| 3013 | |
| 3014 if (archived_db_) { | 2845 if (archived_db_) { |
| 3015 // Close the database and delete the file. | 2846 // Close the database and delete the file. |
| 3016 archived_db_.reset(); | 2847 archived_db_.reset(); |
| 3017 base::FilePath archived_file_name = GetArchivedFileName(); | 2848 base::FilePath archived_file_name = GetArchivedFileName(); |
| 3018 file_util::Delete(archived_file_name, false); | 2849 file_util::Delete(archived_file_name, false); |
| 3019 | 2850 |
| 3020 // Now re-initialize the database (which may fail). | 2851 // Now re-initialize the database (which may fail). |
| 3021 archived_db_.reset(new ArchivedDatabase()); | 2852 archived_db_.reset(new ArchivedDatabase()); |
| 3022 if (!archived_db_->Init(archived_file_name)) { | 2853 if (!archived_db_->Init(archived_file_name)) { |
| 3023 LOG(WARNING) << "Could not initialize the archived database."; | 2854 LOG(WARNING) << "Could not initialize the archived database."; |
| (...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3149 info.url_id = visit.url_id; | 2980 info.url_id = visit.url_id; |
| 3150 info.time = visit.visit_time; | 2981 info.time = visit.visit_time; |
| 3151 info.transition = visit.transition; | 2982 info.transition = visit.transition; |
| 3152 // If we don't have a delegate yet during setup or shutdown, we will drop | 2983 // If we don't have a delegate yet during setup or shutdown, we will drop |
| 3153 // these notifications. | 2984 // these notifications. |
| 3154 if (delegate_) | 2985 if (delegate_) |
| 3155 delegate_->NotifyVisitDBObserversOnAddVisit(info); | 2986 delegate_->NotifyVisitDBObserversOnAddVisit(info); |
| 3156 } | 2987 } |
| 3157 | 2988 |
| 3158 } // namespace history | 2989 } // namespace history |
| OLD | NEW |