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/files/file_enumerator.h" |
17 #include "base/memory/scoped_ptr.h" | 18 #include "base/memory/scoped_ptr.h" |
18 #include "base/memory/scoped_vector.h" | 19 #include "base/memory/scoped_vector.h" |
19 #include "base/message_loop/message_loop.h" | 20 #include "base/message_loop/message_loop.h" |
20 #include "base/metrics/histogram.h" | 21 #include "base/metrics/histogram.h" |
21 #include "base/rand_util.h" | 22 #include "base/rand_util.h" |
22 #include "base/strings/string_util.h" | 23 #include "base/strings/string_util.h" |
23 #include "base/strings/utf_string_conversions.h" | 24 #include "base/strings/utf_string_conversions.h" |
24 #include "base/time/time.h" | 25 #include "base/time/time.h" |
25 #include "chrome/browser/autocomplete/history_url_provider.h" | 26 #include "chrome/browser/autocomplete/history_url_provider.h" |
26 #include "chrome/browser/bookmarks/bookmark_service.h" | 27 #include "chrome/browser/bookmarks/bookmark_service.h" |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
61 VisitDatabase (stores a list of visits for the URLs) | 62 VisitDatabase (stores a list of visits for the URLs) |
62 VisitSegmentDatabase (stores groups of URLs for the most visited view). | 63 VisitSegmentDatabase (stores groups of URLs for the most visited view). |
63 | 64 |
64 ArchivedDatabase (stores history older than 3 months) | 65 ArchivedDatabase (stores history older than 3 months) |
65 URLDatabase (stores a list of URLs) | 66 URLDatabase (stores a list of URLs) |
66 DownloadDatabase (stores a list of downloads) | 67 DownloadDatabase (stores a list of downloads) |
67 VisitDatabase (stores a list of visits for the URLs) | 68 VisitDatabase (stores a list of visits for the URLs) |
68 | 69 |
69 (this does not store visit segments as they expire after 3 mos.) | 70 (this does not store visit segments as they expire after 3 mos.) |
70 | 71 |
71 TextDatabaseManager (manages multiple text database for different times) | |
72 TextDatabase (represents a single month of full-text index). | |
73 ...more TextDatabase objects... | |
74 | |
75 ExpireHistoryBackend (manages moving things from HistoryDatabase to | 72 ExpireHistoryBackend (manages moving things from HistoryDatabase to |
76 the ArchivedDatabase and deleting) | 73 the ArchivedDatabase and deleting) |
77 */ | 74 */ |
78 | 75 |
79 namespace history { | 76 namespace history { |
80 | 77 |
81 // How long we keep segment data for in days. Currently 3 months. | 78 // How long we keep segment data for in days. Currently 3 months. |
82 // This value needs to be greater or equal to | 79 // This value needs to be greater or equal to |
83 // MostVisitedModel::kMostVisitedScope but we don't want to introduce a direct | 80 // MostVisitedModel::kMostVisitedScope but we don't want to introduce a direct |
84 // dependency between MostVisitedModel and the history backend. | 81 // dependency between MostVisitedModel and the history backend. |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
161 } | 158 } |
162 | 159 |
163 private: | 160 private: |
164 friend class base::RefCounted<CommitLaterTask>; | 161 friend class base::RefCounted<CommitLaterTask>; |
165 | 162 |
166 ~CommitLaterTask() {} | 163 ~CommitLaterTask() {} |
167 | 164 |
168 scoped_refptr<HistoryBackend> history_backend_; | 165 scoped_refptr<HistoryBackend> history_backend_; |
169 }; | 166 }; |
170 | 167 |
171 // Handles querying first the main database, then the full text database if that | |
172 // fails. It will optionally keep track of all URLs seen so duplicates can be | |
173 // eliminated. This is used by the querying sub-functions. | |
174 // | |
175 // TODO(brettw): This class may be able to be simplified or eliminated. After | |
176 // this was written, QueryResults can efficiently look up by URL, so the need | |
177 // for this extra set of previously queried URLs is less important. | |
178 class HistoryBackend::URLQuerier { | |
179 public: | |
180 URLQuerier(URLDatabase* main_db, URLDatabase* archived_db, bool track_unique) | |
181 : main_db_(main_db), | |
182 archived_db_(archived_db), | |
183 track_unique_(track_unique) { | |
184 } | |
185 | |
186 // When we're tracking unique URLs, returns true if this URL has been | |
187 // previously queried. Only call when tracking unique URLs. | |
188 bool HasURL(const GURL& url) { | |
189 DCHECK(track_unique_); | |
190 return unique_urls_.find(url) != unique_urls_.end(); | |
191 } | |
192 | |
193 bool GetRowForURL(const GURL& url, URLRow* row) { | |
194 if (!main_db_->GetRowForURL(url, row)) { | |
195 if (!archived_db_ || !archived_db_->GetRowForURL(url, row)) { | |
196 // This row is neither in the main nor the archived DB. | |
197 return false; | |
198 } | |
199 } | |
200 | |
201 if (track_unique_) | |
202 unique_urls_.insert(url); | |
203 return true; | |
204 } | |
205 | |
206 private: | |
207 URLDatabase* main_db_; // Guaranteed non-NULL. | |
208 URLDatabase* archived_db_; // Possibly NULL. | |
209 | |
210 bool track_unique_; | |
211 | |
212 // When track_unique_ is set, this is updated with every URL seen so far. | |
213 std::set<GURL> unique_urls_; | |
214 | |
215 DISALLOW_COPY_AND_ASSIGN(URLQuerier); | |
216 }; | |
217 | |
218 // HistoryBackend -------------------------------------------------------------- | 168 // HistoryBackend -------------------------------------------------------------- |
219 | 169 |
220 HistoryBackend::HistoryBackend(const base::FilePath& history_dir, | 170 HistoryBackend::HistoryBackend(const base::FilePath& history_dir, |
221 int id, | 171 int id, |
222 Delegate* delegate, | 172 Delegate* delegate, |
223 BookmarkService* bookmark_service) | 173 BookmarkService* bookmark_service) |
224 : delegate_(delegate), | 174 : delegate_(delegate), |
225 id_(id), | 175 id_(id), |
226 history_dir_(history_dir), | 176 history_dir_(history_dir), |
227 scheduled_kill_db_(false), | 177 scheduled_kill_db_(false), |
(...skipping 347 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
575 // Update the visit_details for this visit. | 525 // Update the visit_details for this visit. |
576 UpdateVisitDuration(from_visit_id, request.time); | 526 UpdateVisitDuration(from_visit_id, request.time); |
577 } | 527 } |
578 | 528 |
579 // Subsequent transitions in the redirect list must all be server | 529 // Subsequent transitions in the redirect list must all be server |
580 // redirects. | 530 // redirects. |
581 redirect_info = content::PAGE_TRANSITION_SERVER_REDIRECT; | 531 redirect_info = content::PAGE_TRANSITION_SERVER_REDIRECT; |
582 } | 532 } |
583 | 533 |
584 // Last, save this redirect chain for later so we can set titles & favicons | 534 // Last, save this redirect chain for later so we can set titles & favicons |
585 // on the redirected pages properly. It is indexed by the destination page. | 535 // on the redirected pages properly. |
586 recent_redirects_.Put(request.url, redirects); | 536 recent_redirects_.Put(request.url, redirects); |
587 } | 537 } |
588 | 538 |
589 // TODO(brettw) bug 1140015: Add an "add page" notification so the history | 539 // TODO(brettw) bug 1140015: Add an "add page" notification so the history |
590 // views can keep in sync. | 540 // views can keep in sync. |
591 | 541 |
592 // Add the last visit to the tracker so we can get outgoing transitions. | 542 // Add the last visit to the tracker so we can get outgoing transitions. |
593 // TODO(evanm): Due to http://b/1194536 we lose the referrers of a subframe | 543 // TODO(evanm): Due to http://b/1194536 we lose the referrers of a subframe |
594 // navigation anyway, so last_visit_id is always zero for them. But adding | 544 // navigation anyway, so last_visit_id is always zero for them. But adding |
595 // them here confuses main frame history, so we skip them for now. | 545 // them here confuses main frame history, so we skip them for now. |
596 if (stripped_transition != content::PAGE_TRANSITION_AUTO_SUBFRAME && | 546 if (stripped_transition != content::PAGE_TRANSITION_AUTO_SUBFRAME && |
597 stripped_transition != content::PAGE_TRANSITION_MANUAL_SUBFRAME && | 547 stripped_transition != content::PAGE_TRANSITION_MANUAL_SUBFRAME && |
598 !is_keyword_generated) { | 548 !is_keyword_generated) { |
599 tracker_.AddVisit(request.id_scope, request.page_id, request.url, | 549 tracker_.AddVisit(request.id_scope, request.page_id, request.url, |
600 last_ids.second); | 550 last_ids.second); |
601 } | 551 } |
602 | 552 |
603 if (text_database_) { | |
604 text_database_->AddPageURL(request.url, last_ids.first, last_ids.second, | |
605 request.time); | |
606 } | |
607 | |
608 ScheduleCommit(); | 553 ScheduleCommit(); |
609 } | 554 } |
610 | 555 |
611 void HistoryBackend::InitImpl(const std::string& languages) { | 556 void HistoryBackend::InitImpl(const std::string& languages) { |
612 DCHECK(!db_) << "Initializing HistoryBackend twice"; | 557 DCHECK(!db_) << "Initializing HistoryBackend twice"; |
613 // In the rare case where the db fails to initialize a dialog may get shown | 558 // In the rare case where the db fails to initialize a dialog may get shown |
614 // the blocks the caller, yet allows other messages through. For this reason | 559 // the blocks the caller, yet allows other messages through. For this reason |
615 // we only set db_ to the created database if creation is successful. That | 560 // we only set db_ to the created database if creation is successful. That |
616 // way other methods won't do anything as db_ is still NULL. | 561 // way other methods won't do anything as db_ is still NULL. |
617 | 562 |
618 TimeTicks beginning_time = TimeTicks::Now(); | 563 TimeTicks beginning_time = TimeTicks::Now(); |
619 | 564 |
620 // Compute the file names. Note that the index file can be removed when the | 565 // Compute the file names. |
621 // text db manager is finished being hooked up. | |
622 base::FilePath history_name = history_dir_.Append(chrome::kHistoryFilename); | 566 base::FilePath history_name = history_dir_.Append(chrome::kHistoryFilename); |
623 base::FilePath thumbnail_name = GetThumbnailFileName(); | 567 base::FilePath thumbnail_name = GetThumbnailFileName(); |
624 base::FilePath archived_name = GetArchivedFileName(); | 568 base::FilePath archived_name = GetArchivedFileName(); |
625 | 569 |
| 570 // Delete the old index database files which are no longer used. |
| 571 DeleteFTSIndexDatabases(); |
| 572 |
626 // History database. | 573 // History database. |
627 db_.reset(new HistoryDatabase()); | 574 db_.reset(new HistoryDatabase()); |
628 | 575 |
629 // Unretained to avoid a ref loop with db_. | 576 // Unretained to avoid a ref loop with db_. |
630 db_->set_error_callback( | 577 db_->set_error_callback( |
631 base::Bind(&HistoryBackend::DatabaseErrorCallback, | 578 base::Bind(&HistoryBackend::DatabaseErrorCallback, |
632 base::Unretained(this))); | 579 base::Unretained(this))); |
633 | 580 |
634 sql::InitStatus status = db_->Init(history_name); | 581 sql::InitStatus status = db_->Init(history_name); |
635 switch (status) { | 582 switch (status) { |
(...skipping 19 matching lines...) Expand all Loading... |
655 // Fill the in-memory database and send it back to the history service on the | 602 // Fill the in-memory database and send it back to the history service on the |
656 // main thread. | 603 // main thread. |
657 InMemoryHistoryBackend* mem_backend = new InMemoryHistoryBackend; | 604 InMemoryHistoryBackend* mem_backend = new InMemoryHistoryBackend; |
658 if (mem_backend->Init(history_name, db_.get())) | 605 if (mem_backend->Init(history_name, db_.get())) |
659 delegate_->SetInMemoryBackend(id_, mem_backend); // Takes ownership of | 606 delegate_->SetInMemoryBackend(id_, mem_backend); // Takes ownership of |
660 // pointer. | 607 // pointer. |
661 else | 608 else |
662 delete mem_backend; // Error case, run without the in-memory DB. | 609 delete mem_backend; // Error case, run without the in-memory DB. |
663 db_->BeginExclusiveMode(); // Must be after the mem backend read the data. | 610 db_->BeginExclusiveMode(); // Must be after the mem backend read the data. |
664 | 611 |
665 // Create the history publisher which needs to be passed on to the text and | 612 // Create the history publisher which needs to be passed on to the thumbnail |
666 // thumbnail databases for publishing history. | 613 // database for publishing history. |
667 history_publisher_.reset(new HistoryPublisher()); | 614 history_publisher_.reset(new HistoryPublisher()); |
668 if (!history_publisher_->Init()) { | 615 if (!history_publisher_->Init()) { |
669 // The init may fail when there are no indexers wanting our history. | 616 // The init may fail when there are no indexers wanting our history. |
670 // Hence no need to log the failure. | 617 // Hence no need to log the failure. |
671 history_publisher_.reset(); | 618 history_publisher_.reset(); |
672 } | 619 } |
673 | 620 |
674 // Full-text database. This has to be first so we can pass it to the | |
675 // HistoryDatabase for migration. | |
676 text_database_.reset(new TextDatabaseManager(history_dir_, | |
677 db_.get(), db_.get())); | |
678 if (!text_database_->Init(history_publisher_.get())) { | |
679 LOG(WARNING) << "Text database initialization failed, running without it."; | |
680 text_database_.reset(); | |
681 } | |
682 if (db_->needs_version_17_migration()) { | |
683 // See needs_version_17_migration() decl for more. In this case, we want | |
684 // to erase all the text database files. This must be done after the text | |
685 // database manager has been initialized, since it knows about all the | |
686 // files it manages. | |
687 text_database_->DeleteAll(); | |
688 } | |
689 | |
690 // Thumbnail database. | 621 // Thumbnail database. |
691 thumbnail_db_.reset(new ThumbnailDatabase()); | 622 thumbnail_db_.reset(new ThumbnailDatabase()); |
692 if (!db_->GetNeedsThumbnailMigration()) { | 623 if (!db_->GetNeedsThumbnailMigration()) { |
693 // No convertion needed - use new filename right away. | 624 // No convertion needed - use new filename right away. |
694 thumbnail_name = GetFaviconsFileName(); | 625 thumbnail_name = GetFaviconsFileName(); |
695 } | 626 } |
696 if (thumbnail_db_->Init(thumbnail_name, | 627 if (thumbnail_db_->Init(thumbnail_name, |
697 history_publisher_.get(), | 628 history_publisher_.get(), |
698 db_.get()) != sql::INIT_OK) { | 629 db_.get()) != sql::INIT_OK) { |
699 // Unlike the main database, we don't error out when the database is too | 630 // 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... |
732 } | 663 } |
733 | 664 |
734 // Tell the expiration module about all the nice databases we made. This must | 665 // Tell the expiration module about all the nice databases we made. This must |
735 // happen before db_->Init() is called since the callback ForceArchiveHistory | 666 // happen before db_->Init() is called since the callback ForceArchiveHistory |
736 // may need to expire stuff. | 667 // may need to expire stuff. |
737 // | 668 // |
738 // *sigh*, this can all be cleaned up when that migration code is removed. | 669 // *sigh*, this can all be cleaned up when that migration code is removed. |
739 // The main DB initialization should intuitively be first (not that it | 670 // The main DB initialization should intuitively be first (not that it |
740 // actually matters) and the expirer should be set last. | 671 // actually matters) and the expirer should be set last. |
741 expirer_.SetDatabases(db_.get(), archived_db_.get(), | 672 expirer_.SetDatabases(db_.get(), archived_db_.get(), |
742 thumbnail_db_.get(), text_database_.get()); | 673 thumbnail_db_.get()); |
743 | 674 |
744 // Open the long-running transaction. | 675 // Open the long-running transaction. |
745 db_->BeginTransaction(); | 676 db_->BeginTransaction(); |
746 if (thumbnail_db_) | 677 if (thumbnail_db_) |
747 thumbnail_db_->BeginTransaction(); | 678 thumbnail_db_->BeginTransaction(); |
748 if (archived_db_) | 679 if (archived_db_) |
749 archived_db_->BeginTransaction(); | 680 archived_db_->BeginTransaction(); |
750 if (text_database_) | |
751 text_database_->BeginTransaction(); | |
752 | 681 |
753 // Get the first item in our database. | 682 // Get the first item in our database. |
754 db_->GetStartDate(&first_recorded_time_); | 683 db_->GetStartDate(&first_recorded_time_); |
755 | 684 |
756 // Start expiring old stuff. | 685 // Start expiring old stuff. |
757 expirer_.StartArchivingOldStuff(TimeDelta::FromDays(kArchiveDaysThreshold)); | 686 expirer_.StartArchivingOldStuff(TimeDelta::FromDays(kArchiveDaysThreshold)); |
758 | 687 |
759 #if defined(OS_ANDROID) | 688 #if defined(OS_ANDROID) |
760 if (thumbnail_db_) { | 689 if (thumbnail_db_) { |
761 android_provider_backend_.reset(new AndroidProviderBackend( | 690 android_provider_backend_.reset(new AndroidProviderBackend( |
(...skipping 25 matching lines...) Expand all Loading... |
787 db_.reset(); | 716 db_.reset(); |
788 } | 717 } |
789 if (thumbnail_db_) { | 718 if (thumbnail_db_) { |
790 thumbnail_db_->CommitTransaction(); | 719 thumbnail_db_->CommitTransaction(); |
791 thumbnail_db_.reset(); | 720 thumbnail_db_.reset(); |
792 } | 721 } |
793 if (archived_db_) { | 722 if (archived_db_) { |
794 archived_db_->CommitTransaction(); | 723 archived_db_->CommitTransaction(); |
795 archived_db_.reset(); | 724 archived_db_.reset(); |
796 } | 725 } |
797 if (text_database_) { | |
798 text_database_->CommitTransaction(); | |
799 text_database_.reset(); | |
800 } | |
801 } | 726 } |
802 | 727 |
803 std::pair<URLID, VisitID> HistoryBackend::AddPageVisit( | 728 std::pair<URLID, VisitID> HistoryBackend::AddPageVisit( |
804 const GURL& url, | 729 const GURL& url, |
805 Time time, | 730 Time time, |
806 VisitID referring_visit, | 731 VisitID referring_visit, |
807 content::PageTransition transition, | 732 content::PageTransition transition, |
808 VisitSource visit_source) { | 733 VisitSource visit_source) { |
809 // Top-level frame navigations are visible, everything else is hidden | 734 // Top-level frame navigations are visible, everything else is hidden |
810 bool new_hidden = !content::PageTransitionIsMainFrame(transition); | 735 bool new_hidden = !content::PageTransitionIsMainFrame(transition); |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
854 url_info.set_typed_count(typed_increment); | 779 url_info.set_typed_count(typed_increment); |
855 url_info.set_last_visit(time); | 780 url_info.set_last_visit(time); |
856 url_info.set_hidden(new_hidden); | 781 url_info.set_hidden(new_hidden); |
857 | 782 |
858 url_id = db_->AddURL(url_info); | 783 url_id = db_->AddURL(url_info); |
859 if (!url_id) { | 784 if (!url_id) { |
860 NOTREACHED() << "Adding URL failed."; | 785 NOTREACHED() << "Adding URL failed."; |
861 return std::make_pair(0, 0); | 786 return std::make_pair(0, 0); |
862 } | 787 } |
863 url_info.id_ = url_id; | 788 url_info.id_ = url_id; |
864 | |
865 // We don't actually add the URL to the full text index at this point. It | |
866 // might be nice to do this so that even if we get no title or body, the | |
867 // user can search for URL components and get the page. | |
868 // | |
869 // However, in most cases, we'll get at least a title and usually contents, | |
870 // and this add will be redundant, slowing everything down. As a result, | |
871 // we ignore this edge case. | |
872 } | 789 } |
873 | 790 |
874 // Add the visit with the time to the database. | 791 // Add the visit with the time to the database. |
875 VisitRow visit_info(url_id, time, referring_visit, transition, 0); | 792 VisitRow visit_info(url_id, time, referring_visit, transition, 0); |
876 VisitID visit_id = db_->AddVisit(&visit_info, visit_source); | 793 VisitID visit_id = db_->AddVisit(&visit_info, visit_source); |
877 NotifyVisitObservers(visit_info); | 794 NotifyVisitObservers(visit_info); |
878 | 795 |
879 if (visit_info.visit_time < first_recorded_time_) | 796 if (visit_info.visit_time < first_recorded_time_) |
880 first_recorded_time_ = visit_info.visit_time; | 797 first_recorded_time_ = visit_info.visit_time; |
881 | 798 |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
931 NOTREACHED() << "Could not add row to DB"; | 848 NOTREACHED() << "Could not add row to DB"; |
932 return; | 849 return; |
933 } | 850 } |
934 | 851 |
935 if (i->typed_count() > 0) { | 852 if (i->typed_count() > 0) { |
936 modified->changed_urls.push_back(*i); | 853 modified->changed_urls.push_back(*i); |
937 modified->changed_urls.back().set_id(url_id); // *i likely has |id_| 0. | 854 modified->changed_urls.back().set_id(url_id); // *i likely has |id_| 0. |
938 } | 855 } |
939 } | 856 } |
940 | 857 |
941 // Add the page to the full text index. This function is also used for | |
942 // importing. Even though we don't have page contents, we can at least | |
943 // add the title and URL to the index so they can be searched. We don't | |
944 // bother to delete any already-existing FTS entries for the URL, since | |
945 // this is normally called on import. | |
946 // | |
947 // If you ever import *after* first run (selecting import from the menu), | |
948 // then these additional entries will "shadow" the originals when querying | |
949 // for the most recent match only, and the user won't get snippets. This is | |
950 // a very minor issue, and fixing it will make import slower, so we don't | |
951 // bother. | |
952 bool has_indexed = false; | |
953 if (text_database_) { | |
954 // We do not have to make it update the visit database, below, we will | |
955 // create the visit entry with the indexed flag set. | |
956 has_indexed = text_database_->AddPageData(i->url(), url_id, 0, | |
957 i->last_visit(), | |
958 i->title(), string16()); | |
959 } | |
960 | |
961 // Sync code manages the visits itself. | 858 // Sync code manages the visits itself. |
962 if (visit_source != SOURCE_SYNCED) { | 859 if (visit_source != SOURCE_SYNCED) { |
963 // Make up a visit to correspond to the last visit to the page. | 860 // Make up a visit to correspond to the last visit to the page. |
964 VisitRow visit_info(url_id, i->last_visit(), 0, | 861 VisitRow visit_info(url_id, i->last_visit(), 0, |
965 content::PageTransitionFromInt( | 862 content::PageTransitionFromInt( |
966 content::PAGE_TRANSITION_LINK | | 863 content::PAGE_TRANSITION_LINK | |
967 content::PAGE_TRANSITION_CHAIN_START | | 864 content::PAGE_TRANSITION_CHAIN_START | |
968 content::PAGE_TRANSITION_CHAIN_END), 0); | 865 content::PAGE_TRANSITION_CHAIN_END), 0); |
969 visit_info.is_indexed = has_indexed; | |
970 if (!visit_database->AddVisit(&visit_info, visit_source)) { | 866 if (!visit_database->AddVisit(&visit_info, visit_source)) { |
971 NOTREACHED() << "Adding visit failed."; | 867 NOTREACHED() << "Adding visit failed."; |
972 return; | 868 return; |
973 } | 869 } |
974 NotifyVisitObservers(visit_info); | 870 NotifyVisitObservers(visit_info); |
975 | 871 |
976 if (visit_info.visit_time < first_recorded_time_) | 872 if (visit_info.visit_time < first_recorded_time_) |
977 first_recorded_time_ = visit_info.visit_time; | 873 first_recorded_time_ = visit_info.visit_time; |
978 } | 874 } |
979 } | 875 } |
(...skipping 14 matching lines...) Expand all Loading... |
994 | 890 |
995 bool HistoryBackend::IsExpiredVisitTime(const base::Time& time) { | 891 bool HistoryBackend::IsExpiredVisitTime(const base::Time& time) { |
996 return time < expirer_.GetCurrentArchiveTime(); | 892 return time < expirer_.GetCurrentArchiveTime(); |
997 } | 893 } |
998 | 894 |
999 void HistoryBackend::SetPageTitle(const GURL& url, | 895 void HistoryBackend::SetPageTitle(const GURL& url, |
1000 const string16& title) { | 896 const string16& title) { |
1001 if (!db_) | 897 if (!db_) |
1002 return; | 898 return; |
1003 | 899 |
1004 // Update the full text index. | |
1005 if (text_database_) | |
1006 text_database_->AddPageTitle(url, title); | |
1007 | |
1008 // Search for recent redirects which should get the same title. We make a | 900 // Search for recent redirects which should get the same title. We make a |
1009 // dummy list containing the exact URL visited if there are no redirects so | 901 // dummy list containing the exact URL visited if there are no redirects so |
1010 // the processing below can be the same. | 902 // the processing below can be the same. |
1011 history::RedirectList dummy_list; | 903 history::RedirectList dummy_list; |
1012 history::RedirectList* redirects; | 904 history::RedirectList* redirects; |
1013 RedirectCache::iterator iter = recent_redirects_.Get(url); | 905 RedirectCache::iterator iter = recent_redirects_.Get(url); |
1014 if (iter != recent_redirects_.end()) { | 906 if (iter != recent_redirects_.end()) { |
1015 redirects = &iter->second; | 907 redirects = &iter->second; |
1016 | 908 |
1017 // This redirect chain should have the destination URL as the last item. | 909 // This redirect chain should have the destination URL as the last item. |
(...skipping 474 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1492 for (std::vector<URLResult>::iterator it = matching_visits.begin(); | 1384 for (std::vector<URLResult>::iterator it = matching_visits.begin(); |
1493 it != matching_visits.end() && result->size() < max_results; ++it) { | 1385 it != matching_visits.end() && result->size() < max_results; ++it) { |
1494 result->AppendURLBySwapping(&(*it)); | 1386 result->AppendURLBySwapping(&(*it)); |
1495 } | 1387 } |
1496 | 1388 |
1497 if (matching_visits.size() == result->size() && | 1389 if (matching_visits.size() == result->size() && |
1498 options.begin_time <= first_recorded_time_) | 1390 options.begin_time <= first_recorded_time_) |
1499 result->set_reached_beginning(true); | 1391 result->set_reached_beginning(true); |
1500 } | 1392 } |
1501 | 1393 |
1502 void HistoryBackend::QueryHistoryFTS(const string16& text_query, | |
1503 const QueryOptions& options, | |
1504 QueryResults* result) { | |
1505 if (!text_database_) | |
1506 return; | |
1507 | |
1508 // Full text query, first get all the FTS results in the time range. | |
1509 std::vector<TextDatabase::Match> fts_matches; | |
1510 Time first_time_searched; | |
1511 text_database_->GetTextMatches(text_query, options, | |
1512 &fts_matches, &first_time_searched); | |
1513 | |
1514 URLQuerier querier(db_.get(), archived_db_.get(), true); | |
1515 | |
1516 // Now get the row and visit information for each one. | |
1517 URLResult url_result; // Declare outside loop to prevent re-construction. | |
1518 for (size_t i = 0; i < fts_matches.size(); i++) { | |
1519 if (options.max_count != 0 && | |
1520 static_cast<int>(result->size()) >= options.max_count) | |
1521 break; // Got too many items. | |
1522 | |
1523 // Get the URL, querying the main and archived databases as necessary. If | |
1524 // this is not found, the history and full text search databases are out | |
1525 // of sync and we give up with this result. | |
1526 if (!querier.GetRowForURL(fts_matches[i].url, &url_result)) | |
1527 continue; | |
1528 | |
1529 if (!url_result.url().is_valid()) | |
1530 continue; // Don't report invalid URLs in case of corruption. | |
1531 | |
1532 // Copy over the FTS stuff that the URLDatabase doesn't know about. | |
1533 // We do this with swap() to avoid copying, since we know we don't | |
1534 // need the original any more. Note that we override the title with the | |
1535 // one from FTS, since that will match the title_match_positions (the | |
1536 // FTS title and the history DB title may differ). | |
1537 url_result.set_title(fts_matches[i].title); | |
1538 url_result.title_match_positions_.swap( | |
1539 fts_matches[i].title_match_positions); | |
1540 url_result.snippet_.Swap(&fts_matches[i].snippet); | |
1541 | |
1542 // The visit time also comes from the full text search database. Since it | |
1543 // has the time, we can avoid an extra query of the visits table. | |
1544 url_result.set_visit_time(fts_matches[i].time); | |
1545 | |
1546 // Add it to the vector, this will clear our |url_row| object as a | |
1547 // result of the swap. | |
1548 result->AppendURLBySwapping(&url_result); | |
1549 } | |
1550 | |
1551 if (first_time_searched <= first_recorded_time_) | |
1552 result->set_reached_beginning(true); | |
1553 } | |
1554 | |
1555 // Frontend to GetMostRecentRedirectsFrom from the history thread. | 1394 // Frontend to GetMostRecentRedirectsFrom from the history thread. |
1556 void HistoryBackend::QueryRedirectsFrom( | 1395 void HistoryBackend::QueryRedirectsFrom( |
1557 scoped_refptr<QueryRedirectsRequest> request, | 1396 scoped_refptr<QueryRedirectsRequest> request, |
1558 const GURL& url) { | 1397 const GURL& url) { |
1559 if (request->canceled()) | 1398 if (request->canceled()) |
1560 return; | 1399 return; |
1561 bool success = GetMostRecentRedirectsFrom(url, &request->value); | 1400 bool success = GetMostRecentRedirectsFrom(url, &request->value); |
1562 request->ForwardResult(request->handle(), url, success, &request->value); | 1401 request->ForwardResult(request->handle(), url, success, &request->value); |
1563 } | 1402 } |
1564 | 1403 |
(...skipping 239 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1804 GetRedirectsToSpecificVisit(cur_visit, redirects); | 1643 GetRedirectsToSpecificVisit(cur_visit, redirects); |
1805 return true; | 1644 return true; |
1806 } | 1645 } |
1807 | 1646 |
1808 void HistoryBackend::ScheduleAutocomplete(HistoryURLProvider* provider, | 1647 void HistoryBackend::ScheduleAutocomplete(HistoryURLProvider* provider, |
1809 HistoryURLProviderParams* params) { | 1648 HistoryURLProviderParams* params) { |
1810 // ExecuteWithDB should handle the NULL database case. | 1649 // ExecuteWithDB should handle the NULL database case. |
1811 provider->ExecuteWithDB(this, db_.get(), params); | 1650 provider->ExecuteWithDB(this, db_.get(), params); |
1812 } | 1651 } |
1813 | 1652 |
1814 void HistoryBackend::SetPageContents(const GURL& url, | |
1815 const string16& contents) { | |
1816 // This is histogrammed in the text database manager. | |
1817 if (!text_database_) | |
1818 return; | |
1819 text_database_->AddPageContents(url, contents); | |
1820 } | |
1821 | |
1822 void HistoryBackend::SetPageThumbnail( | 1653 void HistoryBackend::SetPageThumbnail( |
1823 const GURL& url, | 1654 const GURL& url, |
1824 const gfx::Image* thumbnail, | 1655 const gfx::Image* thumbnail, |
1825 const ThumbnailScore& score) { | 1656 const ThumbnailScore& score) { |
1826 if (!db_ || !thumbnail_db_) | 1657 if (!db_ || !thumbnail_db_) |
1827 return; | 1658 return; |
1828 | 1659 |
1829 URLRow url_row; | 1660 URLRow url_row; |
1830 URLID url_id = db_->GetRowForURL(url, &url_row); | 1661 URLID url_id = db_->GetRowForURL(url, &url_row); |
1831 if (url_id) { | 1662 if (url_id) { |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1895 if (db_) { | 1726 if (db_) { |
1896 // If there is no thumbnail DB, we can still record a successful migration. | 1727 // If there is no thumbnail DB, we can still record a successful migration. |
1897 if (thumbnail_db_) { | 1728 if (thumbnail_db_) { |
1898 thumbnail_db_->RenameAndDropThumbnails(GetThumbnailFileName(), | 1729 thumbnail_db_->RenameAndDropThumbnails(GetThumbnailFileName(), |
1899 GetFaviconsFileName()); | 1730 GetFaviconsFileName()); |
1900 } | 1731 } |
1901 db_->ThumbnailMigrationDone(); | 1732 db_->ThumbnailMigrationDone(); |
1902 } | 1733 } |
1903 } | 1734 } |
1904 | 1735 |
| 1736 void HistoryBackend::DeleteFTSIndexDatabases() { |
| 1737 // Find files on disk matching the text databases file pattern so we can |
| 1738 // quickly test for and delete them. |
| 1739 base::FilePath::StringType filepattern = |
| 1740 FILE_PATH_LITERAL("History Index *"); |
| 1741 base::FileEnumerator enumerator( |
| 1742 history_dir_, false, base::FileEnumerator::FILES, filepattern); |
| 1743 int num_databases_deleted = 0; |
| 1744 base::FilePath current_file; |
| 1745 while (!(current_file = enumerator.Next()).empty()) { |
| 1746 if (sql::Connection::Delete(current_file)) |
| 1747 num_databases_deleted++; |
| 1748 } |
| 1749 UMA_HISTOGRAM_COUNTS("History.DeleteFTSIndexDatabases", |
| 1750 num_databases_deleted); |
| 1751 } |
| 1752 |
1905 bool HistoryBackend::GetThumbnailFromOlderRedirect( | 1753 bool HistoryBackend::GetThumbnailFromOlderRedirect( |
1906 const GURL& page_url, | 1754 const GURL& page_url, |
1907 std::vector<unsigned char>* data) { | 1755 std::vector<unsigned char>* data) { |
1908 // Look at a few previous visit sessions. | 1756 // Look at a few previous visit sessions. |
1909 VisitVector older_sessions; | 1757 VisitVector older_sessions; |
1910 URLID page_url_id = db_->GetRowForURL(page_url, NULL); | 1758 URLID page_url_id = db_->GetRowForURL(page_url, NULL); |
1911 static const int kVisitsToSearchForThumbnail = 4; | 1759 static const int kVisitsToSearchForThumbnail = 4; |
1912 db_->GetMostRecentVisitsForURL( | 1760 db_->GetMostRecentVisitsForURL( |
1913 page_url_id, kVisitsToSearchForThumbnail, &older_sessions); | 1761 page_url_id, kVisitsToSearchForThumbnail, &older_sessions); |
1914 | 1762 |
(...skipping 746 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2661 thumbnail_db_->CommitTransaction(); | 2509 thumbnail_db_->CommitTransaction(); |
2662 DCHECK(thumbnail_db_->transaction_nesting() == 0) << | 2510 DCHECK(thumbnail_db_->transaction_nesting() == 0) << |
2663 "Somebody left a transaction open"; | 2511 "Somebody left a transaction open"; |
2664 thumbnail_db_->BeginTransaction(); | 2512 thumbnail_db_->BeginTransaction(); |
2665 } | 2513 } |
2666 | 2514 |
2667 if (archived_db_) { | 2515 if (archived_db_) { |
2668 archived_db_->CommitTransaction(); | 2516 archived_db_->CommitTransaction(); |
2669 archived_db_->BeginTransaction(); | 2517 archived_db_->BeginTransaction(); |
2670 } | 2518 } |
2671 | |
2672 if (text_database_) { | |
2673 text_database_->CommitTransaction(); | |
2674 text_database_->BeginTransaction(); | |
2675 } | |
2676 } | 2519 } |
2677 | 2520 |
2678 void HistoryBackend::ScheduleCommit() { | 2521 void HistoryBackend::ScheduleCommit() { |
2679 if (scheduled_commit_.get()) | 2522 if (scheduled_commit_.get()) |
2680 return; | 2523 return; |
2681 scheduled_commit_ = new CommitLaterTask(this); | 2524 scheduled_commit_ = new CommitLaterTask(this); |
2682 base::MessageLoop::current()->PostDelayedTask( | 2525 base::MessageLoop::current()->PostDelayedTask( |
2683 FROM_HERE, | 2526 FROM_HERE, |
2684 base::Bind(&CommitLaterTask::RunCommit, scheduled_commit_.get()), | 2527 base::Bind(&CommitLaterTask::RunCommit, scheduled_commit_.get()), |
2685 base::TimeDelta::FromSeconds(kCommitIntervalSeconds)); | 2528 base::TimeDelta::FromSeconds(kCommitIntervalSeconds)); |
(...skipping 210 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2896 bool success = db_->Raze(); | 2739 bool success = db_->Raze(); |
2897 UMA_HISTOGRAM_BOOLEAN("History.KillHistoryDatabaseResult", success); | 2740 UMA_HISTOGRAM_BOOLEAN("History.KillHistoryDatabaseResult", success); |
2898 | 2741 |
2899 #if defined(OS_ANDROID) | 2742 #if defined(OS_ANDROID) |
2900 // Release AndroidProviderBackend before other objects. | 2743 // Release AndroidProviderBackend before other objects. |
2901 android_provider_backend_.reset(); | 2744 android_provider_backend_.reset(); |
2902 #endif | 2745 #endif |
2903 | 2746 |
2904 // The expirer keeps tabs on the active databases. Tell it about the | 2747 // The expirer keeps tabs on the active databases. Tell it about the |
2905 // databases which will be closed. | 2748 // databases which will be closed. |
2906 expirer_.SetDatabases(NULL, NULL, NULL, NULL); | 2749 expirer_.SetDatabases(NULL, NULL, NULL); |
2907 | 2750 |
2908 // Reopen a new transaction for |db_| for the sake of CloseAllDatabases(). | 2751 // Reopen a new transaction for |db_| for the sake of CloseAllDatabases(). |
2909 db_->BeginTransaction(); | 2752 db_->BeginTransaction(); |
2910 CloseAllDatabases(); | 2753 CloseAllDatabases(); |
2911 } | 2754 } |
2912 | 2755 |
2913 void HistoryBackend::ProcessDBTask( | 2756 void HistoryBackend::ProcessDBTask( |
2914 scoped_refptr<HistoryDBTaskRequest> request) { | 2757 scoped_refptr<HistoryDBTaskRequest> request) { |
2915 DCHECK(request.get()); | 2758 DCHECK(request.get()); |
2916 if (request->canceled()) | 2759 if (request->canceled()) |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2986 // We continue in this error case. If the user wants to delete their | 2829 // We continue in this error case. If the user wants to delete their |
2987 // history, we should delete as much as we can. | 2830 // history, we should delete as much as we can. |
2988 } | 2831 } |
2989 | 2832 |
2990 // ClearAllMainHistory will change the IDs of the URLs in kept_urls. Therfore, | 2833 // ClearAllMainHistory will change the IDs of the URLs in kept_urls. Therfore, |
2991 // we clear the list afterwards to make sure nobody uses this invalid data. | 2834 // we clear the list afterwards to make sure nobody uses this invalid data. |
2992 if (!ClearAllMainHistory(kept_urls)) | 2835 if (!ClearAllMainHistory(kept_urls)) |
2993 LOG(ERROR) << "Main history could not be cleared"; | 2836 LOG(ERROR) << "Main history could not be cleared"; |
2994 kept_urls.clear(); | 2837 kept_urls.clear(); |
2995 | 2838 |
2996 // Delete FTS files & archived history. | 2839 // Delete archived history. |
2997 if (text_database_) { | |
2998 // We assume that the text database has one transaction on them that we need | |
2999 // to close & restart (the long-running history transaction). | |
3000 text_database_->CommitTransaction(); | |
3001 text_database_->DeleteAll(); | |
3002 text_database_->BeginTransaction(); | |
3003 } | |
3004 | |
3005 if (archived_db_) { | 2840 if (archived_db_) { |
3006 // Close the database and delete the file. | 2841 // Close the database and delete the file. |
3007 archived_db_.reset(); | 2842 archived_db_.reset(); |
3008 base::FilePath archived_file_name = GetArchivedFileName(); | 2843 base::FilePath archived_file_name = GetArchivedFileName(); |
3009 sql::Connection::Delete(archived_file_name); | 2844 sql::Connection::Delete(archived_file_name); |
3010 | 2845 |
3011 // Now re-initialize the database (which may fail). | 2846 // Now re-initialize the database (which may fail). |
3012 archived_db_.reset(new ArchivedDatabase()); | 2847 archived_db_.reset(new ArchivedDatabase()); |
3013 if (!archived_db_->Init(archived_file_name)) { | 2848 if (!archived_db_->Init(archived_file_name)) { |
3014 LOG(WARNING) << "Could not initialize the archived database."; | 2849 LOG(WARNING) << "Could not initialize the archived database."; |
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3164 int rank = kPageVisitStatsMaxTopSites; | 2999 int rank = kPageVisitStatsMaxTopSites; |
3165 std::map<GURL, int>::const_iterator it = most_visited_urls_map_.find(url); | 3000 std::map<GURL, int>::const_iterator it = most_visited_urls_map_.find(url); |
3166 if (it != most_visited_urls_map_.end()) | 3001 if (it != most_visited_urls_map_.end()) |
3167 rank = (*it).second; | 3002 rank = (*it).second; |
3168 UMA_HISTOGRAM_ENUMERATION("History.TopSitesVisitsByRank", | 3003 UMA_HISTOGRAM_ENUMERATION("History.TopSitesVisitsByRank", |
3169 rank, kPageVisitStatsMaxTopSites + 1); | 3004 rank, kPageVisitStatsMaxTopSites + 1); |
3170 } | 3005 } |
3171 #endif | 3006 #endif |
3172 | 3007 |
3173 } // namespace history | 3008 } // namespace history |
OLD | NEW |