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" | |
18 #include "base/memory/scoped_ptr.h" | 17 #include "base/memory/scoped_ptr.h" |
19 #include "base/memory/scoped_vector.h" | 18 #include "base/memory/scoped_vector.h" |
20 #include "base/message_loop/message_loop.h" | 19 #include "base/message_loop/message_loop.h" |
21 #include "base/metrics/histogram.h" | 20 #include "base/metrics/histogram.h" |
22 #include "base/rand_util.h" | 21 #include "base/rand_util.h" |
23 #include "base/strings/string_util.h" | 22 #include "base/strings/string_util.h" |
24 #include "base/strings/utf_string_conversions.h" | 23 #include "base/strings/utf_string_conversions.h" |
25 #include "base/time/time.h" | 24 #include "base/time/time.h" |
26 #include "chrome/browser/autocomplete/history_url_provider.h" | 25 #include "chrome/browser/autocomplete/history_url_provider.h" |
27 #include "chrome/browser/bookmarks/bookmark_service.h" | 26 #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) | 61 VisitDatabase (stores a list of visits for the URLs) |
63 VisitSegmentDatabase (stores groups of URLs for the most visited view). | 62 VisitSegmentDatabase (stores groups of URLs for the most visited view). |
64 | 63 |
65 ArchivedDatabase (stores history older than 3 months) | 64 ArchivedDatabase (stores history older than 3 months) |
66 URLDatabase (stores a list of URLs) | 65 URLDatabase (stores a list of URLs) |
67 DownloadDatabase (stores a list of downloads) | 66 DownloadDatabase (stores a list of downloads) |
68 VisitDatabase (stores a list of visits for the URLs) | 67 VisitDatabase (stores a list of visits for the URLs) |
69 | 68 |
70 (this does not store visit segments as they expire after 3 mos.) | 69 (this does not store visit segments as they expire after 3 mos.) |
71 | 70 |
| 71 TextDatabaseManager (manages multiple text database for different times) |
| 72 TextDatabase (represents a single month of full-text index). |
| 73 ...more TextDatabase objects... |
| 74 |
72 ExpireHistoryBackend (manages moving things from HistoryDatabase to | 75 ExpireHistoryBackend (manages moving things from HistoryDatabase to |
73 the ArchivedDatabase and deleting) | 76 the ArchivedDatabase and deleting) |
74 */ | 77 */ |
75 | 78 |
76 namespace history { | 79 namespace history { |
77 | 80 |
78 // How long we keep segment data for in days. Currently 3 months. | 81 // How long we keep segment data for in days. Currently 3 months. |
79 // This value needs to be greater or equal to | 82 // This value needs to be greater or equal to |
80 // MostVisitedModel::kMostVisitedScope but we don't want to introduce a direct | 83 // MostVisitedModel::kMostVisitedScope but we don't want to introduce a direct |
81 // dependency between MostVisitedModel and the history backend. | 84 // dependency between MostVisitedModel and the history backend. |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
158 } | 161 } |
159 | 162 |
160 private: | 163 private: |
161 friend class base::RefCounted<CommitLaterTask>; | 164 friend class base::RefCounted<CommitLaterTask>; |
162 | 165 |
163 ~CommitLaterTask() {} | 166 ~CommitLaterTask() {} |
164 | 167 |
165 scoped_refptr<HistoryBackend> history_backend_; | 168 scoped_refptr<HistoryBackend> history_backend_; |
166 }; | 169 }; |
167 | 170 |
| 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 |
168 // HistoryBackend -------------------------------------------------------------- | 218 // HistoryBackend -------------------------------------------------------------- |
169 | 219 |
170 HistoryBackend::HistoryBackend(const base::FilePath& history_dir, | 220 HistoryBackend::HistoryBackend(const base::FilePath& history_dir, |
171 int id, | 221 int id, |
172 Delegate* delegate, | 222 Delegate* delegate, |
173 BookmarkService* bookmark_service) | 223 BookmarkService* bookmark_service) |
174 : delegate_(delegate), | 224 : delegate_(delegate), |
175 id_(id), | 225 id_(id), |
176 history_dir_(history_dir), | 226 history_dir_(history_dir), |
177 scheduled_kill_db_(false), | 227 scheduled_kill_db_(false), |
(...skipping 347 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
525 // Update the visit_details for this visit. | 575 // Update the visit_details for this visit. |
526 UpdateVisitDuration(from_visit_id, request.time); | 576 UpdateVisitDuration(from_visit_id, request.time); |
527 } | 577 } |
528 | 578 |
529 // Subsequent transitions in the redirect list must all be server | 579 // Subsequent transitions in the redirect list must all be server |
530 // redirects. | 580 // redirects. |
531 redirect_info = content::PAGE_TRANSITION_SERVER_REDIRECT; | 581 redirect_info = content::PAGE_TRANSITION_SERVER_REDIRECT; |
532 } | 582 } |
533 | 583 |
534 // Last, save this redirect chain for later so we can set titles & favicons | 584 // Last, save this redirect chain for later so we can set titles & favicons |
535 // on the redirected pages properly. | 585 // on the redirected pages properly. It is indexed by the destination page. |
536 recent_redirects_.Put(request.url, redirects); | 586 recent_redirects_.Put(request.url, redirects); |
537 } | 587 } |
538 | 588 |
539 // TODO(brettw) bug 1140015: Add an "add page" notification so the history | 589 // TODO(brettw) bug 1140015: Add an "add page" notification so the history |
540 // views can keep in sync. | 590 // views can keep in sync. |
541 | 591 |
542 // Add the last visit to the tracker so we can get outgoing transitions. | 592 // Add the last visit to the tracker so we can get outgoing transitions. |
543 // TODO(evanm): Due to http://b/1194536 we lose the referrers of a subframe | 593 // TODO(evanm): Due to http://b/1194536 we lose the referrers of a subframe |
544 // navigation anyway, so last_visit_id is always zero for them. But adding | 594 // navigation anyway, so last_visit_id is always zero for them. But adding |
545 // them here confuses main frame history, so we skip them for now. | 595 // them here confuses main frame history, so we skip them for now. |
546 if (stripped_transition != content::PAGE_TRANSITION_AUTO_SUBFRAME && | 596 if (stripped_transition != content::PAGE_TRANSITION_AUTO_SUBFRAME && |
547 stripped_transition != content::PAGE_TRANSITION_MANUAL_SUBFRAME && | 597 stripped_transition != content::PAGE_TRANSITION_MANUAL_SUBFRAME && |
548 !is_keyword_generated) { | 598 !is_keyword_generated) { |
549 tracker_.AddVisit(request.id_scope, request.page_id, request.url, | 599 tracker_.AddVisit(request.id_scope, request.page_id, request.url, |
550 last_ids.second); | 600 last_ids.second); |
551 } | 601 } |
552 | 602 |
| 603 if (text_database_) { |
| 604 text_database_->AddPageURL(request.url, last_ids.first, last_ids.second, |
| 605 request.time); |
| 606 } |
| 607 |
553 ScheduleCommit(); | 608 ScheduleCommit(); |
554 } | 609 } |
555 | 610 |
556 void HistoryBackend::InitImpl(const std::string& languages) { | 611 void HistoryBackend::InitImpl(const std::string& languages) { |
557 DCHECK(!db_) << "Initializing HistoryBackend twice"; | 612 DCHECK(!db_) << "Initializing HistoryBackend twice"; |
558 // In the rare case where the db fails to initialize a dialog may get shown | 613 // In the rare case where the db fails to initialize a dialog may get shown |
559 // the blocks the caller, yet allows other messages through. For this reason | 614 // the blocks the caller, yet allows other messages through. For this reason |
560 // we only set db_ to the created database if creation is successful. That | 615 // we only set db_ to the created database if creation is successful. That |
561 // way other methods won't do anything as db_ is still NULL. | 616 // way other methods won't do anything as db_ is still NULL. |
562 | 617 |
563 TimeTicks beginning_time = TimeTicks::Now(); | 618 TimeTicks beginning_time = TimeTicks::Now(); |
564 | 619 |
565 // Compute the file names. | 620 // Compute the file names. Note that the index file can be removed when the |
| 621 // text db manager is finished being hooked up. |
566 base::FilePath history_name = history_dir_.Append(chrome::kHistoryFilename); | 622 base::FilePath history_name = history_dir_.Append(chrome::kHistoryFilename); |
567 base::FilePath thumbnail_name = GetThumbnailFileName(); | 623 base::FilePath thumbnail_name = GetThumbnailFileName(); |
568 base::FilePath archived_name = GetArchivedFileName(); | 624 base::FilePath archived_name = GetArchivedFileName(); |
569 | 625 |
570 // Delete the old index database files which are no longer used. | |
571 DeleteFTSIndexDatabases(); | |
572 | |
573 // History database. | 626 // History database. |
574 db_.reset(new HistoryDatabase()); | 627 db_.reset(new HistoryDatabase()); |
575 | 628 |
576 // Unretained to avoid a ref loop with db_. | 629 // Unretained to avoid a ref loop with db_. |
577 db_->set_error_callback( | 630 db_->set_error_callback( |
578 base::Bind(&HistoryBackend::DatabaseErrorCallback, | 631 base::Bind(&HistoryBackend::DatabaseErrorCallback, |
579 base::Unretained(this))); | 632 base::Unretained(this))); |
580 | 633 |
581 sql::InitStatus status = db_->Init(history_name); | 634 sql::InitStatus status = db_->Init(history_name); |
582 switch (status) { | 635 switch (status) { |
(...skipping 19 matching lines...) Expand all Loading... |
602 // Fill the in-memory database and send it back to the history service on the | 655 // Fill the in-memory database and send it back to the history service on the |
603 // main thread. | 656 // main thread. |
604 InMemoryHistoryBackend* mem_backend = new InMemoryHistoryBackend; | 657 InMemoryHistoryBackend* mem_backend = new InMemoryHistoryBackend; |
605 if (mem_backend->Init(history_name, db_.get())) | 658 if (mem_backend->Init(history_name, db_.get())) |
606 delegate_->SetInMemoryBackend(id_, mem_backend); // Takes ownership of | 659 delegate_->SetInMemoryBackend(id_, mem_backend); // Takes ownership of |
607 // pointer. | 660 // pointer. |
608 else | 661 else |
609 delete mem_backend; // Error case, run without the in-memory DB. | 662 delete mem_backend; // Error case, run without the in-memory DB. |
610 db_->BeginExclusiveMode(); // Must be after the mem backend read the data. | 663 db_->BeginExclusiveMode(); // Must be after the mem backend read the data. |
611 | 664 |
612 // Create the history publisher which needs to be passed on to the thumbnail | 665 // Create the history publisher which needs to be passed on to the text and |
613 // database for publishing history. | 666 // thumbnail databases for publishing history. |
614 history_publisher_.reset(new HistoryPublisher()); | 667 history_publisher_.reset(new HistoryPublisher()); |
615 if (!history_publisher_->Init()) { | 668 if (!history_publisher_->Init()) { |
616 // The init may fail when there are no indexers wanting our history. | 669 // The init may fail when there are no indexers wanting our history. |
617 // Hence no need to log the failure. | 670 // Hence no need to log the failure. |
618 history_publisher_.reset(); | 671 history_publisher_.reset(); |
619 } | 672 } |
620 | 673 |
| 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 |
621 // Thumbnail database. | 690 // Thumbnail database. |
622 thumbnail_db_.reset(new ThumbnailDatabase()); | 691 thumbnail_db_.reset(new ThumbnailDatabase()); |
623 if (!db_->GetNeedsThumbnailMigration()) { | 692 if (!db_->GetNeedsThumbnailMigration()) { |
624 // No convertion needed - use new filename right away. | 693 // No convertion needed - use new filename right away. |
625 thumbnail_name = GetFaviconsFileName(); | 694 thumbnail_name = GetFaviconsFileName(); |
626 } | 695 } |
627 if (thumbnail_db_->Init(thumbnail_name, | 696 if (thumbnail_db_->Init(thumbnail_name, |
628 history_publisher_.get(), | 697 history_publisher_.get(), |
629 db_.get()) != sql::INIT_OK) { | 698 db_.get()) != sql::INIT_OK) { |
630 // Unlike the main database, we don't error out when the database is too | 699 // 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... |
663 } | 732 } |
664 | 733 |
665 // Tell the expiration module about all the nice databases we made. This must | 734 // Tell the expiration module about all the nice databases we made. This must |
666 // happen before db_->Init() is called since the callback ForceArchiveHistory | 735 // happen before db_->Init() is called since the callback ForceArchiveHistory |
667 // may need to expire stuff. | 736 // may need to expire stuff. |
668 // | 737 // |
669 // *sigh*, this can all be cleaned up when that migration code is removed. | 738 // *sigh*, this can all be cleaned up when that migration code is removed. |
670 // The main DB initialization should intuitively be first (not that it | 739 // The main DB initialization should intuitively be first (not that it |
671 // actually matters) and the expirer should be set last. | 740 // actually matters) and the expirer should be set last. |
672 expirer_.SetDatabases(db_.get(), archived_db_.get(), | 741 expirer_.SetDatabases(db_.get(), archived_db_.get(), |
673 thumbnail_db_.get()); | 742 thumbnail_db_.get(), text_database_.get()); |
674 | 743 |
675 // Open the long-running transaction. | 744 // Open the long-running transaction. |
676 db_->BeginTransaction(); | 745 db_->BeginTransaction(); |
677 if (thumbnail_db_) | 746 if (thumbnail_db_) |
678 thumbnail_db_->BeginTransaction(); | 747 thumbnail_db_->BeginTransaction(); |
679 if (archived_db_) | 748 if (archived_db_) |
680 archived_db_->BeginTransaction(); | 749 archived_db_->BeginTransaction(); |
| 750 if (text_database_) |
| 751 text_database_->BeginTransaction(); |
681 | 752 |
682 // Get the first item in our database. | 753 // Get the first item in our database. |
683 db_->GetStartDate(&first_recorded_time_); | 754 db_->GetStartDate(&first_recorded_time_); |
684 | 755 |
685 // Start expiring old stuff. | 756 // Start expiring old stuff. |
686 expirer_.StartArchivingOldStuff(TimeDelta::FromDays(kArchiveDaysThreshold)); | 757 expirer_.StartArchivingOldStuff(TimeDelta::FromDays(kArchiveDaysThreshold)); |
687 | 758 |
688 #if defined(OS_ANDROID) | 759 #if defined(OS_ANDROID) |
689 if (thumbnail_db_) { | 760 if (thumbnail_db_) { |
690 android_provider_backend_.reset(new AndroidProviderBackend( | 761 android_provider_backend_.reset(new AndroidProviderBackend( |
(...skipping 25 matching lines...) Expand all Loading... |
716 db_.reset(); | 787 db_.reset(); |
717 } | 788 } |
718 if (thumbnail_db_) { | 789 if (thumbnail_db_) { |
719 thumbnail_db_->CommitTransaction(); | 790 thumbnail_db_->CommitTransaction(); |
720 thumbnail_db_.reset(); | 791 thumbnail_db_.reset(); |
721 } | 792 } |
722 if (archived_db_) { | 793 if (archived_db_) { |
723 archived_db_->CommitTransaction(); | 794 archived_db_->CommitTransaction(); |
724 archived_db_.reset(); | 795 archived_db_.reset(); |
725 } | 796 } |
| 797 if (text_database_) { |
| 798 text_database_->CommitTransaction(); |
| 799 text_database_.reset(); |
| 800 } |
726 } | 801 } |
727 | 802 |
728 std::pair<URLID, VisitID> HistoryBackend::AddPageVisit( | 803 std::pair<URLID, VisitID> HistoryBackend::AddPageVisit( |
729 const GURL& url, | 804 const GURL& url, |
730 Time time, | 805 Time time, |
731 VisitID referring_visit, | 806 VisitID referring_visit, |
732 content::PageTransition transition, | 807 content::PageTransition transition, |
733 VisitSource visit_source) { | 808 VisitSource visit_source) { |
734 // Top-level frame navigations are visible, everything else is hidden | 809 // Top-level frame navigations are visible, everything else is hidden |
735 bool new_hidden = !content::PageTransitionIsMainFrame(transition); | 810 bool new_hidden = !content::PageTransitionIsMainFrame(transition); |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
779 url_info.set_typed_count(typed_increment); | 854 url_info.set_typed_count(typed_increment); |
780 url_info.set_last_visit(time); | 855 url_info.set_last_visit(time); |
781 url_info.set_hidden(new_hidden); | 856 url_info.set_hidden(new_hidden); |
782 | 857 |
783 url_id = db_->AddURL(url_info); | 858 url_id = db_->AddURL(url_info); |
784 if (!url_id) { | 859 if (!url_id) { |
785 NOTREACHED() << "Adding URL failed."; | 860 NOTREACHED() << "Adding URL failed."; |
786 return std::make_pair(0, 0); | 861 return std::make_pair(0, 0); |
787 } | 862 } |
788 url_info.id_ = url_id; | 863 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. |
789 } | 872 } |
790 | 873 |
791 // Add the visit with the time to the database. | 874 // Add the visit with the time to the database. |
792 VisitRow visit_info(url_id, time, referring_visit, transition, 0); | 875 VisitRow visit_info(url_id, time, referring_visit, transition, 0); |
793 VisitID visit_id = db_->AddVisit(&visit_info, visit_source); | 876 VisitID visit_id = db_->AddVisit(&visit_info, visit_source); |
794 NotifyVisitObservers(visit_info); | 877 NotifyVisitObservers(visit_info); |
795 | 878 |
796 if (visit_info.visit_time < first_recorded_time_) | 879 if (visit_info.visit_time < first_recorded_time_) |
797 first_recorded_time_ = visit_info.visit_time; | 880 first_recorded_time_ = visit_info.visit_time; |
798 | 881 |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
848 NOTREACHED() << "Could not add row to DB"; | 931 NOTREACHED() << "Could not add row to DB"; |
849 return; | 932 return; |
850 } | 933 } |
851 | 934 |
852 if (i->typed_count() > 0) { | 935 if (i->typed_count() > 0) { |
853 modified->changed_urls.push_back(*i); | 936 modified->changed_urls.push_back(*i); |
854 modified->changed_urls.back().set_id(url_id); // *i likely has |id_| 0. | 937 modified->changed_urls.back().set_id(url_id); // *i likely has |id_| 0. |
855 } | 938 } |
856 } | 939 } |
857 | 940 |
| 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 |
858 // Sync code manages the visits itself. | 961 // Sync code manages the visits itself. |
859 if (visit_source != SOURCE_SYNCED) { | 962 if (visit_source != SOURCE_SYNCED) { |
860 // Make up a visit to correspond to the last visit to the page. | 963 // Make up a visit to correspond to the last visit to the page. |
861 VisitRow visit_info(url_id, i->last_visit(), 0, | 964 VisitRow visit_info(url_id, i->last_visit(), 0, |
862 content::PageTransitionFromInt( | 965 content::PageTransitionFromInt( |
863 content::PAGE_TRANSITION_LINK | | 966 content::PAGE_TRANSITION_LINK | |
864 content::PAGE_TRANSITION_CHAIN_START | | 967 content::PAGE_TRANSITION_CHAIN_START | |
865 content::PAGE_TRANSITION_CHAIN_END), 0); | 968 content::PAGE_TRANSITION_CHAIN_END), 0); |
| 969 visit_info.is_indexed = has_indexed; |
866 if (!visit_database->AddVisit(&visit_info, visit_source)) { | 970 if (!visit_database->AddVisit(&visit_info, visit_source)) { |
867 NOTREACHED() << "Adding visit failed."; | 971 NOTREACHED() << "Adding visit failed."; |
868 return; | 972 return; |
869 } | 973 } |
870 NotifyVisitObservers(visit_info); | 974 NotifyVisitObservers(visit_info); |
871 | 975 |
872 if (visit_info.visit_time < first_recorded_time_) | 976 if (visit_info.visit_time < first_recorded_time_) |
873 first_recorded_time_ = visit_info.visit_time; | 977 first_recorded_time_ = visit_info.visit_time; |
874 } | 978 } |
875 } | 979 } |
(...skipping 14 matching lines...) Expand all Loading... |
890 | 994 |
891 bool HistoryBackend::IsExpiredVisitTime(const base::Time& time) { | 995 bool HistoryBackend::IsExpiredVisitTime(const base::Time& time) { |
892 return time < expirer_.GetCurrentArchiveTime(); | 996 return time < expirer_.GetCurrentArchiveTime(); |
893 } | 997 } |
894 | 998 |
895 void HistoryBackend::SetPageTitle(const GURL& url, | 999 void HistoryBackend::SetPageTitle(const GURL& url, |
896 const string16& title) { | 1000 const string16& title) { |
897 if (!db_) | 1001 if (!db_) |
898 return; | 1002 return; |
899 | 1003 |
| 1004 // Update the full text index. |
| 1005 if (text_database_) |
| 1006 text_database_->AddPageTitle(url, title); |
| 1007 |
900 // Search for recent redirects which should get the same title. We make a | 1008 // Search for recent redirects which should get the same title. We make a |
901 // dummy list containing the exact URL visited if there are no redirects so | 1009 // dummy list containing the exact URL visited if there are no redirects so |
902 // the processing below can be the same. | 1010 // the processing below can be the same. |
903 history::RedirectList dummy_list; | 1011 history::RedirectList dummy_list; |
904 history::RedirectList* redirects; | 1012 history::RedirectList* redirects; |
905 RedirectCache::iterator iter = recent_redirects_.Get(url); | 1013 RedirectCache::iterator iter = recent_redirects_.Get(url); |
906 if (iter != recent_redirects_.end()) { | 1014 if (iter != recent_redirects_.end()) { |
907 redirects = &iter->second; | 1015 redirects = &iter->second; |
908 | 1016 |
909 // This redirect chain should have the destination URL as the last item. | 1017 // 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... |
1384 for (std::vector<URLResult>::iterator it = matching_visits.begin(); | 1492 for (std::vector<URLResult>::iterator it = matching_visits.begin(); |
1385 it != matching_visits.end() && result->size() < max_results; ++it) { | 1493 it != matching_visits.end() && result->size() < max_results; ++it) { |
1386 result->AppendURLBySwapping(&(*it)); | 1494 result->AppendURLBySwapping(&(*it)); |
1387 } | 1495 } |
1388 | 1496 |
1389 if (matching_visits.size() == result->size() && | 1497 if (matching_visits.size() == result->size() && |
1390 options.begin_time <= first_recorded_time_) | 1498 options.begin_time <= first_recorded_time_) |
1391 result->set_reached_beginning(true); | 1499 result->set_reached_beginning(true); |
1392 } | 1500 } |
1393 | 1501 |
| 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 |
1394 // Frontend to GetMostRecentRedirectsFrom from the history thread. | 1555 // Frontend to GetMostRecentRedirectsFrom from the history thread. |
1395 void HistoryBackend::QueryRedirectsFrom( | 1556 void HistoryBackend::QueryRedirectsFrom( |
1396 scoped_refptr<QueryRedirectsRequest> request, | 1557 scoped_refptr<QueryRedirectsRequest> request, |
1397 const GURL& url) { | 1558 const GURL& url) { |
1398 if (request->canceled()) | 1559 if (request->canceled()) |
1399 return; | 1560 return; |
1400 bool success = GetMostRecentRedirectsFrom(url, &request->value); | 1561 bool success = GetMostRecentRedirectsFrom(url, &request->value); |
1401 request->ForwardResult(request->handle(), url, success, &request->value); | 1562 request->ForwardResult(request->handle(), url, success, &request->value); |
1402 } | 1563 } |
1403 | 1564 |
(...skipping 239 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1643 GetRedirectsToSpecificVisit(cur_visit, redirects); | 1804 GetRedirectsToSpecificVisit(cur_visit, redirects); |
1644 return true; | 1805 return true; |
1645 } | 1806 } |
1646 | 1807 |
1647 void HistoryBackend::ScheduleAutocomplete(HistoryURLProvider* provider, | 1808 void HistoryBackend::ScheduleAutocomplete(HistoryURLProvider* provider, |
1648 HistoryURLProviderParams* params) { | 1809 HistoryURLProviderParams* params) { |
1649 // ExecuteWithDB should handle the NULL database case. | 1810 // ExecuteWithDB should handle the NULL database case. |
1650 provider->ExecuteWithDB(this, db_.get(), params); | 1811 provider->ExecuteWithDB(this, db_.get(), params); |
1651 } | 1812 } |
1652 | 1813 |
| 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 |
1653 void HistoryBackend::SetPageThumbnail( | 1822 void HistoryBackend::SetPageThumbnail( |
1654 const GURL& url, | 1823 const GURL& url, |
1655 const gfx::Image* thumbnail, | 1824 const gfx::Image* thumbnail, |
1656 const ThumbnailScore& score) { | 1825 const ThumbnailScore& score) { |
1657 if (!db_ || !thumbnail_db_) | 1826 if (!db_ || !thumbnail_db_) |
1658 return; | 1827 return; |
1659 | 1828 |
1660 URLRow url_row; | 1829 URLRow url_row; |
1661 URLID url_id = db_->GetRowForURL(url, &url_row); | 1830 URLID url_id = db_->GetRowForURL(url, &url_row); |
1662 if (url_id) { | 1831 if (url_id) { |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1726 if (db_) { | 1895 if (db_) { |
1727 // If there is no thumbnail DB, we can still record a successful migration. | 1896 // If there is no thumbnail DB, we can still record a successful migration. |
1728 if (thumbnail_db_) { | 1897 if (thumbnail_db_) { |
1729 thumbnail_db_->RenameAndDropThumbnails(GetThumbnailFileName(), | 1898 thumbnail_db_->RenameAndDropThumbnails(GetThumbnailFileName(), |
1730 GetFaviconsFileName()); | 1899 GetFaviconsFileName()); |
1731 } | 1900 } |
1732 db_->ThumbnailMigrationDone(); | 1901 db_->ThumbnailMigrationDone(); |
1733 } | 1902 } |
1734 } | 1903 } |
1735 | 1904 |
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 | |
1753 bool HistoryBackend::GetThumbnailFromOlderRedirect( | 1905 bool HistoryBackend::GetThumbnailFromOlderRedirect( |
1754 const GURL& page_url, | 1906 const GURL& page_url, |
1755 std::vector<unsigned char>* data) { | 1907 std::vector<unsigned char>* data) { |
1756 // Look at a few previous visit sessions. | 1908 // Look at a few previous visit sessions. |
1757 VisitVector older_sessions; | 1909 VisitVector older_sessions; |
1758 URLID page_url_id = db_->GetRowForURL(page_url, NULL); | 1910 URLID page_url_id = db_->GetRowForURL(page_url, NULL); |
1759 static const int kVisitsToSearchForThumbnail = 4; | 1911 static const int kVisitsToSearchForThumbnail = 4; |
1760 db_->GetMostRecentVisitsForURL( | 1912 db_->GetMostRecentVisitsForURL( |
1761 page_url_id, kVisitsToSearchForThumbnail, &older_sessions); | 1913 page_url_id, kVisitsToSearchForThumbnail, &older_sessions); |
1762 | 1914 |
(...skipping 746 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2509 thumbnail_db_->CommitTransaction(); | 2661 thumbnail_db_->CommitTransaction(); |
2510 DCHECK(thumbnail_db_->transaction_nesting() == 0) << | 2662 DCHECK(thumbnail_db_->transaction_nesting() == 0) << |
2511 "Somebody left a transaction open"; | 2663 "Somebody left a transaction open"; |
2512 thumbnail_db_->BeginTransaction(); | 2664 thumbnail_db_->BeginTransaction(); |
2513 } | 2665 } |
2514 | 2666 |
2515 if (archived_db_) { | 2667 if (archived_db_) { |
2516 archived_db_->CommitTransaction(); | 2668 archived_db_->CommitTransaction(); |
2517 archived_db_->BeginTransaction(); | 2669 archived_db_->BeginTransaction(); |
2518 } | 2670 } |
| 2671 |
| 2672 if (text_database_) { |
| 2673 text_database_->CommitTransaction(); |
| 2674 text_database_->BeginTransaction(); |
| 2675 } |
2519 } | 2676 } |
2520 | 2677 |
2521 void HistoryBackend::ScheduleCommit() { | 2678 void HistoryBackend::ScheduleCommit() { |
2522 if (scheduled_commit_.get()) | 2679 if (scheduled_commit_.get()) |
2523 return; | 2680 return; |
2524 scheduled_commit_ = new CommitLaterTask(this); | 2681 scheduled_commit_ = new CommitLaterTask(this); |
2525 base::MessageLoop::current()->PostDelayedTask( | 2682 base::MessageLoop::current()->PostDelayedTask( |
2526 FROM_HERE, | 2683 FROM_HERE, |
2527 base::Bind(&CommitLaterTask::RunCommit, scheduled_commit_.get()), | 2684 base::Bind(&CommitLaterTask::RunCommit, scheduled_commit_.get()), |
2528 base::TimeDelta::FromSeconds(kCommitIntervalSeconds)); | 2685 base::TimeDelta::FromSeconds(kCommitIntervalSeconds)); |
(...skipping 210 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2739 bool success = db_->Raze(); | 2896 bool success = db_->Raze(); |
2740 UMA_HISTOGRAM_BOOLEAN("History.KillHistoryDatabaseResult", success); | 2897 UMA_HISTOGRAM_BOOLEAN("History.KillHistoryDatabaseResult", success); |
2741 | 2898 |
2742 #if defined(OS_ANDROID) | 2899 #if defined(OS_ANDROID) |
2743 // Release AndroidProviderBackend before other objects. | 2900 // Release AndroidProviderBackend before other objects. |
2744 android_provider_backend_.reset(); | 2901 android_provider_backend_.reset(); |
2745 #endif | 2902 #endif |
2746 | 2903 |
2747 // The expirer keeps tabs on the active databases. Tell it about the | 2904 // The expirer keeps tabs on the active databases. Tell it about the |
2748 // databases which will be closed. | 2905 // databases which will be closed. |
2749 expirer_.SetDatabases(NULL, NULL, NULL); | 2906 expirer_.SetDatabases(NULL, NULL, NULL, NULL); |
2750 | 2907 |
2751 // Reopen a new transaction for |db_| for the sake of CloseAllDatabases(). | 2908 // Reopen a new transaction for |db_| for the sake of CloseAllDatabases(). |
2752 db_->BeginTransaction(); | 2909 db_->BeginTransaction(); |
2753 CloseAllDatabases(); | 2910 CloseAllDatabases(); |
2754 } | 2911 } |
2755 | 2912 |
2756 void HistoryBackend::ProcessDBTask( | 2913 void HistoryBackend::ProcessDBTask( |
2757 scoped_refptr<HistoryDBTaskRequest> request) { | 2914 scoped_refptr<HistoryDBTaskRequest> request) { |
2758 DCHECK(request.get()); | 2915 DCHECK(request.get()); |
2759 if (request->canceled()) | 2916 if (request->canceled()) |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2829 // We continue in this error case. If the user wants to delete their | 2986 // We continue in this error case. If the user wants to delete their |
2830 // history, we should delete as much as we can. | 2987 // history, we should delete as much as we can. |
2831 } | 2988 } |
2832 | 2989 |
2833 // ClearAllMainHistory will change the IDs of the URLs in kept_urls. Therfore, | 2990 // ClearAllMainHistory will change the IDs of the URLs in kept_urls. Therfore, |
2834 // we clear the list afterwards to make sure nobody uses this invalid data. | 2991 // we clear the list afterwards to make sure nobody uses this invalid data. |
2835 if (!ClearAllMainHistory(kept_urls)) | 2992 if (!ClearAllMainHistory(kept_urls)) |
2836 LOG(ERROR) << "Main history could not be cleared"; | 2993 LOG(ERROR) << "Main history could not be cleared"; |
2837 kept_urls.clear(); | 2994 kept_urls.clear(); |
2838 | 2995 |
2839 // Delete archived history. | 2996 // Delete FTS files & 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 |
2840 if (archived_db_) { | 3005 if (archived_db_) { |
2841 // Close the database and delete the file. | 3006 // Close the database and delete the file. |
2842 archived_db_.reset(); | 3007 archived_db_.reset(); |
2843 base::FilePath archived_file_name = GetArchivedFileName(); | 3008 base::FilePath archived_file_name = GetArchivedFileName(); |
2844 sql::Connection::Delete(archived_file_name); | 3009 sql::Connection::Delete(archived_file_name); |
2845 | 3010 |
2846 // Now re-initialize the database (which may fail). | 3011 // Now re-initialize the database (which may fail). |
2847 archived_db_.reset(new ArchivedDatabase()); | 3012 archived_db_.reset(new ArchivedDatabase()); |
2848 if (!archived_db_->Init(archived_file_name)) { | 3013 if (!archived_db_->Init(archived_file_name)) { |
2849 LOG(WARNING) << "Could not initialize the archived database."; | 3014 LOG(WARNING) << "Could not initialize the archived database."; |
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2999 int rank = kPageVisitStatsMaxTopSites; | 3164 int rank = kPageVisitStatsMaxTopSites; |
3000 std::map<GURL, int>::const_iterator it = most_visited_urls_map_.find(url); | 3165 std::map<GURL, int>::const_iterator it = most_visited_urls_map_.find(url); |
3001 if (it != most_visited_urls_map_.end()) | 3166 if (it != most_visited_urls_map_.end()) |
3002 rank = (*it).second; | 3167 rank = (*it).second; |
3003 UMA_HISTOGRAM_ENUMERATION("History.TopSitesVisitsByRank", | 3168 UMA_HISTOGRAM_ENUMERATION("History.TopSitesVisitsByRank", |
3004 rank, kPageVisitStatsMaxTopSites + 1); | 3169 rank, kPageVisitStatsMaxTopSites + 1); |
3005 } | 3170 } |
3006 #endif | 3171 #endif |
3007 | 3172 |
3008 } // namespace history | 3173 } // namespace history |
OLD | NEW |