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 // HistoryBackend -------------------------------------------------------------- | 164 // HistoryBackend -------------------------------------------------------------- |
215 | 165 |
216 HistoryBackend::HistoryBackend(const base::FilePath& history_dir, | 166 HistoryBackend::HistoryBackend(const base::FilePath& history_dir, |
217 int id, | 167 int id, |
218 Delegate* delegate, | 168 Delegate* delegate, |
219 BookmarkService* bookmark_service) | 169 BookmarkService* bookmark_service) |
220 : delegate_(delegate), | 170 : delegate_(delegate), |
221 id_(id), | 171 id_(id), |
222 history_dir_(history_dir), | 172 history_dir_(history_dir), |
223 scheduled_kill_db_(false), | 173 scheduled_kill_db_(false), |
(...skipping 342 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
566 // Update the visit_details for this visit. | 516 // Update the visit_details for this visit. |
567 UpdateVisitDuration(from_visit_id, request.time); | 517 UpdateVisitDuration(from_visit_id, request.time); |
568 } | 518 } |
569 | 519 |
570 // Subsequent transitions in the redirect list must all be server | 520 // Subsequent transitions in the redirect list must all be server |
571 // redirects. | 521 // redirects. |
572 redirect_info = content::PAGE_TRANSITION_SERVER_REDIRECT; | 522 redirect_info = content::PAGE_TRANSITION_SERVER_REDIRECT; |
573 } | 523 } |
574 | 524 |
575 // Last, save this redirect chain for later so we can set titles & favicons | 525 // Last, save this redirect chain for later so we can set titles & favicons |
576 // on the redirected pages properly. It is indexed by the destination page. | 526 // on the redirected pages properly. |
577 recent_redirects_.Put(request.url, redirects); | 527 recent_redirects_.Put(request.url, redirects); |
578 } | 528 } |
579 | 529 |
580 // TODO(brettw) bug 1140015: Add an "add page" notification so the history | 530 // TODO(brettw) bug 1140015: Add an "add page" notification so the history |
581 // views can keep in sync. | 531 // views can keep in sync. |
582 | 532 |
583 // Add the last visit to the tracker so we can get outgoing transitions. | 533 // Add the last visit to the tracker so we can get outgoing transitions. |
584 // TODO(evanm): Due to http://b/1194536 we lose the referrers of a subframe | 534 // TODO(evanm): Due to http://b/1194536 we lose the referrers of a subframe |
585 // navigation anyway, so last_visit_id is always zero for them. But adding | 535 // navigation anyway, so last_visit_id is always zero for them. But adding |
586 // them here confuses main frame history, so we skip them for now. | 536 // them here confuses main frame history, so we skip them for now. |
587 if (stripped_transition != content::PAGE_TRANSITION_AUTO_SUBFRAME && | 537 if (stripped_transition != content::PAGE_TRANSITION_AUTO_SUBFRAME && |
588 stripped_transition != content::PAGE_TRANSITION_MANUAL_SUBFRAME && | 538 stripped_transition != content::PAGE_TRANSITION_MANUAL_SUBFRAME && |
589 !is_keyword_generated) { | 539 !is_keyword_generated) { |
590 tracker_.AddVisit(request.id_scope, request.page_id, request.url, | 540 tracker_.AddVisit(request.id_scope, request.page_id, request.url, |
591 last_ids.second); | 541 last_ids.second); |
592 } | 542 } |
593 | 543 |
594 if (text_database_) { | |
595 text_database_->AddPageURL(request.url, last_ids.first, last_ids.second, | |
596 request.time); | |
597 } | |
598 | |
599 ScheduleCommit(); | 544 ScheduleCommit(); |
600 } | 545 } |
601 | 546 |
602 void HistoryBackend::InitImpl(const std::string& languages) { | 547 void HistoryBackend::InitImpl(const std::string& languages) { |
603 DCHECK(!db_) << "Initializing HistoryBackend twice"; | 548 DCHECK(!db_) << "Initializing HistoryBackend twice"; |
604 // In the rare case where the db fails to initialize a dialog may get shown | 549 // In the rare case where the db fails to initialize a dialog may get shown |
605 // the blocks the caller, yet allows other messages through. For this reason | 550 // the blocks the caller, yet allows other messages through. For this reason |
606 // we only set db_ to the created database if creation is successful. That | 551 // we only set db_ to the created database if creation is successful. That |
607 // way other methods won't do anything as db_ is still NULL. | 552 // way other methods won't do anything as db_ is still NULL. |
608 | 553 |
609 TimeTicks beginning_time = TimeTicks::Now(); | 554 TimeTicks beginning_time = TimeTicks::Now(); |
610 | 555 |
611 // Compute the file names. Note that the index file can be removed when the | 556 // Compute the file names. |
612 // text db manager is finished being hooked up. | |
613 base::FilePath history_name = history_dir_.Append(chrome::kHistoryFilename); | 557 base::FilePath history_name = history_dir_.Append(chrome::kHistoryFilename); |
614 base::FilePath thumbnail_name = GetThumbnailFileName(); | 558 base::FilePath thumbnail_name = GetThumbnailFileName(); |
615 base::FilePath archived_name = GetArchivedFileName(); | 559 base::FilePath archived_name = GetArchivedFileName(); |
616 | 560 |
| 561 // Delete the old index database files which are no longer used. |
| 562 DeleteFTSIndexDatabases(); |
| 563 |
617 // History database. | 564 // History database. |
618 db_.reset(new HistoryDatabase()); | 565 db_.reset(new HistoryDatabase()); |
619 | 566 |
620 // Unretained to avoid a ref loop with db_. | 567 // Unretained to avoid a ref loop with db_. |
621 db_->set_error_callback( | 568 db_->set_error_callback( |
622 base::Bind(&HistoryBackend::DatabaseErrorCallback, | 569 base::Bind(&HistoryBackend::DatabaseErrorCallback, |
623 base::Unretained(this))); | 570 base::Unretained(this))); |
624 | 571 |
625 sql::InitStatus status = db_->Init(history_name); | 572 sql::InitStatus status = db_->Init(history_name); |
626 switch (status) { | 573 switch (status) { |
(...skipping 19 matching lines...) Expand all Loading... |
646 // Fill the in-memory database and send it back to the history service on the | 593 // Fill the in-memory database and send it back to the history service on the |
647 // main thread. | 594 // main thread. |
648 InMemoryHistoryBackend* mem_backend = new InMemoryHistoryBackend; | 595 InMemoryHistoryBackend* mem_backend = new InMemoryHistoryBackend; |
649 if (mem_backend->Init(history_name, db_.get())) | 596 if (mem_backend->Init(history_name, db_.get())) |
650 delegate_->SetInMemoryBackend(id_, mem_backend); // Takes ownership of | 597 delegate_->SetInMemoryBackend(id_, mem_backend); // Takes ownership of |
651 // pointer. | 598 // pointer. |
652 else | 599 else |
653 delete mem_backend; // Error case, run without the in-memory DB. | 600 delete mem_backend; // Error case, run without the in-memory DB. |
654 db_->BeginExclusiveMode(); // Must be after the mem backend read the data. | 601 db_->BeginExclusiveMode(); // Must be after the mem backend read the data. |
655 | 602 |
656 // Create the history publisher which needs to be passed on to the text and | 603 // Create the history publisher which needs to be passed on to the thumbnail |
657 // thumbnail databases for publishing history. | 604 // database for publishing history. |
658 history_publisher_.reset(new HistoryPublisher()); | 605 history_publisher_.reset(new HistoryPublisher()); |
659 if (!history_publisher_->Init()) { | 606 if (!history_publisher_->Init()) { |
660 // The init may fail when there are no indexers wanting our history. | 607 // The init may fail when there are no indexers wanting our history. |
661 // Hence no need to log the failure. | 608 // Hence no need to log the failure. |
662 history_publisher_.reset(); | 609 history_publisher_.reset(); |
663 } | 610 } |
664 | 611 |
665 // Full-text database. This has to be first so we can pass it to the | |
666 // HistoryDatabase for migration. | |
667 text_database_.reset(new TextDatabaseManager(history_dir_, | |
668 db_.get(), db_.get())); | |
669 if (!text_database_->Init(history_publisher_.get())) { | |
670 LOG(WARNING) << "Text database initialization failed, running without it."; | |
671 text_database_.reset(); | |
672 } | |
673 if (db_->needs_version_17_migration()) { | |
674 // See needs_version_17_migration() decl for more. In this case, we want | |
675 // to erase all the text database files. This must be done after the text | |
676 // database manager has been initialized, since it knows about all the | |
677 // files it manages. | |
678 text_database_->DeleteAll(); | |
679 } | |
680 | |
681 // Thumbnail database. | 612 // Thumbnail database. |
682 thumbnail_db_.reset(new ThumbnailDatabase()); | 613 thumbnail_db_.reset(new ThumbnailDatabase()); |
683 if (!db_->GetNeedsThumbnailMigration()) { | 614 if (!db_->GetNeedsThumbnailMigration()) { |
684 // No convertion needed - use new filename right away. | 615 // No convertion needed - use new filename right away. |
685 thumbnail_name = GetFaviconsFileName(); | 616 thumbnail_name = GetFaviconsFileName(); |
686 } | 617 } |
687 if (thumbnail_db_->Init(thumbnail_name, | 618 if (thumbnail_db_->Init(thumbnail_name, |
688 history_publisher_.get(), | 619 history_publisher_.get(), |
689 db_.get()) != sql::INIT_OK) { | 620 db_.get()) != sql::INIT_OK) { |
690 // Unlike the main database, we don't error out when the database is too | 621 // 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... |
723 } | 654 } |
724 | 655 |
725 // Tell the expiration module about all the nice databases we made. This must | 656 // Tell the expiration module about all the nice databases we made. This must |
726 // happen before db_->Init() is called since the callback ForceArchiveHistory | 657 // happen before db_->Init() is called since the callback ForceArchiveHistory |
727 // may need to expire stuff. | 658 // may need to expire stuff. |
728 // | 659 // |
729 // *sigh*, this can all be cleaned up when that migration code is removed. | 660 // *sigh*, this can all be cleaned up when that migration code is removed. |
730 // The main DB initialization should intuitively be first (not that it | 661 // The main DB initialization should intuitively be first (not that it |
731 // actually matters) and the expirer should be set last. | 662 // actually matters) and the expirer should be set last. |
732 expirer_.SetDatabases(db_.get(), archived_db_.get(), | 663 expirer_.SetDatabases(db_.get(), archived_db_.get(), |
733 thumbnail_db_.get(), text_database_.get()); | 664 thumbnail_db_.get()); |
734 | 665 |
735 // Open the long-running transaction. | 666 // Open the long-running transaction. |
736 db_->BeginTransaction(); | 667 db_->BeginTransaction(); |
737 if (thumbnail_db_) | 668 if (thumbnail_db_) |
738 thumbnail_db_->BeginTransaction(); | 669 thumbnail_db_->BeginTransaction(); |
739 if (archived_db_) | 670 if (archived_db_) |
740 archived_db_->BeginTransaction(); | 671 archived_db_->BeginTransaction(); |
741 if (text_database_) | |
742 text_database_->BeginTransaction(); | |
743 | 672 |
744 // Get the first item in our database. | 673 // Get the first item in our database. |
745 db_->GetStartDate(&first_recorded_time_); | 674 db_->GetStartDate(&first_recorded_time_); |
746 | 675 |
747 // Start expiring old stuff. | 676 // Start expiring old stuff. |
748 expirer_.StartArchivingOldStuff(TimeDelta::FromDays(kArchiveDaysThreshold)); | 677 expirer_.StartArchivingOldStuff(TimeDelta::FromDays(kArchiveDaysThreshold)); |
749 | 678 |
750 #if defined(OS_ANDROID) | 679 #if defined(OS_ANDROID) |
751 if (thumbnail_db_) { | 680 if (thumbnail_db_) { |
752 android_provider_backend_.reset(new AndroidProviderBackend( | 681 android_provider_backend_.reset(new AndroidProviderBackend( |
(...skipping 13 matching lines...) Expand all Loading... |
766 db_.reset(); | 695 db_.reset(); |
767 } | 696 } |
768 if (thumbnail_db_) { | 697 if (thumbnail_db_) { |
769 thumbnail_db_->CommitTransaction(); | 698 thumbnail_db_->CommitTransaction(); |
770 thumbnail_db_.reset(); | 699 thumbnail_db_.reset(); |
771 } | 700 } |
772 if (archived_db_) { | 701 if (archived_db_) { |
773 archived_db_->CommitTransaction(); | 702 archived_db_->CommitTransaction(); |
774 archived_db_.reset(); | 703 archived_db_.reset(); |
775 } | 704 } |
776 if (text_database_) { | |
777 text_database_->CommitTransaction(); | |
778 text_database_.reset(); | |
779 } | |
780 } | 705 } |
781 | 706 |
782 std::pair<URLID, VisitID> HistoryBackend::AddPageVisit( | 707 std::pair<URLID, VisitID> HistoryBackend::AddPageVisit( |
783 const GURL& url, | 708 const GURL& url, |
784 Time time, | 709 Time time, |
785 VisitID referring_visit, | 710 VisitID referring_visit, |
786 content::PageTransition transition, | 711 content::PageTransition transition, |
787 VisitSource visit_source) { | 712 VisitSource visit_source) { |
788 // Top-level frame navigations are visible, everything else is hidden | 713 // Top-level frame navigations are visible, everything else is hidden |
789 bool new_hidden = !content::PageTransitionIsMainFrame(transition); | 714 bool new_hidden = !content::PageTransitionIsMainFrame(transition); |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
824 url_info.set_typed_count(typed_increment); | 749 url_info.set_typed_count(typed_increment); |
825 url_info.set_last_visit(time); | 750 url_info.set_last_visit(time); |
826 url_info.set_hidden(new_hidden); | 751 url_info.set_hidden(new_hidden); |
827 | 752 |
828 url_id = db_->AddURL(url_info); | 753 url_id = db_->AddURL(url_info); |
829 if (!url_id) { | 754 if (!url_id) { |
830 NOTREACHED() << "Adding URL failed."; | 755 NOTREACHED() << "Adding URL failed."; |
831 return std::make_pair(0, 0); | 756 return std::make_pair(0, 0); |
832 } | 757 } |
833 url_info.id_ = url_id; | 758 url_info.id_ = url_id; |
834 | |
835 // We don't actually add the URL to the full text index at this point. It | |
836 // might be nice to do this so that even if we get no title or body, the | |
837 // user can search for URL components and get the page. | |
838 // | |
839 // However, in most cases, we'll get at least a title and usually contents, | |
840 // and this add will be redundant, slowing everything down. As a result, | |
841 // we ignore this edge case. | |
842 } | 759 } |
843 | 760 |
844 // Add the visit with the time to the database. | 761 // Add the visit with the time to the database. |
845 VisitRow visit_info(url_id, time, referring_visit, transition, 0); | 762 VisitRow visit_info(url_id, time, referring_visit, transition, 0); |
846 VisitID visit_id = db_->AddVisit(&visit_info, visit_source); | 763 VisitID visit_id = db_->AddVisit(&visit_info, visit_source); |
847 NotifyVisitObservers(visit_info); | 764 NotifyVisitObservers(visit_info); |
848 | 765 |
849 if (visit_info.visit_time < first_recorded_time_) | 766 if (visit_info.visit_time < first_recorded_time_) |
850 first_recorded_time_ = visit_info.visit_time; | 767 first_recorded_time_ = visit_info.visit_time; |
851 | 768 |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
901 NOTREACHED() << "Could not add row to DB"; | 818 NOTREACHED() << "Could not add row to DB"; |
902 return; | 819 return; |
903 } | 820 } |
904 | 821 |
905 if (i->typed_count() > 0) { | 822 if (i->typed_count() > 0) { |
906 modified->changed_urls.push_back(*i); | 823 modified->changed_urls.push_back(*i); |
907 modified->changed_urls.back().set_id(url_id); // *i likely has |id_| 0. | 824 modified->changed_urls.back().set_id(url_id); // *i likely has |id_| 0. |
908 } | 825 } |
909 } | 826 } |
910 | 827 |
911 // Add the page to the full text index. This function is also used for | |
912 // importing. Even though we don't have page contents, we can at least | |
913 // add the title and URL to the index so they can be searched. We don't | |
914 // bother to delete any already-existing FTS entries for the URL, since | |
915 // this is normally called on import. | |
916 // | |
917 // If you ever import *after* first run (selecting import from the menu), | |
918 // then these additional entries will "shadow" the originals when querying | |
919 // for the most recent match only, and the user won't get snippets. This is | |
920 // a very minor issue, and fixing it will make import slower, so we don't | |
921 // bother. | |
922 bool has_indexed = false; | |
923 if (text_database_) { | |
924 // We do not have to make it update the visit database, below, we will | |
925 // create the visit entry with the indexed flag set. | |
926 has_indexed = text_database_->AddPageData(i->url(), url_id, 0, | |
927 i->last_visit(), | |
928 i->title(), string16()); | |
929 } | |
930 | |
931 // Sync code manages the visits itself. | 828 // Sync code manages the visits itself. |
932 if (visit_source != SOURCE_SYNCED) { | 829 if (visit_source != SOURCE_SYNCED) { |
933 // Make up a visit to correspond to the last visit to the page. | 830 // Make up a visit to correspond to the last visit to the page. |
934 VisitRow visit_info(url_id, i->last_visit(), 0, | 831 VisitRow visit_info(url_id, i->last_visit(), 0, |
935 content::PageTransitionFromInt( | 832 content::PageTransitionFromInt( |
936 content::PAGE_TRANSITION_LINK | | 833 content::PAGE_TRANSITION_LINK | |
937 content::PAGE_TRANSITION_CHAIN_START | | 834 content::PAGE_TRANSITION_CHAIN_START | |
938 content::PAGE_TRANSITION_CHAIN_END), 0); | 835 content::PAGE_TRANSITION_CHAIN_END), 0); |
939 visit_info.is_indexed = has_indexed; | |
940 if (!visit_database->AddVisit(&visit_info, visit_source)) { | 836 if (!visit_database->AddVisit(&visit_info, visit_source)) { |
941 NOTREACHED() << "Adding visit failed."; | 837 NOTREACHED() << "Adding visit failed."; |
942 return; | 838 return; |
943 } | 839 } |
944 NotifyVisitObservers(visit_info); | 840 NotifyVisitObservers(visit_info); |
945 | 841 |
946 if (visit_info.visit_time < first_recorded_time_) | 842 if (visit_info.visit_time < first_recorded_time_) |
947 first_recorded_time_ = visit_info.visit_time; | 843 first_recorded_time_ = visit_info.visit_time; |
948 } | 844 } |
949 } | 845 } |
(...skipping 14 matching lines...) Expand all Loading... |
964 | 860 |
965 bool HistoryBackend::IsExpiredVisitTime(const base::Time& time) { | 861 bool HistoryBackend::IsExpiredVisitTime(const base::Time& time) { |
966 return time < expirer_.GetCurrentArchiveTime(); | 862 return time < expirer_.GetCurrentArchiveTime(); |
967 } | 863 } |
968 | 864 |
969 void HistoryBackend::SetPageTitle(const GURL& url, | 865 void HistoryBackend::SetPageTitle(const GURL& url, |
970 const string16& title) { | 866 const string16& title) { |
971 if (!db_) | 867 if (!db_) |
972 return; | 868 return; |
973 | 869 |
974 // Update the full text index. | |
975 if (text_database_) | |
976 text_database_->AddPageTitle(url, title); | |
977 | |
978 // Search for recent redirects which should get the same title. We make a | 870 // Search for recent redirects which should get the same title. We make a |
979 // dummy list containing the exact URL visited if there are no redirects so | 871 // dummy list containing the exact URL visited if there are no redirects so |
980 // the processing below can be the same. | 872 // the processing below can be the same. |
981 history::RedirectList dummy_list; | 873 history::RedirectList dummy_list; |
982 history::RedirectList* redirects; | 874 history::RedirectList* redirects; |
983 RedirectCache::iterator iter = recent_redirects_.Get(url); | 875 RedirectCache::iterator iter = recent_redirects_.Get(url); |
984 if (iter != recent_redirects_.end()) { | 876 if (iter != recent_redirects_.end()) { |
985 redirects = &iter->second; | 877 redirects = &iter->second; |
986 | 878 |
987 // This redirect chain should have the destination URL as the last item. | 879 // This redirect chain should have the destination URL as the last item. |
(...skipping 472 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1460 for (std::vector<URLResult>::iterator it = matching_visits.begin(); | 1352 for (std::vector<URLResult>::iterator it = matching_visits.begin(); |
1461 it != matching_visits.end() && result->size() < max_results; ++it) { | 1353 it != matching_visits.end() && result->size() < max_results; ++it) { |
1462 result->AppendURLBySwapping(&(*it)); | 1354 result->AppendURLBySwapping(&(*it)); |
1463 } | 1355 } |
1464 | 1356 |
1465 if (matching_visits.size() == result->size() && | 1357 if (matching_visits.size() == result->size() && |
1466 options.begin_time <= first_recorded_time_) | 1358 options.begin_time <= first_recorded_time_) |
1467 result->set_reached_beginning(true); | 1359 result->set_reached_beginning(true); |
1468 } | 1360 } |
1469 | 1361 |
1470 void HistoryBackend::QueryHistoryFTS(const string16& text_query, | |
1471 const QueryOptions& options, | |
1472 QueryResults* result) { | |
1473 if (!text_database_) | |
1474 return; | |
1475 | |
1476 // Full text query, first get all the FTS results in the time range. | |
1477 std::vector<TextDatabase::Match> fts_matches; | |
1478 Time first_time_searched; | |
1479 text_database_->GetTextMatches(text_query, options, | |
1480 &fts_matches, &first_time_searched); | |
1481 | |
1482 URLQuerier querier(db_.get(), archived_db_.get(), true); | |
1483 | |
1484 // Now get the row and visit information for each one. | |
1485 URLResult url_result; // Declare outside loop to prevent re-construction. | |
1486 for (size_t i = 0; i < fts_matches.size(); i++) { | |
1487 if (options.max_count != 0 && | |
1488 static_cast<int>(result->size()) >= options.max_count) | |
1489 break; // Got too many items. | |
1490 | |
1491 // Get the URL, querying the main and archived databases as necessary. If | |
1492 // this is not found, the history and full text search databases are out | |
1493 // of sync and we give up with this result. | |
1494 if (!querier.GetRowForURL(fts_matches[i].url, &url_result)) | |
1495 continue; | |
1496 | |
1497 if (!url_result.url().is_valid()) | |
1498 continue; // Don't report invalid URLs in case of corruption. | |
1499 | |
1500 // Copy over the FTS stuff that the URLDatabase doesn't know about. | |
1501 // We do this with swap() to avoid copying, since we know we don't | |
1502 // need the original any more. Note that we override the title with the | |
1503 // one from FTS, since that will match the title_match_positions (the | |
1504 // FTS title and the history DB title may differ). | |
1505 url_result.set_title(fts_matches[i].title); | |
1506 url_result.title_match_positions_.swap( | |
1507 fts_matches[i].title_match_positions); | |
1508 url_result.snippet_.Swap(&fts_matches[i].snippet); | |
1509 | |
1510 // The visit time also comes from the full text search database. Since it | |
1511 // has the time, we can avoid an extra query of the visits table. | |
1512 url_result.set_visit_time(fts_matches[i].time); | |
1513 | |
1514 // Add it to the vector, this will clear our |url_row| object as a | |
1515 // result of the swap. | |
1516 result->AppendURLBySwapping(&url_result); | |
1517 } | |
1518 | |
1519 if (first_time_searched <= first_recorded_time_) | |
1520 result->set_reached_beginning(true); | |
1521 } | |
1522 | |
1523 // Frontend to GetMostRecentRedirectsFrom from the history thread. | 1362 // Frontend to GetMostRecentRedirectsFrom from the history thread. |
1524 void HistoryBackend::QueryRedirectsFrom( | 1363 void HistoryBackend::QueryRedirectsFrom( |
1525 scoped_refptr<QueryRedirectsRequest> request, | 1364 scoped_refptr<QueryRedirectsRequest> request, |
1526 const GURL& url) { | 1365 const GURL& url) { |
1527 if (request->canceled()) | 1366 if (request->canceled()) |
1528 return; | 1367 return; |
1529 bool success = GetMostRecentRedirectsFrom(url, &request->value); | 1368 bool success = GetMostRecentRedirectsFrom(url, &request->value); |
1530 request->ForwardResult(request->handle(), url, success, &request->value); | 1369 request->ForwardResult(request->handle(), url, success, &request->value); |
1531 } | 1370 } |
1532 | 1371 |
(...skipping 239 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1772 GetRedirectsToSpecificVisit(cur_visit, redirects); | 1611 GetRedirectsToSpecificVisit(cur_visit, redirects); |
1773 return true; | 1612 return true; |
1774 } | 1613 } |
1775 | 1614 |
1776 void HistoryBackend::ScheduleAutocomplete(HistoryURLProvider* provider, | 1615 void HistoryBackend::ScheduleAutocomplete(HistoryURLProvider* provider, |
1777 HistoryURLProviderParams* params) { | 1616 HistoryURLProviderParams* params) { |
1778 // ExecuteWithDB should handle the NULL database case. | 1617 // ExecuteWithDB should handle the NULL database case. |
1779 provider->ExecuteWithDB(this, db_.get(), params); | 1618 provider->ExecuteWithDB(this, db_.get(), params); |
1780 } | 1619 } |
1781 | 1620 |
1782 void HistoryBackend::SetPageContents(const GURL& url, | |
1783 const string16& contents) { | |
1784 // This is histogrammed in the text database manager. | |
1785 if (!text_database_) | |
1786 return; | |
1787 text_database_->AddPageContents(url, contents); | |
1788 } | |
1789 | |
1790 void HistoryBackend::SetPageThumbnail( | 1621 void HistoryBackend::SetPageThumbnail( |
1791 const GURL& url, | 1622 const GURL& url, |
1792 const gfx::Image* thumbnail, | 1623 const gfx::Image* thumbnail, |
1793 const ThumbnailScore& score) { | 1624 const ThumbnailScore& score) { |
1794 if (!db_ || !thumbnail_db_) | 1625 if (!db_ || !thumbnail_db_) |
1795 return; | 1626 return; |
1796 | 1627 |
1797 URLRow url_row; | 1628 URLRow url_row; |
1798 URLID url_id = db_->GetRowForURL(url, &url_row); | 1629 URLID url_id = db_->GetRowForURL(url, &url_row); |
1799 if (url_id) { | 1630 if (url_id) { |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1863 if (db_) { | 1694 if (db_) { |
1864 // If there is no thumbnail DB, we can still record a successful migration. | 1695 // If there is no thumbnail DB, we can still record a successful migration. |
1865 if (thumbnail_db_) { | 1696 if (thumbnail_db_) { |
1866 thumbnail_db_->RenameAndDropThumbnails(GetThumbnailFileName(), | 1697 thumbnail_db_->RenameAndDropThumbnails(GetThumbnailFileName(), |
1867 GetFaviconsFileName()); | 1698 GetFaviconsFileName()); |
1868 } | 1699 } |
1869 db_->ThumbnailMigrationDone(); | 1700 db_->ThumbnailMigrationDone(); |
1870 } | 1701 } |
1871 } | 1702 } |
1872 | 1703 |
| 1704 void HistoryBackend::DeleteFTSIndexDatabases() { |
| 1705 // Find files on disk matching the text databases file pattern so we can |
| 1706 // quickly test for and delete them. |
| 1707 base::FilePath::StringType filepattern = |
| 1708 FILE_PATH_LITERAL("History Index *"); |
| 1709 base::FileEnumerator enumerator( |
| 1710 history_dir_, false, base::FileEnumerator::FILES, filepattern); |
| 1711 int num_databases_deleted = 0; |
| 1712 base::FilePath current_file; |
| 1713 while (!(current_file = enumerator.Next()).empty()) { |
| 1714 if (sql::Connection::Delete(current_file)) |
| 1715 num_databases_deleted++; |
| 1716 } |
| 1717 UMA_HISTOGRAM_COUNTS("History.DeleteFTSIndexDatabases", |
| 1718 num_databases_deleted); |
| 1719 } |
| 1720 |
1873 bool HistoryBackend::GetThumbnailFromOlderRedirect( | 1721 bool HistoryBackend::GetThumbnailFromOlderRedirect( |
1874 const GURL& page_url, | 1722 const GURL& page_url, |
1875 std::vector<unsigned char>* data) { | 1723 std::vector<unsigned char>* data) { |
1876 // Look at a few previous visit sessions. | 1724 // Look at a few previous visit sessions. |
1877 VisitVector older_sessions; | 1725 VisitVector older_sessions; |
1878 URLID page_url_id = db_->GetRowForURL(page_url, NULL); | 1726 URLID page_url_id = db_->GetRowForURL(page_url, NULL); |
1879 static const int kVisitsToSearchForThumbnail = 4; | 1727 static const int kVisitsToSearchForThumbnail = 4; |
1880 db_->GetMostRecentVisitsForURL( | 1728 db_->GetMostRecentVisitsForURL( |
1881 page_url_id, kVisitsToSearchForThumbnail, &older_sessions); | 1729 page_url_id, kVisitsToSearchForThumbnail, &older_sessions); |
1882 | 1730 |
(...skipping 749 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2632 thumbnail_db_->CommitTransaction(); | 2480 thumbnail_db_->CommitTransaction(); |
2633 DCHECK(thumbnail_db_->transaction_nesting() == 0) << | 2481 DCHECK(thumbnail_db_->transaction_nesting() == 0) << |
2634 "Somebody left a transaction open"; | 2482 "Somebody left a transaction open"; |
2635 thumbnail_db_->BeginTransaction(); | 2483 thumbnail_db_->BeginTransaction(); |
2636 } | 2484 } |
2637 | 2485 |
2638 if (archived_db_) { | 2486 if (archived_db_) { |
2639 archived_db_->CommitTransaction(); | 2487 archived_db_->CommitTransaction(); |
2640 archived_db_->BeginTransaction(); | 2488 archived_db_->BeginTransaction(); |
2641 } | 2489 } |
2642 | |
2643 if (text_database_) { | |
2644 text_database_->CommitTransaction(); | |
2645 text_database_->BeginTransaction(); | |
2646 } | |
2647 } | 2490 } |
2648 | 2491 |
2649 void HistoryBackend::ScheduleCommit() { | 2492 void HistoryBackend::ScheduleCommit() { |
2650 if (scheduled_commit_.get()) | 2493 if (scheduled_commit_.get()) |
2651 return; | 2494 return; |
2652 scheduled_commit_ = new CommitLaterTask(this); | 2495 scheduled_commit_ = new CommitLaterTask(this); |
2653 base::MessageLoop::current()->PostDelayedTask( | 2496 base::MessageLoop::current()->PostDelayedTask( |
2654 FROM_HERE, | 2497 FROM_HERE, |
2655 base::Bind(&CommitLaterTask::RunCommit, scheduled_commit_.get()), | 2498 base::Bind(&CommitLaterTask::RunCommit, scheduled_commit_.get()), |
2656 base::TimeDelta::FromSeconds(kCommitIntervalSeconds)); | 2499 base::TimeDelta::FromSeconds(kCommitIntervalSeconds)); |
(...skipping 210 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2867 bool success = db_->Raze(); | 2710 bool success = db_->Raze(); |
2868 UMA_HISTOGRAM_BOOLEAN("History.KillHistoryDatabaseResult", success); | 2711 UMA_HISTOGRAM_BOOLEAN("History.KillHistoryDatabaseResult", success); |
2869 | 2712 |
2870 #if defined(OS_ANDROID) | 2713 #if defined(OS_ANDROID) |
2871 // Release AndroidProviderBackend before other objects. | 2714 // Release AndroidProviderBackend before other objects. |
2872 android_provider_backend_.reset(); | 2715 android_provider_backend_.reset(); |
2873 #endif | 2716 #endif |
2874 | 2717 |
2875 // The expirer keeps tabs on the active databases. Tell it about the | 2718 // The expirer keeps tabs on the active databases. Tell it about the |
2876 // databases which will be closed. | 2719 // databases which will be closed. |
2877 expirer_.SetDatabases(NULL, NULL, NULL, NULL); | 2720 expirer_.SetDatabases(NULL, NULL, NULL); |
2878 | 2721 |
2879 // Reopen a new transaction for |db_| for the sake of CloseAllDatabases(). | 2722 // Reopen a new transaction for |db_| for the sake of CloseAllDatabases(). |
2880 db_->BeginTransaction(); | 2723 db_->BeginTransaction(); |
2881 CloseAllDatabases(); | 2724 CloseAllDatabases(); |
2882 } | 2725 } |
2883 | 2726 |
2884 void HistoryBackend::ProcessDBTask( | 2727 void HistoryBackend::ProcessDBTask( |
2885 scoped_refptr<HistoryDBTaskRequest> request) { | 2728 scoped_refptr<HistoryDBTaskRequest> request) { |
2886 DCHECK(request.get()); | 2729 DCHECK(request.get()); |
2887 if (request->canceled()) | 2730 if (request->canceled()) |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2957 // We continue in this error case. If the user wants to delete their | 2800 // We continue in this error case. If the user wants to delete their |
2958 // history, we should delete as much as we can. | 2801 // history, we should delete as much as we can. |
2959 } | 2802 } |
2960 | 2803 |
2961 // ClearAllMainHistory will change the IDs of the URLs in kept_urls. Therfore, | 2804 // ClearAllMainHistory will change the IDs of the URLs in kept_urls. Therfore, |
2962 // we clear the list afterwards to make sure nobody uses this invalid data. | 2805 // we clear the list afterwards to make sure nobody uses this invalid data. |
2963 if (!ClearAllMainHistory(kept_urls)) | 2806 if (!ClearAllMainHistory(kept_urls)) |
2964 LOG(ERROR) << "Main history could not be cleared"; | 2807 LOG(ERROR) << "Main history could not be cleared"; |
2965 kept_urls.clear(); | 2808 kept_urls.clear(); |
2966 | 2809 |
2967 // Delete FTS files & archived history. | 2810 // Delete archived history. |
2968 if (text_database_) { | |
2969 // We assume that the text database has one transaction on them that we need | |
2970 // to close & restart (the long-running history transaction). | |
2971 text_database_->CommitTransaction(); | |
2972 text_database_->DeleteAll(); | |
2973 text_database_->BeginTransaction(); | |
2974 } | |
2975 | |
2976 if (archived_db_) { | 2811 if (archived_db_) { |
2977 // Close the database and delete the file. | 2812 // Close the database and delete the file. |
2978 archived_db_.reset(); | 2813 archived_db_.reset(); |
2979 base::FilePath archived_file_name = GetArchivedFileName(); | 2814 base::FilePath archived_file_name = GetArchivedFileName(); |
2980 file_util::Delete(archived_file_name, false); | 2815 file_util::Delete(archived_file_name, false); |
2981 | 2816 |
2982 // Now re-initialize the database (which may fail). | 2817 // Now re-initialize the database (which may fail). |
2983 archived_db_.reset(new ArchivedDatabase()); | 2818 archived_db_.reset(new ArchivedDatabase()); |
2984 if (!archived_db_->Init(archived_file_name)) { | 2819 if (!archived_db_->Init(archived_file_name)) { |
2985 LOG(WARNING) << "Could not initialize the archived database."; | 2820 LOG(WARNING) << "Could not initialize the archived database."; |
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3111 info.url_id = visit.url_id; | 2946 info.url_id = visit.url_id; |
3112 info.time = visit.visit_time; | 2947 info.time = visit.visit_time; |
3113 info.transition = visit.transition; | 2948 info.transition = visit.transition; |
3114 // If we don't have a delegate yet during setup or shutdown, we will drop | 2949 // If we don't have a delegate yet during setup or shutdown, we will drop |
3115 // these notifications. | 2950 // these notifications. |
3116 if (delegate_) | 2951 if (delegate_) |
3117 delegate_->NotifyVisitDBObserversOnAddVisit(info); | 2952 delegate_->NotifyVisitDBObserversOnAddVisit(info); |
3118 } | 2953 } |
3119 | 2954 |
3120 } // namespace history | 2955 } // namespace history |
OLD | NEW |